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