[cairo] Re: Cairo font size should be in units

graydon hoare graydon at redhat.com
Tue Sep 28 13:15:27 PDT 2004


On Tue, 28 Sep 2004 11:11:30 -0700, Bill Spitzak <spitzak at d2.com> wrote:

> My only concern here is to avoid some problems with some other API's. I am
> quite tired of API's where fonts are measured in a different transform
> (usually called "points") than every other graphic. Something about fonts
> seems to produce some mental block so that people keep thinking this is a
> good idea. But would you suggest that "stroke" scale the path so it is in
> points, while "fill" is in units? No? Would anybody even suggest that the
> thickness of lines be measured in "points"? I don't think so. Well doing
> fonts in anything other than units is equally wrong, please stop doing it!

well, we've discussed this somewhat on IRC, and I certainly accept what you're
saying. it may well make sense to do as you say -- it is only a couple lines of
code to change either way -- but I'd like to clarify that this is not actually
a cut-and-dried issues as you suggest:

  - the line spacing is only one metric of a font. you might be interested in
    its ascent, descent, control box height, line spacing, max advance,
    or any number of metrics (in device-dependent or device-independent terms).
    if we make the API take a single number to "size" a font, it's always going
    to be arbitrary which metric that number corresponds to.

  - the font itself doesn't actually provide any interface to asking for font
    sizes by line height; it can at best give you fonts by EM size (either in
    points -- accompanied by a device DPI -- or device units). EM size
    is a completely useless unit for doing layout in, but it turns out that
    many fonts have manual hints at particular integral EM sizes, such as
    10, 12, and 14 points.

  - the correct way to do layout is to pick an EM size which is somewhat near
    the ballpark you want, and ask for metrics. the metrics we provide have
    the information you need. whether your response to those metrics is to
    scale your application to fit them, or request a different font size to
    fit into a fixed-size application, is a heuristic cairo cannot choose for
    you.

    deciding on a layout metric a priori and then telling the graphics toolkit
    to "make the font suit that size" is imo the wrong way to design it; it
    can't even be guaranteed to *work* for anything other than EM size. you
    may measure the unscaled line height, divide out the EM size to get a scale
    factor, set that factor, and get a font which hints to some entirely
    different line height. should cairo hard-code that heuristic? I think no.

  - many APIs start with points. many naive programmers sitting down to work
    with cairo will want to speak in terms of points. points are a natural
    unit which users work in. they may expect to be able to say something like
    "cairo_scale_font(12)" to get a 12 point font. if they do that with our
    current setup, they will get a 12*(72/96) = 9 point font, which in many
    typefaces doesn't even have proper hinting and looks terrible. sure, you
    may say "they are dumb and need to learn the right thing to do"; I think
    part of cairo's purpose, however, is to make the API easy to use correctly.
    perhaps a "cairo_set_font_pointsize()" call would suffice?

> Also it is important (and I think everybody agrees) that Cairo scales should
> not cause the glyph metricies to change, although the glyphs themselves can
> change shape a bit due to hinting. Otherwise it is impossible to reproduce a
> graphic at different sizes, for instance a simple right-justify will no
> longer produce a straight right edge.

I don't understand this paragraph. do you mean:

   - changing cairo's CTM should not cause a glyph's metrics to change?
   - changing cairo's CTM should not cause such metrics to change in a
     way other than multiplying each metric by the CTM?
   - changing cairo's CTM should not cause a font's matrix to change?
   - changing cairo's font matrix should not cause a glyph's metrics to change?
   - something else?

> Yes I know that modern font renderers change the shapes of the glyphs
> depending on the "size". But this should be completely decoupled from the
> actual scale of the glyphs. Selecting a font should be something like:
>
> 	cairo_select_font(name, 2x2matrix, float point_size)
>
> point_size is used to change the glyph shapes. However the actual glyph sizes
> are directly controlled by the 2x2 matrix. If the matrix is [10,0,0,10] then
> the line spacing should be 10 *UNITS*, whether the point_size is 0 or 10^8.

again, I'd summarize my above points:

  - line spacing is only one interpretation of a 2x2 matrix.
  - it is not necessarily the interpretation every user will have.
  - you can't reliably do much layout only knowing line spacing.
  - you can't ask the font to assume a particular line spacing, directly, anyways.

> An alternative that I like is to have no point_size argument at all. Instead
> it is calculated by taking a [0,1] vector, transforming it using the font
> matrix, then multiplying by 72/96 and this is the resulting point size
> (notice that only the font matrix, not the Cairo transform, is used for this).

in a sense this is exactly what we'll be doing if we adopt the approach which
is actually permitted by the font API, which is "make the font appear to have
a 1 user unit EM square when first constructed". I find this not significantly
superior to using "1 point" -- you still should not use that number to do any
layout calculations -- but maybe the ability to draw a box around the EM tile
of the font is important. who knows.

> A third alternative, that I think you are using, is that there is a DPI value
> that replaces the /96 in the above equation, but can be changed by a Cairo
> API. This DPI value must NOT be changed by Cairo transforms!

no, I have merely built the existing code to set the font size in terms of points,
scaled by the CTM. since points are a physical unit, they need to be reduced to a
device unit before rendering; I do this using the device's DPI (which cairo can
retrieve from a surface) before rendering. the DPI of a surface cannot change in
cairo.

but as you (and carl, and others) have argued, maybe a "1 user unit EM square"
makes more sense as an initial font size than a "(1 point * CTM) EM square". it
only takes a moment to change. I have tried it both ways and it works. the
practical difference is that fonts are 0.75x smaller or larger (this 72/96 ratio)
given the same client code. I'm open to either approach.

I would however argue that the approach of hard-coding in a "search for a
particular line height" heuristic is not desirable. if you want to follow that
heuristic, your toolkit should do something like this:

do {
   cairo_scale_font (ct, scale);
   cairo_get_font_extents (ct, extents);
   scale = line_height / extents.height;
} while (++iter < 100 && line_height != extents.height);

-graydon



More information about the cairo mailing list