1 // Created on: 2013-09-16
2 // Copyright (c) 2013-2014 OPEN CASCADE SAS
4 // This file is part of Open CASCADE Technology software library.
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
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.
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
15 #include <Font_BRepFont.hxx>
17 #include <BRep_Tool.hxx>
18 #include <BRepTopAdaptor_FClass2d.hxx>
19 #include <BRepBuilderAPI_MakeFace.hxx>
20 #include <BRepBuilderAPI_MakeWire.hxx>
21 #include <BRepLib_MakeEdge.hxx>
22 #include <Font_FTLibrary.hxx>
23 #include <Font_FontMgr.hxx>
24 #include <Font_TextFormatter.hxx>
25 #include <GCE2d_MakeSegment.hxx>
26 #include <GC_MakeSegment.hxx>
27 #include <Geom_BezierCurve.hxx>
28 #include <Geom_BSplineCurve.hxx>
29 #include <Geom2d_TrimmedCurve.hxx>
30 #include <Geom_Plane.hxx>
31 #include <Geom2d_BezierCurve.hxx>
32 #include <Geom2d_BSplineCurve.hxx>
33 #include <Geom2d_TrimmedCurve.hxx>
34 #include <Geom2d_Line.hxx>
35 #include <GeomAPI.hxx>
36 #include <GeomAdaptor_HSurface.hxx>
37 #include <GeomLib.hxx>
39 #include <TColGeom2d_HSequenceOfBoundedCurve.hxx>
40 #include <TCollection_AsciiString.hxx>
41 #include <TCollection_HAsciiString.hxx>
43 #include <TopExp_Explorer.hxx>
45 #include <TopoDS_Compound.hxx>
46 #include <TopoDS_Vertex.hxx>
47 #include <TopTools_DataMapOfShapeInteger.hxx>
48 #include <TopTools_DataMapOfShapeSequenceOfShape.hxx>
51 #include FT_FREETYPE_H
54 IMPLEMENT_STANDARD_RTTIEXT(Font_BRepFont,Font_FTFont)
58 // pre-defined font rendering options
59 static const unsigned int THE_FONT_SIZE = 72;
60 static const unsigned int THE_RESOLUTION_DPI = 4800;
61 static const Font_FTFontParams THE_FONT_PARAMS (THE_FONT_SIZE, THE_RESOLUTION_DPI);
63 // compute scaling factor for specified font size
64 inline Standard_Real getScale (const Standard_Real theSize)
66 return theSize / Standard_Real(THE_FONT_SIZE) * 72.0 / Standard_Real(THE_RESOLUTION_DPI);
69 //! Auxiliary method to convert FT_Vector to gp_XY
70 static gp_XY readFTVec (const FT_Vector& theVec,
71 const Standard_Real theScaleUnits,
72 const Standard_Real theWidthScaling = 1.0)
74 return gp_XY (theScaleUnits * Standard_Real(theVec.x) * theWidthScaling / 64.0, theScaleUnits * Standard_Real(theVec.y) / 64.0);
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)
82 TopAbs_State aRes = TopAbs_UNKNOWN;
84 TopoDS_Face aF = TopoDS::Face (theF.EmptyCopied());
85 aF.Orientation (TopAbs_FORWARD);
88 BRepTopAdaptor_FClass2d aClass2d (aF, ::Precision::PConfusion());
89 for (TopoDS_Iterator anEdgeIter (theW2); anEdgeIter.More(); anEdgeIter.Next())
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())
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)
104 if (aRes == TopAbs_UNKNOWN)
108 else if (aRes != aState)
110 return TopAbs_UNKNOWN;
119 // =======================================================================
120 // function : Constructor
122 // =======================================================================
123 Font_BRepFont::Font_BRepFont ()
124 : myPrecision (Precision::Confusion()),
126 myIsCompositeCurve (Standard_False),
133 // =======================================================================
136 // =======================================================================
137 void Font_BRepFont::init()
139 mySurface = new Geom_Plane (gp_Pln (gp::XOY()));
140 myCurve2dAdaptor = new Geom2dAdaptor_HCurve();
141 Handle(Adaptor3d_HSurface) aSurfAdaptor = new GeomAdaptor_HSurface (mySurface);
142 myCurvOnSurf.Load (aSurfAdaptor);
145 // =======================================================================
146 // function : Constructor
148 // =======================================================================
149 Font_BRepFont::Font_BRepFont (const NCollection_String& theFontPath,
150 const Standard_Real theSize,
151 const Standard_Integer theFaceId)
152 : myPrecision (Precision::Confusion()),
154 myIsCompositeCurve (Standard_False),
159 if (theSize <= myPrecision * 100.0)
164 myScaleUnits = getScale (theSize);
165 Font_FTFont::Init (theFontPath.ToCString(), THE_FONT_PARAMS, theFaceId);
168 // =======================================================================
169 // function : Constructor
171 // =======================================================================
172 Font_BRepFont::Font_BRepFont (const NCollection_String& theFontName,
173 const Font_FontAspect theFontAspect,
174 const Standard_Real theSize,
175 const Font_StrictLevel theStrictLevel)
176 : myPrecision (Precision::Confusion()),
178 myIsCompositeCurve (Standard_False),
183 if (theSize <= myPrecision * 100.0)
188 myScaleUnits = getScale (theSize);
189 Font_FTFont::FindAndInit (theFontName.ToCString(), theFontAspect, THE_FONT_PARAMS, theStrictLevel);
192 // =======================================================================
193 // function : Release
195 // =======================================================================
196 void Font_BRepFont::Release()
199 Font_FTFont::Release();
202 // =======================================================================
203 // function : SetCompositeCurveMode
205 // =======================================================================
206 void Font_BRepFont::SetCompositeCurveMode (const Standard_Boolean theToConcatenate)
208 if (myIsCompositeCurve != theToConcatenate)
210 myIsCompositeCurve = theToConcatenate;
215 // =======================================================================
218 // =======================================================================
219 bool Font_BRepFont::Init (const NCollection_String& theFontPath,
220 const Standard_Real theSize,
221 const Standard_Integer theFaceId)
223 if (theSize <= myPrecision * 100.0)
228 myScaleUnits = getScale (theSize);
229 return Font_FTFont::Init (theFontPath.ToCString(), THE_FONT_PARAMS, theFaceId);
232 // =======================================================================
233 // function : FindAndInit
235 // =======================================================================
236 bool Font_BRepFont::FindAndInit (const TCollection_AsciiString& theFontName,
237 const Font_FontAspect theFontAspect,
238 const Standard_Real theSize,
239 const Font_StrictLevel theStrictLevel)
241 if (theSize <= myPrecision * 100.0)
246 myScaleUnits = getScale (theSize);
247 return Font_FTFont::FindAndInit (theFontName.ToCString(), theFontAspect, THE_FONT_PARAMS, theStrictLevel);
250 // =======================================================================
251 // function : RenderGlyph
253 // =======================================================================
254 TopoDS_Shape Font_BRepFont::RenderGlyph (const Standard_Utf32Char& theChar)
257 Standard_Mutex::Sentry aSentry (myMutex);
258 renderGlyph (theChar, aShape);
262 // =======================================================================
265 // =======================================================================
266 bool Font_BRepFont::to3d (const Handle(Geom2d_Curve)& theCurve2d,
267 const GeomAbs_Shape theContinuity,
268 Handle(Geom_Curve)& theCurve3d)
270 Standard_Real aMaxDeviation = 0.0;
271 Standard_Real anAverDeviation = 0.0;
272 myCurve2dAdaptor->ChangeCurve2d().Load (theCurve2d);
273 const Handle(Adaptor2d_HCurve2d)& aCurve = myCurve2dAdaptor; // to avoid ambiguity
274 myCurvOnSurf.Load (aCurve);
275 GeomLib::BuildCurve3d (myPrecision, myCurvOnSurf,
276 myCurve2dAdaptor->FirstParameter(), myCurve2dAdaptor->LastParameter(),
277 theCurve3d, aMaxDeviation, anAverDeviation, theContinuity);
278 return !theCurve3d.IsNull();
282 // =======================================================================
283 // function : buildFaces
285 // =======================================================================
286 Standard_Boolean Font_BRepFont::buildFaces (const NCollection_Sequence<TopoDS_Wire>& theWires,
287 TopoDS_Shape& theRes)
290 NCollection_DataMap<TopoDS_Shape, NCollection_Sequence<TopoDS_Wire>, TopTools_ShapeMapHasher> aMapOutInts;
291 TopTools_DataMapOfShapeInteger aMapNbOuts;
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())
297 const TopoDS_Wire& aW1 = aWireIter1.Value();
298 if (!aMapNbOuts.IsBound (aW1))
300 const Standard_Integer aNbOuts = 0;
301 aMapNbOuts.Bind (aW1, aNbOuts);
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())
308 if (aWireIter1Index == aWireIter2Index)
313 const TopoDS_Wire& aW2 = aWireIter2.Value();
314 const TopAbs_State aClass = classifyWW (aW1, aW2, aF);
315 if (aClass == TopAbs_IN)
317 anIntWs->Append (aW2);
318 if (Standard_Integer* aNbOutsPtr = aMapNbOuts.ChangeSeek (aW2))
324 const Standard_Integer aNbOuts = 1;
325 aMapNbOuts.Bind (aW2, aNbOuts);
331 // check out wires and remove "not out" wires from maps
332 for (TopTools_DataMapIteratorOfDataMapOfShapeInteger anOutIter (aMapNbOuts); anOutIter.More(); anOutIter.Next())
334 const Standard_Integer aTmp = anOutIter.Value() % 2;
338 aMapOutInts.UnBind (anOutIter.Key());
342 // create faces for out wires
343 TopTools_MapOfShape anUsedShapes;
344 TopoDS_Compound aFaceComp;
345 myBuilder.MakeCompound (aFaceComp);
346 for (; !aMapOutInts.IsEmpty(); )
348 // find out wire with max number of outs
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())
354 const TopoDS_Shape& aKey = itMOI.Key();
355 const Standard_Integer aNbOuts = aMapNbOuts.Find (aKey);
356 if (aNbOuts > aMaxNbOuts)
358 aMaxNbOuts = aNbOuts;
363 // create face for selected wire
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())
371 TopoDS_Wire aWin = aWireIter.Value();
372 if (anUsedShapes.Contains (aWin))
378 myBuilder.Add (aNewF, aWin);
379 anUsedShapes.Add (aWin);
382 myBuilder.Add (aFaceComp, aNewF);
383 aMapOutInts.UnBind (aW);
386 if (aFaceComp.NbChildren() == 0)
388 return Standard_False;
391 if (aFaceComp.NbChildren() == 1)
393 theRes = TopoDS_Iterator (aFaceComp).Value();
399 return Standard_True;
403 // =======================================================================
404 // function : renderGlyph
406 // =======================================================================
407 Standard_Boolean Font_BRepFont::renderGlyph (const Standard_Utf32Char theChar,
408 TopoDS_Shape& theShape)
411 if (!loadGlyph (theChar)
412 || myActiveFTFace->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
414 return Standard_False;
416 else if (myCache.Find (theChar, theShape))
418 return !theShape.IsNull();
421 const FT_Outline& anOutline = myActiveFTFace->glyph->outline;
422 if (!anOutline.n_contours)
423 return Standard_False;
425 TopLoc_Location aLoc;
426 NCollection_Sequence<TopoDS_Wire> aWires;
427 TopoDS_Compound aFaceCompDraft;
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)
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;
439 if (aPntsNb < 3 && !myFontParams.IsSingleStrokeFont)
441 // closed contour can not be constructed from < 3 points
445 BRepBuilderAPI_MakeWire aWireMaker;
448 gp_XY aPntCurr = readFTVec (aPntList[aPntsNb - 1], myScaleUnits, myWidthScaling);
449 gp_XY aPntNext = readFTVec (aPntList[0], myScaleUnits, myWidthScaling);
451 bool isLineSeg = !myFontParams.IsSingleStrokeFont
452 && FT_CURVE_TAG(aTags[aPntsNb - 1]) == FT_Curve_Tag_On;
453 gp_XY aPntLine1 = aPntCurr;
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)
461 aPntNext = readFTVec (aPntList[(aPntId + 1) % aPntsNb], myScaleUnits, myWidthScaling);
464 if (FT_CURVE_TAG(aTags[aPntId]) == FT_Curve_Tag_On)
468 aPntLine1 = aPntCurr;
473 const gp_XY aDirVec = aPntCurr - aPntLine1;
474 const Standard_Real aLen = aDirVec.Modulus();
475 if (aLen <= myPrecision)
477 aPntLine1 = aPntCurr;
482 if (myIsCompositeCurve)
484 Handle(Geom2d_TrimmedCurve) aLine = GCE2d_MakeSegment (gp_Pnt2d (aPntLine1), gp_Pnt2d (aPntCurr));
485 myConcatMaker.Add (aLine, myPrecision);
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))
493 TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d, 0.0, aLen);
494 myBuilder.UpdateEdge (anEdge, aCurve2d, mySurface, aLoc, myPrecision);
495 aWireMaker.Add (anEdge);
498 aPntLine1 = aPntCurr;
500 else if (FT_CURVE_TAG(aTags[aPntId]) == FT_Curve_Tag_Conic)
503 gp_XY aPntPrev2 = aPntPrev;
504 gp_XY aPntNext2 = aPntNext;
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)
510 aPntPrev2 = (aPntCurr + aPntPrev) * 0.5;
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)
516 aPntNext2 = (aPntCurr + aPntNext) * 0.5;
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)
525 myConcatMaker.Add (aBezierArc, myPrecision);
529 Handle(Geom_Curve) aCurve3d;
530 if (to3d (aBezierArc, GeomAbs_C1, aCurve3d))
532 TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d);
533 myBuilder.UpdateEdge (anEdge, aBezierArc, mySurface, aLoc, myPrecision);
534 aWireMaker.Add (anEdge);
538 else if (FT_CURVE_TAG(aTags[aPntId]) == FT_Curve_Tag_Cubic
539 && FT_CURVE_TAG(aTags[(aPntId + 1) % aPntsNb]) == FT_Curve_Tag_Cubic)
542 my4Poles.SetValue (1, aPntPrev);
543 my4Poles.SetValue (2, aPntCurr);
544 my4Poles.SetValue (3, aPntNext);
545 my4Poles.SetValue (4, gp_Pnt2d(readFTVec (aPntList[(aPntId + 2) % aPntsNb], myScaleUnits, myWidthScaling)));
546 Handle(Geom2d_BezierCurve) aBezier = new Geom2d_BezierCurve (my4Poles);
547 if (myIsCompositeCurve)
549 myConcatMaker.Add (aBezier, myPrecision);
553 Handle(Geom_Curve) aCurve3d;
554 if (to3d (aBezier, GeomAbs_C1, aCurve3d))
556 TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d);
557 myBuilder.UpdateEdge (anEdge, aBezier, mySurface, aLoc, myPrecision);
558 aWireMaker.Add (anEdge);
564 if (myIsCompositeCurve)
566 Handle(Geom2d_BSplineCurve) aDraft2d = myConcatMaker.BSplineCurve();
567 if (aDraft2d.IsNull())
572 const gp_Pnt2d aFirstPnt = aDraft2d->StartPoint();
573 const gp_Pnt2d aLastPnt = aDraft2d->EndPoint();
574 if (!myFontParams.IsSingleStrokeFont
575 && !aFirstPnt.IsEqual (aLastPnt, myPrecision))
577 Handle(Geom2d_TrimmedCurve) aLine = GCE2d_MakeSegment (aLastPnt, aFirstPnt);
578 myConcatMaker.Add (aLine, myPrecision);
581 Handle(Geom2d_BSplineCurve) aCurve2d = myConcatMaker.BSplineCurve();
582 Handle(Geom_Curve) aCurve3d;
583 if (to3d (aCurve2d, GeomAbs_C0, aCurve3d))
585 TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d);
586 myBuilder.UpdateEdge (anEdge, aCurve2d, mySurface, aLoc, myPrecision);
587 aWireMaker.Add (anEdge);
589 myConcatMaker.Clear();
593 if (!aWireMaker.IsDone())
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);
602 if (!myFontParams.IsSingleStrokeFont
603 && !aFirstPoint.IsEqual (aLastPoint, myPrecision))
605 aWireMaker.Add (BRepLib_MakeEdge (aFirstV, aLastV));
609 if (!aWireMaker.IsDone())
614 TopoDS_Wire aWireDraft = aWireMaker.Wire();
615 if (!myFontParams.IsSingleStrokeFont)
617 // collect all wires and set CCW orientation
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)
626 aWireDraft.Reverse();
628 aWires.Append (aWireDraft);
632 if (aFaceCompDraft.IsNull())
634 myBuilder.MakeCompound (aFaceCompDraft);
636 myBuilder.Add (aFaceCompDraft, aWireDraft);
640 if (!aWires.IsEmpty())
642 buildFaces (aWires, theShape);
644 else if (!aFaceCompDraft.IsNull())
646 theShape = aFaceCompDraft;
649 myCache.Bind (theChar, theShape);
650 return !theShape.IsNull();