0031159: Visualization - Font_FontMgr skips sub-faces within .ttc font collections
[occt.git] / src / Font / Font_BRepFont.cxx
CommitLineData
b514beda 1// Created on: 2013-09-16
d5f74e42 2// Copyright (c) 2013-2014 OPEN CASCADE SAS
b514beda 3//
973c2be1 4// This file is part of Open CASCADE Technology software library.
b514beda 5//
d5f74e42 6// This library is free software; you can redistribute it and/or modify it under
7// the terms of the GNU Lesser General Public License version 2.1 as published
973c2be1 8// by the Free Software Foundation, with special exception defined in the file
9// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
10// distribution for complete text of the license and disclaimer of any warranty.
b514beda 11//
973c2be1 12// Alternatively, this file may be used under the terms of Open CASCADE
13// commercial license or contractual agreement.
b514beda 14
15#include <Font_BRepFont.hxx>
16
17#include <BRep_Tool.hxx>
3388cf17 18#include <BRepTopAdaptor_FClass2d.hxx>
b514beda 19#include <BRepBuilderAPI_MakeFace.hxx>
20#include <BRepBuilderAPI_MakeWire.hxx>
21#include <BRepLib_MakeEdge.hxx>
f9801cf9 22#include <Font_FTLibrary.hxx>
1bbd7c79 23#include <Font_FontMgr.hxx>
ac84fcf6 24#include <Font_TextFormatter.hxx>
b514beda 25#include <GCE2d_MakeSegment.hxx>
26#include <GC_MakeSegment.hxx>
27#include <Geom_BezierCurve.hxx>
28#include <Geom_BSplineCurve.hxx>
c04c30b3 29#include <Geom2d_TrimmedCurve.hxx>
b514beda 30#include <Geom_Plane.hxx>
31#include <Geom2d_BezierCurve.hxx>
32#include <Geom2d_BSplineCurve.hxx>
543a9964 33#include <Geom2d_TrimmedCurve.hxx>
b514beda 34#include <Geom2d_Line.hxx>
35#include <GeomAPI.hxx>
36#include <GeomAdaptor_HSurface.hxx>
37#include <GeomLib.hxx>
38#include <gp_Pln.hxx>
b514beda 39#include <TColGeom2d_HSequenceOfBoundedCurve.hxx>
40#include <TCollection_AsciiString.hxx>
41#include <TCollection_HAsciiString.hxx>
42#include <TopExp.hxx>
43#include <TopExp_Explorer.hxx>
44#include <TopoDS.hxx>
45#include <TopoDS_Compound.hxx>
46#include <TopoDS_Vertex.hxx>
3388cf17 47#include <TopTools_DataMapOfShapeInteger.hxx>
48#include <TopTools_DataMapOfShapeSequenceOfShape.hxx>
b514beda 49
f9801cf9 50#include <ft2build.h>
51#include FT_FREETYPE_H
b514beda 52#include FT_OUTLINE_H
53
92efcf78 54IMPLEMENT_STANDARD_RTTIEXT(Font_BRepFont,Font_FTFont)
55
b514beda 56namespace
57{
58 // pre-defined font rendering options
59 static const unsigned int THE_FONT_SIZE = 72;
60 static const unsigned int THE_RESOLUTION_DPI = 4800;
1bbd7c79 61 static const Font_FTFontParams THE_FONT_PARAMS (THE_FONT_SIZE, THE_RESOLUTION_DPI);
b514beda 62
63 // compute scaling factor for specified font size
64 inline Standard_Real getScale (const Standard_Real theSize)
65 {
66 return theSize / Standard_Real(THE_FONT_SIZE) * 72.0 / Standard_Real(THE_RESOLUTION_DPI);
67 }
68
f9801cf9 69 //! Auxiliary method to convert FT_Vector to gp_XY
70 static gp_XY readFTVec (const FT_Vector& theVec,
151da08b 71 const Standard_Real theScaleUnits,
72 const Standard_Real theWidthScaling = 1.0)
f9801cf9 73 {
151da08b 74 return gp_XY (theScaleUnits * Standard_Real(theVec.x) * theWidthScaling / 64.0, theScaleUnits * Standard_Real(theVec.y) / 64.0);
f9801cf9 75 }
76
3388cf17 77 //! Auxiliary method for classification wire theW2 with respect to wire theW1
78 static TopAbs_State classifyWW (const TopoDS_Wire& theW1,
79 const TopoDS_Wire& theW2,
80 const TopoDS_Face& theF)
81 {
82 TopAbs_State aRes = TopAbs_UNKNOWN;
83
84 TopoDS_Face aF = TopoDS::Face (theF.EmptyCopied());
85 aF.Orientation (TopAbs_FORWARD);
86 BRep_Builder aB;
87 aB.Add (aF, theW1);
88 BRepTopAdaptor_FClass2d aClass2d (aF, ::Precision::PConfusion());
89 for (TopoDS_Iterator anEdgeIter (theW2); anEdgeIter.More(); anEdgeIter.Next())
90 {
91 const TopoDS_Edge& anEdge = TopoDS::Edge (anEdgeIter.Value());
92 Standard_Real aPFirst = 0.0, aPLast = 0.0;
93 Handle(Geom2d_Curve) aCurve2d = BRep_Tool::CurveOnSurface (anEdge, theF, aPFirst, aPLast);
94 if (aCurve2d.IsNull())
95 {
96 continue;
97 }
98
99 gp_Pnt2d aPnt2d = aCurve2d->Value ((aPFirst + aPLast) / 2.0);
100 TopAbs_State aState = aClass2d.Perform (aPnt2d, Standard_False);
101 if (aState == TopAbs_OUT
102 || aState == TopAbs_IN)
103 {
104 if (aRes == TopAbs_UNKNOWN)
105 {
106 aRes = aState;
107 }
108 else if (aRes != aState)
109 {
110 return TopAbs_UNKNOWN;
111 }
112 }
113 }
114 return aRes;
115 }
116
a3f6f591 117}
b514beda 118
119// =======================================================================
120// function : Constructor
121// purpose :
122// =======================================================================
123Font_BRepFont::Font_BRepFont ()
124: myPrecision (Precision::Confusion()),
125 myScaleUnits (1.0),
126 myIsCompositeCurve (Standard_False),
127 my3Poles (1, 3),
128 my4Poles (1, 4)
129{
130 init();
131}
132
133// =======================================================================
134// function : init
135// purpose :
136// =======================================================================
137void Font_BRepFont::init()
138{
139 mySurface = new Geom_Plane (gp_Pln (gp::XOY()));
140 myCurve2dAdaptor = new Geom2dAdaptor_HCurve();
543a9964 141 Handle(Adaptor3d_HSurface) aSurfAdaptor = new GeomAdaptor_HSurface (mySurface);
b514beda 142 myCurvOnSurf.Load (aSurfAdaptor);
b514beda 143}
144
145// =======================================================================
146// function : Constructor
147// purpose :
148// =======================================================================
149Font_BRepFont::Font_BRepFont (const NCollection_String& theFontPath,
9a90a452 150 const Standard_Real theSize,
151 const Standard_Integer theFaceId)
b514beda 152: myPrecision (Precision::Confusion()),
153 myScaleUnits (1.0),
154 myIsCompositeCurve (Standard_False),
155 my3Poles (1, 3),
156 my4Poles (1, 4)
157{
158 init();
159 if (theSize <= myPrecision * 100.0)
160 {
161 return;
162 }
163
164 myScaleUnits = getScale (theSize);
9a90a452 165 Font_FTFont::Init (theFontPath.ToCString(), THE_FONT_PARAMS, theFaceId);
b514beda 166}
167
168// =======================================================================
169// function : Constructor
170// purpose :
171// =======================================================================
172Font_BRepFont::Font_BRepFont (const NCollection_String& theFontName,
173 const Font_FontAspect theFontAspect,
1bbd7c79 174 const Standard_Real theSize,
175 const Font_StrictLevel theStrictLevel)
b514beda 176: myPrecision (Precision::Confusion()),
177 myScaleUnits (1.0),
178 myIsCompositeCurve (Standard_False),
179 my3Poles (1, 3),
180 my4Poles (1, 4)
181{
182 init();
183 if (theSize <= myPrecision * 100.0)
184 {
185 return;
186 }
187
188 myScaleUnits = getScale (theSize);
1bbd7c79 189 Font_FTFont::FindAndInit (theFontName.ToCString(), theFontAspect, THE_FONT_PARAMS, theStrictLevel);
b514beda 190}
191
192// =======================================================================
193// function : Release
194// purpose :
195// =======================================================================
196void Font_BRepFont::Release()
197{
198 myCache.Clear();
199 Font_FTFont::Release();
200}
201
202// =======================================================================
203// function : SetCompositeCurveMode
204// purpose :
205// =======================================================================
206void Font_BRepFont::SetCompositeCurveMode (const Standard_Boolean theToConcatenate)
207{
208 if (myIsCompositeCurve != theToConcatenate)
209 {
210 myIsCompositeCurve = theToConcatenate;
211 myCache.Clear();
212 }
213}
214
215// =======================================================================
216// function : Init
217// purpose :
218// =======================================================================
219bool Font_BRepFont::Init (const NCollection_String& theFontPath,
9a90a452 220 const Standard_Real theSize,
221 const Standard_Integer theFaceId)
b514beda 222{
223 if (theSize <= myPrecision * 100.0)
224 {
225 return false;
226 }
227
228 myScaleUnits = getScale (theSize);
9a90a452 229 return Font_FTFont::Init (theFontPath.ToCString(), THE_FONT_PARAMS, theFaceId);
b514beda 230}
231
232// =======================================================================
1bbd7c79 233// function : FindAndInit
b514beda 234// purpose :
235// =======================================================================
1bbd7c79 236bool Font_BRepFont::FindAndInit (const TCollection_AsciiString& theFontName,
237 const Font_FontAspect theFontAspect,
238 const Standard_Real theSize,
239 const Font_StrictLevel theStrictLevel)
b514beda 240{
241 if (theSize <= myPrecision * 100.0)
242 {
243 return false;
244 }
245
246 myScaleUnits = getScale (theSize);
1bbd7c79 247 return Font_FTFont::FindAndInit (theFontName.ToCString(), theFontAspect, THE_FONT_PARAMS, theStrictLevel);
b514beda 248}
249
250// =======================================================================
251// function : RenderGlyph
252// purpose :
253// =======================================================================
254TopoDS_Shape Font_BRepFont::RenderGlyph (const Standard_Utf32Char& theChar)
255{
256 TopoDS_Shape aShape;
257 Standard_Mutex::Sentry aSentry (myMutex);
258 renderGlyph (theChar, aShape);
259 return aShape;
260}
261
262// =======================================================================
263// function : to3d
264// purpose :
265// =======================================================================
543a9964 266bool Font_BRepFont::to3d (const Handle(Geom2d_Curve)& theCurve2d,
b514beda 267 const GeomAbs_Shape theContinuity,
268 Handle(Geom_Curve)& theCurve3d)
269{
270 Standard_Real aMaxDeviation = 0.0;
271 Standard_Real anAverDeviation = 0.0;
272 myCurve2dAdaptor->ChangeCurve2d().Load (theCurve2d);
543a9964 273 const Handle(Adaptor2d_HCurve2d)& aCurve = myCurve2dAdaptor; // to avoid ambiguity
274 myCurvOnSurf.Load (aCurve);
b514beda 275 GeomLib::BuildCurve3d (myPrecision, myCurvOnSurf,
276 myCurve2dAdaptor->FirstParameter(), myCurve2dAdaptor->LastParameter(),
277 theCurve3d, aMaxDeviation, anAverDeviation, theContinuity);
278 return !theCurve3d.IsNull();
279}
280
3388cf17 281
282// =======================================================================
283// function : buildFaces
284// purpose :
285// =======================================================================
286Standard_Boolean Font_BRepFont::buildFaces (const NCollection_Sequence<TopoDS_Wire>& theWires,
287 TopoDS_Shape& theRes)
288{
289 // classify wires
290 NCollection_DataMap<TopoDS_Shape, NCollection_Sequence<TopoDS_Wire>, TopTools_ShapeMapHasher> aMapOutInts;
291 TopTools_DataMapOfShapeInteger aMapNbOuts;
292 TopoDS_Face aF;
293 myBuilder.MakeFace (aF, mySurface, myPrecision);
294 Standard_Integer aWireIter1Index = 1;
295 for (NCollection_Sequence<TopoDS_Wire>::Iterator aWireIter1 (theWires); aWireIter1.More(); ++aWireIter1Index, aWireIter1.Next())
296 {
297 const TopoDS_Wire& aW1 = aWireIter1.Value();
298 if (!aMapNbOuts.IsBound (aW1))
299 {
300 const Standard_Integer aNbOuts = 0;
301 aMapNbOuts.Bind (aW1, aNbOuts);
302 }
303
304 NCollection_Sequence<TopoDS_Wire>* anIntWs = aMapOutInts.Bound (aW1, NCollection_Sequence<TopoDS_Wire>());
305 Standard_Integer aWireIter2Index = 1;
306 for (NCollection_Sequence<TopoDS_Wire>::Iterator aWireIter2 (theWires); aWireIter2.More(); ++aWireIter2Index, aWireIter2.Next())
307 {
308 if (aWireIter1Index == aWireIter2Index)
309 {
310 continue;
311 }
312
313 const TopoDS_Wire& aW2 = aWireIter2.Value();
314 const TopAbs_State aClass = classifyWW (aW1, aW2, aF);
315 if (aClass == TopAbs_IN)
316 {
317 anIntWs->Append (aW2);
318 if (Standard_Integer* aNbOutsPtr = aMapNbOuts.ChangeSeek (aW2))
319 {
320 ++(*aNbOutsPtr);
321 }
322 else
323 {
324 const Standard_Integer aNbOuts = 1;
325 aMapNbOuts.Bind (aW2, aNbOuts);
326 }
327 }
328 }
329 }
330
331 // check out wires and remove "not out" wires from maps
332 for (TopTools_DataMapIteratorOfDataMapOfShapeInteger anOutIter (aMapNbOuts); anOutIter.More(); anOutIter.Next())
333 {
334 const Standard_Integer aTmp = anOutIter.Value() % 2;
335 if (aTmp > 0)
336 {
337 // not out wire
338 aMapOutInts.UnBind (anOutIter.Key());
339 }
340 }
341
342 // create faces for out wires
343 TopTools_MapOfShape anUsedShapes;
344 TopoDS_Compound aFaceComp;
345 myBuilder.MakeCompound (aFaceComp);
346 for (; !aMapOutInts.IsEmpty(); )
347 {
348 // find out wire with max number of outs
349 TopoDS_Shape aW;
350 Standard_Integer aMaxNbOuts = -1;
351 for (NCollection_DataMap<TopoDS_Shape, NCollection_Sequence<TopoDS_Wire>, TopTools_ShapeMapHasher>::Iterator itMOI (aMapOutInts);
352 itMOI.More(); itMOI.Next())
353 {
354 const TopoDS_Shape& aKey = itMOI.Key();
355 const Standard_Integer aNbOuts = aMapNbOuts.Find (aKey);
356 if (aNbOuts > aMaxNbOuts)
357 {
358 aMaxNbOuts = aNbOuts;
359 aW = aKey;
360 }
361 }
362
363 // create face for selected wire
364 TopoDS_Face aNewF;
365 myBuilder.MakeFace (aNewF, mySurface, myPrecision);
366 myBuilder.Add (aNewF, aW);
367 anUsedShapes.Add (aW);
368 const NCollection_Sequence<TopoDS_Wire>& anIns = aMapOutInts.Find (aW);
369 for (NCollection_Sequence<TopoDS_Wire>::Iterator aWireIter (anIns); aWireIter.More(); aWireIter.Next())
370 {
371 TopoDS_Wire aWin = aWireIter.Value();
372 if (anUsedShapes.Contains (aWin))
373 {
374 continue;
375 }
376
377 aWin.Reverse();
378 myBuilder.Add (aNewF, aWin);
379 anUsedShapes.Add (aWin);
380 }
381
382 myBuilder.Add (aFaceComp, aNewF);
383 aMapOutInts.UnBind (aW);
384 }
385
386 if (aFaceComp.NbChildren() == 0)
387 {
388 return Standard_False;
389 }
390
391 if (aFaceComp.NbChildren() == 1)
392 {
393 theRes = TopoDS_Iterator (aFaceComp).Value();
394 }
395 else
396 {
397 theRes = aFaceComp;
398 }
399 return Standard_True;
400}
401
402
b514beda 403// =======================================================================
404// function : renderGlyph
405// purpose :
406// =======================================================================
407Standard_Boolean Font_BRepFont::renderGlyph (const Standard_Utf32Char theChar,
408 TopoDS_Shape& theShape)
409{
410 theShape.Nullify();
411 if (!loadGlyph (theChar)
912761ea 412 || myActiveFTFace->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
b514beda 413 {
414 return Standard_False;
415 }
416 else if (myCache.Find (theChar, theShape))
417 {
418 return !theShape.IsNull();
419 }
420
912761ea 421 const FT_Outline& anOutline = myActiveFTFace->glyph->outline;
be7d4aa2 422 if (!anOutline.n_contours)
423 return Standard_False;
424
b514beda 425 TopLoc_Location aLoc;
3388cf17 426 NCollection_Sequence<TopoDS_Wire> aWires;
e4f0cc46 427 TopoDS_Compound aFaceCompDraft;
be7d4aa2 428
b514beda 429 // Get orientation is useless since it doesn't retrieve any in-font information and just computes orientation.
430 // Because it fails in some cases - leave this to ShapeFix.
431 //const FT_Orientation anOrient = FT_Outline_Get_Orientation (&anOutline);
432 for (short aContour = 0, aStartIndex = 0; aContour < anOutline.n_contours; ++aContour)
433 {
434 const FT_Vector* aPntList = &anOutline.points[aStartIndex];
435 const char* aTags = &anOutline.tags[aStartIndex];
436 const short anEndIndex = anOutline.contours[aContour];
437 const short aPntsNb = (anEndIndex - aStartIndex) + 1;
438 aStartIndex = anEndIndex + 1;
1bbd7c79 439 if (aPntsNb < 3 && !myFontParams.IsSingleStrokeFont)
b514beda 440 {
441 // closed contour can not be constructed from < 3 points
442 continue;
443 }
444
445 BRepBuilderAPI_MakeWire aWireMaker;
446
447 gp_XY aPntPrev;
151da08b 448 gp_XY aPntCurr = readFTVec (aPntList[aPntsNb - 1], myScaleUnits, myWidthScaling);
449 gp_XY aPntNext = readFTVec (aPntList[0], myScaleUnits, myWidthScaling);
b514beda 450
1bbd7c79 451 bool isLineSeg = !myFontParams.IsSingleStrokeFont
e4f0cc46 452 && FT_CURVE_TAG(aTags[aPntsNb - 1]) == FT_Curve_Tag_On;
b514beda 453 gp_XY aPntLine1 = aPntCurr;
454
455 // see http://freetype.sourceforge.net/freetype2/docs/glyphs/glyphs-6.html
456 // for a full description of FreeType tags.
457 for (short aPntId = 0; aPntId < aPntsNb; ++aPntId)
458 {
459 aPntPrev = aPntCurr;
460 aPntCurr = aPntNext;
151da08b 461 aPntNext = readFTVec (aPntList[(aPntId + 1) % aPntsNb], myScaleUnits, myWidthScaling);
b514beda 462
463 // process tags
464 if (FT_CURVE_TAG(aTags[aPntId]) == FT_Curve_Tag_On)
465 {
e4f0cc46 466 if (!isLineSeg)
b514beda 467 {
468 aPntLine1 = aPntCurr;
e4f0cc46 469 isLineSeg = true;
b514beda 470 continue;
471 }
472
473 const gp_XY aDirVec = aPntCurr - aPntLine1;
474 const Standard_Real aLen = aDirVec.Modulus();
475 if (aLen <= myPrecision)
476 {
477 aPntLine1 = aPntCurr;
e4f0cc46 478 isLineSeg = true;
b514beda 479 continue;
480 }
481
482 if (myIsCompositeCurve)
483 {
484 Handle(Geom2d_TrimmedCurve) aLine = GCE2d_MakeSegment (gp_Pnt2d (aPntLine1), gp_Pnt2d (aPntCurr));
485 myConcatMaker.Add (aLine, myPrecision);
486 }
487 else
488 {
489 Handle(Geom_Curve) aCurve3d;
490 Handle(Geom2d_Line) aCurve2d = new Geom2d_Line (gp_Pnt2d (aPntLine1), gp_Dir2d (aDirVec));
491 if (to3d (aCurve2d, GeomAbs_C1, aCurve3d))
492 {
493 TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d, 0.0, aLen);
494 myBuilder.UpdateEdge (anEdge, aCurve2d, mySurface, aLoc, myPrecision);
495 aWireMaker.Add (anEdge);
496 }
497 }
498 aPntLine1 = aPntCurr;
499 }
500 else if (FT_CURVE_TAG(aTags[aPntId]) == FT_Curve_Tag_Conic)
501 {
e4f0cc46 502 isLineSeg = false;
b514beda 503 gp_XY aPntPrev2 = aPntPrev;
504 gp_XY aPntNext2 = aPntNext;
505
506 // previous point is either the real previous point (an "on" point),
507 // or the midpoint between the current one and the previous "conic off" point
508 if (FT_CURVE_TAG(aTags[(aPntId - 1 + aPntsNb) % aPntsNb]) == FT_Curve_Tag_Conic)
509 {
510 aPntPrev2 = (aPntCurr + aPntPrev) * 0.5;
511 }
512
513 // next point is either the real next point or the midpoint
514 if (FT_CURVE_TAG(aTags[(aPntId + 1) % aPntsNb]) == FT_Curve_Tag_Conic)
515 {
516 aPntNext2 = (aPntCurr + aPntNext) * 0.5;
517 }
518
519 my3Poles.SetValue (1, aPntPrev2);
520 my3Poles.SetValue (2, aPntCurr);
521 my3Poles.SetValue (3, aPntNext2);
522 Handle(Geom2d_BezierCurve) aBezierArc = new Geom2d_BezierCurve (my3Poles);
523 if (myIsCompositeCurve)
524 {
525 myConcatMaker.Add (aBezierArc, myPrecision);
526 }
527 else
528 {
529 Handle(Geom_Curve) aCurve3d;
530 if (to3d (aBezierArc, GeomAbs_C1, aCurve3d))
531 {
532 TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d);
533 myBuilder.UpdateEdge (anEdge, aBezierArc, mySurface, aLoc, myPrecision);
534 aWireMaker.Add (anEdge);
535 }
536 }
537 }
538 else if (FT_CURVE_TAG(aTags[aPntId]) == FT_Curve_Tag_Cubic
539 && FT_CURVE_TAG(aTags[(aPntId + 1) % aPntsNb]) == FT_Curve_Tag_Cubic)
540 {
e4f0cc46 541 isLineSeg = false;
b514beda 542 my4Poles.SetValue (1, aPntPrev);
543 my4Poles.SetValue (2, aPntCurr);
544 my4Poles.SetValue (3, aPntNext);
151da08b 545 my4Poles.SetValue (4, gp_Pnt2d(readFTVec (aPntList[(aPntId + 2) % aPntsNb], myScaleUnits, myWidthScaling)));
b514beda 546 Handle(Geom2d_BezierCurve) aBezier = new Geom2d_BezierCurve (my4Poles);
547 if (myIsCompositeCurve)
548 {
549 myConcatMaker.Add (aBezier, myPrecision);
550 }
551 else
552 {
553 Handle(Geom_Curve) aCurve3d;
554 if (to3d (aBezier, GeomAbs_C1, aCurve3d))
555 {
556 TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d);
557 myBuilder.UpdateEdge (anEdge, aBezier, mySurface, aLoc, myPrecision);
558 aWireMaker.Add (anEdge);
559 }
560 }
561 }
562 }
563
564 if (myIsCompositeCurve)
565 {
566 Handle(Geom2d_BSplineCurve) aDraft2d = myConcatMaker.BSplineCurve();
567 if (aDraft2d.IsNull())
568 {
569 continue;
570 }
571
572 const gp_Pnt2d aFirstPnt = aDraft2d->StartPoint();
573 const gp_Pnt2d aLastPnt = aDraft2d->EndPoint();
1bbd7c79 574 if (!myFontParams.IsSingleStrokeFont
e4f0cc46 575 && !aFirstPnt.IsEqual (aLastPnt, myPrecision))
b514beda 576 {
577 Handle(Geom2d_TrimmedCurve) aLine = GCE2d_MakeSegment (aLastPnt, aFirstPnt);
578 myConcatMaker.Add (aLine, myPrecision);
579 }
580
581 Handle(Geom2d_BSplineCurve) aCurve2d = myConcatMaker.BSplineCurve();
582 Handle(Geom_Curve) aCurve3d;
583 if (to3d (aCurve2d, GeomAbs_C0, aCurve3d))
584 {
585 TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d);
586 myBuilder.UpdateEdge (anEdge, aCurve2d, mySurface, aLoc, myPrecision);
587 aWireMaker.Add (anEdge);
588 }
589 myConcatMaker.Clear();
590 }
591 else
592 {
593 if (!aWireMaker.IsDone())
594 {
595 continue;
596 }
597
598 TopoDS_Vertex aFirstV, aLastV;
599 TopExp::Vertices (aWireMaker.Wire(), aFirstV, aLastV);
600 gp_Pnt aFirstPoint = BRep_Tool::Pnt (aFirstV);
601 gp_Pnt aLastPoint = BRep_Tool::Pnt (aLastV);
1bbd7c79 602 if (!myFontParams.IsSingleStrokeFont
e4f0cc46 603 && !aFirstPoint.IsEqual (aLastPoint, myPrecision))
b514beda 604 {
605 aWireMaker.Add (BRepLib_MakeEdge (aFirstV, aLastV));
606 }
607 }
608
609 if (!aWireMaker.IsDone())
610 {
611 continue;
612 }
613
614 TopoDS_Wire aWireDraft = aWireMaker.Wire();
1bbd7c79 615 if (!myFontParams.IsSingleStrokeFont)
e4f0cc46 616 {
3388cf17 617 // collect all wires and set CCW orientation
618 TopoDS_Face aFace;
619 myBuilder.MakeFace (aFace, mySurface, myPrecision);
620 myBuilder.Add (aFace, aWireDraft);
621 BRepTopAdaptor_FClass2d aClass2d (aFace, ::Precision::PConfusion());
622 TopAbs_State aState = aClass2d.PerformInfinitePoint();
623 if (aState != TopAbs_OUT)
e4f0cc46 624 {
3388cf17 625 // need to reverse
626 aWireDraft.Reverse();
e4f0cc46 627 }
3388cf17 628 aWires.Append (aWireDraft);
e4f0cc46 629 }
630 else
631 {
632 if (aFaceCompDraft.IsNull())
633 {
634 myBuilder.MakeCompound (aFaceCompDraft);
635 }
636 myBuilder.Add (aFaceCompDraft, aWireDraft);
637 }
b514beda 638 }
639
3388cf17 640 if (!aWires.IsEmpty())
b514beda 641 {
3388cf17 642 buildFaces (aWires, theShape);
e4f0cc46 643 }
644 else if (!aFaceCompDraft.IsNull())
645 {
646 theShape = aFaceCompDraft;
b514beda 647 }
648
649 myCache.Bind (theChar, theShape);
650 return !theShape.IsNull();
651}