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