Search code examples
fontstruetypeopentype

How to indicate missing glyphs in a SBIX TrueType/OpenType font table


I am trying to implement support for the SBIX table for an icon font writer, but the TrueType/OpenType specification is not clear to me.

The spec for the SBIX table can be found here:
https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6sbix.html
https://learn.microsoft.com/en-us/typography/opentype/spec/sbix

It says:
"Not every glyph needs to have an entry in every strike in an 'sbix' table. If a glyph has no entry in any strike of an 'sbix' table, then its outline is drawn."

So not for every glyph a bitmap has to be provided, fair enough.

But how do I indicate that no bitmap is available for a glyph? I do not see any mappings from glyph index to entries in a strike.

Does it mean that simply the data field in the glyph data record is empty (lenght 0) for glyphs without bitmap? Or has the whole glyph data record a length of zero? It seems the spec is not very clear on this. Or am I missing something completely?


Solution

  • Sort of, SBIX is a fully self-contained table, there is no secondary indexing table for it like glyf/loca or the cmap subtables. The way you check whether there is data does not require actually reading the glyph's data block, you just need to check the offset array for the strike associated with the point size the text that is being shaped uses.

    To quote the spec:

    the length of data for glyph N is glyphDataOffset[N+1] – glyphDataOffset[N]. If this is zero, there is no bitmap data for that glyph in this strike. There is one extra offset in the array in order to provide the length of data for the last glyph.

    So: you find your glyph's id the usual way, then you check the offset values for your id as well as the next glyph's id, and if they're the same there is no data associated with your glyph in the SBIX table.

    Conversely, if you're writing the code to generate the SBIX table, then for each point size that needs bitmap fallback you create a strike, and each strike must have an offset field that is an Offset32[maxp.numGlyphs + 1]. That is: you don't get a choice, every glyph will have an offset but if a glyph does not require a bitmap for the strike, you don't write any glyph data to the glyph data block, and you leave the offset alone until some subsequent glyph that does have associated bitmap data.

    1. initialise an offset32 array of length maxp.numGlyphs + 1,
    2. initialise a variable length data structure for glyph data records,
    3. initialise a rolling offset tracker to the value strike.startOffset + 4 + offsets.length * 4,
    4. starting at glyph id 0, for each glyph do:
      1. create a glyph data record if it needs a bitmap, or signal no record is to be written to the glyph data block (e.g. null or false or 0 or whatever works best in your language of choice),
        • if there is no record, do nothing.
        • if there is a record to write, append it to the glyph data data structure, and add bytelength(glyphDataRecord) to the offset tracker.
      2. set offsets[id] = trackervalue, increment the glyph id by one, and repeat the loop
    5. after processing all glyphs, add the final offset tracker value offsets[maxp.numGlyphs] = trackvalue, storing the offset at which this strike ends.

    Note of course that (3) can be entirely done in a deferred manner: if you don't know strike.startOffset yet, then you just start at 0 and once you know where your data actually goes in your file, update all the values in your offsets array by adding the constant stroke.startOffset + 4 + offsets.length * 4 to every value.