Comparion of LogicalFontInstance::ImplGetGlyphBoundRect() between platforms

Chris Sherlock chris.sherlock79 at gmail.com
Sat Oct 15 09:12:48 UTC 2022


I've done some testing of LogicalFontInstance::GetGlyphBoundRect(). Each
platform seems to give different values!

The commit in gerrit is: https://gerrit.libreoffice.org/c/core/+/141234

(many thanks to Hossein for some suggestions in a different gerrit patch
about some things around unit tests I didn't know - see comments at
https://gerrit.libreoffice.org/c/core/+/141103/)

Ultimately, the differences boil down to the pure function
LogicalFontInstance::ImplGetGlyphBoundRect(), which each platform must
implement to get the glyph's bounding rect.

To try to understand the differences, I've looked at each platform's
implementation with some notes, if this is at all helpful. Any comments
would be appreciated! I'd love to standardize this function :-)

Chris
---

Comparion of LogicalFontInstance::ImplGetGlyphBoundRect() between platforms
============================================================================

WIN32
-----

ImplGetGlyphBoundRect() is implemented in WinFontInstance, derived by
from LogicalFontInstance

Located in vcl/win/gdi/salfont.cxx

Process:

Step 1: select the font
-----------------------

1. Get the HDC of the current graphics
2. Get the current GDI font's HFONT
3. Get the the HFONT of the font referenced by the WinFontInstance
4. If the current GDI HFONT is not the WinFontInstance's HFONT then
   explicitly select the WinFontInstance's HFONT
5. Setup a guard to restore the original font after
   ImplGetGlyphBoundRect() finishes

Step 2: Setup transformation matrix
-----------------------------------

MAT2 is a 3x3 transformation matrix

If using horizontal text, setup an identity matrix (means that nothing
happens when applying the matrix)

If using vertical writing then matrix appropriately rotates the glyph

Step 3: Setup to get the glyph's bounding rect
----------------------------------------------

1. Set the flag for GetGlyphOutlineW to use GGO_METRICS and
   GGO_GLYPH_INDEX

   - GGO_METRICS indicates to retrieve the GLYPHMETRICS structure
   - GGO_GLYPH_INDEX indicates that we use the TrueType glyph index
     instead of the character code

2. Zero initialize the GLYPHMETRICS fields

3. Call on GetGlyphOutlineW using the transformation matrix to
   populate the glyph metrics

Step 4: Get the bounding rect of the glyph
------------------------------------------

The next bit takes the glyph metrics from the previous step.

1. Populate the glyph rectangle with the origin being the x- and y-
   coords of the upper left corner of the smallest rectangle that
   completely encloses the glyph, and the width and height of the
   glyph's "black box", which is the smallest rectangle that
   encloses the glyph

2. Scale the bounding rectangle, adding a point to the right and
   bottom coords of the rectangle

SUMMARY:

Basically, we call on Win32's GetGlyphOutlineW() to get the GLYPHMETRICS.
It is important to quote Microsoft on this structure:

   The GLYPHMETRICS structure specifies the width of the character cell
   and the location of a glyph within the character cell. The origin of
   the character cell is located at the left side of the cell at the
   baseline of the font. The location of the glyph origin is relative to
   the character cell origin. The height of a character cell, the
   baseline, and other metrics global to the font are given by the
   OUTLINETEXTMETRIC structure.

https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getglyphoutlinew


MAC
---

ImplGetGlyphBoundRect() is implemented in CoreTextStyle, derived from
LogicalFontInstance

Located in vcl/quartz/ctfont.cxx

Process:

Step 1: Get the glyph rectangle
-------------------------------

1. Set the CGGlyph variable nCGGlyph the glyph index
2. Get the font by looking up the mpStyleDict dictionary for the
   kCTFontAttributeName, which gives the font of the text to
   which this attribute applies

Note: Currently does not handle vertical text

3. Get the glyph rectangle in a CGRect by calling on
   CTFontGetBoundingRectsForGlyphs()
4. Apply any font rotation for horizontal text

Step 2: Return the tools::Rectangle bounding rect
-------------------------------------------------

1. std::floor() the origin x, y (i.e. the top left)
2. std::ceil() the bottom right of the rectangle
   i.e. to get this, aCGRect.origin.x + aCGRect.size.width
                     aCGRect.origin.y + aCGRect.size.height

3. so the rectangle gets the positive x origin and a negative y origin (???)
   and for the bottom left a positive x and a negative y (???)

SUMMARY:

Use CTFontGetBoundRectsForGlyphs() to get the bounding rect of
the glyph.

Note that the Core Text documentation says the following:

  The bounding rectangles of the individual glyphs are returned through
  the boundingRects parameter. These are the design metrics from the
  font transformed in font space.

https://developer.apple.com/documentation/coretext/1509419-ctfontgetboundingrectsforglyphs


UNIX
====

Note there are two variants: Qt and Freetype

Qt
==

ImplGetGlyphBoundRect() is implemented in QtFont, derived from
LogicalFontInstance

Located in vcl/qt5/QtFont.cxx

Process:

Literally gets the font, then gets the glyph bounding rect, which it
converts to a tools::Rectangle

SUMMARY:

Kind of opaque - not sure how Qt derives the bounding rect. Qt's
documentation has one line to describe it, which is:

   Returns the smallest rectangle containing the glyph with the given
   glyphIndex.

https://doc.qt.io/qt-5/qrawfont.html#boundingRect


FreeType
========

ImplGetGlyphBoundRect() is implmeneted in FreetypeFontInstance, derived
from LogicalFontInstance. This is, however, merely a wrapper
around FreeTypeFont::GetGlyphBoundRect() via member mxFreeTypeFont.

FreeTypeFont::GetGlyphBoundRect() is defined in
vcl/unx/generic/glyphs/freetype_glyphcache.cxx

Process:

Step 1: Load the font
---------------------

1. activates the size of the font face via FT_Activate_Size() for
   FT_Load_Glyph()
2. loads the glyph (see below)

Step 2: Get the glyph
---------------------

1. embolden the glyph if needed
2. loads the glyph
3. applies a tranform to the glyph is needed

Step 3: Get the bounds box of the glyph
---------------------------------------

1. Load the glyph's control box, which encloses the outlines
   points, via FT_Glyph_Get_CBox(), using the flag
   FT_GLYPH_BBOX_PIXELS
2. destroys the glyph via FT_Done_Glyph()
3. Creates a tools::Rectangle via:

   tools::Rectangle aRect(aBbox.xMin, -aBbox.yMax, aBbox.xMax, -aBbox.yMin);

4. Calculates bounding rectangle of rotated glyph if necessary

NOTES:

A few things to note - firstly the font is loaded via the flags
FT_LOAD_DEFAULT
and FT_LOAD_IGNORE_TRANSFORM. (see below)

The second thing is: a FT_BBox has a very specific format in turns of
negative yMin, yMax, xMin and xMax values. We seem to ignore these! (see
below)


From
https://freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx

   FT_LOAD_DEFAULT

   Corresponding to 0, this value is used as the default glyph load
   operation. In this case, the following happens:

   1. FreeType looks for a bitmap for the glyph corresponding to the
      face's current size. If one is found, the function returns. The
      bitmap data can be accessed from the glyph slot (see note
      below).

   2. If no embedded bitmap is searched for or found, FreeType looks for
      a scalable outline. If one is found, it is loaded from the font
      file, scaled to device pixels, then ‘hinted’ to the pixel grid in
      order to optimize it. The outline data can be accessed from the
      glyph slot (see note below).

   Note that by default the glyph loader doesn't render outlines into
   bitmaps.

   FT_LOAD_IGNORE_TRANSFORM

   Ignore the transform matrix set by FT_Set_Transform.

From
https://freetype.org/freetype2/docs/reference/ft2-basic_types.html#ft_bbox

   FT_BBox

   Defined in FT_IMAGE_H (freetype/ftimage.h).

   typedef struct  FT_BBox_
   {
      FT_Pos  xMin, yMin;
      FT_Pos  xMax, yMax;
   } FT_BBox;

   A structure used to hold an outline's bounding box, i.e., the
   coordinates of its extrema in the horizontal and vertical
   directions.

   fields
      xMin: The horizontal minimum (left-most).
      yMin: The vertical minimum (bottom-most).
      xMax: The horizontal maximum (right-most).
      yMax: The vertical maximum (top-most).

   note

   The bounding box is specified with the coordinates of the lower left
   and the upper right corner. In PostScript, those values are often
   called (llx,lly) and (urx,ury), respectively.

   If yMin is negative, this value gives the glyph's descender. Otherwise,
   the glyph doesn't descend below the baseline. Similarly, if yMax is
   positive, this value gives the glyph's ascender.

   xMin gives the horizontal distance from the glyph's origin to the left
   edge of the glyph's bounding box. If xMin is negative, the glyph
   extends to the left of the origin.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/libreoffice/attachments/20221015/2055f7b9/attachment.htm>


More information about the LibreOffice mailing list