sfntly icon indicating copy to clipboard operation
sfntly copied to clipboard

FontViewer: Composite Glyph Rendering

Open paulushub opened this issue 6 years ago • 4 comments

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?

paulushub avatar Nov 01 '18 11:11 paulushub

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.

rillig avatar Nov 01 '18 23:11 rillig

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);
			}
		}
	}
}

paulushub avatar Nov 02 '18 02:11 paulushub

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. :)

rillig avatar Nov 07 '18 21:11 rillig

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

paulushub avatar Nov 07 '18 21:11 paulushub