d25b1557b86a97c451739e301a5d5e64fec884f5
[occt.git] / src / Font / Font_BRepFont.cxx
1 // Created on: 2013-09-16
2 // Copyright (c) 2013-2014 OPEN CASCADE SAS
3 //
4 // This file is part of Open CASCADE Technology software library.
5 //
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.
11 //
12 // Alternatively, this file may be used under the terms of Open CASCADE
13 // commercial license or contractual agreement.
14
15 #include <Font_BRepFont.hxx>
16
17 #include <BRep_Tool.hxx>
18 #include <BRepBuilderAPI_MakeFace.hxx>
19 #include <BRepBuilderAPI_MakeWire.hxx>
20 #include <BRepLib_MakeEdge.hxx>
21 #include <GCE2d_MakeSegment.hxx>
22 #include <GC_MakeSegment.hxx>
23 #include <Geom_BezierCurve.hxx>
24 #include <Geom_BSplineCurve.hxx>
25 #include <Geom2d_TrimmedCurve.hxx>
26 #include <Geom_Plane.hxx>
27 #include <Geom2d_BezierCurve.hxx>
28 #include <Geom2d_BSplineCurve.hxx>
29 #include <Geom2d_Line.hxx>
30 #include <GeomAPI.hxx>
31 #include <GeomAdaptor_HSurface.hxx>
32 #include <GeomLib.hxx>
33 #include <gp_Pln.hxx>
34 #include <ShapeBuild_ReShape.hxx>
35 #include <ShapeFix_Edge.hxx>
36 #include <ShapeFix_Wire.hxx>
37 #include <TColGeom2d_HSequenceOfBoundedCurve.hxx>
38 #include <TCollection_AsciiString.hxx>
39 #include <TCollection_HAsciiString.hxx>
40 #include <TopExp.hxx>
41 #include <TopExp_Explorer.hxx>
42 #include <TopoDS.hxx>
43 #include <TopoDS_Compound.hxx>
44 #include <TopoDS_Vertex.hxx>
45
46 #include FT_OUTLINE_H
47
48
49 namespace
50 {
51   // pre-defined font rendering options
52   static const unsigned int THE_FONT_SIZE      = 72;
53   static const unsigned int THE_RESOLUTION_DPI = 4800;
54
55   // compute scaling factor for specified font size
56   inline Standard_Real getScale (const Standard_Real theSize)
57   {
58     return theSize / Standard_Real(THE_FONT_SIZE) * 72.0 / Standard_Real(THE_RESOLUTION_DPI);
59   }
60
61 };
62
63 // =======================================================================
64 // function : Constructor
65 // purpose  :
66 // =======================================================================
67 Font_BRepFont::Font_BRepFont ()
68 : myPrecision  (Precision::Confusion()),
69   myScaleUnits (1.0),
70   myIsCompositeCurve (Standard_False),
71   my3Poles     (1, 3),
72   my4Poles     (1, 4)
73 {
74   init();
75 }
76
77 // =======================================================================
78 // function : init
79 // purpose  :
80 // =======================================================================
81 void Font_BRepFont::init()
82 {
83   mySurface        = new Geom_Plane (gp_Pln (gp::XOY()));
84   myCurve2dAdaptor = new Geom2dAdaptor_HCurve();
85   Handle(GeomAdaptor_HSurface) aSurfAdaptor = new GeomAdaptor_HSurface (mySurface);
86   myCurvOnSurf.Load (aSurfAdaptor);
87
88   myFixer.FixWireMode()          = 1;
89   myFixer.FixOrientationMode()   = 1;
90   myFixer.FixSplitFaceMode()     = 1; // some glyphs might be composed from several faces
91   Handle(ShapeFix_Wire) aWireFixer = myFixer.FixWireTool();
92   aWireFixer->FixConnectedMode() = 1;
93   aWireFixer->ClosedWireMode()   = Standard_True;
94   Handle(ShapeBuild_ReShape) aContext = new ShapeBuild_ReShape();
95   myFixer.SetContext (aContext);
96 }
97
98 // =======================================================================
99 // function : Constructor
100 // purpose  :
101 // =======================================================================
102 Font_BRepFont::Font_BRepFont (const NCollection_String& theFontPath,
103                               const Standard_Real       theSize)
104 : myPrecision  (Precision::Confusion()),
105   myScaleUnits (1.0),
106   myIsCompositeCurve (Standard_False),
107   my3Poles     (1, 3),
108   my4Poles     (1, 4)
109 {
110   init();
111   if (theSize <= myPrecision * 100.0)
112   {
113     return;
114   }
115
116   myScaleUnits = getScale (theSize);
117   Font_FTFont::Init (theFontPath, THE_FONT_SIZE, THE_RESOLUTION_DPI);
118 }
119
120 // =======================================================================
121 // function : Constructor
122 // purpose  :
123 // =======================================================================
124 Font_BRepFont::Font_BRepFont (const NCollection_String& theFontName,
125                               const Font_FontAspect     theFontAspect,
126                               const Standard_Real       theSize)
127 : myPrecision  (Precision::Confusion()),
128   myScaleUnits (1.0),
129   myIsCompositeCurve (Standard_False),
130   my3Poles     (1, 3),
131   my4Poles     (1, 4)
132 {
133   init();
134   if (theSize <= myPrecision * 100.0)
135   {
136     return;
137   }
138
139   myScaleUnits = getScale (theSize);
140   Font_FTFont::Init (theFontName, theFontAspect, THE_FONT_SIZE, THE_RESOLUTION_DPI);
141 }
142
143 // =======================================================================
144 // function : Release
145 // purpose  :
146 // =======================================================================
147 void Font_BRepFont::Release()
148 {
149   myCache.Clear();
150   Font_FTFont::Release();
151 }
152
153 // =======================================================================
154 // function : SetCompositeCurveMode
155 // purpose  :
156 // =======================================================================
157 void Font_BRepFont::SetCompositeCurveMode (const Standard_Boolean theToConcatenate)
158 {
159   if (myIsCompositeCurve != theToConcatenate)
160   {
161     myIsCompositeCurve = theToConcatenate;
162     myCache.Clear();
163   }
164 }
165
166 // =======================================================================
167 // function : Init
168 // purpose  :
169 // =======================================================================
170 bool Font_BRepFont::Init (const NCollection_String& theFontPath,
171                           const Standard_Real       theSize)
172 {
173   if (theSize <= myPrecision * 100.0)
174   {
175     return false;
176   }
177
178   myScaleUnits = getScale (theSize);
179   return Font_FTFont::Init (theFontPath, THE_FONT_SIZE, THE_RESOLUTION_DPI);
180 }
181
182 // =======================================================================
183 // function : Init
184 // purpose  :
185 // =======================================================================
186 bool Font_BRepFont::Init (const NCollection_String& theFontName,
187                           const Font_FontAspect     theFontAspect,
188                           const Standard_Real       theSize)
189 {
190   if (theSize <= myPrecision * 100.0)
191   {
192     return false;
193   }
194
195   myScaleUnits = getScale (theSize);
196   return Font_FTFont::Init (theFontName, theFontAspect, THE_FONT_SIZE, THE_RESOLUTION_DPI);
197 }
198
199 // =======================================================================
200 // function : RenderGlyph
201 // purpose  :
202 // =======================================================================
203 TopoDS_Shape Font_BRepFont::RenderGlyph (const Standard_Utf32Char& theChar)
204 {
205   TopoDS_Shape aShape;
206   Standard_Mutex::Sentry aSentry (myMutex);
207   renderGlyph (theChar, aShape);
208   return aShape;
209 }
210
211 // =======================================================================
212 // function : to3d
213 // purpose  :
214 // =======================================================================
215 bool Font_BRepFont::to3d (const Handle(Geom2d_Curve) theCurve2d,
216                           const GeomAbs_Shape        theContinuity,
217                           Handle(Geom_Curve)&        theCurve3d)
218 {
219   Standard_Real aMaxDeviation   = 0.0;
220   Standard_Real anAverDeviation = 0.0;
221   myCurve2dAdaptor->ChangeCurve2d().Load (theCurve2d);
222   myCurvOnSurf.Load (myCurve2dAdaptor);
223   GeomLib::BuildCurve3d (myPrecision, myCurvOnSurf,
224                          myCurve2dAdaptor->FirstParameter(), myCurve2dAdaptor->LastParameter(),
225                          theCurve3d, aMaxDeviation, anAverDeviation, theContinuity);
226   return !theCurve3d.IsNull();
227 }
228
229 // =======================================================================
230 // function : renderGlyph
231 // purpose  :
232 // =======================================================================
233 Standard_Boolean Font_BRepFont::renderGlyph (const Standard_Utf32Char theChar,
234                                              TopoDS_Shape&            theShape)
235 {
236   theShape.Nullify();
237   if (!loadGlyph (theChar)
238    || myFTFace->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
239   {
240     return Standard_False;
241   }
242   else if (myCache.Find (theChar, theShape))
243   {
244     return !theShape.IsNull();
245   }
246
247   TopLoc_Location aLoc;
248   TopoDS_Face aFaceDraft;
249   myBuilder.MakeFace (aFaceDraft, mySurface, myPrecision);
250   FT_Outline& anOutline = myFTFace->glyph->outline;
251   // Get orientation is useless since it doesn't retrieve any in-font information and just computes orientation.
252   // Because it fails in some cases - leave this to ShapeFix.
253   //const FT_Orientation anOrient = FT_Outline_Get_Orientation (&anOutline);
254   for (short aContour = 0, aStartIndex = 0; aContour < anOutline.n_contours; ++aContour)
255   {
256     const FT_Vector* aPntList = &anOutline.points[aStartIndex];
257     const char* aTags      = &anOutline.tags[aStartIndex];
258     const short anEndIndex = anOutline.contours[aContour];
259     const short aPntsNb    = (anEndIndex - aStartIndex) + 1;
260     aStartIndex = anEndIndex + 1;
261     if (aPntsNb < 3)
262     {
263       // closed contour can not be constructed from < 3 points
264       continue;
265     }
266
267     BRepBuilderAPI_MakeWire aWireMaker;
268
269     gp_XY aPntPrev;
270     gp_XY aPntCurr = readFTVec (aPntList[aPntsNb - 1]);
271     gp_XY aPntNext = readFTVec (aPntList[0]);
272
273     Standard_Integer aLinePnts = (FT_CURVE_TAG(aTags[aPntsNb - 1]) == FT_Curve_Tag_On) ? 1 : 0;
274     gp_XY aPntLine1 = aPntCurr;
275
276     // see http://freetype.sourceforge.net/freetype2/docs/glyphs/glyphs-6.html
277     // for a full description of FreeType tags.
278     for (short aPntId = 0; aPntId < aPntsNb; ++aPntId)
279     {
280       aPntPrev = aPntCurr;
281       aPntCurr = aPntNext;
282       aPntNext = readFTVec (aPntList[(aPntId + 1) % aPntsNb]);
283
284       // process tags
285       if (FT_CURVE_TAG(aTags[aPntId]) == FT_Curve_Tag_On)
286       {
287         if (aLinePnts < 1)
288         {
289           aPntLine1 = aPntCurr;
290           aLinePnts = 1;
291           continue;
292         }
293
294         const gp_XY         aDirVec  = aPntCurr - aPntLine1;
295         const Standard_Real aLen     = aDirVec.Modulus();
296         if (aLen <= myPrecision)
297         {
298           aPntLine1 = aPntCurr;
299           aLinePnts = 1;
300           continue;
301         }
302
303         if (myIsCompositeCurve)
304         {
305           Handle(Geom2d_TrimmedCurve) aLine = GCE2d_MakeSegment (gp_Pnt2d (aPntLine1), gp_Pnt2d (aPntCurr));
306           myConcatMaker.Add (aLine, myPrecision);
307         }
308         else
309         {
310           Handle(Geom_Curve)  aCurve3d;
311           Handle(Geom2d_Line) aCurve2d = new Geom2d_Line (gp_Pnt2d (aPntLine1), gp_Dir2d (aDirVec));
312           if (to3d (aCurve2d, GeomAbs_C1, aCurve3d))
313           {
314             TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d, 0.0, aLen);
315             myBuilder.UpdateEdge (anEdge, aCurve2d, mySurface, aLoc, myPrecision);
316             aWireMaker.Add (anEdge);
317           }
318         }
319         aPntLine1 = aPntCurr;
320       }
321       else if (FT_CURVE_TAG(aTags[aPntId]) == FT_Curve_Tag_Conic)
322       {
323         aLinePnts = 0;
324         gp_XY aPntPrev2 = aPntPrev;
325         gp_XY aPntNext2 = aPntNext;
326
327         // previous point is either the real previous point (an "on" point),
328         // or the midpoint between the current one and the previous "conic off" point
329         if (FT_CURVE_TAG(aTags[(aPntId - 1 + aPntsNb) % aPntsNb]) == FT_Curve_Tag_Conic)
330         {
331           aPntPrev2 = (aPntCurr + aPntPrev) * 0.5;
332         }
333
334         // next point is either the real next point or the midpoint
335         if (FT_CURVE_TAG(aTags[(aPntId + 1) % aPntsNb]) == FT_Curve_Tag_Conic)
336         {
337           aPntNext2 = (aPntCurr + aPntNext) * 0.5;
338         }
339
340         my3Poles.SetValue (1, aPntPrev2);
341         my3Poles.SetValue (2, aPntCurr);
342         my3Poles.SetValue (3, aPntNext2);
343         Handle(Geom2d_BezierCurve) aBezierArc = new Geom2d_BezierCurve (my3Poles);
344         if (myIsCompositeCurve)
345         {
346           myConcatMaker.Add (aBezierArc, myPrecision);
347         }
348         else
349         {
350           Handle(Geom_Curve) aCurve3d;
351           if (to3d (aBezierArc, GeomAbs_C1, aCurve3d))
352           {
353             TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d);
354             myBuilder.UpdateEdge (anEdge, aBezierArc, mySurface, aLoc, myPrecision);
355             aWireMaker.Add (anEdge);
356           }
357         }
358       }
359       else if (FT_CURVE_TAG(aTags[aPntId])                 == FT_Curve_Tag_Cubic
360             && FT_CURVE_TAG(aTags[(aPntId + 1) % aPntsNb]) == FT_Curve_Tag_Cubic)
361       {
362         aLinePnts = 0;
363         my4Poles.SetValue (1, aPntPrev);
364         my4Poles.SetValue (2, aPntCurr);
365         my4Poles.SetValue (3, aPntNext);
366         my4Poles.SetValue (4, gp_Pnt2d(readFTVec (aPntList[(aPntId + 2) % aPntsNb])));
367         Handle(Geom2d_BezierCurve) aBezier = new Geom2d_BezierCurve (my4Poles);
368         if (myIsCompositeCurve)
369         {
370           myConcatMaker.Add (aBezier, myPrecision);
371         }
372         else
373         {
374           Handle(Geom_Curve) aCurve3d;
375           if (to3d (aBezier, GeomAbs_C1, aCurve3d))
376           {
377             TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d);
378             myBuilder.UpdateEdge (anEdge, aBezier, mySurface, aLoc, myPrecision);
379             aWireMaker.Add (anEdge);
380           }
381         }
382       }
383     }
384
385     if (myIsCompositeCurve)
386     {
387       Handle(Geom2d_BSplineCurve) aDraft2d = myConcatMaker.BSplineCurve();
388       if (aDraft2d.IsNull())
389       {
390         continue;
391       }
392
393       const gp_Pnt2d aFirstPnt = aDraft2d->StartPoint();
394       const gp_Pnt2d aLastPnt  = aDraft2d->EndPoint();
395       if (!aFirstPnt.IsEqual (aLastPnt, myPrecision))
396       {
397         Handle(Geom2d_TrimmedCurve) aLine = GCE2d_MakeSegment (aLastPnt, aFirstPnt);
398         myConcatMaker.Add (aLine, myPrecision);
399       }
400
401       Handle(Geom2d_BSplineCurve) aCurve2d = myConcatMaker.BSplineCurve();
402       Handle(Geom_Curve)          aCurve3d;
403       if (to3d (aCurve2d, GeomAbs_C0, aCurve3d))
404       {
405         TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d);
406         myBuilder.UpdateEdge (anEdge, aCurve2d, mySurface, aLoc, myPrecision);
407         aWireMaker.Add (anEdge);
408       }
409       myConcatMaker.Clear();
410     }
411     else
412     {
413       if (!aWireMaker.IsDone())
414       {
415         continue;
416       }
417
418       TopoDS_Vertex aFirstV, aLastV;
419       TopExp::Vertices (aWireMaker.Wire(), aFirstV, aLastV);
420       gp_Pnt aFirstPoint = BRep_Tool::Pnt (aFirstV);
421       gp_Pnt aLastPoint  = BRep_Tool::Pnt (aLastV);
422       if (!aFirstPoint.IsEqual (aLastPoint, myPrecision))
423       {
424         aWireMaker.Add (BRepLib_MakeEdge (aFirstV, aLastV));
425       }
426     }
427
428     if (!aWireMaker.IsDone())
429     {
430       continue;
431     }
432
433     TopoDS_Wire aWireDraft = aWireMaker.Wire();
434     //if (anOrient == FT_ORIENTATION_FILL_LEFT)
435     //{
436     // According to the TrueType specification, clockwise contours must be filled
437     aWireDraft.Reverse();
438     //}
439     myBuilder.Add (aFaceDraft, aWireDraft);
440   }
441
442   myFixer.Init (aFaceDraft);
443   myFixer.Perform();
444   theShape = myFixer.Result();
445   if (!theShape.IsNull()
446   &&  theShape.ShapeType() != TopAbs_FACE)
447   {
448     // shape fix can not fix orientation within the single call
449     TopoDS_Compound aComp;
450     myBuilder.MakeCompound (aComp);
451     for (TopExp_Explorer aFaceIter (theShape, TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
452     {
453       TopoDS_Face aFace = TopoDS::Face (aFaceIter.Current());
454       myFixer.Init (aFace);
455       myFixer.Perform();
456       myBuilder.Add (aComp, myFixer.Result());
457     }
458     theShape = aComp;
459   }
460
461   myCache.Bind (theChar, theShape);
462   return !theShape.IsNull();
463 }
464
465 // =======================================================================
466 // function : RenderText
467 // purpose  :
468 // =======================================================================
469 TopoDS_Shape Font_BRepFont::RenderText (const NCollection_String& theString)
470 {
471   if (theString.IsEmpty())
472   {
473     return TopoDS_Shape();
474   }
475
476   gp_Trsf          aTrsf;
477   gp_XYZ           aPen;
478   Standard_Integer aLine = 0;
479   TopoDS_Shape     aGlyphShape;
480   TopoDS_Compound  aResult;
481   myBuilder.MakeCompound (aResult);
482   Standard_Mutex::Sentry aSentry (myMutex);
483   for (NCollection_Utf8Iter anIter = theString.Iterator(); *anIter != 0;)
484   {
485     const Standard_Utf32Char aCharCurr =   *anIter;
486     const Standard_Utf32Char aCharNext = *++anIter;
487     if (aCharCurr == '\x0D' // CR  (carriage return)
488      || aCharCurr == '\a'   // BEL (alarm)
489      || aCharCurr == '\f'   // FF  (form feed) NP (new page)
490      || aCharCurr == '\b'   // BS  (backspace)
491      || aCharCurr == '\v')  // VT  (vertical tab)
492     {
493       continue; // skip unsupported carriage control codes
494     }
495     else if (aCharCurr == ' ' || aCharCurr == '\t')
496     {
497       aPen.SetX (aPen.X() + AdvanceX (aCharCurr, aCharNext));
498       continue;
499     }
500     else if (aCharCurr == '\n')
501     {
502       ++aLine;
503       aPen.SetX (0.0);
504       aPen.SetY (-Standard_Real(aLine) * LineSpacing());
505       continue;
506     }
507
508     if (renderGlyph (aCharCurr, aGlyphShape))
509     {
510       aTrsf.SetTranslation (gp_Vec (aPen));
511       aGlyphShape.Move (aTrsf);
512       myBuilder.Add (aResult, aGlyphShape);
513     }
514     aPen.SetX (aPen.X() + AdvanceX (aCharCurr, aCharNext));
515   }
516   return aResult;
517 }