From feb8de27f848b5213423389cf0e19cbd88095682 Mon Sep 17 00:00:00 2001
From: Sven Gothel <sgothel@jausoft.com>
Date: Fri, 10 Feb 2023 14:20:35 +0100
Subject: Font/Graph, {Font, Glyph}/Typecast: Add kerning and expose values in
 original font-units (FU) to have them scaled later ( fu * pixelScale /
 unitsPerEM )

Scaling from font-units (funits, or FU) is now performed by the renderer itself,
i.e. applying the scale-factor 'fontPixelSize / font.getMetrics().getUnitsPerEM()'
to the PMV matrix to render the whole graph GLRegion.

This finally provides proper device and resolution independent font utilization.

Further, preliminary kerning has been added.

+++

Also ...

TypecastFont:
- getGlyphID(..) getGlyph(..) enforce symbol mapping to Glyph.ID_SPACE Glyph.ID_CR,
  as some fonts ave an erroneous cmap (FreeSerif-Regular)

- add getKerning(..)
  TODO: Add binary search

- Set TypecastFont.USE_PRESCALED_ADVANCE := false,
  i.e. dropping all prescaled pixel-sized advance values mapped to font pixel-size
  as we utilize font-units only for later uniform scaling.

- Drop virtual getPixelSize() and add static FontScale.toPixels(..)

- Add fullString() for debugging purposed, including some font tables
---
 .../jogamp/graph/curve/opengl/TextRegionUtil.java  | 119 +++++-----
 src/jogl/classes/com/jogamp/graph/font/Font.java   | 250 +++++++++++++++------
 .../classes/com/jogamp/graph/font/FontScale.java   |  70 ++++++
 3 files changed, 317 insertions(+), 122 deletions(-)
 create mode 100644 src/jogl/classes/com/jogamp/graph/font/FontScale.java

(limited to 'src/jogl/classes/com/jogamp/graph')

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 344e1e51f..5aa2be258 100644
--- a/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java
+++ b/src/jogl/classes/com/jogamp/graph/curve/opengl/TextRegionUtil.java
@@ -74,41 +74,41 @@ public class TextRegionUtil {
 
     /**
      * Visit each {@link Font.Glyph}'s {@link OutlineShape} with the given {@link ShapeVisitor}
-     * additionally passing the progressed {@link AffineTransform}.
-     * The latter reflects the given font metric, pixelSize and hence character position.
+     * additionally passing the progressed {@link AffineTransform} in font-units.
+     * The latter reflects the given font metric in font-units and hence character position.
      * @param visitor
      * @param transform optional given transform
      * @param font the target {@link Font}
-     * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size.
      * @param str string text
      * @param temp1 temporary AffineTransform storage, mandatory, will be passed to {@link ShapeVisitor#visit(OutlineShape, AffineTransform)} and can be modified.
      * @param temp2 temporary AffineTransform storage, mandatory, can be re-used in {@link ShapeVisitor#visit(OutlineShape, AffineTransform)} by user code.
      */
     public static void processString(final ShapeVisitor visitor, final AffineTransform transform,
-                                     final Font font, final float pixelSize, final CharSequence str,
+                                     final Font font, final CharSequence str,
                                      final AffineTransform temp1, final AffineTransform temp2) {
         final int charCount = str.length();
 
         // region.setFlipped(true);
         final Font.Metrics metrics = font.getMetrics();
-        final float lineHeight = font.getLineHeight(pixelSize);
+        final int lineHeight = font.getLineHeightFU();
 
-        final float scale = metrics.getScale(pixelSize);
-
-        float y = 0;
-        float advanceTotal = 0;
+        int y = 0;
+        int advanceTotal = 0;
+        char left_character = 0;
+        int left_glyphid = 0;
 
         for(int i=0; i< charCount; i++) {
             final char character = str.charAt(i);
             if( '\n' == character ) {
                 y -= lineHeight;
                 advanceTotal = 0;
+                left_glyphid = 0;
+                left_character = 0;
             } else if (character == ' ') {
-                advanceTotal += font.getAdvanceWidth(Glyph.ID_SPACE, pixelSize);
+                advanceTotal += font.getAdvanceWidthFU(Glyph.ID_SPACE);
+                left_glyphid = 0;
+                left_character = character;
             } else {
-                if(Region.DEBUG_INSTANCE) {
-                    System.err.println("XXXXXXXXXXXXXXx char: "+character+", scale: "+scale+"; translate: "+advanceTotal+", "+y);
-                }
                 // reset transform
                 if( null != transform ) {
                     temp1.setTransform(transform);
@@ -116,51 +116,65 @@ public class TextRegionUtil {
                     temp1.setToIdentity();
                 }
                 temp1.translate(advanceTotal, y, temp2);
-                temp1.scale(scale, scale, temp2);
 
                 final Font.Glyph glyph = font.getGlyph(character);
                 final OutlineShape glyphShape = glyph.getShape();
                 if( null == glyphShape ) {
+                    left_glyphid = 0;
                     continue;
                 }
                 visitor.visit(glyphShape, temp1);
-
-                advanceTotal += glyph.getAdvance(pixelSize, true);
+                final int right_glyphid = glyph.getID();
+                final int kern = font.getKerningFU(left_glyphid, right_glyphid);
+                final int advance = glyph.getAdvanceFU();
+                advanceTotal += advance + kern;
+                if( Region.DEBUG_INSTANCE && 0 != left_character && kern > 0f ) {
+                    System.err.println(": '"+left_character+"'/"+left_glyphid+" -> '"+character+"'/"+right_glyphid+
+                                       ": a "+advance+"px + k ["+kern+"em, "+kern+"px = "+(advance+kern)+"px -> "+advanceTotal+"px, y "+y);
+                }
+                // advanceTotal += glyph.getAdvance(pixelSize, true)
+                //               + font.getKerning(left_glyphid, right_glyphid);
+                left_glyphid = right_glyphid;
+                left_character = character;
             }
         }
     }
 
     /**
-     * Add the string in 3D space w.r.t. the font and pixelSize at the end of the {@link GLRegion}.
+     * Add the string in 3D space w.r.t. the font using font-units at the end of the {@link GLRegion}.
+     * <p>
+     * The resulting GLRegion should be scaled by the chosen pixelSize of the font divided by the font's unitsPerEM.
+     * </p>
      * @param region the {@link GLRegion} sink
      * @param vertexFactory vertex impl factory {@link Factory}
      * @param font the target {@link Font}
-     * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size.
      * @param str string text
      * @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored.
      * @param temp1 temporary AffineTransform storage, mandatory
      * @param temp2 temporary AffineTransform storage, mandatory
      */
     public static void addStringToRegion(final GLRegion region, final Factory<? extends Vertex> vertexFactory,
-                                         final Font font, final float pixelSize, final CharSequence str, final float[] rgbaColor,
+                                         final Font font, final CharSequence str, final float[] rgbaColor,
                                          final AffineTransform temp1, final AffineTransform temp2) {
         final ShapeVisitor visitor = new ShapeVisitor() {
+            @Override
             public final void visit(final OutlineShape shape, final AffineTransform t) {
                 region.addOutlineShape(shape, t, region.hasColorChannel() ? rgbaColor : null);
             } };
-        processString(visitor, null, font, pixelSize, str, temp1, temp2);
+        processString(visitor, null, font, str, temp1, temp2);
     }
 
     /**
-     * Render the string in 3D space w.r.t. the font and pixelSize
-     * using a cached {@link GLRegion} for reuse.
+     * Render the string in 3D space w.r.t. the font using font-units at the end of an internally cached {@link GLRegion}.
+     * <p>
+     * The resulting GLRegion should be scaled by the chosen pixelSize of the font divided by the font's unitsPerEM.
+     * </p>
      * <p>
      * Cached {@link GLRegion}s will be destroyed w/ {@link #clear(GL2ES2)} or to free memory.
      * </p>
      * @param gl the current GL state
      * @param renderer TODO
      * @param font {@link Font} to be used
-     * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size.
      * @param str text to be rendered
      * @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored.
      * @param sampleCount desired multisampling sample count for msaa-rendering.
@@ -168,34 +182,35 @@ public class TextRegionUtil {
      * @throws Exception if TextRenderer not initialized
      */
     public void drawString3D(final GL2ES2 gl,
-                             final RegionRenderer renderer, final Font font, final float pixelSize,
-                             final CharSequence str, final float[] rgbaColor, final int[/*1*/] sampleCount) {
+                             final RegionRenderer renderer, final Font font, final CharSequence str,
+                             final float[] rgbaColor, final int[/*1*/] sampleCount) {
         if( !renderer.isInitialized() ) {
             throw new GLException("TextRendererImpl01: not initialized!");
         }
         final int special = 0;
-        GLRegion region = getCachedRegion(font, str, pixelSize, special);
+        GLRegion region = getCachedRegion(font, str, special);
         if(null == region) {
             region = GLRegion.create(renderModes, null);
-            addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str, rgbaColor, tempT1, tempT2);
-            addCachedRegion(gl, font, str, pixelSize, special, region);
+            addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, str, rgbaColor, tempT1, tempT2);
+            addCachedRegion(gl, font, str, special, region);
         }
         region.draw(gl, renderer, sampleCount);
     }
 
     /**
-     * Render the string in 3D space w.r.t. the font and pixelSize
-     * using a temporary {@link GLRegion}, which will be destroyed afterwards.
+     * Render the string in 3D space w.r.t. the font using font-units at the end of an internally temporary {@link GLRegion}.
+     * <p>
+     * The resulting GLRegion should be scaled by the chosen pixelSize of the font divided by the font's unitsPerEM.
+     * </p>
      * <p>
      * In case of a multisampling region renderer, i.e. {@link Region#VBAA_RENDERING_BIT}, recreating the {@link GLRegion}
      * is a huge performance impact.
-     * In such case better use {@link #drawString3D(GL2ES2, GLRegion, RegionRenderer, Font, float, CharSequence, float[], int[], AffineTransform, AffineTransform)}
+     * In such case better use {@link #drawString3D(GL2ES2, GLRegion, RegionRenderer, Font, CharSequence, float[], int[], AffineTransform, AffineTransform)}
      * instead.
      * </p>
      * @param gl the current GL state
      * @param renderModes TODO
      * @param font {@link Font} to be used
-     * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size.
      * @param str text to be rendered
      * @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored.
      * @param sampleCount desired multisampling sample count for msaa-rendering.
@@ -205,24 +220,26 @@ public class TextRegionUtil {
      * @throws Exception if TextRenderer not initialized
      */
     public static void drawString3D(final GL2ES2 gl, final int renderModes,
-                                    final RegionRenderer renderer, final Font font, final float pixelSize,
-                                    final CharSequence str, final float[] rgbaColor, final int[/*1*/] sampleCount,
-                                    final AffineTransform temp1, final AffineTransform temp2) {
+                                    final RegionRenderer renderer, final Font font, final CharSequence str,
+                                    final float[] rgbaColor, final int[/*1*/] sampleCount, final AffineTransform temp1,
+                                    final AffineTransform temp2) {
         if(!renderer.isInitialized()){
             throw new GLException("TextRendererImpl01: not initialized!");
         }
         final GLRegion region = GLRegion.create(renderModes, null);
-        addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str, rgbaColor, temp1, temp2);
+        addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, str, rgbaColor, temp1, temp2);
         region.draw(gl, renderer, sampleCount);
         region.destroy(gl);
     }
 
     /**
-     * Render the string in 3D space w.r.t. the font and pixelSize
-     * using the given {@link GLRegion}, which will {@link GLRegion#clear(GL2ES2) cleared} beforehand.
+     * Render the string in 3D space w.r.t. the font using font-units at the end of the given {@link GLRegion},
+     * which will {@link GLRegion#clear(GL2ES2) cleared} beforehand.
+     * <p>
+     * The resulting GLRegion should be scaled by the chosen pixelSize of the font divided by the font's unitsPerEM.
+     * </p>
      * @param gl the current GL state
      * @param font {@link Font} to be used
-     * @param pixelSize Use {@link Font#getPixelSize(float, float)} for resolution correct pixel-size.
      * @param str text to be rendered
      * @param rgbaColor if {@link Region#hasColorChannel()} RGBA color must be passed, otherwise value is ignored.
      * @param sampleCount desired multisampling sample count for msaa-rendering.
@@ -232,14 +249,14 @@ public class TextRegionUtil {
      * @throws Exception if TextRenderer not initialized
      */
     public static void drawString3D(final GL2ES2 gl, final GLRegion region, final RegionRenderer renderer,
-                                    final Font font, final float pixelSize, final CharSequence str,
-                                    final float[] rgbaColor, final int[/*1*/] sampleCount,
-                                    final AffineTransform temp1, final AffineTransform temp2) {
+                                    final Font font, final CharSequence str, final float[] rgbaColor,
+                                    final int[/*1*/] sampleCount, final AffineTransform temp1,
+                                    final AffineTransform temp2) {
         if(!renderer.isInitialized()){
             throw new GLException("TextRendererImpl01: not initialized!");
         }
         region.clear(gl);
-        addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, pixelSize, str, rgbaColor, temp1, temp2);
+        addStringToRegion(region, renderer.getRenderState().getVertexFactory(), font, str, rgbaColor, temp1, temp2);
         region.draw(gl, renderer, sampleCount);
     }
 
@@ -297,13 +314,13 @@ public class TextRegionUtil {
        }
    }
 
-   protected final GLRegion getCachedRegion(final Font font, final CharSequence str, final float pixelSize, final int special) {
-       return stringCacheMap.get(getKey(font, str, pixelSize, special));
+   protected final GLRegion getCachedRegion(final Font font, final CharSequence str, final int special) {
+       return stringCacheMap.get(getKey(font, str, special));
    }
 
-   protected final void addCachedRegion(final GL2ES2 gl, final Font font, final CharSequence str, final float pixelSize, final int special, final GLRegion glyphString) {
+   protected final void addCachedRegion(final GL2ES2 gl, final Font font, final CharSequence str, final int special, final GLRegion glyphString) {
        if ( 0 != getCacheLimit() ) {
-           final String key = getKey(font, str, pixelSize, special);
+           final String key = getKey(font, str, special);
            final GLRegion oldRegion = stringCacheMap.put(key, glyphString);
            if ( null == oldRegion ) {
                // new entry ..
@@ -313,8 +330,8 @@ public class TextRegionUtil {
        }
    }
 
-   protected final void removeCachedRegion(final GL2ES2 gl, final Font font, final CharSequence str, final int pixelSize, final int special) {
-       final String key = getKey(font, str, pixelSize, special);
+   protected final void removeCachedRegion(final GL2ES2 gl, final Font font, final CharSequence str, final int special) {
+       final String key = getKey(font, str, special);
        final GLRegion region = stringCacheMap.remove(key);
        if(null != region) {
            region.destroy(gl);
@@ -332,10 +349,10 @@ public class TextRegionUtil {
        }
    }
 
-   protected final String getKey(final Font font, final CharSequence str, final float pixelSize, final int special) {
+   protected final String getKey(final Font font, final CharSequence str, final int special) {
        final StringBuilder sb = new StringBuilder();
        return font.getName(sb, Font.NAME_UNIQUNAME)
-              .append(".").append(str.hashCode()).append(".").append(Float.floatToIntBits(pixelSize)).append(special).toString();
+              .append(".").append(str.hashCode()).append(".").append(special).toString();
    }
 
    /** Default cache limit, see {@link #setCacheLimit(int)} */
diff --git a/src/jogl/classes/com/jogamp/graph/font/Font.java b/src/jogl/classes/com/jogamp/graph/font/Font.java
index 597a7e76f..9f25b5481 100644
--- a/src/jogl/classes/com/jogamp/graph/font/Font.java
+++ b/src/jogl/classes/com/jogamp/graph/font/Font.java
@@ -37,7 +37,7 @@ import com.jogamp.opengl.math.geom.AABBox;
  * TrueType Font Specification:
  * <ul>
  *   <li>http://www.freetype.org/freetype2/documentation.html</li>
- *   <li>http://developer.apple.com/fonts/ttrefman/rm06/Chap6.html</li>
+ *   <li>https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6.html</li>
  *   <li>http://www.microsoft.com/typography/SpecificationsOverview.mspx</li>
  *   <li>http://www.microsoft.com/typography/otspec/</li>
  * </ul>
@@ -76,20 +76,54 @@ public interface Font {
      * Depending on the font's direction, horizontal or vertical,
      * the following tables shall be used:
      *
-     * Vertical http://developer.apple.com/fonts/TTRefMan/RM06/Chap6vhea.html
-     * Horizontal http://developer.apple.com/fonts/TTRefMan/RM06/Chap6hhea.html
+     * Vertical https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6vhea.html
+     * Horizontal https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6hhea.html
      */
     public interface Metrics {
-        float getAscent(final float pixelSize);
-        float getDescent(final float pixelSize);
-        float getLineGap(final float pixelSize);
-        float getMaxExtend(final float pixelSize);
-        float getScale(final float pixelSize);
+        /**
+         * @return ascent in font-units to be divided by {@link #getUnitsPerEM()}
+         */
+        int getAscentFU();
+
+        /**
+         * @return descent in font-units to be divided by {@link #getUnitsPerEM()}
+         */
+        int getDescentFU();
+
+        /**
+         * @return line-gap in font-units to be divided by {@link #getUnitsPerEM()}
+         */
+        int getLineGapFU();
+
+        /**
+         * @return max-extend in font-units to be divided by {@link #getUnitsPerEM()}
+         */
+        int getMaxExtendFU();
+
+        /** Returns the font's units per EM from the 'head' table. One em square covers one glyph. */
+        int getUnitsPerEM();
+
+        /**
+         * Return fractional font em-size [0..1], i.e. funits divided by {@link #getUnitsPerEM()}, i.e.
+         * <pre>
+         *    return funits / head.unitsPerEM;
+         * </pre>
+         * @param funits smallest font unit, where {@link #getUnitsPerEM()} square covers whole glyph
+         * @return fractional font em-size [0..1]
+         */
+        float getScale(final int funits);
+
+        /**
+         * @param dest AABBox instance set to this metrics boundary in font-units
+         * @return the given and set AABBox 'dest' in font units
+         */
+        AABBox getBBox(final AABBox dest);
+
         /**
          * @param dest AABBox instance set to this metrics boundary w/ given pixelSize
-         * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link Font#getPixelSize(float, float)}
+         * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
          * @param tmpV3 caller provided temporary 3-component vector
-         * @return the given and set AABBox 'dest'
+         * @return the given and set AABBox 'dest' in pixel size
          */
         AABBox getBBox(final AABBox dest, final float pixelSize, final float[] tmpV3);
     }
@@ -108,118 +142,192 @@ public interface Font {
         public static final int ID_CR = 2;
         public static final int ID_SPACE = 3;
 
-        public Font getFont();
-        public char getSymbol();
-        public short getID();
-        public AABBox getBBox();
+        Font getFont();
+        char getSymbol();
+        int getID();
+
         /**
-         *
-         * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link Font#getPixelSize(float, float)}
-         * @return
+         * Return fractional font em-size [0..1], i.e. funits divided by {@link #getUnitsPerEM()}, i.e.
+         * <pre>
+         *    return funits / head.unitsPerEM;
+         * </pre>
+         * @param funits smallest font unit, where {@link #getUnitsPerEM()} square covers whole glyph
+         * @return fractional font em-size [0..1]
          */
-        public float getScale(final float pixelSize);
+        float getScale(final int funits);
+
         /**
          * @param dest AABBox instance set to this metrics boundary w/ given pixelSize
-         * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link Font#getPixelSize(float, float)}
+         * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
          * @param tmpV3 caller provided temporary 3-component vector
-         * @return the given and set AABBox 'dest'
+         * @return the given and set AABBox 'dest' in pixel size
+         */
+        AABBox getBBox(final AABBox dest, final float pixelSize, float[] tmpV3);
+
+        /**
+         * Return the AABBox in font-units to be divided by unitsPerEM
+         * @param dest AABBox instance set to this metrics boundary in font-units
+         * @return the given and set AABBox 'dest' in font-units
+         */
+        AABBox getBBoxFU(final AABBox dest);
+
+        /**
+         * Return the AABBox in font-units to be divided by unitsPerEM
          */
-        public AABBox getBBox(final AABBox dest, final float pixelSize, float[] tmpV3);
+        AABBox getBBoxFU();
+
+        /** Return advance in font units to be divided by unitsPerEM */
+        int getAdvanceFU();
+
+        /** Return advance in font em-size [0..1] */
+        float getAdvance();
+
         /**
-         *
-         * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link Font#getPixelSize(float, float)}
-         * @param useFrationalMetrics
-         * @return
+         * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
+         * @return pixel size of advance
          */
-        public float getAdvance(final float pixelSize, boolean useFrationalMetrics);
-        public OutlineShape getShape();
-        public int hashCode();
+        float getAdvance(final float pixelSize);
+
+        OutlineShape getShape();
+
+        @Override
+        int hashCode();
     }
 
 
-    public String getName(final int nameIndex);
-    public StringBuilder getName(final StringBuilder string, final int nameIndex);
+    String getName(final int nameIndex);
+    StringBuilder getName(final StringBuilder string, final int nameIndex);
 
     /** Shall return the family and subfamily name, separated a dash.
      * <p>{@link #getName(StringBuilder, int)} w/ {@link #NAME_FAMILY} and {@link #NAME_SUBFAMILY}</p>
      * <p>Example: "{@code Ubuntu-Regular}"</p>  */
-    public StringBuilder getFullFamilyName(final StringBuilder buffer);
+    StringBuilder getFullFamilyName(final StringBuilder buffer);
+
+    StringBuilder getAllNames(final StringBuilder string, final String separator);
 
-    public StringBuilder getAllNames(final StringBuilder string, final String separator);
+    /**
+     *
+     * @param glyphID
+     * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
+     * @return pixel size of advance width
+     */
+    float getAdvanceWidth(final int glyphID, final float pixelSize);
 
     /**
-     * <pre>
-        Font Scale Formula:
-         inch: 25.4 mm
-         pointSize: [point] = [1/72 inch]
-
-         [1]      Scale := pointSize * resolution / ( 72 points per inch * units_per_em )
-         [2]  PixelSize := pointSize * resolution / ( 72 points per inch )
-         [3]      Scale := PixelSize / units_per_em
-     * </pre>
-     * @param fontSize in point-per-inch
-     * @param resolution display resolution in dots-per-inch
-     * @return pixel-per-inch, pixelSize scale factor for font operations.
+     * Return advance-width of given glyphID in font em-size [0..1]
+     * @param glyphID
      */
-    public float getPixelSize(final float fontSize /* points per inch */, final float resolution);
+    float getAdvanceWidth(final int glyphID);
 
     /**
-     *
+     * Return advance-width of given glyphID in font-units to be divided by unitsPerEM
      * @param glyphID
-     * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)}
-     * @return
      */
-    public float getAdvanceWidth(final int glyphID, final float pixelSize);
-    public Metrics getMetrics();
-    public Glyph getGlyph(final char symbol);
-    public int getNumGlyphs();
+    int getAdvanceWidthFU(final int glyphID);
 
     /**
+     * Returns the optional kerning inter-glyph distance within words in fractional font em-size [0..1].
      *
-     * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)}
-     * @return
+     * @param left_glyphid left glyph code id
+     * @param right_glyphid right glyph code id
+     * @return fractional font em-size distance [0..1]
      */
-    public float getLineHeight(final float pixelSize);
+    float getKerning(final int left_glyphid, final int right_glyphid);
+
+    /**
+     * Returns the optional kerning inter-glyph distance within words in fractional font-units to be divided by unitsPerEM
+     *
+     * @param left_glyphid left glyph code id
+     * @param right_glyphid right glyph code id
+     * @return font-units to be divided by unitsPerEM
+     */
+    int getKerningFU(final int left_glyphid, final int right_glyphid);
+
+    Metrics getMetrics();
+    int getGlyphID(final char symbol);
+    Glyph getGlyph(final char symbol);
+    int getNumGlyphs();
+
+    /**
+     *
+     * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
+     * @return pixel size of line height
+     */
+    float getLineHeight(final float pixelSize);
+
+    /**
+     * Return line height in font em-size [0..1]
+     */
+    float getLineHeight();
+
+    /**
+     * Return line height in font-units to be divided by unitsPerEM
+     */
+    int getLineHeightFU();
+
     /**
      *
      * @param string
-     * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)}
-     * @return
+     * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
+     * @return pixel size of metric width
      */
-    public float getMetricWidth(final CharSequence string, final float pixelSize);
+    float getMetricWidth(final CharSequence string, final float pixelSize);
+
+    /** Return metric-width in font em-size */
+    float getMetricWidth(final CharSequence string);
+
+    /** Return metric-width in font-units */
+    int getMetricWidthFU(final CharSequence string);
+
     /**
      *
      * @param string
-     * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)}
-     * @param tmp
-     * @return
+     * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
+     * @return pixel size of metric height
      */
-    public float getMetricHeight(final CharSequence string, final float pixelSize, final AABBox tmp);
+    float getMetricHeight(final CharSequence string, final float pixelSize);
+
+    /** Return metric-height in font em-size */
+    float getMetricHeight(final CharSequence string);
+
+    /** Return metric-height in font-units */
+    int getMetricHeightFU(final CharSequence string);
+
     /**
      * Return the <i>layout</i> bounding box as computed by each glyph's metrics.
-     * The result is not pixel correct, bit reflects layout specific metrics.
+     * The result is not pixel correct, but reflects layout specific metrics.
      * <p>
      * See {@link #getPointsBounds(AffineTransform, CharSequence, float, AffineTransform, AffineTransform)} for pixel correct results.
      * </p>
      * @param string string text
-     * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)}
+     * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
      */
-    public AABBox getMetricBounds(final CharSequence string, final float pixelSize);
+    AABBox getMetricBounds(final CharSequence string, final float pixelSize);
+
+    /** Return layout metric-bounds in font em-size, see {@link #getMetricBounds(CharSequence, float)} */
+    AABBox getMetricBounds(final CharSequence string);
+
+    /** Return layout metric-bounds in font-units, see {@link #getMetricBounds(CharSequence, float)} */
+    AABBox getMetricBoundsFU(final CharSequence string);
 
     /**
      * Return the bounding box by taking each glyph's point-based bounding box into account.
      * @param transform optional given transform
      * @param string string text
-     * @param pixelSize Use <code>pointSize * resolution</code> for resolution correct pixel-size, see {@link #getPixelSize(float, float)}
-     * @param temp1 temporary AffineTransform storage, mandatory
-     * @param temp2 temporary AffineTransform storage, mandatory
+     * @param pixelSize pixel-size of font, for resolution correct pixel-size use {@link FontScale#toPixels(float, float)}
      */
-    public AABBox getPointsBounds(final AffineTransform transform, final CharSequence string, final float pixelSize,
-                                  final AffineTransform temp1, final AffineTransform temp2);
+    AABBox getPointsBounds(final AffineTransform transform, final CharSequence string, final float pixelSize);
+
+    AABBox getPointsBounds(final AffineTransform transform, final CharSequence string);
 
-    public boolean isPrintableChar(final char c);
+    AABBox getPointsBoundsFU(final AffineTransform transform, final CharSequence string);
+
+    boolean isPrintableChar(final char c);
 
     /** Shall return {@link #getFullFamilyName()} */
     @Override
     public String toString();
+
+    /** Return all font details as string. */
+    String fullString();
 }
diff --git a/src/jogl/classes/com/jogamp/graph/font/FontScale.java b/src/jogl/classes/com/jogamp/graph/font/FontScale.java
new file mode 100644
index 000000000..a0f2ef4e0
--- /dev/null
+++ b/src/jogl/classes/com/jogamp/graph/font/FontScale.java
@@ -0,0 +1,70 @@
+package com.jogamp.graph.font;
+
+/**
+ * Simple static font scale methods for unit conversion.
+ */
+public class FontScale {
+    /**
+     * Converts typical font size in points (per-inch) and screen resolution in dpi to font size in pixels (per-inch),
+     * which can be used for pixel-size font scaling operations.
+     * <p>
+     * Note that 1em == size of the selected font.<br/>
+     * In case the pixel per em size is required (advance etc),
+     * the resulting pixel-size (per-inch) of this method shall be used if rendering directly into the screen resolution!
+     * </p>
+     * <pre>
+        Font Scale Formula:
+         1 inch = 25.4 mm
+
+         1 inch = 72 points
+         pointSize: [point] = [1/72 inch]
+
+         [1]      Scale := pointSize * resolution / ( 72 points per inch * units_per_em )
+         [2]  PixelSize := pointSize * resolution / ( 72 points per inch )
+         [3]      Scale := PixelSize / units_per_em
+     * </pre>
+     * @param font_sz_pt in points (per-inch)
+     * @param res_dpi display resolution in dots-per-inch
+     * @return pixel-per-inch, pixelSize scale factor for font operations.
+     * @see #toPixels2(float, float)
+     */
+    public static float toPixels(final float font_sz_pt /* points per inch */, final float res_dpi /* dots per inch */) {
+        return ( font_sz_pt / 72f /* points per inch */ ) * res_dpi;
+    }
+
+    /**
+     * Converts typical font size in points-per-inch and screen resolution in points-per-mm to font size in pixels (per-inch),
+     * which can be used for pixel-size font scaling operations.
+     *
+     * @param font_sz_pt in points (per-inch)
+     * @param res_ppmm display resolution in dots-per-mm
+     * @return pixel-per-inch, pixelSize scale factor for font operations.
+     * @see #toPixels(float, float)
+     */
+    public static float toPixels2(final float font_sz_pt /* points per inch */, final float res_ppmm /* pixels per mm */) {
+        return ( font_sz_pt / 72f /* points per inch */ ) * ( res_ppmm * 25.4f /* mm per inch */ ) ;
+    }
+
+    /**
+     * Converts [1/mm] to [1/inch] in place
+     * @param ppmm float[2] [1/mm] value
+     * @return return [1/inch] value
+     */
+    public static float[/*2*/] perMMToPerInch(final float[/*2*/] ppmm) {
+        ppmm[0] *= 25.4f;
+        ppmm[1] *= 25.4f;
+        return ppmm;
+    }
+
+    /**
+     * Converts [1/mm] to [1/inch] into res storage
+     * @param ppmm float[2] [1/mm] value
+     * @param res the float[2] result storage
+     * @return return [1/inch] value, i.e. the given res storage
+     */
+    public static float[/*2*/] ppmmToPPI(final float[/*2*/] ppmm, final float[/*2*/] res) {
+        res[0] = ppmm[0] * 25.4f;
+        res[1] = ppmm[1] * 25.4f;
+        return res;
+    }
+}
-- 
cgit v1.2.3