0024023: Revamp the OCCT Handle -- ambiguity
[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>
543a9964 29#include <Geom2d_TrimmedCurve.hxx>
b514beda 30#include <Geom2d_Line.hxx>
31#include <GeomAPI.hxx>
32#include <GeomAdaptor_HSurface.hxx>
33#include <GeomLib.hxx>
34#include <gp_Pln.hxx>
35#include <ShapeBuild_ReShape.hxx>
36#include <ShapeFix_Edge.hxx>
37#include <ShapeFix_Wire.hxx>
38#include <TColGeom2d_HSequenceOfBoundedCurve.hxx>
39#include <TCollection_AsciiString.hxx>
40#include <TCollection_HAsciiString.hxx>
41#include <TopExp.hxx>
42#include <TopExp_Explorer.hxx>
43#include <TopoDS.hxx>
44#include <TopoDS_Compound.hxx>
45#include <TopoDS_Vertex.hxx>
46
47#include FT_OUTLINE_H
48
b514beda 49
50namespace
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// =======================================================================
68Font_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// =======================================================================
82void Font_BRepFont::init()
83{
84 mySurface = new Geom_Plane (gp_Pln (gp::XOY()));
85 myCurve2dAdaptor = new Geom2dAdaptor_HCurve();
543a9964 86 Handle(Adaptor3d_HSurface) aSurfAdaptor = new GeomAdaptor_HSurface (mySurface);
b514beda 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// =======================================================================
103Font_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// =======================================================================
125Font_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// =======================================================================
148void Font_BRepFont::Release()
149{
150 myCache.Clear();
151 Font_FTFont::Release();
152}
153
154// =======================================================================
155// function : SetCompositeCurveMode
156// purpose :
157// =======================================================================
158void 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// =======================================================================
171bool 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// =======================================================================
187bool 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// =======================================================================
204TopoDS_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// =======================================================================
543a9964 216bool Font_BRepFont::to3d (const Handle(Geom2d_Curve)& theCurve2d,
b514beda 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);
543a9964 223 const Handle(Adaptor2d_HCurve2d)& aCurve = myCurve2dAdaptor; // to avoid ambiguity
224 myCurvOnSurf.Load (aCurve);
b514beda 225 GeomLib::BuildCurve3d (myPrecision, myCurvOnSurf,
226 myCurve2dAdaptor->FirstParameter(), myCurve2dAdaptor->LastParameter(),
227 theCurve3d, aMaxDeviation, anAverDeviation, theContinuity);
228 return !theCurve3d.IsNull();
229}
230
231// =======================================================================
232// function : renderGlyph
233// purpose :
234// =======================================================================
235Standard_Boolean Font_BRepFont::renderGlyph (const Standard_Utf32Char theChar,
236 TopoDS_Shape& theShape)
237{
238 theShape.Nullify();
239 if (!loadGlyph (theChar)
240 || myFTFace->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
241 {
242 return Standard_False;
243 }
244 else if (myCache.Find (theChar, theShape))
245 {
246 return !theShape.IsNull();
247 }
248
249 TopLoc_Location aLoc;
250 TopoDS_Face aFaceDraft;
251 myBuilder.MakeFace (aFaceDraft, mySurface, myPrecision);
252 FT_Outline& anOutline = myFTFace->glyph->outline;
253 // Get orientation is useless since it doesn't retrieve any in-font information and just computes orientation.
254 // Because it fails in some cases - leave this to ShapeFix.
255 //const FT_Orientation anOrient = FT_Outline_Get_Orientation (&anOutline);
256 for (short aContour = 0, aStartIndex = 0; aContour < anOutline.n_contours; ++aContour)
257 {
258 const FT_Vector* aPntList = &anOutline.points[aStartIndex];
259 const char* aTags = &anOutline.tags[aStartIndex];
260 const short anEndIndex = anOutline.contours[aContour];
261 const short aPntsNb = (anEndIndex - aStartIndex) + 1;
262 aStartIndex = anEndIndex + 1;
263 if (aPntsNb < 3)
264 {
265 // closed contour can not be constructed from < 3 points
266 continue;
267 }
268
269 BRepBuilderAPI_MakeWire aWireMaker;
270
271 gp_XY aPntPrev;
272 gp_XY aPntCurr = readFTVec (aPntList[aPntsNb - 1]);
273 gp_XY aPntNext = readFTVec (aPntList[0]);
274
275 Standard_Integer aLinePnts = (FT_CURVE_TAG(aTags[aPntsNb - 1]) == FT_Curve_Tag_On) ? 1 : 0;
276 gp_XY aPntLine1 = aPntCurr;
277
278 // see http://freetype.sourceforge.net/freetype2/docs/glyphs/glyphs-6.html
279 // for a full description of FreeType tags.
280 for (short aPntId = 0; aPntId < aPntsNb; ++aPntId)
281 {
282 aPntPrev = aPntCurr;
283 aPntCurr = aPntNext;
284 aPntNext = readFTVec (aPntList[(aPntId + 1) % aPntsNb]);
285
286 // process tags
287 if (FT_CURVE_TAG(aTags[aPntId]) == FT_Curve_Tag_On)
288 {
289 if (aLinePnts < 1)
290 {
291 aPntLine1 = aPntCurr;
292 aLinePnts = 1;
293 continue;
294 }
295
296 const gp_XY aDirVec = aPntCurr - aPntLine1;
297 const Standard_Real aLen = aDirVec.Modulus();
298 if (aLen <= myPrecision)
299 {
300 aPntLine1 = aPntCurr;
301 aLinePnts = 1;
302 continue;
303 }
304
305 if (myIsCompositeCurve)
306 {
307 Handle(Geom2d_TrimmedCurve) aLine = GCE2d_MakeSegment (gp_Pnt2d (aPntLine1), gp_Pnt2d (aPntCurr));
308 myConcatMaker.Add (aLine, myPrecision);
309 }
310 else
311 {
312 Handle(Geom_Curve) aCurve3d;
313 Handle(Geom2d_Line) aCurve2d = new Geom2d_Line (gp_Pnt2d (aPntLine1), gp_Dir2d (aDirVec));
314 if (to3d (aCurve2d, GeomAbs_C1, aCurve3d))
315 {
316 TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d, 0.0, aLen);
317 myBuilder.UpdateEdge (anEdge, aCurve2d, mySurface, aLoc, myPrecision);
318 aWireMaker.Add (anEdge);
319 }
320 }
321 aPntLine1 = aPntCurr;
322 }
323 else if (FT_CURVE_TAG(aTags[aPntId]) == FT_Curve_Tag_Conic)
324 {
325 aLinePnts = 0;
326 gp_XY aPntPrev2 = aPntPrev;
327 gp_XY aPntNext2 = aPntNext;
328
329 // previous point is either the real previous point (an "on" point),
330 // or the midpoint between the current one and the previous "conic off" point
331 if (FT_CURVE_TAG(aTags[(aPntId - 1 + aPntsNb) % aPntsNb]) == FT_Curve_Tag_Conic)
332 {
333 aPntPrev2 = (aPntCurr + aPntPrev) * 0.5;
334 }
335
336 // next point is either the real next point or the midpoint
337 if (FT_CURVE_TAG(aTags[(aPntId + 1) % aPntsNb]) == FT_Curve_Tag_Conic)
338 {
339 aPntNext2 = (aPntCurr + aPntNext) * 0.5;
340 }
341
342 my3Poles.SetValue (1, aPntPrev2);
343 my3Poles.SetValue (2, aPntCurr);
344 my3Poles.SetValue (3, aPntNext2);
345 Handle(Geom2d_BezierCurve) aBezierArc = new Geom2d_BezierCurve (my3Poles);
346 if (myIsCompositeCurve)
347 {
348 myConcatMaker.Add (aBezierArc, myPrecision);
349 }
350 else
351 {
352 Handle(Geom_Curve) aCurve3d;
353 if (to3d (aBezierArc, GeomAbs_C1, aCurve3d))
354 {
355 TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d);
356 myBuilder.UpdateEdge (anEdge, aBezierArc, mySurface, aLoc, myPrecision);
357 aWireMaker.Add (anEdge);
358 }
359 }
360 }
361 else if (FT_CURVE_TAG(aTags[aPntId]) == FT_Curve_Tag_Cubic
362 && FT_CURVE_TAG(aTags[(aPntId + 1) % aPntsNb]) == FT_Curve_Tag_Cubic)
363 {
364 aLinePnts = 0;
365 my4Poles.SetValue (1, aPntPrev);
366 my4Poles.SetValue (2, aPntCurr);
367 my4Poles.SetValue (3, aPntNext);
368 my4Poles.SetValue (4, gp_Pnt2d(readFTVec (aPntList[(aPntId + 2) % aPntsNb])));
369 Handle(Geom2d_BezierCurve) aBezier = new Geom2d_BezierCurve (my4Poles);
370 if (myIsCompositeCurve)
371 {
372 myConcatMaker.Add (aBezier, myPrecision);
373 }
374 else
375 {
376 Handle(Geom_Curve) aCurve3d;
377 if (to3d (aBezier, GeomAbs_C1, aCurve3d))
378 {
379 TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d);
380 myBuilder.UpdateEdge (anEdge, aBezier, mySurface, aLoc, myPrecision);
381 aWireMaker.Add (anEdge);
382 }
383 }
384 }
385 }
386
387 if (myIsCompositeCurve)
388 {
389 Handle(Geom2d_BSplineCurve) aDraft2d = myConcatMaker.BSplineCurve();
390 if (aDraft2d.IsNull())
391 {
392 continue;
393 }
394
395 const gp_Pnt2d aFirstPnt = aDraft2d->StartPoint();
396 const gp_Pnt2d aLastPnt = aDraft2d->EndPoint();
397 if (!aFirstPnt.IsEqual (aLastPnt, myPrecision))
398 {
399 Handle(Geom2d_TrimmedCurve) aLine = GCE2d_MakeSegment (aLastPnt, aFirstPnt);
400 myConcatMaker.Add (aLine, myPrecision);
401 }
402
403 Handle(Geom2d_BSplineCurve) aCurve2d = myConcatMaker.BSplineCurve();
404 Handle(Geom_Curve) aCurve3d;
405 if (to3d (aCurve2d, GeomAbs_C0, aCurve3d))
406 {
407 TopoDS_Edge anEdge = BRepLib_MakeEdge (aCurve3d);
408 myBuilder.UpdateEdge (anEdge, aCurve2d, mySurface, aLoc, myPrecision);
409 aWireMaker.Add (anEdge);
410 }
411 myConcatMaker.Clear();
412 }
413 else
414 {
415 if (!aWireMaker.IsDone())
416 {
417 continue;
418 }
419
420 TopoDS_Vertex aFirstV, aLastV;
421 TopExp::Vertices (aWireMaker.Wire(), aFirstV, aLastV);
422 gp_Pnt aFirstPoint = BRep_Tool::Pnt (aFirstV);
423 gp_Pnt aLastPoint = BRep_Tool::Pnt (aLastV);
424 if (!aFirstPoint.IsEqual (aLastPoint, myPrecision))
425 {
426 aWireMaker.Add (BRepLib_MakeEdge (aFirstV, aLastV));
427 }
428 }
429
430 if (!aWireMaker.IsDone())
431 {
432 continue;
433 }
434
435 TopoDS_Wire aWireDraft = aWireMaker.Wire();
436 //if (anOrient == FT_ORIENTATION_FILL_LEFT)
437 //{
438 // According to the TrueType specification, clockwise contours must be filled
439 aWireDraft.Reverse();
440 //}
441 myBuilder.Add (aFaceDraft, aWireDraft);
442 }
443
444 myFixer.Init (aFaceDraft);
445 myFixer.Perform();
446 theShape = myFixer.Result();
447 if (!theShape.IsNull()
448 && theShape.ShapeType() != TopAbs_FACE)
449 {
450 // shape fix can not fix orientation within the single call
451 TopoDS_Compound aComp;
452 myBuilder.MakeCompound (aComp);
453 for (TopExp_Explorer aFaceIter (theShape, TopAbs_FACE); aFaceIter.More(); aFaceIter.Next())
454 {
455 TopoDS_Face aFace = TopoDS::Face (aFaceIter.Current());
456 myFixer.Init (aFace);
457 myFixer.Perform();
458 myBuilder.Add (aComp, myFixer.Result());
459 }
460 theShape = aComp;
461 }
462
463 myCache.Bind (theChar, theShape);
464 return !theShape.IsNull();
465}
466
467// =======================================================================
468// function : RenderText
469// purpose :
470// =======================================================================
471TopoDS_Shape Font_BRepFont::RenderText (const NCollection_String& theString)
472{
473 if (theString.IsEmpty())
474 {
475 return TopoDS_Shape();
476 }
477
478 gp_Trsf aTrsf;
479 gp_XYZ aPen;
480 Standard_Integer aLine = 0;
481 TopoDS_Shape aGlyphShape;
482 TopoDS_Compound aResult;
483 myBuilder.MakeCompound (aResult);
484 Standard_Mutex::Sentry aSentry (myMutex);
485 for (NCollection_Utf8Iter anIter = theString.Iterator(); *anIter != 0;)
486 {
487 const Standard_Utf32Char aCharCurr = *anIter;
488 const Standard_Utf32Char aCharNext = *++anIter;
489 if (aCharCurr == '\x0D' // CR (carriage return)
490 || aCharCurr == '\a' // BEL (alarm)
491 || aCharCurr == '\f' // FF (form feed) NP (new page)
492 || aCharCurr == '\b' // BS (backspace)
493 || aCharCurr == '\v') // VT (vertical tab)
494 {
495 continue; // skip unsupported carriage control codes
496 }
497 else if (aCharCurr == ' ' || aCharCurr == '\t')
498 {
499 aPen.SetX (aPen.X() + AdvanceX (aCharCurr, aCharNext));
500 continue;
501 }
502 else if (aCharCurr == '\n')
503 {
504 ++aLine;
505 aPen.SetX (0.0);
506 aPen.SetY (-Standard_Real(aLine) * LineSpacing());
507 continue;
508 }
509
510 if (renderGlyph (aCharCurr, aGlyphShape))
511 {
512 aTrsf.SetTranslation (gp_Vec (aPen));
513 aGlyphShape.Move (aTrsf);
514 myBuilder.Add (aResult, aGlyphShape);
515 }
516 aPen.SetX (aPen.X() + AdvanceX (aCharCurr, aCharNext));
517 }
518 return aResult;
519}