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