What's wrong with this CFF2 data

lancejpollardlancejpollard Posts: 3
edited January 2019 in Font Technology
I have a fully working TrueType font, using only 2 characters (a and b, but a goes to .notdef and b is just left b) for simplicity to get started. When I switch to using OpenType, specifically with CFF2 table, I am building this data but it breaks:
$ spot -t CFF2 Regular.otf
### [CFF2] (0000009c)
00000000  0200 0500 201e 0a00 0488 2812 5f1e 0f1e  |?.?. ??.??(?_???|
00000010  0f1e 0a00 0488 2812 5f1e 0f1e 0f0c 07b4  |???.??(?_???????|
00000020  11f7 050c 2400 0000 0000 0000 0201 0116  |????$.......????|
00000030  41ef ef05 f888 8b15 8b1c 076c 15fc 888b  |A??????????l????|
00000040  158b 1cf8 9415 8b1c 0554 059f 8b15 8bfd  |?????????T??????|
00000050  3e15 f748 8b15 fb5c fd3e 158b 1c05 5415  |>??H???\?>????T?|
00000060  9ffd 5205 f734 8b15 fb34 fd16 158b f916  |??R??4???4??????|
00000070  1500 0000 0101 0105 8bf7 2112            |?...??????!?    |
The whole font file looks like this:
$ xxd Regular.otf
00000000: 4f54 544f 0009 0080 0003 0010 4346 4632  OTTO........CFF2
00000010: 0000 0000 0000 009c 0000 007c 4f53 2f32  ...........|OS/2
00000020: 0000 0000 0000 0118 0000 0064 636d 6170  ...........dcmap
00000030: 0000 0000 0000 017c 0000 003a 6865 6164  .......|...:head
00000040: 0000 0000 0000 01b8 0000 0036 6868 6561  ...........6hhea
00000050: 0000 0000 0000 01f0 0000 0024 686d 7478  ...........$hmtx
00000060: 0000 0000 0000 0214 0000 0008 6d61 7870  ............maxp
00000070: 0000 0000 0000 021c 0000 0020 6e61 6d65  ........... name
00000080: 0000 0000 0000 023c 0000 00ad 706f 7374  .......<....post
00000090: 0000 0000 0000 02ec 0000 0020 0200 0500  ........... ....
000000a0: 201e 0a00 0488 2812 5f1e 0f1e 0f1e 0a00   .....(._.......
000000b0: 0488 2812 5f1e 0f1e 0f0c 07b4 11f7 050c  ..(._...........
000000c0: 2400 0000 0000 0000 0201 0116 41ef ef05  $...........A...
000000d0: f888 8b15 8b1c 076c 15fc 888b 158b 1cf8  .......l........
000000e0: 9415 8b1c 0554 059f 8b15 8bfd 3e15 f748  .....T......>..H
000000f0: 8b15 fb5c fd3e 158b 1c05 5415 9ffd 5205  ...\.>....T...R.
00000100: f734 8b15 fb34 fd16 158b f916 1500 0000  .4...4..........
00000110: 0101 0105 8bf7 2112 0005 02aa 01f4 0005  ......!.........
00000120: 0000 028a 02bb 0000 008c 028a 02bb 0000  ................
00000130: 01df 0031 0102 0000 0000 0009 0000 0000  ...1............
00000140: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000150: 0000 5858 5858 0040 0061 0062 0554 02aa  ..XXXX.@.a.b.T..
00000160: 0000 07d0 0000 0000 0001 0000 0000 02aa  ................
00000170: 0028 0000 0000 0000 0000 0003 0000 0001  .(..............
00000180: 0003 0001 0000 000c 0004 002e 0000 0006  ................
00000190: 0004 0001 0002 0061 0062 ffff 0000 0061  .......a.b.....a
000001a0: 0062 ffff ff9f ff9f 0001 0000 0000 0000  .b..............
000001b0: 0000 0001 0000 0000 0001 0000 0001 116f  ...............o
000001c0: 0000 0000 5f0f 3cf5 0003 0800 0000 0000  ...._.<.........
000001d0: d878 25f8 0000 0000 d878 25f8 0000 0000  .x%......x%.....
000001e0: 0258 07d0 0000 0009 0002 0001 0000 0000  .X..............
000001f0: 0001 0000 0554 02aa 0000 02aa 0000 0000  .....T..........
00000200: 0000 0001 0000 0000 0000 0000 0000 0000  ................
00000210: 0000 0002 02aa 0064 02aa 0064 0001 0000  .......d...d....
00000220: 0002 0012 0002 0000 0000 0001 0000 0000  ................
00000230: 0000 0000 0000 0000 0000 0000 0000 0007  ................
00000240: 005a 0001 0000 0000 0001 0003 0000 0001  .Z..............
00000250: 0000 0000 0002 0007 0003 0001 0000 0000  ................
00000260: 0003 0003 000a 0001 0000 0000 0004 000b  ................
00000270: 000d 0001 0000 0000 0005 000d 0046 0001  .............F..
00000280: 0000 0000 0006 0003 0000 0001 0000 0000  ................
00000290: 000d 002e 0018 466f 6f52 6567 756c 6172  ......FooRegular
000002a0: 666f 6f46 6f6f 2052 6567 756c 6172 4c69  fooFoo RegularLi
000002b0: 6365 6e73 6564 2075 6e64 6572 2074 6865  censed under the
000002c0: 2041 7061 6368 6520 4c69 6365 6e73 652c   Apache License,
000002d0: 2056 6572 7369 6f6e 2032 2e30 5665 7273   Version 2.0Vers
000002e0: 696f 6e20 312e 3030 3000 0000 0003 0000  ion 1.000.......
000002f0: 0000 0000 0000 0000 0000 0001 0000 0000  ................
00000300: 0000 0000 0000 0000 0000 0000            ............
I am generating this from scratch, not using any tools or anything, just custom JavaScript. Once again, I have a fully working TrueType version (with just the b glyph), but it breaks when I use CFF2. I think it has to do with the Font DICT and Private field for that dictionary. I don't have any private values and the spec says you can set its size to 0, so that's what I did in the code above. The last lines 0101 0105 8bf7 2112 essentially say "Font DICT Index size = 1, offSize = 1, first offset = 1, second offset = 5, 0 (139 prefix = 8b, from spec), 247.33.18 I think this is an offset. TTX works fine:
$ ttx -f Regular.otf
Dumping "Regular.otf" to "Regular.ttx"...
Dumping 'GlyphOrder' table...
Dumping 'head' table...
Dumping 'hhea' table...
Dumping 'maxp' table...
Dumping 'OS/2' table...
Dumping 'hmtx' table...
Dumping 'cmap' table...
Dumping 'name' table...
Dumping 'post' table...
Dumping 'CFF2' table...
<?xml version="1.0" encoding="UTF-8"?>
<ttFont sfntVersion="OTTO" ttLibVersion="3.35">

  <GlyphOrder>
    <!-- The 'id' attribute is only for humans; it is ignored when parsed. -->
    <GlyphID id="0" name=".notdef"/>
    <GlyphID id="1" name="b"/>
  </GlyphOrder>

  <head>
    <!-- Most of this table will be recalculated by the compiler -->
    <tableVersion value="1.0"/>
    <fontRevision value="1.0681"/>
    <checkSumAdjustment value="0x0"/>
    <magicNumber value="0x5f0f3cf5"/>
    <flags value="00000000 00000011"/>
    <unitsPerEm value="2048"/>
    <created value="Thu Jan 31 04:11:36 2019"/>
    <modified value="Thu Jan 31 04:11:36 2019"/>
    <xMin value="0"/>
    <yMin value="0"/>
    <xMax value="600"/>
    <yMax value="2000"/>
    <macStyle value="00000000 00000000"/>
    <lowestRecPPEM value="9"/>
    <fontDirectionHint value="2"/>
    <indexToLocFormat value="1"/>
    <glyphDataFormat value="0"/>
  </head>

  <hhea>
    <tableVersion value="0x00010000"/>
    <ascent value="1364"/>
    <descent value="682"/>
    <lineGap value="0"/>
    <advanceWidthMax value="682"/>
    <minLeftSideBearing value="0"/>
    <minRightSideBearing value="0"/>
    <xMaxExtent value="0"/>
    <caretSlopeRise value="1"/>
    <caretSlopeRun value="0"/>
    <caretOffset value="0"/>
    <reserved0 value="0"/>
    <reserved1 value="0"/>
    <reserved2 value="0"/>
    <reserved3 value="0"/>
    <metricDataFormat value="0"/>
    <numberOfHMetrics value="2"/>
  </hhea>

  <maxp>
    <!-- Most of this table will be recalculated by the compiler -->
    <tableVersion value="0x10000"/>
    <numGlyphs value="2"/>
    <maxPoints value="18"/>
    <maxContours value="2"/>
    <maxCompositePoints value="0"/>
    <maxCompositeContours value="0"/>
    <maxZones value="1"/>
    <maxTwilightPoints value="0"/>
    <maxStorage value="0"/>
    <maxFunctionDefs value="0"/>
    <maxInstructionDefs value="0"/>
    <maxStackElements value="0"/>
    <maxSizeOfInstructions value="0"/>
    <maxComponentElements value="0"/>
    <maxComponentDepth value="0"/>
  </maxp>

  <OS_2>
    <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex'
         will be recalculated by the compiler -->
    <version value="5"/>
    <xAvgCharWidth value="682"/>
    <usWeightClass value="500"/>
    <usWidthClass value="5"/>
    <fsType value="00000000 00000000"/>
    <ySubscriptXSize value="650"/>
    <ySubscriptYSize value="699"/>
    <ySubscriptXOffset value="0"/>
    <ySubscriptYOffset value="140"/>
    <ySuperscriptXSize value="650"/>
    <ySuperscriptYSize value="699"/>
    <ySuperscriptXOffset value="0"/>
    <ySuperscriptYOffset value="479"/>
    <yStrikeoutSize value="49"/>
    <yStrikeoutPosition value="258"/>
    <sFamilyClass value="0"/>
    <panose>
      <bFamilyType value="0"/>
      <bSerifStyle value="0"/>
      <bWeight value="0"/>
      <bProportion value="9"/>
      <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="XXXX"/>
    <fsSelection value="00000000 01000000"/>
    <usFirstCharIndex value="97"/>
    <usLastCharIndex value="98"/>
    <sTypoAscender value="1364"/>
    <sTypoDescender value="682"/>
    <sTypoLineGap value="0"/>
    <usWinAscent value="2000"/>
    <usWinDescent value="0"/>
    <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/>
    <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/>
    <sxHeight value="682"/>
    <sCapHeight value="40"/>
    <usDefaultChar value="0"/>
    <usBreakChar value="0"/>
    <usMaxContext value="0"/>
    <usLowerOpticalPointSize value="0.0"/>
    <usUpperOpticalPointSize value="0.15"/>
  </OS_2>

  <hmtx>
    <mtx name=".notdef" width="682" lsb="100"/>
    <mtx name="b" width="682" lsb="100"/>
  </hmtx>

  <cmap>
    <tableVersion version="0"/>
    <cmap_format_4 platformID="3" platEncID="1" language="0">
      <map code="0x62" name="b"/><!-- LATIN SMALL LETTER B -->
    </cmap_format_4>
  </cmap>

  <name>
    <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True">
      Foo
    </namerecord>
    <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True">
      Regular
    </namerecord>
    <namerecord nameID="3" platformID="1" platEncID="0" langID="0x0" unicode="True">
      foo
    </namerecord>
    <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True">
      Foo Regular
    </namerecord>
    <namerecord nameID="5" platformID="1" platEncID="0" langID="0x0" unicode="True">
      Version 1.000
    </namerecord>
    <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True">
      Foo
    </namerecord>
    <namerecord nameID="13" platformID="1" platEncID="0" langID="0x0" unicode="True">
      Licensed under the Apache License, Version 2.0
    </namerecord>
  </name>

  <post>
    <formatType value="3.0"/>
    <italicAngle value="0.0"/>
    <underlinePosition value="0"/>
    <underlineThickness value="0"/>
    <isFixedPitch value="1"/>
    <minMemType42 value="0"/>
    <maxMemType42 value="0"/>
    <minMemType1 value="0"/>
    <maxMemType1 value="0"/>
  </post>

  <CFF2>
    <major value="2"/>
    <minor value="0"/>
    <CFFFont name="CFF2Font">
      <FontMatrix value="0.00048828125 0.0 0.0 0.00048828125 0.0 0.0"/>
      <FDArray>
        <FontDict index="0">
          <Private>
            <BlueScale value="0.039625"/>
            <BlueShift value="7"/>
            <BlueFuzz value="1"/>
          </Private>
        </FontDict>
      </FDArray>
      <CharStrings>
        <CharString name=".notdef">
          100 100 rlineto
          500 0 rmoveto
          0 1900 rmoveto
          -500 0 rmoveto
          0 -1900 rmoveto
        </CharString>
        <CharString name="b">
          0 1364 rlineto
          20 0 rmoveto
          0 -682 rmoveto
          180 0 rmoveto
          -200 -682 rmoveto
          0 1364 rmoveto
          20 -702 rlineto
          160 0 rmoveto
          -160 -642 rmoveto
          0 642 rmoveto
        </CharString>
      </CharStrings>
    </CFFFont>

    <GlobalSubrs>
      <!-- The 'index' attribute is only for humans; it is ignored when parsed. -->
    </GlobalSubrs>
  </CFF2>

</ttFont>
However, in Chrome, I get OTS parsing error: Unable to instantiate font face from font data.. With fontbakery, I get this error which could shed some more light on it:
>> com.google.fonts/check/036 with (('font[0]', 'Regular.otf'),)
   Checking with ots-sanitize.
 * FAIL: ots-sanitize returned an error code (1). Output follows:

ERROR: no supported glyph shapes table(s) present
Failed to sanitize file!
Wondering if you have any advice or can read that CFF2 table, the last part of it (Font DICT + Private) and tell what's wrong from it.

Comments

  • Khaled HosnyKhaled Hosny Posts: 289
    edited January 2019
    OTS does not support CFF2 table, and most probably fontbakery does not as well.
  • Cool, I see that here https://github.com/khaledhosny/ots/issues/175 as well.
  • Thomas PhinneyThomas Phinney Posts: 2,732
    edited January 2019
    Just to broaden the scope a bit, and explain why so many pieces of tech do not (yet) support CFF2 for storing outlines.

    CFF2 is very cool, and may eventually be well-supported, but the spec was developed and finalized a bit after the rest of OpenType 1.8, particularly after the rest of the OpenType variations (variable fonts) stuff.

    As a result, it sort of missed the boat for getting picked up by various apps/components/services when they were scrambling to implement OT 1.8 and variations support. Then after that, fixing bugs and finishing initial implementations has often been more pressing than adding support for CFF2. At least, that’s how I see it.

    Additionally, some components are especially foundational. For example, FontLab is dependent on several other pieces for CFF2 support. (In theory we could work around some lacks, but that would be a *lot* more work.)

    The good news is that when CFF2 support arrives in various places, it will almost certainly come with CFF2 variations as well. OT Variations aren't supported with old CFF, so this is a major motivator for getting CFF2 support. Perhaps the biggest one, even.
Sign In or Register to comment.