[cairo] Win32 font notes

Owen Taylor otaylor at redhat.com
Mon Jan 31 21:03:26 PST 2005


Here's a dump of my notes on Win32 fonts based on a couple days of
experimentation.

If anybody happens to be an expert on Win32 fonts and wants to give
this a read-through and sanity-check / explain to me what I'm missing
I'd appreciate it.

Thanks,
					Owen

Needs
=====

First, what do we want out of the Win32 font system?

 A) We want to be able to draw a string of text in a way identical to what
    a native app would draw for the same string of text, with the same
    font fallbacks and so forth.

 B) We want to be able to get the glyph indices and positions for the
    way the system drew the string in A). By getting the outlines for
    the glyphs, we can then do text-as-path.

 C) Give a glyph, we want to be able to find out the advance of the
    glyph and the ink extents of the glyph. The ink extents shoudl
    be given as an axis-aligned rectangle in *user* space coordinates.
    
Functions
=========

ExtTextOut: This function, when called with a Unicode string, does font fallback, 
   shaping, reordering, and everything that you'd want from a decent
   text API.

   In combination with SetWorldTransform(), it can be used to draw
   arbitrarily transformed text as well.
  
   So, as long as we want to just *draw* strings, we have exactly what we need.

   Passing ETO_GLYPH_INDEX allows using this function for drawing glyphs.

GetTextExtentsPoint32: This function gives us the logical extents (advance,
   and ascent+descent) of a Unicode string using the same rules as
   ExtTextOut. The vertical extents seem to be the same as from
   GetTextMetrics, ignoring font fallback.

GetCharacterPlacement: This function was the pre-Uniscribe method of doing
   character => glyph translation. As long as the specified font covers
   all the glyphs, it seems to give results consistent with ExtTextOut.
   However, it doesn't do font fallback.

GetAbcCharWidths[Float]: These functions determine per-glyph advances; 
   The a c components are "space before", "space after", so b is something like the 
   ink width of the character.
  
   There is no apparent way to get the same information per *glyph* instead
   of per-character.

   The interpretation of these metrics with respect to transforms is exactly
   what we want. They are in user space and respect the choice of hinting
   vs. no-hinting that ExTextOut makes. (ExTextOut seems to turn off hinting
   if any transform is in place.

GetGlyphOutline: Despite the name, this function retrieves either metrics
   and/or glyph outlines. The metrics retrieved are the ink extents
   and X/Y advances in *device space*. Converting the X/Y advance from
   device space to user space is easy enough, but the ink device space
   ink extents only give a poor approximation to a user-space ink ink
   extents.

GetTextMetric: This function gets the metrics of the font in logical 
   units. The set of metrics isn't extensive, but covers everything
   that Cairo needs.

   The behavior with respect to transformation is obscure. For some reason,
   as soon as a font is transformed (by a non-90 degree angle) the ascent
   and descent increase by a large amount (same for all transformations).
   The amount increases with the point size ... it's consistently about
   25% bigger. (And much bigger than in the design of the font.)

   90-degree angles increase the ascent and descent, but by less and 
   not increasing with size. (1-2 pixels, independent of size.)

APIS
====

Other than the standard GDI functions listed above, there are two
relevant external DLLs

 - Uniscribe. The modern text layout services framework on Windows.
   Rather painful to use. This is being used internally to 
   the GDI functions to handle complex scripts.

 - MLang. Was introduced in the IE4 timeframe to allow appplications
   to access the system "font linking" mechanism that is used to
   do fallbacks for Asian fonts:
 
     http://msdn.microsoft.com/workshop/misc/mlang/tutorials/fontlinking.asp

   Seems to be distinct from the fallback mechanism used by ExTextOut
   which is associated with Uniscribe, though not *exposed* through uniscribe.

Conclusions
===========

So far, my research has been an incredibly frustrating excercise in 
so-close-and-yet-so-far. 

 - Windows *has* an internal font fallback mechanism
   but there is no way to access the results to get text-as-glyphs.

   Consider three sophisticated cross-platform systems for doing text on
   Windows, that use the Uniscribe engine directly. How do they do 
   fallbacks?

   - Pango: Uses a list of fallbacks in a config file.

   - OpenOffice: seems to use an configurable list of fallback fonts. Hard to
     decipher the code at a quick glance. (The list is passed in some
     fashion from the backend-independent part to the backend.)

   - SWT (from Eclipse): Uses the 'mlang' DLL font linking mechanism

   The end result is that the Toy API either is going to be very big and
   complex, or it isn't going to handle font fallbacks. And text fallbacks
   we can implement won't exactly match the system, though they would
   be better than nothing.

   (Odd note: GDI+ also has font fallbacks that are inconsistent with 
   ExtTextOut! It can handle font fallbacks for Hindi, but not for more 
   recently added scripts like Bengali, while ExtTextOut handles both.
   Also, ExtTextOut adjust point sizes for the different fonts to make
   their extents match, while GDI+ doesn't.)
  
   First pass is likely to be no-fallbacks. No-fallbacks also has the
   considerable advantage of avoiding fixing the fact that the backend
   interfaces aren't correct for doing fallbacks in the toy API... 
   the code assumes that a single glyph index suffices.

   If you wanted to be able to do font fallbacks in the toy API, the
   right thing to do is likely to simply to push show_text(), 
   text_extents(), and text_path() into the font backend perhaps
   with fallback implementations in terms of a text_to_glyphs().

 - Glyph metrics:

   Advances: We could compute them in device space and then translate
   back. The main problem with this is that unless we can get
   sufficient precision, we may end up with dy != 0 for text
   along the baseline, so characters drift off the baselines. (For
   monospace fonts, the error will be cumulative.)

   The other approach would be, when a non-scaling transform is
   in effect, use unhinted values to get font space numbers that
   we transform outselves into user space. (The standard way
   to get unhinted values using GetGlyphOutline is to ask for
   the font metrics at units-per-em point size. So, 2048 pt.
   for a normal TT font.)

   Some experiments indicate that this gives good, though not perfect
   agreement with the values Windows uses when laying out text. 

   The most obvious difference is that for a 90-degree transform,
   the metrics that Windows uses are rounded to integer values in
   device space. So, probably the right thing to do for 90-degree
   transforms is to use the metrics from Windows and rotate them
   ourselves. (How do you determine when something is a 90-degree
   rotation and you'll get integer metrics? Windows considers
   a 90.1 degree rotation a 90-degree rotation. A 90.2 degree
   rotation is however, not.)

   (Generally, we do want integer metrics for 90-degree transforms,
   because if we do positioning with non-integer metrics in
   device space, we'll get uneven character alignment. The same
   problem occurs for other transforms, but there is nothing
   we can do about it other than rendering text-as-geometry.)

   The nice thing about font-space method is that it makes it very
   easy to compute ink extents. Which is very painful otherwise.
   (We'd have to get the glyph outline, transform it into user
   space and then compute the bounding box of the outline.)

   The not-nice thing about it is that we are encoding assumptions
   about about when Windows hints and when Windows doesn't hint
   into Cairo.

 - Font metrics: GetTextMetrics is buggy with transformed fonts.
   The best thing to do is to, for anything other than 90 degree
   transforms, compute the text metrics in font space and scale
   those to user space.

Since various techniques above involve compute metrics in font space,
we might eventually want to cache font-space metrics for a font
independet of size/transform. This would involve figuring out "font
identity" for Windows, which isn't completely easy; the
facename/weight/italic triplet should be a reasonable approximation
where we have that information


-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: This is a digitally signed message part
Url : http://lists.freedesktop.org/archives/cairo/attachments/20050201/41a98613/attachment.pgp


More information about the cairo mailing list