summaryrefslogtreecommitdiffstats
path: root/src/jogl/classes/jogamp/graph/font
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2023-02-12 07:17:16 +0100
committerSven Gothel <[email protected]>2023-02-12 07:17:16 +0100
commit93c51380f34c3eb203f46df52fed49a8a967510e (patch)
tree52d8745dffc4f7581efdc118f7ec0792b7b75315 /src/jogl/classes/jogamp/graph/font
parent87060fe41b559418ea7b383e162d3d80fb515e0b (diff)
Graph font/typecast: Adopt to our Typecast updates (see below); Fix kerning; Use TestTextRendererNEWT01 to produce validation snaps
- Move kerning handling from Font to Font.Glyph using binary-search for right-glyph-id on kerning subset from this instance (left) - TextRegionUtil: Kerning must be added before translation as it applies before the current right-glyph. - TestTextRendererNEWT01 produces validation snapshots against LibreOffice print-preview snapshots - GPUTextRendererListenerBase01 added another text for kerning validation, show more font-size details (pt, px, mm)
Diffstat (limited to 'src/jogl/classes/jogamp/graph/font')
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java61
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/TypecastFontConstructor.java4
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java133
-rw-r--r--src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java5
4 files changed, 147 insertions, 56 deletions
diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java
index 885261bf9..146bc0380 100644
--- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java
+++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java
@@ -27,13 +27,14 @@
*/
package jogamp.graph.font.typecast;
-import jogamp.graph.font.typecast.ot.OTFont;
import jogamp.graph.font.typecast.ot.OTFontCollection;
+import jogamp.graph.font.typecast.ot.TTFont;
import jogamp.graph.font.typecast.ot.table.CmapFormat;
import jogamp.graph.font.typecast.ot.table.CmapIndexEntry;
import jogamp.graph.font.typecast.ot.table.CmapTable;
import jogamp.graph.font.typecast.ot.table.HdmxTable;
import jogamp.graph.font.typecast.ot.table.ID;
+import jogamp.graph.font.typecast.ot.table.KernSubtable;
import jogamp.graph.font.typecast.ot.table.KernSubtableFormat0;
import jogamp.graph.font.typecast.ot.table.KernTable;
import jogamp.graph.font.typecast.ot.table.KerningPair;
@@ -54,7 +55,7 @@ class TypecastFont implements Font {
private static final Vertex.Factory<SVertex> vertexFactory = SVertex.factory();
// private final OTFontCollection fontset;
- /* pp */ final OTFont font;
+ /* pp */ final TTFont font;
private final CmapFormat cmapFormat;
private final int cmapentries;
private final IntObjectHashMap char2Glyph;
@@ -153,22 +154,16 @@ class TypecastFont implements Font {
}
@Override
- public StringBuilder getName(final StringBuilder sb, final int nameIndex) {
- return font.getName(nameIndex, sb);
- }
- @Override
public String getName(final int nameIndex) {
- return getName(null, nameIndex).toString();
+ return font.getName(nameIndex);
}
@Override
public StringBuilder getAllNames(final StringBuilder sb, final String separator) {
return font.getAllNames(sb, separator);
}
@Override
- public StringBuilder getFullFamilyName(StringBuilder sb) {
- sb = getName(sb, Font.NAME_FAMILY).append("-");
- getName(sb, Font.NAME_SUBFAMILY);
- return sb;
+ public String getFullFamilyName() {
+ return getName(Font.NAME_FAMILY) + "-" + getName(Font.NAME_SUBFAMILY);
}
@Override
@@ -190,34 +185,6 @@ class TypecastFont implements Font {
}
@Override
- public final float getKerning(final int left_glyphid, final int right_glyphid) {
- return metrics.getScale( getKerningFU(left_glyphid, right_glyphid) );
- }
-
- @Override
- public int getKerningFU(final int left_glyphid, final int right_glyphid) {
- if( 0 == left_glyphid || 0 == right_glyphid ) {
- return 0;
- }
- final KernTable kern = font.getKernTable();
- if (kern != null) {
- final int kernSubtableCount = kern.getSubtableCount();
- if( 0 < kernSubtableCount ) {
- final KernSubtableFormat0 kst0 = kern.getSubtable0();
- if( null != kst0 && kst0.areKerningValues() && kst0.isHorizontal() && !kst0.isCrossstream() ) {
- for (int i = 0; i < kst0.getKerningPairCount(); i++) {
- final KerningPair kpair = kst0.getKerningPair(i);
- if( kpair.getLeft() == left_glyphid && kpair.getRight() == right_glyphid ) {
- return kpair.getValue();
- }
- }
- }
- }
- }
- return 0;
- }
-
- @Override
public int getGlyphID(final char symbol) {
// enforce mapping as some fonts have an erroneous cmap (FreeSerif-Regular)
switch(symbol) {
@@ -241,7 +208,7 @@ class TypecastFont implements Font {
if (null == result) {
final int glyph_id = getGlyphID( symbol );
- jogamp.graph.font.typecast.ot.OTGlyph glyph = font.getGlyph(glyph_id);
+ jogamp.graph.font.typecast.ot.Glyph glyph = font.getGlyph(glyph_id);
final int glyph_advance;
final AABBox glyph_bbox;
if(null == glyph) {
@@ -264,7 +231,14 @@ class TypecastFont implements Font {
throw new RuntimeException("Could not retrieve glyph for symbol: <"+symbol+"> "+(int)symbol+" -> glyph id "+glyph_id);
}
final OutlineShape shape = TypecastRenderer.buildShape(symbol, glyph, vertexFactory);
- result = new TypecastGlyph(this, symbol, glyph_id, glyph_bbox, glyph_advance, shape);
+ KernSubtable kernSub = null;
+ {
+ final KernTable kern = font.getKernTable();
+ if (kern != null ) {
+ kernSub = kern.getSubtable0();
+ }
+ }
+ result = new TypecastGlyph(this, symbol, glyph_id, glyph_bbox, glyph_advance, kernSub, shape);
if(DEBUG) {
final PostTable post = font.getPostTable();
final String glyph_name = null != post ? post.getGlyphName(glyph_id) : "n/a";
@@ -557,9 +531,10 @@ class TypecastFont implements Font {
@Override
public String toString() {
- return getFullFamilyName(null).toString();
+ return getFullFamilyName();
}
+ @SuppressWarnings("unused")
@Override
public String fullString() {
final StringBuilder sb = new StringBuilder();
@@ -569,7 +544,7 @@ class TypecastFont implements Font {
if( null != font.getVheaTable() ) {
sb.append("\n\n").append(font.getVheaTable());
}
- if( null != font.getKernTable() ) {
+ if( false && null != font.getKernTable() ) { // too long
final PostTable post = font.getPostTable();
final KernTable kern = font.getKernTable();
sb.append("\n\n").append(kern);
diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastFontConstructor.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFontConstructor.java
index ef7f38e64..72dd95da2 100644
--- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastFontConstructor.java
+++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFontConstructor.java
@@ -40,11 +40,11 @@ public class TypecastFontConstructor implements FontConstructor {
@Override
public Font create(final File ffile) throws IOException {
- return new TypecastFont( OTFontCollection.create(ffile) );
+ return new TypecastFont( new OTFontCollection(ffile) );
}
@Override
public Font create(final InputStream istream, final int streamLen) throws IOException {
- return new TypecastFont( OTFontCollection.create(istream, streamLen) );
+ return new TypecastFont( new OTFontCollection(istream, streamLen) );
}
}
diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java
index b36196ee5..b5876758f 100644
--- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java
+++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java
@@ -32,6 +32,8 @@ import com.jogamp.graph.curve.OutlineShape;
import com.jogamp.graph.font.Font;
import com.jogamp.opengl.math.geom.AABBox;
+import jogamp.graph.font.typecast.ot.table.KernSubtable;
+import jogamp.graph.font.typecast.ot.table.KerningPair;
import jogamp.graph.font.typecast.ot.table.PostTable;
public final class TypecastGlyph implements Font.Glyph {
@@ -170,9 +172,36 @@ public final class TypecastGlyph implements Font.Glyph {
public static final short INVALID_ID = (short)((1 << 16) - 1);
public static final short MAX_ID = (short)((1 << 16) - 2);
+ private static int[][] growPairArray(final int[][] src) {
+ final int length = src.length;
+ final int new_length = length * 2;
+ final int[/*right_glyphid*/][/*value*/] dst = new int[new_length][2];
+ for (int i = 0; i < length; i++) {
+ dst[i][0] = src[i][0];
+ dst[i][1] = src[i][1];
+ }
+ return dst;
+ }
+
+ private static int[][] trimPairArray(final int[][] src, final int new_length) {
+ final int length = src.length;
+ if( new_length >= length ) {
+ return src;
+ }
+ final int[/*right_glyphid*/][/*value*/] dst = new int[new_length][2];
+ for (int i = 0; i < new_length; i++) {
+ dst[i][0] = src[i][0];
+ dst[i][1] = src[i][1];
+ }
+ return dst;
+ }
+
private final char symbol;
- private final OutlineShape shape; // in EM units
private final int id;
+ private final int[/*right_glyphid*/][/*value*/] kerning;
+ private final boolean kerning_horizontal;
+ private final boolean kerning_crossstream;
+ private final OutlineShape shape; // in EM units
private final Metrics metrics;
/**
@@ -184,10 +213,37 @@ public final class TypecastGlyph implements Font.Glyph {
* @param advance from hmtx in font-units
* @param shape
*/
- protected TypecastGlyph(final TypecastFont font, final char symbol, final int id, final AABBox bbox, final int advance, final OutlineShape shape) {
+ protected TypecastGlyph(final TypecastFont font, final char symbol, final int id, final AABBox bbox, final int advance,
+ final KernSubtable kernSub, final OutlineShape shape) {
this.symbol = symbol;
- this.shape = shape;
this.id = id;
+ if( null != kernSub && kernSub.areKerningValues() ) {
+ int pair_sz = 64;
+ int pair_idx = 0;
+ int[/*right_glyphid*/][/*value*/] pairs = new int[pair_sz][2];
+ for (int i = 0; i < kernSub.getKerningPairCount(); i++) {
+ final KerningPair kpair = kernSub.getKerningPair(i);
+ if( kpair.getLeft() == id ) {
+ if( pair_idx == pair_sz ) {
+ pairs = growPairArray(pairs);
+ pair_sz = pairs.length;
+ }
+ pairs[pair_idx][0] = kpair.getRight();
+ pairs[pair_idx][1] = kpair.getValue();
+ ++pair_idx;
+ } else if( kpair.getLeft() > id ) {
+ break; // early out
+ }
+ }
+ this.kerning = trimPairArray(pairs, pair_idx);
+ this.kerning_horizontal = kernSub.isHorizontal();
+ this.kerning_crossstream = kernSub.isCrossstream();
+ } else {
+ this.kerning = new int[0][0];
+ this.kerning_horizontal = true;
+ this.kerning_crossstream = true;
+ }
+ this.shape = shape;
this.metrics = new Metrics(font, bbox, advance);
}
@@ -248,6 +304,39 @@ public final class TypecastGlyph implements Font.Glyph {
}
@Override
+ public final boolean isKerningHorizontal() { return kerning_horizontal; }
+
+ @Override
+ public final boolean isKerningCrossstream() { return kerning_crossstream; }
+
+ @Override
+ public final int getKerningPairCount() { return kerning.length; }
+
+ @Override
+ public final int getKerningFU(final int right_glyphid) {
+ // binary search in ordered kerning table
+ int l = 0;
+ int h = kerning.length-1;
+ while( l <= h ) {
+ final int i = ( l + h ) / 2;
+ final int k_right = kerning[i][0];
+ if ( k_right < right_glyphid ) {
+ l = i + 1;
+ } else if ( k_right > right_glyphid ) {
+ h = i - 1;
+ } else {
+ return kerning[i][1];
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public final float getKerning(final int right_glyphid) {
+ return getScale( getKerningFU(right_glyphid) );
+ }
+
+ @Override
public final OutlineShape getShape() {
return this.shape;
}
@@ -263,10 +352,38 @@ public final class TypecastGlyph implements Font.Glyph {
public String toString() {
final PostTable post = metrics.getFont().getPostTable();
final String glyph_name = null != post ? post.getGlyphName(id) : "n/a";
- return new StringBuilder()
- .append("Glyph id ").append(id).append(" '").append(glyph_name).append("'")
- .append(", advance ").append(getAdvanceFU())
- .append(", ").append(getBBoxFU())
- .toString();
+ final StringBuilder sb = new StringBuilder();
+ sb.append("Glyph id ").append(id).append(" '").append(glyph_name).append("'")
+ .append(", advance ").append(getAdvanceFU())
+ .append(", kerning[size ").append(kerning.length).append(", horiz ").append(this.isKerningHorizontal()).append(", cross ").append(this.isKerningCrossstream()).append("]");
+ return sb.toString();
+ }
+
+ @Override
+ public String fullString() {
+ final PostTable post = metrics.getFont().getPostTable();
+ final String glyph_name = null != post ? post.getGlyphName(id) : "n/a";
+ final StringBuilder sb = new StringBuilder();
+ sb.append("Glyph id ").append(id).append(" '").append(glyph_name).append("'")
+ .append(", advance ").append(getAdvanceFU())
+ .append(", ").append(getBBoxFU());
+
+ sb.append("\n Kerning: size ").append(kerning.length).append(", horiz ").append(this.isKerningHorizontal()).append(", cross ").append(this.isKerningCrossstream());
+ final int left = getID();
+ for (int i = 0; i < kerning.length; i++) {
+ final int right = kerning[i][0];
+ final int value = kerning[i][1];
+ final String leftS;
+ final String rightS;
+ if( null == post ) {
+ leftS = String.valueOf(left);
+ rightS = String.valueOf(left);
+ } else {
+ leftS = post.getGlyphName(left)+"/"+String.valueOf(left);
+ rightS = post.getGlyphName(right)+"/"+String.valueOf(right);
+ }
+ sb.append("\n kp[").append(i).append("]: ").append(leftS).append(" -> ").append(rightS).append(" = ").append(value);
+ }
+ return sb.toString();
}
}
diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java
index 09a63d845..472e3e58e 100644
--- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java
+++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastRenderer.java
@@ -27,7 +27,6 @@
*/
package jogamp.graph.font.typecast;
-import jogamp.graph.font.typecast.ot.OTGlyph;
import jogamp.graph.font.typecast.ot.Point;
import jogamp.opengl.Debug;
@@ -76,7 +75,7 @@ public class TypecastRenderer {
shape.addVertex(0, p3.x, p3.y, p3.onCurve);
} */
- public static OutlineShape buildShape(final char symbol, final OTGlyph glyph, final Factory<? extends Vertex> vertexFactory) {
+ public static OutlineShape buildShape(final char symbol, final jogamp.graph.font.typecast.ot.Glyph glyph, final Factory<? extends Vertex> vertexFactory) {
//
// See Typecast: GlyphPathFactory.addContourToPath(..)
//
@@ -111,7 +110,7 @@ public class TypecastRenderer {
}
} */
- private static void buildShapeImpl(final OutlineShape shape, final char symbol, final OTGlyph glyph) {
+ private static void buildShapeImpl(final OutlineShape shape, final char symbol, final jogamp.graph.font.typecast.ot.Glyph glyph) {
// Iterate through all of the points in the glyph. Each time we find a
// contour end point, add the point range to the path.
int startIndex = 0;