From a8de1673ca83475227fcc914fd94a9a0be1cba79 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Sun, 24 Sep 2023 16:50:49 +0200
Subject: Bug 1462 - Graph Font: Add name + codepoint to ID and Glyph mapping
 plus traversing through all Glyphs

See UISceneDemo03
new Button(options.renderModes, fontSymbols, " "+fontSymbols.getUTF16String("pause")+" ", buttonWidth, buttonHeight); // pause

Unicode codepoint symbol is also contained in FontGlyph
---
 .../com/jogamp/graph/curve/opengl/GLRegion.java    |   2 +-
 .../jogamp/graph/curve/opengl/TextRegionUtil.java  |   4 +-
 src/jogl/classes/com/jogamp/graph/font/Font.java   |  96 +++++--
 .../jogamp/graph/font/typecast/TypecastFont.java   | 299 ++++++++++++++-------
 .../jogamp/graph/font/typecast/TypecastGlyph.java  |  32 ++-
 5 files changed, 293 insertions(+), 140 deletions(-)

(limited to 'src/jogl/classes')

diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java
index 47f8e1b2a..2cd40c6e4 100644
--- a/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/GLRegion.java
@@ -186,7 +186,7 @@ public abstract class GLRegion extends Region {
         final int[] vertIndexCount = { 0, 0 };
         final Font.GlyphVisitor2 visitor = new Font.GlyphVisitor2() {
             @Override
-            public final void visit(final char symbol, final Font.Glyph glyph) {
+            public final void visit(final Font.Glyph glyph) {
                 if( !glyph.isNonContour() ) {
                     Region.countOutlineShape(glyph.getShape(), vertIndexCount);
                 }
diff --git a/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java b/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java
index 782615c3e..e6e4f3e0c 100644
--- a/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java
@@ -144,7 +144,7 @@ public class TextRegionUtil {
                                            final AffineTransform temp1, final AffineTransform temp2) {
         final Font.GlyphVisitor visitor = new Font.GlyphVisitor() {
             @Override
-            public void visit(final char symbol, final Glyph glyph, final AffineTransform t) {
+            public void visit(final Glyph glyph, final AffineTransform t) {
                 if( !glyph.isNonContour() ) {
                     region.addOutlineShape(glyph.getShape(), t, rgbaColor);
                 }
@@ -173,7 +173,7 @@ public class TextRegionUtil {
     public static int[] countStringRegion(final Font font, final CharSequence str, final int[/*2*/] vertIndexCount) {
         final Font.GlyphVisitor2 visitor = new Font.GlyphVisitor2() {
             @Override
-            public final void visit(final char symbol, final Font.Glyph glyph) {
+            public final void visit(final Font.Glyph glyph) {
                 if( !glyph.isNonContour() ) {
                     Region.countOutlineShape(glyph.getShape(), vertIndexCount);
                 }
diff --git a/src/jogl/classes/com/jogamp/graph/font/Font.java b/src/jogl/classes/com/jogamp/graph/font/Font.java
index 997eacbb8..c8682a4e5 100644
--- a/src/jogl/classes/com/jogamp/graph/font/Font.java
+++ b/src/jogl/classes/com/jogamp/graph/font/Font.java
@@ -240,11 +240,11 @@ public interface Font {
          */
         AABBox getBounds();
 
-        /** Return advance in font units, sourced from `hmtx` table. */
-        int getAdvanceFU();
+        /** Returns advance in font units, sourced from `hmtx` table. */
+        int getAdvanceWidthFU();
 
-        /** Return advance in font em-size [0..1], sourced from `hmtx` table. */
-        float getAdvance();
+        /** Returns advance in font em-size [0..1], sourced from `hmtx` table. */
+        float getAdvanceWidth();
 
         /** Return leftSideBearings in font units, sourced from `hmtx` table. */
         int getLeftSideBearingsFU();
@@ -258,7 +258,7 @@ public interface Font {
         /** True if kerning values are perpendicular to text flow, otherwise along with flow */
         boolean isKerningCrossstream();
 
-        /** Return the number of kerning values stored for this glyph, associated to a right hand glyph. */
+        /** Returns the number of kerning values stored for this glyph, associated to a right hand glyph. */
         int getKerningPairCount();
 
         /**
@@ -295,25 +295,47 @@ public interface Font {
     public static interface GlyphVisitor {
         /**
          * Visiting the given {@link Font.Glyph} having an {@link OutlineShape} with it's corresponding {@link AffineTransform}.
-         * @param symbol the character symbol matching the given glyph
          * @param glyph {@link Font.Glyph} which contains an {@link OutlineShape} via {@link Font.Glyph#getShape()}.
          * @param t may be used immediately as is, otherwise a copy shall be made if stored.
          */
-        public void visit(final char symbol, final Glyph glyph, final AffineTransform t);
+        public void visit(final Glyph glyph, final AffineTransform t);
     }
 
     /**
-     * Constrained {@link Font.Glyph} visitor w/o {@link AffineTransform}.
+     * General purpose {@link Font.Glyph} visitor w/o {@link AffineTransform}.
      */
     public static interface GlyphVisitor2 {
         /**
-         * Visiting the given {@link Font.Glyph} having an {@link OutlineShape}.
-         * @param symbol the character symbol matching the given glyph
-         * @param glyph {@link Font.Glyph} which contains an {@link OutlineShape} via {@link Font.Glyph#getShape()}.
+         * Visiting the given {@link Font.Glyph}
+         * @param glyph {@link Font.Glyph} which may contain an {@link OutlineShape} via {@link Font.Glyph#getShape()}.
          */
-        public void visit(final char symbol, final Glyph glyph);
+        public void visit(final Glyph glyph);
     }
 
+    /**
+     * General purpose (unicode) `codepoint` symbol and {@link Font.Glyph} ID visitor without enforcing {@link Glyph} caching.
+     */
+    public static interface CodepointIDVisitor {
+        /**
+         * Visiting the given (unicode) `codepoint` symbol and {@link Font.Glyph} ID.
+         * @param codepoint (unicode) `codepoint` symbol
+         * @param glyph_id {@link Font.Glyph} ID
+         */
+        public void visit(final char codepoint, final int glyph_id);
+    }
+
+    /**
+     * Returns UTF-16 representation of the specified (unicode) `codepoint` symbol like {@link Character#toChars(int)} or {@link Character#toString()}.
+     * <p>
+     * The returned string can be inserted in any text.
+     * </p>
+     * @param codepoint the (unicode) `codepoint` symbol
+     * @return the Java {@link String} conforming result
+     */
+    public static String getUTF16String(final char codepoint) {
+        return Character.toString(codepoint);
+        // return new String(Character.toChars(codepoint));
+    }
 
     String getName(final int nameIndex);
 
@@ -343,32 +365,64 @@ public interface Font {
     boolean equals(final Object o);
 
     /**
-     * Return advance-width of given glyphID in font-units, sourced from `hmtx` table.
+     * Returns advance-width of given glyphID in font-units, sourced from `hmtx` table - same as {@link Glyph#getAdvanceWidthFU()}.
      * @param glyphID
      */
     int getAdvanceWidthFU(final int glyphID);
 
     /**
-     * Return advance-width of given glyphID in font em-size [0..1], sourced from `hmtx` table.
+     * Returns advance-width of given glyphID in font em-size [0..1], sourced from `hmtx` table  - same as {@link Glyph#getAdvanceWidth()}.
      * @param glyphID
      */
     float getAdvanceWidth(final int glyphID);
 
     Metrics getMetrics();
 
-    /** Return the {@link Glyph} ID mapped to given `symbol`, usually UTF16 unicode. Returned ID can be used to retrieve the {@link Glyph} via {@link #getGlyph(int)}. */
-    int getGlyphID(final char symbol);
-
-    /** Return number of {@link Glyph} IDs available, i.e. retrievable via {@link #getGlyph(int)} [0..count). */
+    /** Returns number of {@link Glyph} IDs available, i.e. retrievable via {@link #getGlyph(int)} [0..count). */
     int getGlyphCount();
 
-    /** Return the {@link Glyph} using given ID, see {@link #getGlyphCount()}. */
+    /** Returns the {@link Glyph} (unicode) `codepoint` symbol mapped to given {@link Glyph} `name`. */
+    char getGlyphCodepoint(final String name);
+
+    /**
+     * Returns UTF-16 representation of the specified {@link Glyph} `name` using {@link #getGlyphCodepoint(String)} and {@link #getUTF16String(char)}.
+     * <p>
+     * The returned string can be inserted in any text.
+     * </p>
+     * @param codepoint the (unicode) `codepoint` symbol
+     * @return the Java {@link String} conforming result
+     */
+    String getUTF16String(final String name);
+
+    /** Returns the {@link Glyph} ID mapped to given UTF16 (unicode) `codepoint` symbol. */
+    int getGlyphID(final char codepoint);
+
+    /** Returns the {@link Glyph} mapped to given `name`. */
+    Glyph getGlyph(final String name);
+
+    /** Returns the {@link Glyph} mapped to given (unicode) `codepoint` symbol. */
+    Glyph getGlyph(final char codepoint);
+
+    /** Returns the {@link Glyph} using given ID. */
     Glyph getGlyph(final int glyph_id);
 
-    int getNumGlyphs();
+    /**
+     * Visit all (unicode) `codepoint` symbol and {@link Glyph} ID tuple of this font.
+     * @param visitor handling each (unicode) `codepoint` symbol and {@link Glyph} ID tuple.
+     */
+    void forAllCodepoints(final Font.CodepointIDVisitor visitor);
+
+    /**
+     * Visit all {@link Glyph}s of this font.
+     * <p>
+     * Warning: All {@link Glyph}s will be cached.
+     * </p>
+     * @param visitor handling each {@link Glyph}
+     */
+    void forAllGlyphs(final Font.GlyphVisitor2 visitor);
 
     /**
-     * Return line height, baseline-to-baseline in font-units, composed from `hhea' table entries.
+     * Returns line height, baseline-to-baseline in font-units, composed from `hhea' table entries.
      * <pre>
      *   return ascent - descent + linegap;
      * </pre>
diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java
index a8651389c..e8c6a4ed8 100644
--- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java
+++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastFont.java
@@ -27,6 +27,9 @@
  */
 package jogamp.graph.font.typecast;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import com.jogamp.common.util.IntObjectHashMap;
 import com.jogamp.graph.curve.OutlineShape;
 import com.jogamp.graph.font.Font;
@@ -52,14 +55,24 @@ import jogamp.opengl.Debug;
 
 class TypecastFont implements Font {
     private static final boolean DEBUG = Debug.debug("graph.font.Font");
+    private static final char UNDEF_SYMBOL = 0xffff;
+    static class SymAndID {
+        public final char codepoint;
+        public final int id;
+        public final String name;
+        public TypecastGlyph glyph;
+        SymAndID(final char codepoint, final int id, final String name) { this.codepoint = codepoint; this.id = id; this.name = name; this.glyph = null; }
+    }
+    private static final SymAndID UNDEF_VALUE = new SymAndID(UNDEF_SYMBOL, Glyph.ID_UNKNOWN, TypecastGlyph.dot_undef_NAME);
 
     // private final OTFontCollection fontset;
     /* pp */ final TTFont font;
     private final CmapFormat cmapFormat;
     private final int cmapentries;
     private final IntObjectHashMap idToGlyph;
+    private final IntObjectHashMap cpToGlyph;
+    private final Map<String, SymAndID> nameToGlyph;
     private final TypecastHMetrics metrics;
-    // FIXME: Add cache size to limit memory usage ??
 
     private static final boolean forceAscii = false; // FIXME ??? (ASCII/Macintosh cmap format)
 
@@ -92,27 +105,58 @@ class TypecastFont implements Font {
             throw new RuntimeException("Cannot find a suitable cmap table");
         }
         cmapFormat = cmapFmt;
-        if(DEBUG) {
-            System.err.println("Selected CmapFormat: platform " + platform + ", encoding "+encoding + ": "+cmapFormat);
-        }
-
         {
             int _cmapentries = 0;
-            for (int i = 0; i < cmapFormat.getRangeCount(); ++i) {
+            for(int i=0; i<cmapFormat.getRangeCount(); ++i) {
                 final CmapFormat.Range range = cmapFormat.getRange(i);
                 _cmapentries += range.getEndCode() - range.getStartCode() + 1; // end included
             }
             cmapentries = _cmapentries;
+            idToGlyph = new IntObjectHashMap(cmapentries + cmapentries/4);
+            idToGlyph.setKeyNotFoundValue(UNDEF_VALUE);
+            cpToGlyph = new IntObjectHashMap(cmapentries + cmapentries/4);
+            cpToGlyph.setKeyNotFoundValue(UNDEF_VALUE);
+            nameToGlyph = new HashMap<String, SymAndID>(cmapentries + cmapentries/4);
+            final PostTable post = font.getPostTable();
+            for(int i=0; i<cmapFormat.getRangeCount(); ++i) {
+                final CmapFormat.Range range = cmapFormat.getRange(i);
+                for(int codepoint = range.getStartCode(); codepoint <= range.getEndCode(); ++codepoint) {
+                    final int id = cmapFormat.mapCharCode(codepoint);
+                    if( 0 < id ) {
+                        boolean mapped = false;
+                        if( null != post ) {
+                            final String name = post.getGlyphName(id);
+                            if( null != name && name.length() > 0 ) {
+                                final SymAndID value = new SymAndID((char)codepoint, id, name);
+                                nameToGlyph.put(name, value);
+                                idToGlyph.put(id, value);
+                                cpToGlyph.put(codepoint, value);
+                                mapped = true;
+                            }
+                        }
+                        if( !mapped ) {
+                            final SymAndID value = new SymAndID((char)codepoint, id, "");
+                            idToGlyph.put(id, value);
+                            cpToGlyph.put(codepoint, value);
+                        }
+                    }
+                }
+            }
+            if( DEBUG ) {
+                System.err.println("Selected CmapFormat: platform " + platform + ", encoding "+encoding + ": "+cmapFormat.getClass().getSimpleName());
+                System.err.println("Map Result");
+                System.err.println("Map: idToGlyph: "+idToGlyph.size());
+                System.err.println("Map: nameToGlyph: "+nameToGlyph.size());
+                System.err.println("CMap entries: "+cmapentries);
+                System.err.println("Font glyph-count "+this.getGlyphCount()+", num-glyphs "+font.getNumGlyphs());
+            }
         }
-        idToGlyph = new IntObjectHashMap(cmapentries + cmapentries/4);
         metrics = new TypecastHMetrics(this);
+        getGlyphImpl(UNDEF_VALUE);
 
         if(DEBUG) {
             final int max_id = 36; // "A"
             System.err.println("font direction hint: "+font.getHeadTable().getFontDirectionHint());
-            System.err.println("num glyphs: "+font.getNumGlyphs());
-            System.err.println("num cmap entries: "+cmapentries);
-            System.err.println("num cmap ranges: "+cmapFormat.getRangeCount());
 
             for (int i = 0; i < cmapFormat.getRangeCount(); ++i) {
                 final CmapFormat.Range range = cmapFormat.getRange(i);
@@ -177,12 +221,31 @@ class TypecastFont implements Font {
     }
 
     @Override
-    public int getGlyphID(final char symbol) {
-        final int glyphID = cmapFormat.mapCharCode(symbol);
-        if( 0 < glyphID ) {
-            return glyphID;
+    public int getGlyphCount() { return font.getGlyphCount(); }
+
+    @Override
+    public char getGlyphCodepoint(final String name) {
+        final SymAndID value = nameToGlyph.get(name);
+        if( null != value ) {
+            return value.codepoint;
+        } else {
+            return UNDEF_VALUE.codepoint;
+        }
+    }
+
+    @Override
+    public String getUTF16String(final String name) {
+        return Font.getUTF16String( getGlyphCodepoint( name ) );
+    }
+
+    @Override
+    public int getGlyphID(final char codepoint) {
+        final SymAndID value = (SymAndID) cpToGlyph.get(codepoint);
+        if( null != value ) {
+            return value.id;
+        } else {
+            return UNDEF_VALUE.id;
         }
-        return Glyph.ID_UNKNOWN;
     }
 
     /** pp **/ PostTable getPostTable() {
@@ -190,73 +253,112 @@ class TypecastFont implements Font {
     }
 
     @Override
-    public int getGlyphCount() { return font.getGlyphCount(); }
+    public synchronized Glyph getGlyph(final String name) {
+        final SymAndID value = nameToGlyph.get(name);
+        if( null != value ) {
+            return getGlyphImpl(value);
+        } else {
+            return UNDEF_VALUE.glyph;
+        }
+    }
+
+    @Override
+    public synchronized Glyph getGlyph(final char codepoint) {
+        final SymAndID value = (SymAndID) cpToGlyph.get(codepoint);
+        if( null != value ) {
+            return getGlyphImpl(value);
+        } else {
+            return UNDEF_VALUE.glyph;
+        }
+    }
 
     @Override
-    public Glyph getGlyph(final int glyph_id) {
-        TypecastGlyph result = (TypecastGlyph) idToGlyph.get(glyph_id);
-        if (null == result) {
-            final jogamp.graph.font.typecast.ot.Glyph glyph = font.getGlyph(glyph_id);
-            final String glyph_name;
-            {
-                final PostTable post = font.getPostTable();
-                glyph_name = null != post ? post.getGlyphName(glyph_id) : "";
+    public synchronized Glyph getGlyph(final int glyph_id) {
+        final SymAndID value = (SymAndID) idToGlyph.get(glyph_id);
+        if( null != value ) {
+            return getGlyphImpl(value);
+        } else {
+            return UNDEF_VALUE.glyph;
+        }
+    }
+
+    @Override
+    public void forAllCodepoints(final Font.CodepointIDVisitor visitor) {
+        for(int i=0; i<cmapFormat.getRangeCount(); ++i) {
+            final CmapFormat.Range range = cmapFormat.getRange(i);
+            for(int codepoint = range.getStartCode(); codepoint <= range.getEndCode(); ++codepoint) {
+                visitor.visit( (char)codepoint, getGlyphID((char)codepoint) );
             }
-            final boolean isUndefined = Glyph.ID_UNKNOWN == glyph_id || TypecastGlyph.isUndefName(glyph_name);
-            final int glyph_height = metrics.getAscentFU() - metrics.getDescentFU();
-            final int glyph_advance;
-            final int glyph_leftsidebearings;
-            final boolean isWhitespace;
-            final AABBox glyph_bbox;
-            final OutlineShape shape;
-            final int mode;
-            if( null != glyph ) {
-                glyph_advance = glyph.getAdvanceWidth();
-                glyph_leftsidebearings = glyph.getLeftSideBearing();
-                final AABBox sb = glyph.getBBox();
-                final OutlineShape os = TypecastRenderer.buildShape(metrics.getUnitsPerEM(), glyph);
-                if( 0 < os.getVertexCount() ) {
-                    // Case 1: Either valid contour glyph, undefined or a whitespace (Case 2 with zero-area shape)
-                    isWhitespace = isUndefined ? false : os.getBounds().hasZero2DArea();
-                    glyph_bbox = sb;
-                    shape = ( !isWhitespace && !isUndefined ) || Glyph.ID_UNKNOWN == glyph_id ? os : null;
-                    mode = 1;
-                } else {
-                    // Case 2: Non-contour glyph -> whitespace or undefined
-                    isWhitespace = !isUndefined;
-                    glyph_bbox = new AABBox(0f,0f,0f, glyph_advance, glyph_height, 0f);
-                    shape = Glyph.ID_UNKNOWN == glyph_id ? TypecastRenderer.buildEmptyShape(metrics.getUnitsPerEM(), glyph_bbox) : null;
-                    mode = 2;
-                }
+        }
+    }
+    @Override
+    public void forAllGlyphs(final Font.GlyphVisitor2 visitor) {
+        for(int i=0; i<cmapFormat.getRangeCount(); ++i) {
+            final CmapFormat.Range range = cmapFormat.getRange(i);
+            for(int codepoint = range.getStartCode(); codepoint <= range.getEndCode(); ++codepoint) {
+                visitor.visit( getGlyph( (char)codepoint ) );
+            }
+        }
+    }
+
+    private Glyph getGlyphImpl(final SymAndID key) {
+        if( null != key.glyph ) {
+            return key.glyph;
+        }
+        final jogamp.graph.font.typecast.ot.Glyph glyph = font.getGlyph(key.id);
+        final boolean isUndefined = Glyph.ID_UNKNOWN == key.id || TypecastGlyph.isUndefName(key.name);
+        final int glyph_height = metrics.getAscentFU() - metrics.getDescentFU();
+        final int glyph_advance;
+        final int glyph_leftsidebearings;
+        final boolean isWhitespace;
+        final AABBox glyph_bbox;
+        final OutlineShape shape;
+        final int mode;
+        if( null != glyph ) {
+            glyph_advance = glyph.getAdvanceWidth();
+            glyph_leftsidebearings = glyph.getLeftSideBearing();
+            final AABBox sb = glyph.getBBox();
+            final OutlineShape os = TypecastRenderer.buildShape(metrics.getUnitsPerEM(), glyph);
+            if( 0 < os.getVertexCount() ) {
+                // Case 1: Either valid contour glyph, undefined or a whitespace (Case 2 with zero-area shape)
+                isWhitespace = isUndefined ? false : os.getBounds().hasZero2DArea();
+                glyph_bbox = sb;
+                shape = ( !isWhitespace && !isUndefined ) || Glyph.ID_UNKNOWN == key.id ? os : null;
+                mode = 1;
             } else {
-                // Case 3: Non-contour glyph -> whitespace or undefined
-                glyph_advance = getAdvanceWidthFU(glyph_id);
-                glyph_leftsidebearings = 0;
+                // Case 2: Non-contour glyph -> whitespace or undefined
                 isWhitespace = !isUndefined;
                 glyph_bbox = new AABBox(0f,0f,0f, glyph_advance, glyph_height, 0f);
-                shape = Glyph.ID_UNKNOWN == glyph_id ? TypecastRenderer.buildEmptyShape(metrics.getUnitsPerEM(), glyph_bbox) : null;
-                mode = 3;
-            }
-            KernSubtable kernSub = null;
-            {
-                final KernTable kern = font.getKernTable();
-                if (kern != null ) {
-                    kernSub = kern.getSubtable0();
-                }
+                shape = Glyph.ID_UNKNOWN == key.id ? TypecastRenderer.buildEmptyShape(metrics.getUnitsPerEM(), glyph_bbox) : null;
+                mode = 2;
             }
-            result = new TypecastGlyph(this, glyph_id, glyph_name, glyph_bbox, glyph_advance, glyph_leftsidebearings, kernSub, shape,
-                                       isUndefined, isWhitespace);
-            if( DEBUG || TypecastRenderer.DEBUG ) {
-                System.err.println("New glyph: " + glyph_id + "/'"+glyph_name+"', shape " + (null != shape)+", mode "+mode);
-                System.err.println("  tc_glyph "+glyph);
-                System.err.println("     glyph "+result);
-            }
-            if( null != glyph ) {
-                glyph.clearPointData();
+        } else {
+            // Case 3: Non-contour glyph -> whitespace or undefined
+            glyph_advance = getAdvanceWidthFU(key.id);
+            glyph_leftsidebearings = 0;
+            isWhitespace = !isUndefined;
+            glyph_bbox = new AABBox(0f,0f,0f, glyph_advance, glyph_height, 0f);
+            shape = Glyph.ID_UNKNOWN == key.id ? TypecastRenderer.buildEmptyShape(metrics.getUnitsPerEM(), glyph_bbox) : null;
+            mode = 3;
+        }
+        KernSubtable kernSub = null;
+        {
+            final KernTable kern = font.getKernTable();
+            if (kern != null ) {
+                kernSub = kern.getSubtable0();
             }
-
-            idToGlyph.put(glyph_id, result);
         }
+        final TypecastGlyph result = new TypecastGlyph(this, key.codepoint, key.id, key.name, glyph_bbox, glyph_advance, glyph_leftsidebearings, kernSub,
+                                                       shape, isUndefined, isWhitespace);
+        if( DEBUG || TypecastRenderer.DEBUG ) {
+            System.err.println("New glyph: id 0x" + Integer.toHexString(key.id) + "/'"+key.name+"', sym 0x"+Integer.toHexString(key.codepoint)+", shape " + (null != shape)+", mode "+mode);
+            System.err.println("  tc_glyph "+glyph);
+            System.err.println("     glyph "+result);
+        }
+        if( null != glyph ) {
+            glyph.clearPointData();
+        }
+        key.glyph = result;
         return result;
     }
 
@@ -336,34 +438,33 @@ class TypecastFont implements Font {
         final AABBox temp_box = new AABBox();
 
         for(int i=0; i< charCount; i++) {
-            final char character = string.charAt(i);
-            if( '\n' == character ) {
+            final char codepoint = string.charAt(i);
+            if( '\n' == codepoint ) {
                 y -= lineHeight;
                 advanceTotal = 0;
                 left_glyph = null;
             } else {
                 // reset transform
                 temp1.setToIdentity();
-                final int glyph_id = getGlyphID(character);
-                final Font.Glyph glyph = getGlyph(glyph_id);
+                final Font.Glyph glyph = getGlyph( codepoint );
                 if( glyph.isUndefined() ) {
                     // break kerning, drop undefined
-                    advanceTotal += glyph.getAdvanceFU();
+                    advanceTotal += glyph.getAdvanceWidthFU();
                     left_glyph = null;
                 } else if( glyph.isWhitespace() ) {
                     // break kerning, but include its bounding box space
                     left_glyph = null;
                     temp1.translate(advanceTotal, y, temp2);
                     res.resize(temp1.transform(glyph.getBoundsFU(), temp_box));
-                    advanceTotal += glyph.getAdvanceFU();
+                    advanceTotal += glyph.getAdvanceWidthFU();
                 } else {
                     // regular contour
                     if( null != left_glyph ) {
-                        advanceTotal += left_glyph.getKerningFU(glyph_id);
+                        advanceTotal += left_glyph.getKerningFU( glyph.getID() );
                     }
                     temp1.translate(advanceTotal, y, temp2);
                     res.resize(temp1.transform(glyph.getBoundsFU(), temp_box));
-                    advanceTotal += glyph.getAdvanceFU();
+                    advanceTotal += glyph.getAdvanceWidthFU();
                     left_glyph = glyph;
                 }
             }
@@ -383,7 +484,7 @@ class TypecastFont implements Font {
         }
         final Font.GlyphVisitor visitor = new Font.GlyphVisitor() {
             @Override
-            public final void visit(final char symbol, final Font.Glyph shape, final AffineTransform t) {
+            public final void visit(final Font.Glyph shape, final AffineTransform t) {
                 // nop
             } };
         return processString(visitor, transform, string, temp1, temp2);
@@ -414,8 +515,8 @@ class TypecastFont implements Font {
         final AABBox temp_box = new AABBox();
 
         for(int i=0; i< charCount; i++) {
-            final char character = string.charAt(i);
-            if( '\n' == character ) {
+            final char codepoint = string.charAt(i);
+            if( '\n' == codepoint ) {
                 y -= lineHeight;
                 advanceTotal = 0;
                 left_glyph = null;
@@ -426,29 +527,27 @@ class TypecastFont implements Font {
                 } else {
                     temp1.setToIdentity();
                 }
-                final int glyph_id = getGlyphID(character);
-
-                final Font.Glyph glyph = getGlyph(glyph_id);
+                final Font.Glyph glyph = getGlyph( codepoint );
                 if( glyph.isUndefined() ) {
                     // break kerning, drop undefined
-                    advanceTotal += glyph.getAdvance();
+                    advanceTotal += glyph.getAdvanceWidth();
                     left_glyph = null;
                 } else if( glyph.isWhitespace() ) {
                     // break kerning, but include its bounding box space and visit the visitor
                     left_glyph = null;
                     temp1.translate(advanceTotal, y, temp2);
                     res.resize(temp1.transform(glyph.getBounds(), temp_box));
-                    visitor.visit(character, glyph, temp1);
-                    advanceTotal += glyph.getAdvance();
+                    visitor.visit(glyph, temp1);
+                    advanceTotal += glyph.getAdvanceWidth();
                 } else {
                     // regular contour
                     if( null != left_glyph ) {
-                        advanceTotal += left_glyph.getKerning(glyph_id);
+                        advanceTotal += left_glyph.getKerning( glyph.getID() );
                     }
                     temp1.translate(advanceTotal, y, temp2);
                     res.resize(temp1.transform(glyph.getShape().getBounds(), temp_box));
-                    visitor.visit(character, glyph, temp1);
-                    advanceTotal += glyph.getAdvance();
+                    visitor.visit(glyph, temp1);
+                    advanceTotal += glyph.getAdvanceWidth();
                     left_glyph = glyph;
                 }
             }
@@ -464,21 +563,13 @@ class TypecastFont implements Font {
         final int charCount = string.length();
 
         for(int i=0; i< charCount; i++) {
-            final char character = string.charAt(i);
-            if( '\n' != character ) {
-                final Glyph glyph = getGlyph(getGlyphID(character));
-                if( null != glyph.getShape() ) { // also covers 'space' and all non-contour symbols
-                    visitor.visit(character, glyph);
-                }
+            final char codepoint = string.charAt(i);
+            if( '\n' != codepoint ) {
+                visitor.visit( getGlyph( codepoint ) );
             }
         }
     }
 
-    @Override
-    final public int getNumGlyphs() {
-        return font.getNumGlyphs();
-    }
-
     @Override
     public boolean isPrintableChar( final char c ) {
         return FontFactory.isPrintableChar(c);
diff --git a/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java b/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java
index fa8271088..d49fb7393 100644
--- a/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java
+++ b/src/jogl/classes/jogamp/graph/font/typecast/TypecastGlyph.java
@@ -39,7 +39,7 @@ 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 final String dot_undef_NAME = ".notdef";
+    /* pp */ static final String dot_undef_NAME = ".notdef";
     private static final String NULL_NAME = "NULL";
     private static final String null_NAME = "null";
     private static final String dot_null_NAME = ".null";
@@ -83,6 +83,7 @@ public final class TypecastGlyph implements Font.Glyph {
         return dst;
     }
 
+    private final char codepoint;
     private final int id;
     private final String name;
     private final boolean isUndefined;
@@ -101,17 +102,19 @@ public final class TypecastGlyph implements Font.Glyph {
     /**
      *
      * @param font
-     * @param name from `post` table
+     * @param codepoint TODO
      * @param id
+     * @param name from `post` table
      * @param bbox in font-units
      * @param advance from hmtx in font-units
      * @param leftSideBearings from hmtx in font-units
      * @param shape
      */
-    protected TypecastGlyph(final TypecastFont font, final int id, final String name,
-                            final AABBox bbox, final int advance, final int leftSideBearings,
-                            final KernSubtable kernSub, final OutlineShape shape,
-                            final boolean isUndefined, final boolean isWhiteSpace) {
+    protected TypecastGlyph(final TypecastFont font, final char codepoint, final int id,
+                            final String name, final AABBox bbox, final int advance,
+                            final int leftSideBearings, final KernSubtable kernSub,
+                            final OutlineShape shape, final boolean isUndefined, final boolean isWhiteSpace) {
+        this.codepoint = codepoint;
         this.id = id;
         this.name = name;
         this.isUndefined = isUndefined;
@@ -154,6 +157,9 @@ public final class TypecastGlyph implements Font.Glyph {
         return font;
     }
 
+    @Override
+    public char getCodepoint() { return codepoint; }
+
     @Override
     public final int getID() { return id; }
 
@@ -187,10 +193,10 @@ public final class TypecastGlyph implements Font.Glyph {
     }
 
     @Override
-    public final int getAdvanceFU() { return advance; }
+    public final int getAdvanceWidthFU() { return advance; }
 
     @Override
-    public float getAdvance() { return font.getMetrics().getScale( advance ); }
+    public float getAdvanceWidth() { return font.getMetrics().getScale( advance ); }
 
     @Override
     public final int getLeftSideBearingsFU() { return leftSideBearings; }
@@ -267,9 +273,10 @@ public final class TypecastGlyph implements Font.Glyph {
         }
         final String name_s = null != name ? name : "";
         final String shape_s = null != shape ? "shape "+shape.getVertexCount()+"v" : "shape null";
-        sb.append("Glyph[id ").append(id).append(" '").append(name_s).append("', ").append(contour_s)
+        sb.append("Glyph[id 0x").append(Integer.toHexString(id)).append(", cp 0x").append(Integer.toHexString(codepoint))
+          .append(", name '").append(name_s).append("', ").append(contour_s)
           .append(", ").append(shape_s)
-          .append(", advance ").append(getAdvanceFU())
+          .append(", advance ").append(getAdvanceWidthFU())
           .append(", leftSideBearings ").append(getLeftSideBearingsFU())
           .append(", kerning[size ").append(kerning.length).append(", horiz ").append(this.isKerningHorizontal()).append(", cross ").append(this.isKerningCrossstream()).append("]")
           .append("]");
@@ -290,9 +297,10 @@ public final class TypecastGlyph implements Font.Glyph {
         }
         final String name_s = null != name ? name : "";
         final String shape_s = null != shape ? "shape "+shape.getVertexCount()+"v" : "shape null";
-        sb.append("Glyph id ").append(id).append(" '").append(name_s).append("', ").append(contour_s)
+        sb.append("Glyph[id 0x").append(Integer.toHexString(id)).append(", cp 0x").append(Integer.toHexString(codepoint))
+          .append(" name '").append(name_s).append("', ").append(contour_s)
           .append(", shape ").append(shape_s)
-          .append(", advance ").append(getAdvanceFU())
+          .append(", advance ").append(getAdvanceWidthFU())
           .append(", leftSideBearings ").append(getLeftSideBearingsFU())
           .append(", ").append(getBoundsFU());
 
-- 
cgit v1.2.3