fontello/svg2ttf

The wrong bounding box causes Chrome to refuse to render

Closed this issue · 10 comments

Here is a simple example to help us improve the bounding box of the generated font.

SVG file:

<svg viewBox="0 0 62464 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200"><path d="M62464 0H0v1024h62464V0z" fill="#FF0000"></path></svg>

OpenType Sanitizer error prompt:

ERROR: head: Bad x dimension in the font bounding box (0, -3072)
ERROR: head: Failed to parse table
Failed to sanitize file!

Font Table

<head>
    <!-- Most of this table will be recalculated by the compiler -->
    <tableVersion value="1.0"/>
    <fontRevision value="1.0"/>
    <checkSumAdjustment value="0xd63f6d33"/>
    <magicNumber value="0x5f0f3cf5"/>
    <flags value="00000000 00001011"/>
    <unitsPerEm value="1024"/>
    <created value="Mon Jun 21 11:04:19 2021"/>
    <modified value="Mon Jun 21 11:04:19 2021"/>
    <xMin value="0"/>
    <yMin value="-128"/>
    <xMax value="-3072"/>
    <yMax value="896"/>
    <macStyle value="00000000 00000000"/>
    <lowestRecPPEM value="8"/>
    <fontDirectionHint value="2"/>
    <indexToLocFormat value="0"/>
    <glyphDataFormat value="0"/>
  </head>

  <OS_2>
    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
         will be recalculated by the compiler -->
    <version value="4"/>
    <xAvgCharWidth value="31744"/>
    <usWeightClass value="500"/>
    <usWidthClass value="5"/>
    <fsType value="00000000 00000000"/>
    <ySubscriptXSize value="649"/>
    <ySubscriptYSize value="716"/>
    <ySubscriptXOffset value="0"/>
    <ySubscriptYOffset value="143"/>
    <ySuperscriptXSize value="649"/>
    <ySuperscriptYSize value="716"/>
    <ySuperscriptXOffset value="0"/>
    <ySuperscriptYOffset value="491"/>
    <yStrikeoutSize value="50"/>
    <yStrikeoutPosition value="264"/>
    <sFamilyClass value="0"/>
    <panose>
      <bFamilyType value="2"/>
      <bSerifStyle value="0"/>
      <bWeight value="5"/>
      <bProportion value="3"/>
      <bContrast value="0"/>
      <bStrokeVariation value="0"/>
      <bArmStyle value="0"/>
      <bLetterForm value="0"/>
      <bMidline value="0"/>
      <bXHeight value="0"/>
    </panose>
    <ulUnicodeRange1 value="00000000 00000000 00000000 00000000"/>
    <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/>
    <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/>
    <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/>
    <achVendID value="PfEd"/>
    <fsSelection value="00000000 11000000"/>
    <usFirstCharIndex value="59053"/>
    <usLastCharIndex value="59053"/>
    <sTypoAscender value="896"/>
    <sTypoDescender value="-128"/>
    <sTypoLineGap value="92"/>
    <usWinAscent value="988"/>
    <usWinDescent value="128"/>
    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
    <sxHeight value="0"/>
    <sCapHeight value="0"/>
    <usDefaultChar value="0"/>
    <usBreakChar value="0"/>
    <usMaxContext value="2"/>
  </OS_2>

AFAIK term "wrong" is not precise:

  • Bbox is calculated from real data and can not be wrong (after last fixes).
  • BUT (!) too high values are "highly likely" caused by bad input.

Questions:

  • We need concrete criteria of issue resolve
    • Probably "throw error on too big bbox values"
  • We need concrete bounds for bbox
  • Probably, use ones from Chrome/FF (need to extract somehow)

Chrome and Firefox use OpenType Sanitizer to detect fonts, the code is here:
https://github.com/khaledhosny/ots/blob/ff09cd0b6b0dd210a4c1bafd61fcbbba21738751/src/head.cc#L66-L73

if (this->xmin > this->xmax) {
    return Error("Bad x dimension in the font bounding box (%d, %d)",
                  this->xmin, this->xmax);
  }
  if (this->ymin > this->ymax) {
    return Error("Bad y dimension in the font bounding box (%d, %d)",
                  this->ymin, this->ymax);
  }

Glyf table also has related checks:
https://github.com/khaledhosny/ots/blob/f2c7b32198080b6cce338a72beef8f0ebbc2db96/src/glyf.cc#L282-L293

    // workaround for fonts in http://www.princexml.com/fonts/
    if ((xmin == 32767) &&
        (xmax == -32767) &&
        (ymin == 32767) &&
        (ymax == -32767)) {
      Warning("bad xmin/xmax/ymin/ymax values");
      xmin = xmax = ymin = ymax = 0;
    }

    if (xmin > xmax || ymin > ymax) {
      return Error("Bad bounding box values bl=(%d, %d), tr=(%d, %d) in glyph %d", xmin, ymin, xmax, ymax, i);
    }

At first glance, those checks are not actual for svg2ttf. I mean, load errors happened due different checks. Or, probably i miss something.

I mean, condition xmin > xmax can not happen.

In the txt dump above xmin is 0 and xmax is negative, so xmin is greater than xmax.

Hm... strange, i will check. Thanks for clarification.

Yes, it is precisely because the font generated by svg2ttf has xMin> xMax that it will trigger OpenType Sanitizer to report an error.

 <xMin value="0"/>
 <xMax value="-3072"/>

The very first instruction in the svg path is M62464, which is exactly what xMax is calculated to be.

The field type allocated to it is: signed int16. We write 62464 there, which is the same as -3072 (62464+3072=65536).

So we have two issues here:

  • invalid glyph (or at least a glyph we can't safely convert to ttf)
  • no boundary checks

What do you expect svg2ttf to do here? Would an error (if glyph boundaries fall outside of ±32767) be enough?

Fixed, now this font should output an error.

Looks good, thanks!

Published 6.0.2