diff options
author | Bernhard Haumacher <[email protected]> | 2020-05-10 11:45:50 +0200 |
---|---|---|
committer | Sven Göthel <[email protected]> | 2024-02-03 01:58:22 +0100 |
commit | a237a956fcc925e27d72ba49d242dcc1dc09072c (patch) | |
tree | bc177d059a8ab84c57dee605cf567afb0891c247 /src/jogl/classes | |
parent | a71ee81c72baa1c4d6a58220df7733acca499166 (diff) |
Added documentation to the 'glyf' table and structures.
* Completed missing toString() functions in some table.
* Added dump() functionality to create a human readable description of
all font tables with complete detail.
* Fixed some signed/unsigned problems in the parser.
# Conflicts:
# src/jogl/classes/jogamp/graph/font/typecast/ot/OTFont.java
# src/jogl/classes/jogamp/graph/font/typecast/ot/TTFont.java
# src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfCompositeDescript.java
# src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfDescript.java
# src/jogl/classes/jogamp/graph/font/typecast/ot/table/NameTable.java
# src/test/java/net/java/dev/typecast/ot/TTFontTest.java
Diffstat (limited to 'src/jogl/classes')
19 files changed, 989 insertions, 152 deletions
diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/Bits.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/Bits.java index d79d8b169..69d6f775d 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/Bits.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/Bits.java @@ -41,4 +41,11 @@ public class Bits { return 0x01 << n; } + /** + * Whether all bits in the given mask are set in the given bit set. + */ + public static boolean isSet(int bitSet, int mask) { + return (bitSet & mask) == mask; + } + } diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/Disassembler.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/Disassembler.java index 5bb8e934a..988c65d21 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/Disassembler.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/Disassembler.java @@ -87,7 +87,7 @@ public class Disassembler { for (int i = 0; i < leadingSpaces; i++) { sb.append(" "); } - sb.append(ip).append(": "); + sb.append(Fmt.pad(3, ip)).append(": "); sb.append(Mnemonic.getMnemonic(instructions[ip])); if (getPushCount(instructions, ip) > 0) { int[] data = getPushData(instructions, ip); diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/Fixed_2_14.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/Fixed_2_14.java new file mode 100644 index 000000000..96c6593ac --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/Fixed_2_14.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020 Business Operation Systems GmbH. All Rights Reserved. + */ +package jogamp.graph.font.typecast.ot; + +import java.io.DataInput; +import java.io.IOException; + +/** + * Utilities to convert 2.14 fixed floating point format. + * + * @author <a href="mailto:[email protected]">Bernhard Haumacher</a> + */ +public class Fixed_2_14 { + + /** + * Reads a value in fixed 2.14 floating point format. + */ + public static double read(DataInput di) throws IOException { + return toDouble(di.readShort()); + } + + private static double toDouble(int i) { + return (double) i / (double) 0x4000; + } + +} diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/Fmt.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/Fmt.java new file mode 100644 index 000000000..82666c737 --- /dev/null +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/Fmt.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2020 Business Operation Systems GmbH. All Rights Reserved. + */ +package jogamp.graph.font.typecast.ot; + +/** + * Formatting utilities. + * + * @author <a href="mailto:[email protected]">Bernhard Haumacher</a> + */ +public class Fmt { + + private static final String PADDING = " "; + + /** + * Left aligned number in a field of the given number of digits. + */ + public static String pad(int digits, int value) { + String result = Integer.toString(value); + if (result.length() >= digits) { + return result; + } + return PADDING.substring(0, digits - result.length()) + result; + } + +} diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/OTFont.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/OTFont.java index ca45c21aa..a2e9e7a19 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/OTFont.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/OTFont.java @@ -20,6 +20,7 @@ package jogamp.graph.font.typecast.ot; import java.io.DataInputStream; import java.io.IOException; +import java.io.Writer; import jogamp.graph.font.typecast.ot.table.CmapTable; import jogamp.graph.font.typecast.ot.table.GsubTable; @@ -35,7 +36,6 @@ import jogamp.graph.font.typecast.ot.table.Table; import jogamp.graph.font.typecast.ot.table.TableDirectory; import jogamp.graph.font.typecast.ot.table.VheaTable; - /** * The TrueType font. * @author <a href="mailto:[email protected]">David Schweinsberg</a> @@ -195,4 +195,33 @@ public abstract class OTFont { public String toString() { return _head.toString(); } + + /** + * Dumps information of all tables to the given {@link Writer}. + */ + public void dumpTo(Writer out) throws IOException { + dump(out, getHeadTable()); + dump(out, getOS2Table()); + dump(out, getCmapTable()); + dump(out, getHheaTable()); + dump(out, getHmtxTable()); + dump(out, getMaxpTable()); + dump(out, getNameTable()); + dump(out, getPostTable()); + dump(out, getVheaTable()); + dump(out, getGsubTable()); + } + + /** + * Writes the toString() representation of the given table to the given {@link Writer}. + */ + protected static void dump(Writer out, Table table) throws IOException { + if (table != null) { + table.dump(out); + out.write("\n"); + out.write("\n"); + } + } + + } diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/TTFont.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/TTFont.java index 4cae9bc19..5d091a3c8 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/TTFont.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/TTFont.java @@ -24,6 +24,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.Writer; import jogamp.graph.font.typecast.ot.table.GaspTable; import jogamp.graph.font.typecast.ot.table.GlyfDescript; @@ -180,4 +181,13 @@ public class TTFont extends OTFont { } } + @Override + public void dumpTo(Writer out) throws IOException { + super.dumpTo(out); + dump(out, getGlyfTable()); + dump(out, getGaspTable()); + dump(out, getKernTable()); + dump(out, getHdmxTable()); + dump(out, getVdmxTable()); + } } diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/TTGlyph.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/TTGlyph.java index d5f9eedc5..880625520 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/TTGlyph.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/TTGlyph.java @@ -105,7 +105,7 @@ public class TTGlyph extends Glyph { _points[i] = new Point( gd.getXCoordinate(i), gd.getYCoordinate(i), - (gd.getFlags(i) & GlyfDescript.onCurve) != 0, + (gd.getFlags(i) & GlyfDescript.ON_CURVE_POINT) != 0, endPt); } diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfCompositeComp.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfCompositeComp.java index 93b7ad1f0..e4a85b260 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfCompositeComp.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfCompositeComp.java @@ -53,34 +53,204 @@ package jogamp.graph.font.typecast.ot.table; import java.io.DataInput; import java.io.IOException; +import jogamp.graph.font.typecast.ot.Bits; +import jogamp.graph.font.typecast.ot.Fixed_2_14; + /** + * A component of a {@link GlyfCompositeDescript} + * + * @see GlyfCompositeDescript#getComponent(int) + * @see "https://docs.microsoft.com/en-us/typography/opentype/spec/glyf" + * * @author <a href="mailto:[email protected]">David Schweinsberg</a> */ public class GlyfCompositeComp { + /** + * Bit 0: If this is set, the arguments are 16-bit (uint16 or int16); + * otherwise, they are bytes (uint8 or int8). + */ private static final short ARG_1_AND_2_ARE_WORDS = 0x0001; + + /** + * Bit 1: If this is set, the arguments are signed xy values; otherwise, + * they are unsigned point numbers. + * + * <p> + * Argument1 and argument2 can be either x and y offsets to be added to the + * glyph (the {@link #ARGS_ARE_XY_VALUES} flag is set), or two point numbers + * (the {@link #ARGS_ARE_XY_VALUES} flag is not set). In the latter case, + * the first point number indicates the point that is to be matched to the + * new glyph. The second number indicates the new glyph’s “matched” point. + * Once a glyph is added, its point numbers begin directly after the last + * glyphs (endpoint of first glyph + 1). + * </p> + */ private static final short ARGS_ARE_XY_VALUES = 0x0002; + + /** + * Bit 2: For the xy values if the preceding is true. + */ public static final short ROUND_XY_TO_GRID = 0x0004; + + /** + * Bit 3: This indicates that there is a simple scale for the component. + * Otherwise, scale = 1.0. + * + * <p> + * If the bit {@link #WE_HAVE_A_SCALE} is set, the scale value is read in + * {@link Fixed_2_14} format - the value can be between -2 to almost +2. The + * glyph will be scaled by this value before grid-fitting. + * </p> + */ private static final short WE_HAVE_A_SCALE = 0x0008; + + /** + * Bit 5: Indicates at least one more glyph after this one. + */ public static final short MORE_COMPONENTS = 0x0020; + + /** + * Bit 6: The x direction will use a different scale from the y direction. + */ private static final short WE_HAVE_AN_X_AND_Y_SCALE = 0x0040; + + /** + * Bit 7: There is a 2 by 2 transformation that will be used to scale the + * component. + * + * <p> + * The bit {@link #WE_HAVE_A_TWO_BY_TWO} allows for linear transformation of + * the X and Y coordinates by specifying a 2 × 2 matrix. This could be used + * for scaling and 90-degree rotations of the glyph components, for example. + * </p> + */ private static final short WE_HAVE_A_TWO_BY_TWO = 0x0080; + + /** + * Bit 8: Following the last component are instructions for the composite character. + */ public static final short WE_HAVE_INSTRUCTIONS = 0x0100; + + /** + * Bit 9: If set, this forces the aw and lsb (and rsb) for the composite to + * be equal to those from this original glyph. This works for hinted and + * unhinted characters. + * + * <p> + * The purpose of {@link #USE_MY_METRICS} is to force the lsb and rsb to + * take on a desired value. For example, an i-circumflex (U+00EF) is often + * composed of the circumflex and a dotless-i. In order to force the + * composite to have the same metrics as the dotless-i, set + * {@link #USE_MY_METRICS} for the dotless-i component of the composite. + * Without this bit, the rsb and lsb would be calculated from the 'hmtx' + * entry for the composite (or would need to be explicitly set with TrueType + * instructions). + * </p> + * + * <p> + * Note that the behavior of the {@link #USE_MY_METRICS} operation is + * undefined for rotated composite components. + * </p> + */ public static final short USE_MY_METRICS = 0x0200; + + /** + * Bit 10: If set, the components of the compound glyph overlap. Use of this + * flag is not required in OpenType — that is, it is valid to have + * components overlap without having this flag set. It may affect behaviors + * in some platforms, however. (See Apple’s specification for details + * regarding behavior in Apple platforms.) When used, it must be set on the + * flag word for the first component. See additional remarks, above, for the + * similar {@link GlyfDescript#OVERLAP_SIMPLE} flag used in simple-glyph + * descriptions. + */ + public static final short OVERLAP_COMPOUND = 0x0400; + + /** + * Bit 11: The composite is designed to have the component offset scaled. + * + * <p> + * The {@link #SCALED_COMPONENT_OFFSET} and + * {@link #UNSCALED_COMPONENT_OFFSET} flags are used to determine how x and + * y offset values are to be interpreted when the component glyph is scaled. + * If the {@link #SCALED_COMPONENT_OFFSET} flag is set, then the x and y + * offset values are deemed to be in the component glyph’s coordinate + * system, and the scale transformation is applied to both values. If the + * {@link #UNSCALED_COMPONENT_OFFSET} flag is set, then the x and y offset + * values are deemed to be in the current glyph’s coordinate system, and the + * scale transformation is not applied to either value. If neither flag is + * set, then the rasterizer will apply a default behavior. On Microsoft and + * Apple platforms, the default behavior is the same as when the + * {@link #UNSCALED_COMPONENT_OFFSET} flag is set; this behavior is + * recommended for all rasterizer implementations. If a font has both flags + * set, this is invalid; the rasterizer should use its default behavior for + * this case. + * </p> + * + * @see #UNSCALED_COMPONENT_OFFSET + */ + public static final short SCALED_COMPONENT_OFFSET = 0x0800; + + /** + * Bit 12: The composite is designed not to have the component offset + * scaled. + * + * @see #SCALED_COMPONENT_OFFSET + */ + public static final short UNSCALED_COMPONENT_OFFSET = 0x1000; private final int _firstIndex; private final int _firstContour; - private short _argument1; - private short _argument2; + + /** + * @see #getFlags() + */ private int _flags; + + /** + * @see #getGlyphIndex() + */ private int _glyphIndex; + + /** + * @see #getXScale() + */ private double _xscale = 1.0; - private double _yscale = 1.0; + + /** + * @see #getScale01() + */ private double _scale01 = 0.0; + + /** + * @see #getScale10() + */ private double _scale10 = 0.0; + + /** + * @see #getYScale() + */ + private double _yscale = 1.0; + + /** + * @see #getXTranslate() + */ private int _xtranslate = 0; + + /** + * @see #getYTranslate() + */ private int _ytranslate = 0; + + /** + * @see #getPoint1() + */ private int _point1 = 0; + + /** + * @see #getPoint2() + */ private int _point2 = 0; GlyfCompositeComp(int firstIndex, int firstContour, DataInput di) @@ -90,42 +260,49 @@ public class GlyfCompositeComp { _flags = di.readUnsignedShort(); _glyphIndex = di.readUnsignedShort(); - // Get the arguments as just their raw values - if ((_flags & ARG_1_AND_2_ARE_WORDS) != 0) { - _argument1 = di.readShort(); - _argument2 = di.readShort(); - } else { - _argument1 = di.readByte(); - _argument2 = di.readByte(); - } - - // Assign the arguments according to the flags + // Argument1 and argument2 can be either x and y offsets to be added to + // the glyph (the ARGS_ARE_XY_VALUES flag is set), or two point numbers + // (the ARGS_ARE_XY_VALUES flag is not set). In the latter case, the + // first point number indicates the point that is to be matched to the + // new glyph. The second number indicates the new glyph’s “matched” + // point. Once a glyph is added, its point numbers begin directly after + // the last glyphs (endpoint of first glyph + 1). + + boolean wordArgs = (_flags & ARG_1_AND_2_ARE_WORDS) != 0; if ((_flags & ARGS_ARE_XY_VALUES) != 0) { - _xtranslate = _argument1; - _ytranslate = _argument2; + // TODO: + // When arguments 1 and 2 are an x and a y offset instead of points and + // the bit ROUND_XY_TO_GRID is set to 1, the values are rounded to those + // of the closest grid lines before they are added to the glyph. X and Y + // offsets are described in FUnits. + if (wordArgs) { + _xtranslate = di.readShort(); + _ytranslate = di.readShort(); + } else { + _xtranslate = di.readByte(); + _ytranslate = di.readByte(); + } } else { - _point1 = _argument1; - _point2 = _argument2; + if (wordArgs) { + _point1 = di.readUnsignedShort(); + _point2 = di.readUnsignedShort(); + } else { + _point1 = di.readUnsignedByte(); + _point2 = di.readUnsignedByte(); + } } - + // Get the scale values (if any) if ((_flags & WE_HAVE_A_SCALE) != 0) { - int i = di.readShort(); - _xscale = _yscale = (double) i / (double) 0x4000; + _xscale = _yscale = Fixed_2_14.read(di); } else if ((_flags & WE_HAVE_AN_X_AND_Y_SCALE) != 0) { - short i = di.readShort(); - _xscale = (double) i / (double) 0x4000; - i = di.readShort(); - _yscale = (double) i / (double) 0x4000; + _xscale = Fixed_2_14.read(di); + _yscale = Fixed_2_14.read(di); } else if ((_flags & WE_HAVE_A_TWO_BY_TWO) != 0) { - int i = di.readShort(); - _xscale = (double) i / (double) 0x4000; - i = di.readShort(); - _scale01 = (double) i / (double) 0x4000; - i = di.readShort(); - _scale10 = (double) i / (double) 0x4000; - i = di.readShort(); - _yscale = (double) i / (double) 0x4000; + _xscale = Fixed_2_14.read(di); + _scale01 = Fixed_2_14.read(di); + _scale10 = Fixed_2_14.read(di); + _yscale = Fixed_2_14.read(di); } } @@ -137,45 +314,104 @@ public class GlyfCompositeComp { return _firstContour; } - public short getArgument1() { - return _argument1; - } - - public short getArgument2() { - return _argument2; - } - + /** + * uint16 Component flags. + * + * @see #ARG_1_AND_2_ARE_WORDS + * @see #ARGS_ARE_XY_VALUES + * @see #ROUND_XY_TO_GRID + * @see #WE_HAVE_A_SCALE + * @see #MORE_COMPONENTS + * @see #WE_HAVE_AN_X_AND_Y_SCALE + * @see #WE_HAVE_INSTRUCTIONS + * @see #USE_MY_METRICS + * @see #OVERLAP_COMPOUND + * @see #SCALED_COMPONENT_OFFSET + * @see #UNSCALED_COMPONENT_OFFSET + */ public int getFlags() { return _flags; } + /** + * uint16 Glyph index of this component. + * + * @see #getReferencedGlyph(GlyfTable) + */ public int getGlyphIndex() { return _glyphIndex; } + /** + * Coordinate transformation. + * + * <p> + * The transformation matrix is: + * </p> + * + * <pre> + * [ {@link #getXScale()} {@link #getScale01()} {@link #getXTranslate()} ] + * [ {@link #getScale10()} {@link #getYScale()} {@link #getYTranslate()} ] + * [ 0 0 1 ] + * </pre> + */ + public double getXScale() { + return _xscale; + } + + /** + * @see #getXScale() + */ public double getScale01() { return _scale01; } + /** + * @see #getXScale() + */ public double getScale10() { return _scale10; } - public double getXScale() { - return _xscale; - } - + /** + * @see #getXScale() + */ public double getYScale() { return _yscale; } + /** + * @see #getXScale() + */ public int getXTranslate() { return _xtranslate; } + /** + * @see #getXScale() + */ public int getYTranslate() { return _ytranslate; } + + /** + * The point that is to be matched to the new glyph. + * + * @see #getPoint2() + */ + public int getPoint1() { + return _point1; + } + + /** + * The new glyph’s “matched” point. Once a glyph is added, its point numbers + * begin directly after the last glyphs (endpoint of first glyph + 1). + * + * @see #getPoint1() + */ + public int getPoint2() { + return _point2; + } /** * Transforms an x-coordinate of a point for this component. @@ -184,7 +420,7 @@ public class GlyfCompositeComp { * @return The transformed x-coordinate */ public int scaleX(int x, int y) { - return (int)((double) x * _xscale + (double) y * _scale10); + return (int)(x * _xscale + y * _scale10); } /** @@ -194,6 +430,47 @@ public class GlyfCompositeComp { * @return The transformed y-coordinate */ public int scaleY(int x, int y) { - return (int)((double) x * _scale01 + (double) y * _yscale); + return (int)(x * _scale01 + y * _yscale); + } + + /** + * The transformed X coordinate of the given point. + */ + public short transformX(int x, int y) { + return (short) (scaleX(x, y) + getXTranslate()); + } + + /** + * The transformed Y coordinate of the given point. + */ + public short transformY(int x, int y) { + return (short) (scaleY(x, y) + getYTranslate()); + } + + /** + * The glyph referenced by this {@link GlyfCompositeComp} + * + * @see #getGlyphIndex() + * @see GlyfTable#getDescription(int) + */ + public GlyfDescript getReferencedGlyph(GlyfTable table) { + return table.getDescription(getGlyphIndex()); + } + + @Override + public String toString() { + return " glyphIndex: " + getGlyphIndex() + + "\n firstIndex: " + getFirstIndex() + + "\n firstContour: " + getFirstContour() + + "\n flags: " + + (Bits.isSet(_flags, USE_MY_METRICS) ? "USE_MY_METRICS " : "") + + (Bits.isSet(_flags, OVERLAP_COMPOUND) ? "OVERLAP_COMPOUND " : "") + + (Bits.isSet(_flags, SCALED_COMPONENT_OFFSET) ? "SCALED_COMPONENT_OFFSET " : "")+ + (Bits.isSet(_flags, UNSCALED_COMPONENT_OFFSET) ? "UNSCALED_COMPONENT_OFFSET" : "")+ + "\n transform:"+ + "\n "+ getXScale() + " " + getScale01() + " " + getXTranslate() + + "\n "+ getScale10() + " " + getYScale() + " " + getYTranslate() + + "\n point1: " + getPoint1() + + "\n point2: " + getPoint2(); } } diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfCompositeDescript.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfCompositeDescript.java index 1d55221c7..9c1e81b4e 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfCompositeDescript.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfCompositeDescript.java @@ -54,6 +54,7 @@ import java.io.DataInput; import java.io.IOException; import java.util.ArrayList; +import java.util.List; /** * Glyph description for composite glyphs. Composite glyphs are made up of one @@ -64,8 +65,24 @@ import java.util.ArrayList; */ public class GlyfCompositeDescript extends GlyfDescript { - private final ArrayList<GlyfCompositeComp> _components = new ArrayList<>(); - + /** + * See {@link #getComponent(int)} + */ + private final List<GlyfCompositeComp> _components = new ArrayList<>(); + + /** + * Creates a {@link GlyfCompositeDescript} from the given reader. + * + * <p> + * A composite glyph starts with two uint16 values (“flags” and + * “glyphIndex,” i.e. the index of the first contour in this composite + * glyph); the data then varies according to “flags”). + * </p> + * + * @param parentTable See {@link #getParentTable()}. + * @param glyphIndex See {@link #getGlyphIndex()}. + * @param di The reader to read from. + */ public GlyfCompositeDescript( final GlyfTable parentTable, final int glyphIndex, @@ -74,13 +91,13 @@ public class GlyfCompositeDescript extends GlyfDescript { // Get all of the composite components GlyfCompositeComp comp; - int firstIndex = 0; + int index = 0; int firstContour = 0; do { - _components.add(comp = new GlyfCompositeComp(firstIndex, firstContour, di)); - final GlyfDescript desc = parentTable.getDescription(comp.getGlyphIndex()); + _components.add(comp = new GlyfCompositeComp(index, firstContour, di)); + GlyfDescript desc = parentTable.getDescription(comp.getGlyphIndex()); if (desc != null) { - firstIndex += desc.getPointCount(); + index += desc.getPointCount(); firstContour += desc.getContourCount(); } } while ((comp.getFlags() & GlyfCompositeComp.MORE_COMPONENTS) != 0); @@ -92,11 +109,11 @@ public class GlyfCompositeDescript extends GlyfDescript { } @Override - public int getEndPtOfContours(final int i) { - final GlyfCompositeComp c = getCompositeCompEndPt(i); + public int getEndPtOfContours(int contour) { + GlyfCompositeComp c = getCompositeCompEndPt(contour); if (c != null) { - final GlyphDescription gd = _parentTable.getDescription(c.getGlyphIndex()); - return gd.getEndPtOfContours(i - c.getFirstContour()) + c.getFirstIndex(); + final GlyphDescription gd = getReferencedGlyph(c); + return gd.getEndPtOfContours(contour - c.getFirstContour()) + c.getFirstIndex(); } return 0; } @@ -105,7 +122,7 @@ public class GlyfCompositeDescript extends GlyfDescript { public byte getFlags(final int i) { final GlyfCompositeComp c = getCompositeComp(i); if (c != null) { - final GlyphDescription gd = _parentTable.getDescription(c.getGlyphIndex()); + final GlyphDescription gd = getReferencedGlyph(c); return gd.getFlags(i - c.getFirstIndex()); } return 0; @@ -115,28 +132,27 @@ public class GlyfCompositeDescript extends GlyfDescript { public short getXCoordinate(final int i) { final GlyfCompositeComp c = getCompositeComp(i); if (c != null) { - final GlyphDescription gd = _parentTable.getDescription(c.getGlyphIndex()); + final GlyphDescription gd = getReferencedGlyph(c); final int n = i - c.getFirstIndex(); final int x = gd.getXCoordinate(n); final int y = gd.getYCoordinate(n); - short x1 = (short) c.scaleX(x, y); - x1 += c.getXTranslate(); - return x1; + return c.transformX(x, y); } return 0; } - @Override - public short getYCoordinate(final int i) { - final GlyfCompositeComp c = getCompositeComp(i); + private GlyfDescript getReferencedGlyph(GlyfCompositeComp c) { + return c.getReferencedGlyph(_parentTable); + } + + public short getYCoordinate(int i) { + GlyfCompositeComp c = getCompositeComp(i); if (c != null) { - final GlyphDescription gd = _parentTable.getDescription(c.getGlyphIndex()); - final int n = i - c.getFirstIndex(); - final int x = gd.getXCoordinate(n); - final int y = gd.getYCoordinate(n); - short y1 = (short) c.scaleY(x, y); - y1 += c.getYTranslate(); - return y1; + GlyphDescription gd = getReferencedGlyph(c); + int n = i - c.getFirstIndex(); + int x = gd.getXCoordinate(n); + int y = gd.getYCoordinate(n); + return c.transformY(x, y); } return 0; } @@ -148,10 +164,10 @@ public class GlyfCompositeDescript extends GlyfDescript { @Override public int getPointCount() { - final GlyfCompositeComp c = _components.get(_components.size()-1); - final GlyphDescription gd = _parentTable.getDescription(c.getGlyphIndex()); + GlyfCompositeComp last = lastComponent(); + GlyphDescription gd = getReferencedGlyph(last); if (gd != null) { - return c.getFirstIndex() + gd.getPointCount(); + return last.getFirstIndex() + gd.getPointCount(); } else { return 0; } @@ -159,44 +175,73 @@ public class GlyfCompositeDescript extends GlyfDescript { @Override public int getContourCount() { - final GlyfCompositeComp c = _components.get(_components.size()-1); - final GlyfDescript d = _parentTable.getDescription(c.getGlyphIndex()); - return c.getFirstContour() + ( null != d ? d.getContourCount() : 0 ); + GlyfCompositeComp last = lastComponent(); + return last.getFirstContour() + getReferencedGlyph(last).getContourCount(); + } + + private GlyfCompositeComp lastComponent() { + return _components.get(_components.size() - 1); } public int getComponentIndex(final int i) { return _components.get(i).getFirstIndex(); } + /** + * The number of {@link GlyfCompositeComp} in this {@link GlyfCompositeDescript}. + * + * @see #getComponent(int) + */ public int getComponentCount() { return _components.size(); } - public GlyfCompositeComp getComponent(final int i) { + /** + * The {@link GlyfCompositeComp} with the given index. + * + * @see #getComponentCount() + */ + public GlyfCompositeComp getComponent(int i) { return _components.get(i); } - private GlyfCompositeComp getCompositeComp(final int i) { - for (final GlyfCompositeComp c : _components) { - final GlyphDescription gd = _parentTable.getDescription(c.getGlyphIndex()); - if( null != gd ) { - if (c.getFirstIndex() <= i && i < (c.getFirstIndex() + gd.getPointCount())) { - return c; - } + private GlyfCompositeComp getCompositeComp(int i) { + GlyfCompositeComp c; + for (GlyfCompositeComp component : _components) { + c = component; + GlyphDescription gd = getReferencedGlyph(c); + if (c.getFirstIndex() <= i && i < (c.getFirstIndex() + gd.getPointCount())) { + return c; } } return null; } - private GlyfCompositeComp getCompositeCompEndPt(final int i) { - for (final GlyfCompositeComp c : _components) { - final GlyphDescription gd = _parentTable.getDescription(c.getGlyphIndex()); - if( null != gd ) { - if (c.getFirstContour() <= i && i < (c.getFirstContour() + gd.getContourCount())) { - return c; - } + private GlyfCompositeComp getCompositeCompEndPt(int i) { + GlyfCompositeComp c; + for (GlyfCompositeComp component : _components) { + c = component; + GlyphDescription gd = getReferencedGlyph(c); + if (c.getFirstContour() <= i && i < (c.getFirstContour() + gd.getContourCount())) { + return c; } } return null; } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(" Composite Glyph\n"); + sb.append(" ---------------\n"); + sb.append(super.toString()); + sb.append("\n\n"); + for (GlyfCompositeComp component : _components) { + sb.append(" Component\n"); + sb.append(" ---------\n"); + sb.append(component.toString()); + sb.append("\n\n"); + } + return sb.toString(); + } } diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfDescript.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfDescript.java index 45d06e8cb..0eddb3633 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfDescript.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfDescript.java @@ -54,17 +54,120 @@ import java.io.DataInput; import java.io.IOException; /** + * Glyph description. + * + * <p> + * Note: The glyph descriptions do not include side bearing information. + * {@link HmtxTable#getLeftSideBearing(int) Left side bearings} are provided in + * the {@link HmtxTable 'hmtx'} table, and right side bearings are inferred from + * the {@link HmtxTable#getAdvanceWidth(int) advance width} (also provided in + * the 'hmtx' table) and the bounding box coordinates (see + * {@link #getXMinimum()}, {@link #getYMinimum()}, {@link #getXMaximum()}, + * {@link #getYMaximum()}) provided in the {@link GlyfTable 'glyf'} table. For + * vertical layout, {@link VmtxTable#getTopSideBearing(int) top side bearings} + * are provided in the {@link VmtxTable 'vmtx'} table, and bottom side bearings + * are inferred. The rasterizer will generate a representation of side bearings + * in the form of “phantom” points, which are added as four additional points at + * the end of the glyph description and which can be referenced and manipulated + * by glyph instructions. See the chapter Instructing TrueType Glyphs for more + * background on phantom points. + * </p> + * * @author <a href="mailto:[email protected]">David Schweinsberg</a> + * + * @see "https://docs.microsoft.com/en-us/typography/opentype/spec/glyf" */ public abstract class GlyfDescript extends Program implements GlyphDescription { - // flags - public static final byte onCurve = 0x01; - static final byte xShortVector = 0x02; - static final byte yShortVector = 0x04; - static final byte repeat = 0x08; - static final byte xDual = 0x10; - static final byte yDual = 0x20; + /** + * Bit 0: If set, the point is on the curve; otherwise, it is off the curve. + */ + public static final byte ON_CURVE_POINT = 0x01; + + /** + * Bit 1: If set, the corresponding x-coordinate is 1 byte long. If not set, + * it is two bytes long. For the sign of this value, see the description of + * the {@link #X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR} flag. + */ + public static final byte X_SHORT_VECTOR = 0x02; + + /** + * Bit 2: If set, the corresponding y-coordinate is 1 byte long. If not set, + * it is two bytes long. For the sign of this value, see the description of + * the {@link #Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR} flag. + */ + public static final byte Y_SHORT_VECTOR = 0x04; + + /** + * Bit 3: If set, the next byte (read as unsigned) specifies the number of + * additional times this flag byte is to be repeated in the logical flags + * array — that is, the number of additional logical flag entries inserted + * after this entry. (In the expanded logical array, this bit is ignored.) + * In this way, the number of flags listed can be smaller than the number of + * points in the glyph description. + */ + public static final byte REPEAT_FLAG = 0x08; + + /** + * Bit 4: This flag has two meanings, depending on how the + * {@link #X_SHORT_VECTOR} flag is set. If {@link #X_SHORT_VECTOR} is set, + * this bit describes the sign of the value, with 1 equaling positive and 0 + * negative. If {@link #X_SHORT_VECTOR} is not set and this bit is set, then + * the current x-coordinate is the same as the previous x-coordinate. If + * {@link #X_SHORT_VECTOR} is not set and this bit is also not set, the + * current x-coordinate is a signed 16-bit delta vector. + */ + public static final byte X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR = 0x10; + + /** + * Bit 5: This flag has two meanings, depending on how the + * {@link #Y_SHORT_VECTOR} flag is set. If {@link #Y_SHORT_VECTOR} is set, + * this bit describes the sign of the value, with 1 equaling positive and 0 + * negative. If Y_SHORT_VECTOR is not set and this bit is set, then the + * current y-coordinate is the same as the previous y-coordinate. If + * {@link #Y_SHORT_VECTOR} is not set and this bit is also not set, the + * current y-coordinate is a signed 16-bit delta vector. + */ + public static final byte Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR = 0x20; + + /** + * Bit 6: If set, contours in the glyph description may overlap. Use of this + * flag is not required in OpenType — that is, it is valid to have contours + * overlap without having this flag set. It may affect behaviors in some + * platforms, however. (See the discussion of “Overlapping contours” in + * Apple’s specification for details regarding behavior in Apple platforms.) + * When used, it must be set on the first flag byte for the glyph. See + * additional details below. + * + * <p> + * A non-zero-fill algorithm is needed to avoid dropouts when contours + * overlap. The {@link #OVERLAP_SIMPLE} flag is used by some rasterizer + * implementations to ensure that a non-zero-fill algorithm is used rather + * than an even-odd-fill algorithm. Implementations that always use a + * non-zero-fill algorithm will ignore this flag. Note that some + * implementations might check this flag specifically in non-variable fonts, + * but always use a non-zero-fill algorithm for variable fonts. This flag + * can be used in order to provide broad interoperability of fonts — + * particularly non-variable fonts — when glyphs have overlapping contours. + * </p> + * + * <p> + * Note that variable fonts often make use of overlapping contours. This has + * implications for tools that generate static-font data for a specific + * instance of a variable font, if broad interoperability of the derived + * font is desired: if a glyph has overlapping contours in the given + * instance, then the tool should either set this flag in the derived glyph + * data, or else should merge contours to remove overlap of separate + * contours. + * </p> + * + * <p> + * Note: The OVERLAP_COMPOUND flag, described below, has a similar purpose + * in relation to composite glyphs. The same considerations described for + * the OVERLAP_SIMPLE flag also apply to the OVERLAP_COMPOUND flag. + * </p> + */ + public static final byte OVERLAP_SIMPLE = 0x40; final GlyfTable _parentTable; private final int _glyphIndex; @@ -87,9 +190,12 @@ public abstract class GlyfDescript extends Program implements GlyphDescription { _xMax = di.readShort(); _yMax = di.readShort(); } - - int getNumberOfContours() { - return _numberOfContours; + + /** + * The {@link GlyfTable} this {@link GlyfDescript} belongs to. + */ + public GlyfTable getParentTable() { + return _parentTable; } @Override @@ -98,8 +204,8 @@ public abstract class GlyfDescript extends Program implements GlyphDescription { } @Override - public short getXMaximum() { - return _xMax; + public int getNumberOfContours() { + return _numberOfContours; } @Override @@ -108,13 +214,18 @@ public abstract class GlyfDescript extends Program implements GlyphDescription { } @Override - public short getYMaximum() { - return _yMax; + public short getYMinimum() { + return _yMin; } @Override - public short getYMinimum() { - return _yMin; + public short getXMaximum() { + return _xMax; + } + + @Override + public short getYMaximum() { + return _yMax; } @Override diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfSimpleDescript.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfSimpleDescript.java index 406c64f45..76b45fbe4 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfSimpleDescript.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfSimpleDescript.java @@ -52,19 +52,65 @@ package jogamp.graph.font.typecast.ot.table; import java.io.DataInput; import java.io.IOException; + import jogamp.graph.font.typecast.ot.Disassembler; +import jogamp.graph.font.typecast.ot.Fmt; /** + * Simple Glyph Description + * + * <p> + * This is the table information needed if numberOfContours is greater than or + * equal to zero, that is, a glyph is not a composite. Note that point numbers + * are base-zero indices that are numbered sequentially across all of the + * contours for a glyph; that is, the first point number of each contour (except + * the first) is one greater than the last point number of the preceding + * contour. + * </p> + * + * @see "https://docs.microsoft.com/en-us/typography/opentype/spec/glyf" + * * @author <a href="mailto:[email protected]">David Schweinsberg</a> */ public class GlyfSimpleDescript extends GlyfDescript { + /** + * @see #getEndPtOfContours(int) + */ private int[] _endPtsOfContours; + + /** + * @see #getFlags(int) + */ private byte[] _flags; + + /** + * @see #getXCoordinate(int) + */ private short[] _xCoordinates; + + /** + * @see #getYCoordinate(int) + */ private short[] _yCoordinates; + + /** + * @see #getPointCount() + */ private int _count; + /** + * Creates a {@link GlyfSimpleDescript}. + * + * @param parentTable + * The {@link GlyfTable} this instance belongs to. + * @param glyphIndex + * See {@link #getGlyphIndex()} + * @param numberOfContours + * See {@link #getNumberOfContours()} + * @param di + * The reader to read from. + */ public GlyfSimpleDescript( GlyfTable parentTable, int glyphIndex, @@ -75,7 +121,7 @@ public class GlyfSimpleDescript extends GlyfDescript { // Simple glyph description _endPtsOfContours = new int[numberOfContours]; for (int i = 0; i < numberOfContours; i++) { - _endPtsOfContours[i] = di.readShort(); + _endPtsOfContours[i] = di.readUnsignedShort(); } // The last end point index reveals the total number of points @@ -84,36 +130,48 @@ public class GlyfSimpleDescript extends GlyfDescript { _xCoordinates = new short[_count]; _yCoordinates = new short[_count]; - int instructionCount = di.readShort(); + int instructionCount = di.readUnsignedShort(); readInstructions(di, instructionCount); readFlags(_count, di); readCoords(_count, di); } + + @Override + public int getNumberOfContours() { + return _endPtsOfContours.length; + } - public int getEndPtOfContours(int i) { - return _endPtsOfContours[i]; + @Override + public int getEndPtOfContours(int contour) { + return _endPtsOfContours[contour]; } + @Override public byte getFlags(int i) { return _flags[i]; } + @Override public short getXCoordinate(int i) { return _xCoordinates[i]; } + @Override public short getYCoordinate(int i) { return _yCoordinates[i]; } + @Override public boolean isComposite() { return false; } + @Override public int getPointCount() { return _count; } + @Override public int getContourCount() { return getNumberOfContours(); } @@ -126,20 +184,39 @@ public class GlyfSimpleDescript extends GlyfDescript { return 1; } */ + /** + * Reads the glyph coordinates. + * + * <p> + * Note: In the 'glyf' table, the position of a point is not stored in + * absolute terms but as a vector relative to the previous point. The + * delta-x and delta-y vectors represent these (often small) changes in + * position. Coordinate values are in font design units, as defined by the + * {@link HeadTable#getUnitsPerEm() unitsPerEm} field in the + * {@link HeadTable 'head'} table. Note that smaller + * {@link HeadTable#getUnitsPerEm() unitsPerEm} values will make it more + * likely that delta-x and delta-y values can fit in a smaller + * representation (8-bit rather than 16-bit), though with a trade-off in the + * level or precision that can be used for describing an outline. + * </p> + * + * <p> * The table is stored as relative values, but we'll store them as absolutes + * </p> */ private void readCoords(int count, DataInput di) throws IOException { short x = 0; short y = 0; for (int i = 0; i < count; i++) { - if ((_flags[i] & xDual) != 0) { - if ((_flags[i] & xShortVector) != 0) { + byte flag = _flags[i]; + if ((flag & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) != 0) { + if ((flag & X_SHORT_VECTOR) != 0) { x += (short) di.readUnsignedByte(); } } else { - if ((_flags[i] & xShortVector) != 0) { - x += (short) -((short) di.readUnsignedByte()); + if ((flag & X_SHORT_VECTOR) != 0) { + x -= (short) di.readUnsignedByte(); } else { x += di.readShort(); } @@ -148,13 +225,13 @@ public class GlyfSimpleDescript extends GlyfDescript { } for (int i = 0; i < count; i++) { - if ((_flags[i] & yDual) != 0) { - if ((_flags[i] & yShortVector) != 0) { + if ((_flags[i] & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) != 0) { + if ((_flags[i] & Y_SHORT_VECTOR) != 0) { y += (short) di.readUnsignedByte(); } } else { - if ((_flags[i] & yShortVector) != 0) { - y += (short) -((short) di.readUnsignedByte()); + if ((_flags[i] & Y_SHORT_VECTOR) != 0) { + y -= (short) di.readUnsignedByte(); } else { y += di.readShort(); } @@ -164,14 +241,38 @@ public class GlyfSimpleDescript extends GlyfDescript { } /** - * The flags are run-length encoded + * Reads the flags table. + * + * <p> + * Note: The flags are run-length encoded. + * </p> + * + * <p> + * Each element in the flags array is a single byte, each of which has + * multiple flag bits with distinct meanings, see {@link #getFlags(int)}. + * </p> + * + * <p> + * In logical terms, there is one flag byte element, one x-coordinate, and + * one y-coordinate for each point. Note, however, that the flag byte + * elements and the coordinate arrays use packed representations. In + * particular, if a logical sequence of flag elements or sequence of x- or + * y-coordinates is repeated, then the actual flag byte element or + * coordinate value can be given in a single entry, with special flags used + * to indicate that this value is repeated for subsequent logical entries. + * The actual stored size of the flags or coordinate arrays must be + * determined by parsing the flags array entries. See the flag descriptions + * below for details. + * </p> + * + * @see #getFlags(int) */ private void readFlags(int flagCount, DataInput di) throws IOException { try { for (int index = 0; index < flagCount; index++) { _flags[index] = di.readByte(); - if ((_flags[index] & repeat) != 0) { - int repeats = di.readByte(); + if ((_flags[index] & REPEAT_FLAG) != 0) { + int repeats = di.readUnsignedByte(); for (int i = 1; i <= repeats; i++) { _flags[index + i] = _flags[index]; } @@ -183,45 +284,58 @@ public class GlyfSimpleDescript extends GlyfDescript { } } + @Override public String toString() { StringBuilder sb = new StringBuilder(); + sb.append(" Simple Glyph\n"); + sb.append(" ------------\n"); sb.append(super.toString()); - sb.append("\n\n EndPoints\n ---------"); + sb.append("\n\n"); + sb.append(" EndPoints\n"); + sb.append(" ---------"); for (int i = 0; i < _endPtsOfContours.length; i++) { sb.append("\n ").append(i).append(": ").append(_endPtsOfContours[i]); } - sb.append("\n\n Length of Instructions: "); + sb.append("\n\n"); + sb.append(" Instructions\n"); + sb.append(" ------------\n"); + sb.append(" length: "); sb.append(getInstructions().length).append("\n"); - sb.append(Disassembler.disassemble(getInstructions(), 8)); + sb.append(Disassembler.disassemble(getInstructions(), 10)); sb.append("\n Flags\n -----"); for (int i = 0; i < _flags.length; i++) { - sb.append("\n ").append(i).append(": "); - if ((_flags[i] & 0x20) != 0) { + sb.append("\n ").append(Fmt.pad(3, i)).append(": "); + if ((_flags[i] & OVERLAP_SIMPLE) != 0) { + sb.append("SOver "); + } else { + sb.append(" "); + } + if ((_flags[i] & Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR) != 0) { sb.append("YDual "); } else { sb.append(" "); } - if ((_flags[i] & 0x10) != 0) { + if ((_flags[i] & X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) != 0) { sb.append("XDual "); } else { sb.append(" "); } - if ((_flags[i] & 0x08) != 0) { + if ((_flags[i] & REPEAT_FLAG) != 0) { sb.append("Repeat "); } else { sb.append(" "); } - if ((_flags[i] & 0x04) != 0) { + if ((_flags[i] & Y_SHORT_VECTOR) != 0) { sb.append("Y-Short "); } else { sb.append(" "); } - if ((_flags[i] & 0x02) != 0) { + if ((_flags[i] & X_SHORT_VECTOR) != 0) { sb.append("X-Short "); } else { sb.append(" "); } - if ((_flags[i] & 0x01) != 0) { + if ((_flags[i] & ON_CURVE_POINT) != 0) { sb.append("On"); } else { sb.append(" "); @@ -231,7 +345,7 @@ public class GlyfSimpleDescript extends GlyfDescript { short oldX = 0; short oldY = 0; for (int i = 0; i < _xCoordinates.length; i++) { - sb.append("\n ").append(i) + sb.append("\n ").append(Fmt.pad(3, i)) .append(": Rel (").append(_xCoordinates[i] - oldX) .append(", ").append(_yCoordinates[i] - oldY) .append(") -> Abs (").append(_xCoordinates[i]) diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfTable.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfTable.java index 035f0f314..d1c4fe7dd 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfTable.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfTable.java @@ -54,14 +54,54 @@ import java.io.ByteArrayInputStream; import java.io.DataInput; import java.io.DataInputStream; import java.io.IOException; +import java.io.Writer; /** + * Glyph Data + * + * <p> + * This table contains information that describes the glyphs in the font in the + * TrueType outline format. Information regarding the rasterizer (scaler) refers + * to the TrueType rasterizer. + * </p> + * + * <h2>Table Organization</h2> + * + * The 'glyf' table is comprised of a list of glyph data blocks, each of which + * provides the description for a single glyph. Glyphs are referenced by + * identifiers (glyph IDs), which are sequential integers beginning at zero. The + * total number of glyphs is specified by the {@link MaxpTable#getNumGlyphs() + * numGlyphs} field in the {@link MaxpTable 'maxp'} table. The 'glyf' table does + * not include any overall table header or records providing offsets to glyph + * data blocks. Rather, the {@link LocaTable 'loca'} table provides an array of + * offsets, indexed by glyph IDs, which provide the location of each glyph data + * block within the 'glyf' table. Note that the 'glyf' table must always be used + * in conjunction with the 'loca' and 'maxp' tables. The size of each glyph data + * block is inferred from the difference between two consecutive offsets in the + * 'loca' table (with one extra offset provided to give the size of the last + * glyph data block). As a result of the 'loca' format, glyph data blocks within + * the 'glyf' table must be in glyph ID order. + * * @author <a href="mailto:[email protected]">David Schweinsberg</a> + * + * @see "https://docs.microsoft.com/en-us/typography/opentype/spec/glyf" */ public class GlyfTable implements Table { private final GlyfDescript[] _descript; + /** + * Creates a {@link GlyfTable}. + * + * @param di + * The reader to read from. + * @param length + * The length of the table in bytes. + * @param maxp + * The corresponding {@link MaxpTable}. + * @param loca + * The corresponding {@link LocaTable}. + */ public GlyfTable( final DataInput di, final int length, @@ -119,5 +159,32 @@ public class GlyfTable implements Table { return null; } } + + @Override + public String toString() { + return "'glyf' Table - Glyph Data\n--------------------------" + + "\n numGlyphs: " + getNumGlyphs(); + } + + @Override + public void dump(Writer out) throws IOException { + Table.super.dump(out); + out.write("\n"); + + for (int n = 0, cnt = getNumGlyphs(); n< cnt; n++) { + GlyfDescript glyph = getDescription(n); + out.write(" Glyph "); + out.write(Integer.toString(n)); + out.write(": "); + if (glyph == null) { + out.write("(none)"); + } else { + out.write("\n"); + out.write(glyph.toString()); + out.write("\n"); + } + out.write("\n"); + } + } } diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyphDescription.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyphDescription.java index 59b85637d..1cc87ab18 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyphDescription.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyphDescription.java @@ -56,24 +56,65 @@ package jogamp.graph.font.typecast.ot.table; */ public interface GlyphDescription { + /** + * Index of the glyph in the {@link GlyfTable}. + * + * @see GlyfTable#getDescription(int) + */ int getGlyphIndex(); - int getEndPtOfContours(int i); + /** + * int16 + * + * If the number of contours is greater than or equal to zero, this is a + * simple glyph. If negative, this is a composite glyph — the value -1 + * should be used for composite glyphs. + */ + int getNumberOfContours(); + + /** + * int16 xMin Minimum x for coordinate data. + */ + short getXMinimum(); + + /** + * int16 yMin Minimum y for coordinate data. + */ + short getYMinimum(); + + /** + * int16 xMax Maximum x for coordinate data. + */ + short getXMaximum(); + + /** + * int16 yMax Maximum y for coordinate data. + */ + short getYMaximum(); + + /** + * uint16 endPtsOfContours[numberOfContours] Array of point indices for the + * last point of each contour, in increasing numeric order. + */ + int getEndPtOfContours(int contour); + /** + * The flags for the point with the given index. + * + * @see GlyfDescript#ON_CURVE_POINT + * @see GlyfDescript#X_SHORT_VECTOR + * @see GlyfDescript#Y_SHORT_VECTOR + * @see GlyfDescript#REPEAT_FLAG + * @see GlyfDescript#X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR + * @see GlyfDescript#Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR + * @see GlyfDescript#OVERLAP_SIMPLE + */ byte getFlags(int i); short getXCoordinate(int i); short getYCoordinate(int i); - short getXMaximum(); - - short getXMinimum(); - - short getYMaximum(); - - short getYMinimum(); - boolean isComposite(); int getPointCount(); diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/HeadTable.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/HeadTable.java index 8bb4661fc..a03c9204c 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/HeadTable.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/HeadTable.java @@ -320,7 +320,7 @@ public class HeadTable implements Table { * Bit 0: Baseline for font at y=0; * * Bit 1: Left sidebearing point at x=0 (relevant only for TrueType - * rasterizers) — see the note below regarding variable fonts; + * rasterizers) — see {@link #isLeftSidebearingNormalized()}; * * Bit 2: Instructions may depend on point size; * @@ -372,6 +372,22 @@ public class HeadTable implements Table { public short getFlags() { return _flags; } + + /** + * The Left sidebearing point is at x=0 for all glyphs (relevant only for + * TrueType rasterizers) + * + * <p> + * Note: The scaler will perform better if the glyph coordinates have been + * created such that the xMin is equal to the lsb. For example, if the lsb + * is 123, then xMin for the glyph should be 123. If the lsb is -12 then the + * xMin should be -12. If the lsb is 0 then xMin is 0. If all glyphs are + * done like this, set bit 1 of flags field in the 'head' table. + * </p> + */ + public boolean isLeftSidebearingNormalized() { + return Bits.bit(_flags, 1); + } /** * uint16 diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/NameRecord.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/NameRecord.java index ebb70433d..36e0fc6e8 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/NameRecord.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/NameRecord.java @@ -136,7 +136,7 @@ public class NameRecord { "\n Name ID: " + _nameId + "\n Length: " + _stringLength + "\n Offset: " + _stringOffset + - "\n\n" + _record; + "\n Record: " + _record; return sb; } } diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/NameTable.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/NameTable.java index 970427f9f..161a56914 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/NameTable.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/NameTable.java @@ -54,12 +54,24 @@ import java.io.ByteArrayInputStream; import java.io.DataInput; import java.io.DataInputStream; import java.io.IOException; +import java.util.Arrays; +import java.util.stream.Collectors; /** * The naming table allows multilingual strings to be associated with the - * OpenType font file. These strings can represent copyright notices, font + * OpenType font file. These strings can represent copyright notices, font * names, family names, style names, and so on. + * + * Other parts of the OpenType font that require these strings can refer to them + * using a language-independent name ID. In addition to language variants, the + * table also allows for platform-specific character-encoding variants. Clients + * that need a particular string can look it up by its platform ID, encoding ID, + * language ID and name ID. Note that different platforms may have different + * requirements for the encoding of strings. + * * @author <a href="mailto:[email protected]">David Schweinsberg</a> + * + * @see "https://docs.microsoft.com/en-us/typography/opentype/spec/name" */ public class NameTable implements Table { @@ -92,15 +104,32 @@ public class NameTable implements Table { new DataInputStream(new ByteArrayInputStream(buffer))); } } - + @Override public int getType() { return name; } + /** + * uint16 Format selector (=0 or 1). + */ + public short getFormat() { + return _formatSelector; + } + + /** + * uint16 count Number of name records. + */ public short getNumberOfNameRecords() { return _numberOfNameRecords; } + + /** + * Offset16 stringOffset Offset to start of string storage (from start of table). + */ + public short getStringStorageOffset() { + return _stringStorageOffset; + } public NameRecord getRecord(final int i) { @@ -129,4 +158,14 @@ public class NameTable implements Table { return ""; } + @Override + public String toString() { + return "'name' Table - Naming Table\n--------------------------------" + + "\n 'name' format: " + _formatSelector + + "\n count: " + _numberOfNameRecords + + "\n stringOffset: " + _stringStorageOffset + + "\n records:" + + Arrays.asList(_records).stream().map(r -> "\n" + r.toString()).collect(Collectors.joining("\n")); + } + } diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/Program.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/Program.java index a210b6ffd..216b89a6f 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/Program.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/Program.java @@ -17,11 +17,29 @@ import java.io.IOException; abstract class Program { private short[] instructions; + + /** + * uint16 instructionLength Total number of bytes for instructions. If + * instructionLength is zero, no instructions are present for this glyph, + * and this field is followed directly by the flags field. + */ + public int getInstructionLength() { + return instructions.length; + } - short[] getInstructions() { + /** + * uint8 instructions[{@link #getInstructionLength()}] Array of instruction + * byte code for the glyph. + */ + public short[] getInstructions() { return instructions; } + /** + * Reads the instructions array. + * + * @see #getInstructions() + */ void readInstructions(DataInput di, int count) throws IOException { instructions = new short[count]; for (int i = 0; i < count; i++) { diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/Table.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/Table.java index 30fab2b6f..837411d83 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/Table.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/Table.java @@ -17,6 +17,9 @@ */ package jogamp.graph.font.typecast.ot.table; +import java.io.IOException; +import java.io.Writer; + /** * @author <a href="mailto:[email protected]">David Schweinsberg</a> */ @@ -65,4 +68,11 @@ public interface Table { * The type code of this {@link Table}. */ int getType(); + + /** + * Writes full debug information to the given writer. + */ + default void dump(Writer out) throws IOException { + out.write(toString()); + } } diff --git a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/VmtxTable.java b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/VmtxTable.java index 6e6397d90..a63b299fc 100644 --- a/src/jogl/classes/jogamp/graph/font/typecast/ot/table/VmtxTable.java +++ b/src/jogl/classes/jogamp/graph/font/typecast/ot/table/VmtxTable.java @@ -65,7 +65,7 @@ class VmtxTable implements Table { } } - private short getTopSideBearing(int i) { + public short getTopSideBearing(int i) { if (_vMetrics == null) { return 0; } |