rillig / sfntly

A Library for Using, Editing, and Creating SFNT-based Fonts
176 stars 40 forks source link

FontViewer: Composite Glyph Rendering #10

Open paulushub opened 5 years ago

paulushub commented 5 years ago

First, the cast to SimpleGlyph in the following rendering codes is not correct, throwing exceptions for nested composite glyphs. Try rendering Glyph ID 126 in the font NotoSerif-Thin.ttf.

    private void paintCompositeGlyph(Graphics2D g, CompositeGlyph composite) {
      for (int i = 0; i < composite.numGlyphs(); i++) {
        int glyphIndex = composite.glyphIndex(i);
        int offset = loca.glyphOffset(glyphIndex);
        int length = loca.glyphLength(glyphIndex);
        if (length != 0) {
          SimpleGlyph simple = (SimpleGlyph) glyf.glyph(offset, length);    // <--- Here
          int deltaX = composite.argument1(i);
          int deltaY = composite.argument2(i);
          paintSimpleGlyph(g, simple, deltaX, deltaY);
        }
      }
    }

Next, checking for the nested composite glyph and calling the paintCompositeGlyph(...) recursively seems to work but rendering is incomplete and will not render the denominator in some fractional representations like 1/4 (where the 4 is not rendered in fonts like NotoSerif-Thin.ttf). The denominator is actually rendered but clipped off the screen (it seems deltaY is very large).

What is the best way to fix the nested rendering of composite glyphs?

rillig commented 5 years ago

Thanks for the detailed instructions.

Fixing the ClassCastException was trivial, but I wonder why the glyph for 4 is not rendered correctly. Glyph 126 is composed of glyphs 2570, 534 and 2563. When I look up the latter glyph in the glyf table, it is not rendered at all, although it is a simple glyph. I will investigate this further.

Note that I wrote the glyph renderer without actually reading the specification carefully, let alone following it word by word. It was just a quick and dirty way to see how the glyphs look approximately. You will probably find more bugs and inconsistencies if you look closely.

paulushub commented 5 years ago

Thanks for the quick response. The following seems to work, but still glyphs like 129 and 745 are partly off the view.

private void paintCompositeGlyph(Graphics2D g, CompositeGlyph composite) {
    for (int i = 0; i < composite.numGlyphs(); i++) {
        int glyphIndex = composite.glyphIndex(i);
        int offset = loca.glyphOffset(glyphIndex);
        int length = loca.glyphLength(glyphIndex);
        if (length != 0) {
            Glyph glyph = glyf.glyph(offset, length);
            int deltaX = (short) composite.argument1(i);
            int deltaY = (short) composite.argument2(i);
            if (glyph.glyphType() == Glyph.GlyphType.Simple) {
                paintSimpleGlyph(g, (SimpleGlyph) glyph, deltaX, deltaY);
            } else {
                paintCompositeGlyph(g, (CompositeGlyph) glyph, deltaX, deltaY);
            }
        }
    }
}

private void paintCompositeGlyph(Graphics2D g, CompositeGlyph composite, int offsetX, int offsetY) {
    for (int i = 0; i < composite.numGlyphs(); i++) {
        int glyphIndex = composite.glyphIndex(i);
        int offset = loca.glyphOffset(glyphIndex);
        int length = loca.glyphLength(glyphIndex);
        if (length != 0) {
            Glyph glyph = glyf.glyph(offset, length);
            int deltaX = (short) composite.argument1(i);
            int deltaY = (short) composite.argument2(i);
            if (glyph.glyphType() == Glyph.GlyphType.Simple) {
                paintSimpleGlyph(g, (SimpleGlyph) glyph, offsetX, deltaY);
            } else {
                // Cannot tell, if we will still get here!!!
                paintCompositeGlyph(g, (CompositeGlyph) glyph, deltaX, deltaY);
            }
        }
    }
}
rillig commented 5 years ago

It'll take some time until I finish the remaining edge cases. I need to read and understand the specification first. Up to now, I did the glyph drawing by trial and error, which was probably not a too good idea. :)

paulushub commented 5 years ago

Understood, I am also looking into it. So far, it seems the defined transforms in the glyph are not applied.

My work, I ported the library to .NET/C# and the FontViewer application was the last part I had to work on because it involves UI. The samples, testing libraries, tools, unit tests so far worked as expected within the limitations of the library. The FontViewer issues are the remaining few I could not resolve, but I have started reading the specs too to address some of the limitations of the library and also resolve these issues.

fontviewer