foliojs / fontkit

An advanced font engine for Node and the browser
1.49k stars 222 forks source link

getGlyph returns empty codePoints #248

Open andreinitescu opened 3 years ago

andreinitescu commented 3 years ago

Any idea why getGlyph(gid) returns a Glyph with codePoints as an empty array?

I'm sure the gid I pass is correct.

andreinitescu commented 3 years ago

I'm trying to get the unicode for a glyph.

In the example below, I'm looking to get the F000 value for the glyph with index 66:

image

Per my understanding, in fontkit "unicode code point" refers to the "index" in the image above. How can I get the unicode (F000) value?

guidoferreyra commented 2 years ago

Having the same issue @andreinitescu did you find a workaround for this?

Pomax commented 2 years ago

@andreinitescu is there a specific public release for that font that you can link to, with some minimal code that does everything to reproduce the problem you had?

guidoferreyra commented 2 years ago

Here a reduced test case

test-Codepoints.zip

andreinitescu commented 2 years ago

@Pomax I cannot remember exactly which font was it, but I think it was Font Awesome (glass-martini glyph in the screenshot).

@guidoferreyra Sorry, I cannot remember, it's been a year.

Pomax commented 2 years ago

@guidoferreyra Interestingly, your test case uses a version of Roboto that has no official release. Turning that zip file into a post:

const fontkit = require('fontkit');

const font = fontkit.openSync('Roboto-Thin.ttf');

for (let i = 0; i < font.numGlyphs; i++) {
  const glyph = font.getGlyph(i);
  console.log(glyph.id, glyph.name, glyph.codePoints)
}

Does this also happen for v2.138? If so, can we turn that for loop into a specific single character for which this is known to go wrong, rather than a few tens of thousands of console logs? =)

guidoferreyra commented 2 years ago

I used Roboto just because was the first font that appeared on Google Fonts, but it’s happening with any font.

guidoferreyra commented 2 years ago

Just tested and this is happening with v2.138, v2.0 and v1.9

Typogram commented 1 year ago

I dig into the source code, the way getGlyph() is written is never meant to return codePoints, and it takes the second parameters and use that as codePoints, since the usage getGlyph(glyphId) omit the second parameter, it default the codePoints to an empty array [] and that is being used to construct the Glyph object and being returned.

When font.layout() internally called getGlyph(), the text string is converted to codePoints and pass into the getGlyph() as the second parameter and that get added to the Glyph object inside GlyphRun, so these Glyph has codePoints.

I find this API design confusing, making the Glyph object that get returned very unreliable.

Related source code snippets:

guidoferreyra commented 11 months ago

is it too nasty to use font._cmapProcessor.codePointsForGlyph(id); on the glyph constructor? It works for me.

export default class Glyph {
  constructor(id, codePoints, font) {
    /**
     * The glyph id in the font
     * @type {number}
     */
    this.id = id;

    /**
     * An array of unicode code points that are represented by this glyph.
     * There can be multiple code points in the case of ligatures and other glyphs
     * that represent multiple visual characters.
     * @type {number[]}
     */
    if (codePoints.length == 0) {
      this.codePoints = font._cmapProcessor.codePointsForGlyph(id);
    } else {
      this.codePoints = codePoints;
    }

    this._font = font;

    // TODO: get this info from GDEF if available
    this.isMark = this.codePoints.length > 0 && this.codePoints.every(isMark);
    this.isLigature = this.codePoints.length > 1;
  }