0031687: Draw Harness, ViewerTest - extend command vrenderparams with option updating...
[occt.git] / src / Font / Font_FTFont.cxx
CommitLineData
a174a3c5 1// Created on: 2013-01-28
2// Created by: Kirill GAVRILOV
d5f74e42 3// Copyright (c) 2013-2014 OPEN CASCADE SAS
a174a3c5 4//
973c2be1 5// This file is part of Open CASCADE Technology software library.
a174a3c5 6//
d5f74e42 7// This library is free software; you can redistribute it and/or modify it under
8// the terms of the GNU Lesser General Public License version 2.1 as published
973c2be1 9// by the Free Software Foundation, with special exception defined in the file
10// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11// distribution for complete text of the license and disclaimer of any warranty.
a174a3c5 12//
973c2be1 13// Alternatively, this file may be used under the terms of Open CASCADE
14// commercial license or contractual agreement.
a174a3c5 15
16#include <Font_FTFont.hxx>
f9801cf9 17
18#include <Font_FTLibrary.hxx>
b514beda 19#include <Font_FontMgr.hxx>
317d68c9 20#include <Font_TextFormatter.hxx>
1bbd7c79 21#include <Message.hxx>
22#include <Message_Messenger.hxx>
317d68c9 23
912761ea 24#include <algorithm>
25
f9801cf9 26#include <ft2build.h>
27#include FT_FREETYPE_H
a174a3c5 28
92efcf78 29IMPLEMENT_STANDARD_RTTIEXT(Font_FTFont,Standard_Transient)
30
a174a3c5 31// =======================================================================
32// function : Font_FTFont
33// purpose :
34// =======================================================================
35Font_FTFont::Font_FTFont (const Handle(Font_FTLibrary)& theFTLib)
151da08b 36: myFTLib (theFTLib),
37 myFTFace (NULL),
912761ea 38 myActiveFTFace(NULL),
39 myFontAspect (Font_FontAspect_Regular),
151da08b 40 myWidthScaling(1.0),
41 myLoadFlags (FT_LOAD_NO_HINTING | FT_LOAD_TARGET_NORMAL),
912761ea 42 myUChar (0U),
43 myToUseUnicodeSubsetFallback (Font_FontMgr::ToUseUnicodeSubsetFallback())
a174a3c5 44{
45 if (myFTLib.IsNull())
46 {
47 myFTLib = new Font_FTLibrary();
48 }
49}
50
51// =======================================================================
52// function : Font_FTFont
53// purpose :
54// =======================================================================
55Font_FTFont::~Font_FTFont()
56{
57 Release();
58}
59
60// =======================================================================
f9801cf9 61// function : Release
a174a3c5 62// purpose :
63// =======================================================================
64void Font_FTFont::Release()
65{
66 myGlyphImg.Clear();
67 myFontPath.Clear();
68 myUChar = 0;
69 if (myFTFace != NULL)
70 {
71 FT_Done_Face (myFTFace);
72 myFTFace = NULL;
73 }
912761ea 74 myActiveFTFace = NULL;
1bbd7c79 75 myBuffer.Nullify();
a174a3c5 76}
77
78// =======================================================================
b514beda 79// function : Init
a174a3c5 80// purpose :
81// =======================================================================
1bbd7c79 82bool Font_FTFont::Init (const Handle(NCollection_Buffer)& theData,
83 const TCollection_AsciiString& theFileName,
9a90a452 84 const Font_FTFontParams& theParams,
85 const Standard_Integer theFaceId)
a174a3c5 86{
87 Release();
1bbd7c79 88 myBuffer = theData;
89 myFontPath = theFileName;
90 myFontParams = theParams;
a174a3c5 91 if (!myFTLib->IsValid())
92 {
a87b1b37 93 Message::SendTrace ("FreeType library is unavailable");
a174a3c5 94 Release();
95 return false;
96 }
97
1bbd7c79 98 if (!theData.IsNull())
a174a3c5 99 {
9a90a452 100 if (FT_New_Memory_Face (myFTLib->Instance(), theData->Data(), (FT_Long )theData->Size(), (FT_Long )theFaceId, &myFTFace) != 0)
1bbd7c79 101 {
a87b1b37 102 Message::SendTrace (TCollection_AsciiString("Font '") + myFontPath + "' failed to load from memory");
1bbd7c79 103 Release();
104 return false;
105 }
a174a3c5 106 }
1bbd7c79 107 else
a174a3c5 108 {
9a90a452 109 if (FT_New_Face (myFTLib->Instance(), myFontPath.ToCString(), (FT_Long )theFaceId, &myFTFace) != 0)
1bbd7c79 110 {
a87b1b37 111 //Message::SendTrace (TCollection_AsciiString("Font '") + myFontPath + "' failed to load from file");
1bbd7c79 112 Release();
113 return false;
114 }
115 }
116
117 if (FT_Select_Charmap (myFTFace, ft_encoding_unicode) != 0)
118 {
a87b1b37 119 Message::SendTrace (TCollection_AsciiString("Font '") + myFontPath + "' doesn't contains Unicode charmap");
a174a3c5 120 Release();
121 return false;
122 }
1bbd7c79 123 else if (FT_Set_Char_Size (myFTFace, 0L, toFTPoints (theParams.PointSize), theParams.Resolution, theParams.Resolution) != 0)
a174a3c5 124 {
a87b1b37 125 Message::SendTrace (TCollection_AsciiString("Font '") + myFontPath + "' doesn't contains Unicode charmap of requested size");
a174a3c5 126 Release();
127 return false;
128 }
1bbd7c79 129
130 if (theParams.ToSynthesizeItalic)
131 {
132 const double THE_SHEAR_ANGLE = 10.0 * M_PI / 180.0;
133
134 FT_Matrix aMat;
135 aMat.xx = FT_Fixed (Cos (-THE_SHEAR_ANGLE) * (1 << 16));
136 aMat.xy = 0;
137 aMat.yx = 0;
138 aMat.yy = aMat.xx;
139
140 FT_Fixed aFactor = FT_Fixed (Tan (THE_SHEAR_ANGLE) * (1 << 16));
141 aMat.xy += FT_MulFix (aFactor, aMat.xx);
142
143 FT_Set_Transform (myFTFace, &aMat, 0);
144 }
912761ea 145 myActiveFTFace = myFTFace;
a174a3c5 146 return true;
147}
148
b514beda 149// =======================================================================
1bbd7c79 150// function : FindAndCreate
b514beda 151// purpose :
152// =======================================================================
1bbd7c79 153Handle(Font_FTFont) Font_FTFont::FindAndCreate (const TCollection_AsciiString& theFontName,
154 const Font_FontAspect theFontAspect,
155 const Font_FTFontParams& theParams,
156 const Font_StrictLevel theStrictLevel)
b514beda 157{
158 Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
5b377041 159 Font_FontAspect aFontAspect = theFontAspect;
36e28f96 160 Font_FTFontParams aParams = theParams;
1bbd7c79 161 if (Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFont (theFontName, theStrictLevel, aFontAspect))
e4f0cc46 162 {
1bbd7c79 163 if (aRequestedFont->IsSingleStrokeFont())
164 {
165 aParams.IsSingleStrokeFont = true;
166 }
167
9a90a452 168 Standard_Integer aFaceId = 0;
169 const TCollection_AsciiString& aPath = aRequestedFont->FontPathAny (aFontAspect, aParams.ToSynthesizeItalic, aFaceId);
1bbd7c79 170 Handle(Font_FTFont) aFont = new Font_FTFont();
9a90a452 171 if (aFont->Init (aPath, aParams, aFaceId))
1bbd7c79 172 {
912761ea 173 aFont->myFontAspect = aFontAspect;
1bbd7c79 174 return aFont;
175 }
e4f0cc46 176 }
36e28f96 177 else if (theStrictLevel == Font_StrictLevel_Any)
178 {
179 switch (theFontAspect)
180 {
181 case Font_FontAspect_UNDEFINED:
182 case Font_FontAspect_Regular:
183 case Font_FontAspect_Bold:
184 aFontAspect = Font_FontAspect_Regular;
185 break;
186 case Font_FontAspect_Italic:
187 case Font_FontAspect_BoldItalic:
188 aFontAspect = Font_FontAspect_Italic;
189 aParams.ToSynthesizeItalic = true;
190 break;
191 }
36e28f96 192 Handle(Font_FTFont) aFont = new Font_FTFont();
21095f2d 193 if (aFont->Init (Font_FontMgr::EmbedFallbackFont(), "Embed Fallback Font", aParams, 0))
36e28f96 194 {
195 aFont->myFontAspect = aFontAspect;
196 return aFont;
197 }
198 }
1bbd7c79 199 return Handle(Font_FTFont)();
200}
201
202// =======================================================================
203// function : FindAndInit
204// purpose :
205// =======================================================================
206bool Font_FTFont::FindAndInit (const TCollection_AsciiString& theFontName,
207 Font_FontAspect theFontAspect,
208 const Font_FTFontParams& theParams,
209 Font_StrictLevel theStrictLevel)
210{
211 Font_FTFontParams aParams = theParams;
912761ea 212 myFontAspect = theFontAspect;
1bbd7c79 213 Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
912761ea 214 if (Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFont (theFontName.ToCString(), theStrictLevel, myFontAspect))
1bbd7c79 215 {
216 if (aRequestedFont->IsSingleStrokeFont())
217 {
218 aParams.IsSingleStrokeFont = true;
219 }
220
9a90a452 221 Standard_Integer aFaceId = 0;
222 const TCollection_AsciiString& aPath = aRequestedFont->FontPathAny (myFontAspect, aParams.ToSynthesizeItalic, aFaceId);
223 return Init (aPath, aParams, aFaceId);
1bbd7c79 224 }
36e28f96 225 else if (theStrictLevel == Font_StrictLevel_Any)
226 {
227 if (theFontAspect == Font_FontAspect_Italic
228 || theFontAspect == Font_FontAspect_BoldItalic)
229 {
230 aParams.ToSynthesizeItalic = true;
231 }
21095f2d 232 return Init (Font_FontMgr::EmbedFallbackFont(), "Embed Fallback Font", aParams, 0);
36e28f96 233 }
1bbd7c79 234 Release();
e4f0cc46 235 return false;
b514beda 236}
237
912761ea 238// =======================================================================
239// function : findAndInitFallback
240// purpose :
241// =======================================================================
242bool Font_FTFont::findAndInitFallback (Font_UnicodeSubset theSubset)
243{
244 if (!myFallbackFaces[theSubset].IsNull())
245 {
246 return myFallbackFaces[theSubset]->IsValid();
247 }
248
249 myFallbackFaces[theSubset] = new Font_FTFont (myFTLib);
250 myFallbackFaces[theSubset]->myToUseUnicodeSubsetFallback = false; // no recursion
251
252 Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
253 if (Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFallbackFont (theSubset, myFontAspect))
254 {
255 Font_FTFontParams aParams = myFontParams;
256 aParams.IsSingleStrokeFont = aRequestedFont->IsSingleStrokeFont();
257
9a90a452 258 Standard_Integer aFaceId = 0;
259 const TCollection_AsciiString& aPath = aRequestedFont->FontPathAny (myFontAspect, aParams.ToSynthesizeItalic, aFaceId);
260 if (myFallbackFaces[theSubset]->Init (aPath, aParams, aFaceId))
912761ea 261 {
a87b1b37 262 Message::SendTrace (TCollection_AsciiString ("Font_FTFont, using fallback font '") + aRequestedFont->FontName() + "'"
263 + " for symbols unsupported by '" + myFTFace->family_name + "'");
912761ea 264 }
265 }
266 return myFallbackFaces[theSubset]->IsValid();
267}
268
269// =======================================================================
270// function : HasSymbol
271// purpose :
272// =======================================================================
273bool Font_FTFont::HasSymbol (Standard_Utf32Char theUChar) const
274{
275 return FT_Get_Char_Index (myFTFace, theUChar) != 0;
276}
277
a174a3c5 278// =======================================================================
279// function : loadGlyph
280// purpose :
281// =======================================================================
282bool Font_FTFont::loadGlyph (const Standard_Utf32Char theUChar)
283{
284 if (myUChar == theUChar)
285 {
82be4141 286 return myUChar != 0;
a174a3c5 287 }
288
289 myGlyphImg.Clear();
290 myUChar = 0;
912761ea 291 myActiveFTFace = myFTFace;
292 if (theUChar == 0)
293 {
294 return false;
295 }
296
297 if (myToUseUnicodeSubsetFallback
298 && !HasSymbol (theUChar))
299 {
300 // try using fallback
301 const Font_UnicodeSubset aSubset = CharSubset (theUChar);
302 if (findAndInitFallback (aSubset)
303 && myFallbackFaces[aSubset]->HasSymbol (theUChar))
304 {
305 myActiveFTFace = myFallbackFaces[aSubset]->myFTFace;
306 }
307 }
308
309 if (FT_Load_Char (myActiveFTFace, theUChar, FT_Int32(myLoadFlags)) != 0
310 || myActiveFTFace->glyph == NULL)
a174a3c5 311 {
312 return false;
313 }
314
315 myUChar = theUChar;
316 return true;
317}
318
319// =======================================================================
320// function : RenderGlyph
321// purpose :
322// =======================================================================
323bool Font_FTFont::RenderGlyph (const Standard_Utf32Char theUChar)
324{
325 myGlyphImg.Clear();
326 myUChar = 0;
912761ea 327 myActiveFTFace = myFTFace;
328
329 if (theUChar != 0
330 && myToUseUnicodeSubsetFallback
331 && !HasSymbol (theUChar))
332 {
333 // try using fallback
334 const Font_UnicodeSubset aSubset = CharSubset (theUChar);
335 if (findAndInitFallback (aSubset)
336 && myFallbackFaces[aSubset]->HasSymbol (theUChar))
337 {
338 myActiveFTFace = myFallbackFaces[aSubset]->myFTFace;
339 }
340 }
341
a174a3c5 342 if (theUChar == 0
912761ea 343 || FT_Load_Char (myActiveFTFace, theUChar, FT_Int32(myLoadFlags | FT_LOAD_RENDER)) != 0
344 || myActiveFTFace->glyph == NULL
345 || myActiveFTFace->glyph->format != FT_GLYPH_FORMAT_BITMAP)
a174a3c5 346 {
347 return false;
348 }
349
912761ea 350 FT_Bitmap aBitmap = myActiveFTFace->glyph->bitmap;
351 if (aBitmap.buffer == NULL || aBitmap.width == 0 || aBitmap.rows == 0)
a174a3c5 352 {
353 return false;
354 }
912761ea 355
356 if (aBitmap.pixel_mode == FT_PIXEL_MODE_GRAY)
357 {
358 if (!myGlyphImg.InitWrapper (Image_Format_Alpha, aBitmap.buffer,
359 aBitmap.width, aBitmap.rows, Abs (aBitmap.pitch)))
360 {
361 return false;
362 }
363 myGlyphImg.SetTopDown (aBitmap.pitch > 0);
364 }
365 else if (aBitmap.pixel_mode == FT_PIXEL_MODE_MONO)
366 {
367 if (!myGlyphImg.InitTrash (Image_Format_Gray, aBitmap.width, aBitmap.rows))
368 {
369 return false;
370 }
371
372 myGlyphImg.SetTopDown (aBitmap.pitch > 0);
373 const int aNumOfBytesInRow = aBitmap.width / 8 + (aBitmap.width % 8 ? 1 : 0);
374 for (int aRow = 0; aRow < (int )aBitmap.rows; ++aRow)
375 {
376 for (int aCol = 0; aCol < (int )aBitmap.width; ++aCol)
377 {
378 const int aBitOn = aBitmap.buffer[aNumOfBytesInRow * aRow + aCol / 8] & (0x80 >> (aCol % 8));
379 *myGlyphImg.ChangeRawValue (aRow, aCol) = aBitOn ? 255 : 0;
380 }
381 }
382 }
383 else
a174a3c5 384 {
385 return false;
386 }
912761ea 387
a174a3c5 388 myUChar = theUChar;
389 return true;
390}
391
392// =======================================================================
393// function : GlyphMaxSizeX
394// purpose :
395// =======================================================================
912761ea 396unsigned int Font_FTFont::GlyphMaxSizeX (bool theToIncludeFallback) const
a174a3c5 397{
912761ea 398 if (!theToIncludeFallback)
399 {
400 float aWidth = (FT_IS_SCALABLE(myFTFace) != 0)
401 ? float(myFTFace->bbox.xMax - myFTFace->bbox.xMin) * (float(myFTFace->size->metrics.x_ppem) / float(myFTFace->units_per_EM))
402 : fromFTPoints<float> (myFTFace->size->metrics.max_advance);
403 return (unsigned int)(aWidth + 0.5f);
404 }
405
406 unsigned int aWidth = GlyphMaxSizeX (false);
407 if (theToIncludeFallback)
408 {
409 for (Standard_Integer aFontIter = 0; aFontIter < Font_UnicodeSubset_NB; ++aFontIter)
410 {
411 if (!myFallbackFaces[aFontIter].IsNull()
412 && myFallbackFaces[aFontIter]->IsValid())
413 {
414 aWidth = std::max (aWidth, myFallbackFaces[aFontIter]->GlyphMaxSizeX (false));
415 }
416 }
417 }
418 return aWidth;
a174a3c5 419}
420
421// =======================================================================
422// function : GlyphMaxSizeY
423// purpose :
424// =======================================================================
912761ea 425unsigned int Font_FTFont::GlyphMaxSizeY (bool theToIncludeFallback) const
a174a3c5 426{
912761ea 427 if (!theToIncludeFallback)
428 {
429 float aHeight = (FT_IS_SCALABLE(myFTFace) != 0)
430 ? float(myFTFace->bbox.yMax - myFTFace->bbox.yMin) * (float(myFTFace->size->metrics.y_ppem) / float(myFTFace->units_per_EM))
431 : fromFTPoints<float> (myFTFace->size->metrics.height);
432 return (unsigned int)(aHeight + 0.5f);
433 }
434
435 unsigned int aHeight = GlyphMaxSizeY (false);
436 if (theToIncludeFallback)
437 {
438 for (Standard_Integer aFontIter = 0; aFontIter < Font_UnicodeSubset_NB; ++aFontIter)
439 {
440 if (!myFallbackFaces[aFontIter].IsNull()
441 && myFallbackFaces[aFontIter]->IsValid())
442 {
443 aHeight = std::max (aHeight, myFallbackFaces[aFontIter]->GlyphMaxSizeY (false));
444 }
445 }
446 }
447 return aHeight;
a174a3c5 448}
449
f9801cf9 450// =======================================================================
451// function : Ascender
452// purpose :
453// =======================================================================
454float Font_FTFont::Ascender() const
455{
456 return float(myFTFace->ascender) * (float(myFTFace->size->metrics.y_ppem) / float(myFTFace->units_per_EM));
457}
458
459// =======================================================================
460// function : Descender
461// purpose :
462// =======================================================================
463float Font_FTFont::Descender() const
464{
465 return float(myFTFace->descender) * (float(myFTFace->size->metrics.y_ppem) / float(myFTFace->units_per_EM));
466}
467
468// =======================================================================
469// function : LineSpacing
470// purpose :
471// =======================================================================
472float Font_FTFont::LineSpacing() const
473{
474 return float(myFTFace->height) * (float(myFTFace->size->metrics.y_ppem) / float(myFTFace->units_per_EM));
475}
476
a174a3c5 477// =======================================================================
478// function : AdvanceX
479// purpose :
480// =======================================================================
82be4141 481float Font_FTFont::AdvanceX (Standard_Utf32Char theUChar,
482 Standard_Utf32Char theUCharNext)
a174a3c5 483{
484 loadGlyph (theUChar);
485 return AdvanceX (theUCharNext);
486}
487
488// =======================================================================
489// function : AdvanceY
490// purpose :
491// =======================================================================
82be4141 492float Font_FTFont::AdvanceY (Standard_Utf32Char theUChar,
493 Standard_Utf32Char theUCharNext)
a174a3c5 494{
495 loadGlyph (theUChar);
496 return AdvanceY (theUCharNext);
497}
498
912761ea 499// =======================================================================
500// function : getKerning
501// purpose :
502// =======================================================================
82be4141 503bool Font_FTFont::getKerning (FT_Vector& theKern,
504 Standard_Utf32Char theUCharCurr,
505 Standard_Utf32Char theUCharNext) const
506{
507 theKern.x = 0;
508 theKern.y = 0;
912761ea 509 if (theUCharNext != 0 && FT_HAS_KERNING(myActiveFTFace) != 0)
82be4141 510 {
912761ea 511 const FT_UInt aCharCurr = FT_Get_Char_Index (myActiveFTFace, theUCharCurr);
512 const FT_UInt aCharNext = FT_Get_Char_Index (myActiveFTFace, theUCharNext);
82be4141 513 if (aCharCurr == 0 || aCharNext == 0
912761ea 514 || FT_Get_Kerning (myActiveFTFace, aCharCurr, aCharNext, FT_KERNING_UNFITTED, &theKern) != 0)
82be4141 515 {
516 theKern.x = 0;
517 theKern.y = 0;
518 return false;
519 }
520 return true;
521 }
522 return false;
523}
524
a174a3c5 525// =======================================================================
526// function : AdvanceX
527// purpose :
528// =======================================================================
82be4141 529float Font_FTFont::AdvanceX (Standard_Utf32Char theUCharNext) const
a174a3c5 530{
531 if (myUChar == 0)
532 {
533 return 0.0f;
534 }
535
82be4141 536 FT_Vector aKern;
537 getKerning (aKern, myUChar, theUCharNext);
912761ea 538 return myWidthScaling * fromFTPoints<float> (myActiveFTFace->glyph->advance.x + aKern.x);
a174a3c5 539}
540
541// =======================================================================
542// function : AdvanceY
543// purpose :
544// =======================================================================
82be4141 545float Font_FTFont::AdvanceY (Standard_Utf32Char theUCharNext) const
a174a3c5 546{
547 if (myUChar == 0)
548 {
549 return 0.0f;
550 }
551
82be4141 552 FT_Vector aKern;
553 getKerning (aKern, myUChar, theUCharNext);
912761ea 554 return fromFTPoints<float> (myActiveFTFace->glyph->advance.y + aKern.y);
f9801cf9 555}
556
557// =======================================================================
558// function : GlyphsNumber
559// purpose :
560// =======================================================================
912761ea 561Standard_Integer Font_FTFont::GlyphsNumber (bool theToIncludeFallback) const
f9801cf9 562{
912761ea 563 Standard_Integer aNbGlyphs = myFTFace->num_glyphs;
564 if (theToIncludeFallback)
565 {
566 for (Standard_Integer aFontIter = 0; aFontIter < Font_UnicodeSubset_NB; ++aFontIter)
567 {
568 if (!myFallbackFaces[aFontIter].IsNull()
569 && myFallbackFaces[aFontIter]->IsValid())
570 {
571 aNbGlyphs += myFallbackFaces[aFontIter]->GlyphsNumber (false);
572 }
573 }
574 }
575 return aNbGlyphs;
f9801cf9 576}
577
578// =======================================================================
912761ea 579// function : GlyphRect
f9801cf9 580// purpose :
581// =======================================================================
582void Font_FTFont::GlyphRect (Font_Rect& theRect) const
583{
912761ea 584 const FT_Bitmap& aBitmap = myActiveFTFace->glyph->bitmap;
585 theRect.Left = float(myActiveFTFace->glyph->bitmap_left);
586 theRect.Top = float(myActiveFTFace->glyph->bitmap_top);
587 theRect.Right = float(myActiveFTFace->glyph->bitmap_left + (int )aBitmap.width);
588 theRect.Bottom = float(myActiveFTFace->glyph->bitmap_top - (int )aBitmap.rows);
a174a3c5 589}
317d68c9 590
591// =======================================================================
592// function : BoundingBox
593// purpose :
594// =======================================================================
d2eddacc 595Font_Rect Font_FTFont::BoundingBox (const NCollection_String& theString,
596 const Graphic3d_HorizontalTextAlignment theAlignX,
597 const Graphic3d_VerticalTextAlignment theAlignY)
317d68c9 598{
599 Font_TextFormatter aFormatter;
600 aFormatter.SetupAlignment (theAlignX, theAlignY);
601 aFormatter.Reset();
602
603 aFormatter.Append (theString, *this);
604 aFormatter.Format();
605
d2eddacc 606 Font_Rect aBndBox;
317d68c9 607 aFormatter.BndBox (aBndBox);
317d68c9 608 return aBndBox;
609}