0024023: Revamp the OCCT Handle -- general
[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>
c04c30b3 25#include <Geom2d_TrimmedCurve.hxx>
b514beda 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
b514beda 48
49namespace
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// =======================================================================
67Font_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// =======================================================================
81void 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// =======================================================================
102Font_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// =======================================================================
124Font_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// =======================================================================
147void Font_BRepFont::Release()
148{
149 myCache.Clear();
150 Font_FTFont::Release();
151}
152
153// =======================================================================
154// function : SetCompositeCurveMode
155// purpose :
156// =======================================================================
157void 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// =======================================================================
170bool 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// =======================================================================
186bool 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// =======================================================================
203TopoDS_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// =======================================================================
215bool 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// =======================================================================
233Standard_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// =======================================================================
469TopoDS_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}