[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