aboutsummaryrefslogtreecommitdiffstats
path: root/src/jogl/classes
diff options
context:
space:
mode:
authorBernhard Haumacher <[email protected]>2020-05-10 11:45:50 +0200
committerSven Göthel <[email protected]>2024-02-03 01:58:22 +0100
commita237a956fcc925e27d72ba49d242dcc1dc09072c (patch)
treebc177d059a8ab84c57dee605cf567afb0891c247 /src/jogl/classes
parenta71ee81c72baa1c4d6a58220df7733acca499166 (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')
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/Bits.java7
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/Disassembler.java2
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/Fixed_2_14.java27
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/Fmt.java26
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/OTFont.java31
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/TTFont.java10
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/TTGlyph.java2
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfCompositeComp.java369
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfCompositeDescript.java137
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfDescript.java143
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfSimpleDescript.java166
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyfTable.java67
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/table/GlyphDescription.java59
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/table/HeadTable.java18
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/table/NameRecord.java2
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/table/NameTable.java43
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/table/Program.java20
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/table/Table.java10
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/ot/table/VmtxTable.java2
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;
}