<div dir="ltr"><div><span style="font-family:monospace">I've done some testing of LogicalFontInstance::GetGlyphBoundRect(). Each platform seems to give different values!</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">The commit in gerrit is: <a href="https://gerrit.libreoffice.org/c/core/+/141234">https://gerrit.libreoffice.org/c/core/+/141234</a></span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">(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 <a href="https://gerrit.libreoffice.org/c/core/+/141103/">https://gerrit.libreoffice.org/c/core/+/141103/</a>)</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">Ultimately, the differences boil down to the pure function LogicalFontInstance::ImplGetGlyphBoundRect(), which each platform must implement to get the glyph's bounding rect. <br></span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">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 :-)</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">Chris</span></div><div><span style="font-family:monospace">---<br></span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">Comparion of LogicalFontInstance::ImplGetGlyphBoundRect() between platforms</span></div><div><span style="font-family:monospace"></span></div><span style="font-family:monospace">============================================================================<br><br>WIN32<br>-----<br><br>ImplGetGlyphBoundRect() is implemented in WinFontInstance, derived by<br>from LogicalFontInstance<br><br>Located in vcl/win/gdi/salfont.cxx<br><br>Process:<br><br>Step 1: select the font<br>-----------------------<br><br>1. Get the HDC of the current graphics<br>2. Get the current GDI font's HFONT<br>3. Get the the HFONT of the font referenced by the WinFontInstance<br>4. If the current GDI HFONT is not the WinFontInstance's HFONT then<br>   explicitly select the WinFontInstance's HFONT<br>5. Setup a guard to restore the original font after<br>   ImplGetGlyphBoundRect() finishes<br><br>Step 2: Setup transformation matrix<br>-----------------------------------<br><br>MAT2 is a 3x3 transformation matrix<br><br>If using horizontal text, setup an identity matrix (means that nothing<br>happens when applying the matrix)<br><br>If using vertical writing then matrix appropriately rotates the glyph<br><br>Step 3: Setup to get the glyph's bounding rect<br>----------------------------------------------<br><br>1. Set the flag for GetGlyphOutlineW to use GGO_METRICS and<br>   GGO_GLYPH_INDEX<br><br>   - GGO_METRICS indicates to retrieve the GLYPHMETRICS structure<br>   - GGO_GLYPH_INDEX indicates that we use the TrueType glyph index<br>     instead of the character code<br><br>2. Zero initialize the GLYPHMETRICS fields<br><br>3. Call on GetGlyphOutlineW using the transformation matrix to<br>   populate the glyph metrics<br><br>Step 4: Get the bounding rect of the glyph<br>------------------------------------------<br><br>The next bit takes the glyph metrics from the previous step.<br><br>1. Populate the glyph rectangle with the origin being the x- and y-<br>   coords of the upper left corner of the smallest rectangle that<br>   completely encloses the glyph, and the width and height of the<br>   glyph's "black box", which is the smallest rectangle that<br>   encloses the glyph<br><br>2. Scale the bounding rectangle, adding a point to the right and<br>   bottom coords of the rectangle<br><br>SUMMARY:<br><br>Basically, we call on Win32's GetGlyphOutlineW() to get the GLYPHMETRICS.<br>It is important to quote Microsoft on this structure:<br><br>   The GLYPHMETRICS structure specifies the width of the character cell<br>   and the location of a glyph within the character cell. The origin of<br>   the character cell is located at the left side of the cell at the<br>   baseline of the font. The location of the glyph origin is relative to<br>   the character cell origin. The height of a character cell, the<br>   baseline, and other metrics global to the font are given by the<br>   OUTLINETEXTMETRIC structure.<br><br><a href="https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getglyphoutlinew">https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-getglyphoutlinew</a><br><br><br>MAC<br>---<br><br>ImplGetGlyphBoundRect() is implemented in CoreTextStyle, derived from<br>LogicalFontInstance<br><br>Located in vcl/quartz/ctfont.cxx<br><br>Process:<br><br>Step 1: Get the glyph rectangle<br>-------------------------------<br><br>1. Set the CGGlyph variable nCGGlyph the glyph index<br>2. Get the font by looking up the mpStyleDict dictionary for the<br>   kCTFontAttributeName, which gives the font of the text to<br>   which this attribute applies<br><br>Note: Currently does not handle vertical text<br><br>3. Get the glyph rectangle in a CGRect by calling on<br>   CTFontGetBoundingRectsForGlyphs()<br>4. Apply any font rotation for horizontal text<br><br>Step 2: Return the tools::Rectangle bounding rect<br>-------------------------------------------------<br><br>1. std::floor() the origin x, y (i.e. the top left)<br>2. std::ceil() the bottom right of the rectangle<br>   i.e. to get this, aCGRect.origin.x + aCGRect.size.width<br>                     aCGRect.origin.y + aCGRect.size.height<br><br>3. so the rectangle gets the positive x origin and a negative y origin (???)<br>   and for the bottom left a positive x and a negative y (???)<br><br>SUMMARY:<br><br>Use CTFontGetBoundRectsForGlyphs() to get the bounding rect of<br>the glyph. <br><br>Note that the Core Text documentation says the following:<br><br>  The bounding rectangles of the individual glyphs are returned through<br>  the boundingRects parameter. These are the design metrics from the<br>  font transformed in font space.<br><br><a href="https://developer.apple.com/documentation/coretext/1509419-ctfontgetboundingrectsforglyphs">https://developer.apple.com/documentation/coretext/1509419-ctfontgetboundingrectsforglyphs</a><br><br><br>UNIX<br>====<br><br>Note there are two variants: Qt and Freetype<br><br>Qt<br>==<br><br>ImplGetGlyphBoundRect() is implemented in QtFont, derived from<br>LogicalFontInstance<br><br>Located in vcl/qt5/QtFont.cxx<br><br>Process:<br><br>Literally gets the font, then gets the glyph bounding rect, which it<br>converts to a tools::Rectangle<br><br>SUMMARY:<br><br>Kind of opaque - not sure how Qt derives the bounding rect. Qt's<br>documentation has one line to describe it, which is:<br><br>   Returns the smallest rectangle containing the glyph with the given<br>   glyphIndex.<br><br><a href="https://doc.qt.io/qt-5/qrawfont.html#boundingRect">https://doc.qt.io/qt-5/qrawfont.html#boundingRect</a><br><br><br>FreeType<br>========<br><br>ImplGetGlyphBoundRect() is implmeneted in FreetypeFontInstance, derived<br>from LogicalFontInstance. This is, however, merely a wrapper<br>around FreeTypeFont::GetGlyphBoundRect() via member mxFreeTypeFont.<br><br>FreeTypeFont::GetGlyphBoundRect() is defined in<br>vcl/unx/generic/glyphs/freetype_glyphcache.cxx<br><br>Process:<br><br>Step 1: Load the font<br>---------------------<br><br>1. activates the size of the font face via FT_Activate_Size() for<br>   FT_Load_Glyph()<br>2. loads the glyph (see below)<br><br>Step 2: Get the glyph<br>---------------------<br><br>1. embolden the glyph if needed<br>2. loads the glyph<br>3. applies a tranform to the glyph is needed<br><br>Step 3: Get the bounds box of the glyph<br>---------------------------------------<br><br>1. Load the glyph's control box, which encloses the outlines<br>   points, via FT_Glyph_Get_CBox(), using the flag<br>   FT_GLYPH_BBOX_PIXELS<br>2. destroys the glyph via FT_Done_Glyph()<br>3. Creates a tools::Rectangle via:<br><br>   tools::Rectangle aRect(aBbox.xMin, -aBbox.yMax, aBbox.xMax, -aBbox.yMin);<br><br>4. Calculates bounding rectangle of rotated glyph if necessary<br><br>NOTES:<br><br>A few things to note - firstly the font is loaded via the flags FT_LOAD_DEFAULT<br>and FT_LOAD_IGNORE_TRANSFORM. (see below)<br><br>The second thing is: a FT_BBox has a very specific format in turns of <br>negative yMin, yMax, xMin and xMax values. We seem to ignore these! (see below)<br><br><br>From <a href="https://freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx">https://freetype.org/freetype2/docs/reference/ft2-base_interface.html#ft_load_xxx</a><br><br>   FT_LOAD_DEFAULT<br><br>   Corresponding to 0, this value is used as the default glyph load<br>   operation. In this case, the following happens:<br><br>   1. FreeType looks for a bitmap for the glyph corresponding to the<br>      face's current size. If one is found, the function returns. The<br>      bitmap data can be accessed from the glyph slot (see note<br>      below).<br><br>   2. If no embedded bitmap is searched for or found, FreeType looks for<br>      a scalable outline. If one is found, it is loaded from the font<br>      file, scaled to device pixels, then ‘hinted’ to the pixel grid in<br>      order to optimize it. The outline data can be accessed from the<br>      glyph slot (see note below).<br><br>   Note that by default the glyph loader doesn't render outlines into<br>   bitmaps.<br><br>   FT_LOAD_IGNORE_TRANSFORM<br><br>   Ignore the transform matrix set by FT_Set_Transform.<br><br>From <a href="https://freetype.org/freetype2/docs/reference/ft2-basic_types.html#ft_bbox">https://freetype.org/freetype2/docs/reference/ft2-basic_types.html#ft_bbox</a><br><br>   FT_BBox<br><br>   Defined in FT_IMAGE_H (freetype/ftimage.h).<br><br>   typedef struct  FT_BBox_<br>   {<br>      FT_Pos  xMin, yMin;<br>      FT_Pos  xMax, yMax;<br>   } FT_BBox;<br><br>   A structure used to hold an outline's bounding box, i.e., the<br>   coordinates of its extrema in the horizontal and vertical<br>   directions.<br><br>   fields<br>      xMin: The horizontal minimum (left-most).<br>      yMin: The vertical minimum (bottom-most).<br>      xMax: The horizontal maximum (right-most).<br>      yMax: The vertical maximum (top-most).<br><br>   note<br><br>   The bounding box is specified with the coordinates of the lower left<br>   and the upper right corner. In PostScript, those values are often<br>   called (llx,lly) and (urx,ury), respectively.<br><br>   If yMin is negative, this value gives the glyph's descender. Otherwise,<br>   the glyph doesn't descend below the baseline. Similarly, if yMax is<br>   positive, this value gives the glyph's ascender.<br><br>   xMin gives the horizontal distance from the glyph's origin to the left<br>   edge of the glyph's bounding box. If xMin is negative, the glyph<br>   extends to the left of the origin.</span></div>