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 | |
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(); |
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 | // ======================================================================= |
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 | // ======================================================================= |
543a9964 |
216 | bool 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 | // ======================================================================= |
235 | Standard_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 | // ======================================================================= |
471 | TopoDS_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 | } |