freetype-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Devel] Bitmap font metric issues in FreeType


From: Keith Packard
Subject: [Devel] Bitmap font metric issues in FreeType
Date: Fri, 06 Feb 2004 16:08:20 -0800

I'm trying to avoid ever shipping .bdf/.pcf files from freedesktop.org and
so I'm using Juliusz's fonttosfnt application to convert the existing bitmap
fonts.  I aspire to make .ttf fonts loaded into the X server act exactly like
the same font loaded from a .bdf file..  I've run into some "issues" over
font metrics and labeling within FreeType and the .ttf file.

BDF fonts have essentially four different vertical metrics:

        1)      Nominal point size (POINT_SIZE)
        2)      Nominal pixel size (PIXEL_SIZE)
        3)      Font ascent/descent (FONT_ASCENT/FONT_DESCENT)
        4)      Maximum glyph ascent/descent (max over all glyphs)

  The max ascent/descent values are "uninteresting" in terms of spacing 
  the font or naming the font size, so we'll ignore them from here on out.

TTF has n different metrics:

        1)      Unscaled ascent/descent/gap in EMs ('hhea' Ascender/Descender)
        2)      Strike ascent/descent in pixels ('EBLC' line metrics ascender/
                descender)
        3)      Strike ppemX/ppemY in pixels ('EBLC' ppemX/ppemY)

FreeType has 3 different metrics:

        1)      bitmap "sizes"     (face->available_sizes[n])
        2)      Current size       (face->size.metrics)
        3)      Current v metrics  (face->ascender, face->descender, 
face->height)

  Here's what's inside the first two:

    face->available_sizes (FT_Bitmap_Size):

        height          baseline-to-baseline distance in pixels         (<< 0)
        width           average width in pixels                         (<< 0)
        size            nominal point size                              (<< 6)
        x_ppem          horizontal pixels-per-EM                        (<< 6)
        y_ppem          vertical pixels-per-EM                          (<< 6)

    face->size->metrics (FT_Size_Metrics):

        x_ppem          horizontal pixels-per-EM                        (<< 0)
        y_ppem          vertical pixels-per-EM                          (<< 0)
        ascender        ascent in pixels                                (<< 6)
        descender       descent in pixels                               (<< 6)
        max_advance     maximum 'width' in pixels                       (<< 6)

  And, to mess things up further, the current v metrics are not defined 
  when loading a bitmap font (TTF sets them, BDF does not).

Ok, now let's see what FreeType does with our BDF font.  It must set the 
available_sizes values, and size->metrics values:

    BDF face->available_sizes (FT_Bitmap_Size)

                height = PIXEL_SIZE
                width = AVERAGE_WIDTH
                size = POINT_SIZE * constant
                x_ppem = RESOLUTION_X * POINT_SIZE / 72
                y_ppem = RESOLUTION_Y * POINT_SIZE / 72

        height is wrong (should be FONT_ASCENT + FONT_DESCENT)
        width is correct
        size is correct
        y_ppem is not quite right (should be PIXEL_SIZE)
                Note that this will be close to how it is computed, modulo 
                rounding errors
        x_ppem could be computed differently 
                (PIXEL_SIZE * RESOLUTION_X / RESOLUTION_Y)?

   BDF face->size->metrics (FT_Size_Metrics) (valid afer FT_Set_Char_Size)

                x_ppem = char_width passed to FT_Set_Char_Size 
                                        (adjusted for h_res)
                y_ppem = char_height passed to FT_Set_Char_Size 
                                        (adjusted for v_res)
                ascender = maxbounds.ascent
                descender = -maxbounds.descent
                height = maxbounds.height
                max_advance = maxbounds.width

        That FT_Set_Char_Size actually wants the EM-box dimensions is
                somewhat confusing.  Fontconfig stores the height values 
                instead of the EM-box values, so I'll have to fix that.

        x_ppem/y_ppem are right (by definition, it's global FreeType code 
                which sets them)
        ascender is wrong (should be font_ascent)
        descender is wrong (should be font_descent)
        height is wrong (should be font_ascent + font_descent)
        max_advance is correct

        One other issue here is that the BDF loader compares the
        specified height (stored in y_ppem) against the 
        available_sizes.height value.

Now, let's look at what FreeType will do with a bitmap TTF font.  Again, 
it must set the available_sizes and size->metrics values:

    TTF face->available_sizes (FT_Bitmap_Size)

           hhea.Ascender = unscaled ascender from 'hhea' table
           hhea_height = hhea.Ascender - hhea.Descender + hhea.Line_Gap

                height = hhea_height * (Strike ppemY)
                width = (os2 average char width) * (Strike ppemX)
                size = (Strike ppemY)
                x_ppem = (Strike ppemX)
                y_ppem = (Strike ppemY)

        height is wrong (should be Strike ascender + Strike Descender + ?)
        width is right
        size is kinda wrong - it's assuming 72 dpi (hardly matters, I guess)
        x_ppem/y_ppem are correct
        
        The problem with height is that using the global metrics is
        incorrect as that will introduce significant rounding errors.
        However, the strike doesn't include any Line_Gap value, so
        this computation does make some sense.  I suggest using the
        global Line_Gap value and the strike ascender/descender values.
        For our BDF fonts, Line_Gap will always be zero, so this will work
        fine.

    TTF face->size->metrics (FT_Size_Metrics) (valid after FT_Set_Char_Size)

                x_ppem/y_ppem = values passed to FT_Set_Char_Size 
                                                (adjusted for res)
                ascender = (Strike ascender)
                descender = (Strike descender)
                height = (Strike ascender - Strike descender)
                max_advance = (something hard, marked with 'XXX')

        x_ppem/y_ppem are (again, set by FreeType base) correct by definition
        ascender is correct
        descender is correct
        height is almost correct; it ignores Line_Gap, which (it appears)
                bitmap fonts don't get to have
        max_advance may well be wrong, it it's probably good enough

Ok, so now that I know what FreeType is doing, the first question is how 
to fix (or work around) the problems.  Let's start with the assumption 
that the BDF loader is the most broken; it's newest, and least well tested.
Plus, we can change it in arbitrary ways and not hurt anyone who has 
correct TTF fonts.

I suggest that the two metrics (available_sizes and face->size->metrics) 
should be reconciled so that one can use available_sizes to compute what 
values will appear in face->size->metrics, and reliably select character 
sizes based on those values.  

The values passed to FT_Set_Char_Size/FT_Set_Pixel_Sizes must be the EM box
values, that's (nominally) what the 'point size'/'pixel size' of a font
names.  So, the first thing to fix is to change the BDF driver to match 
the x_ppem/y_ppem values instead of width/height.  Simple enough.  The 
remaining BDF computations look easy enough to fix; that's not urgently 
needed in a FreeType release as I can create the TTF files with a fixed 
version and not run afowl of the existing BDF reader on other peoples 
boxes.

Now, the TTF loader is scrambling the height values in the available_sizes 
array, but I mostly don't care; fontconfig uses available_sizes only for 
recording the list of pixel sizes, which is nicely stored (correctly) in 
ppem_y.

I've attached a patch which makes the BDF driver behave as described 
above, I haven't taken a stab at fixing the TTF driver yet.

-keith

Attachment: bdf-driver.diff
Description: bdf-driver.diff


reply via email to

[Prev in Thread] Current Thread [Next in Thread]