From d4137a890fff8e09c87427d6a5fef91e25ba13b1 Mon Sep 17 00:00:00 2001
From: Alexander Schulz-Rosengarten <als@informatik.uni-kiel.de>
Date: Sat, 1 May 2021 11:02:24 +0200
Subject: [PATCH] Improved font size handling for offscreen rendering.

Also adjusted svg renderer to use pixel sizes in offscreen mode.
---
 .../freehep/SemanticSVGGraphics2D.java        | 41 ++++++++++++++++++-
 .../src/de/cau/cs/kieler/klighd/Klighd.java   | 27 +++++++++++-
 .../klighd/microlayout/PlacementUtil.java     | 12 ++++--
 3 files changed, 74 insertions(+), 6 deletions(-)

diff --git a/plugins/de.cau.cs.kieler.klighd.piccolo.freehep/src/de/cau/cs/kieler/klighd/piccolo/freehep/SemanticSVGGraphics2D.java b/plugins/de.cau.cs.kieler.klighd.piccolo.freehep/src/de/cau/cs/kieler/klighd/piccolo/freehep/SemanticSVGGraphics2D.java
index fbf4ee87b..ef98416a6 100644
--- a/plugins/de.cau.cs.kieler.klighd.piccolo.freehep/src/de/cau/cs/kieler/klighd/piccolo/freehep/SemanticSVGGraphics2D.java
+++ b/plugins/de.cau.cs.kieler.klighd.piccolo.freehep/src/de/cau/cs/kieler/klighd/piccolo/freehep/SemanticSVGGraphics2D.java
@@ -961,6 +961,43 @@ private String getTextsString(String text, Properties style, String indentation)
                 y += lineHeight;
             }
             
+        } else if (Klighd.simulateSwtFontSizeInAwt()) {
+            // If simulation of SWT font sizes in AWT is active we can use px size and set text length
+            
+            final int fontSize = getFont().getSize();
+            final float y = fontSize * pointToPxFactor;
+            final Double nextLength = this.nextTextLength;
+            this.nextTextLength = null;
+            
+            // if we have a multi-line text and a configured nextTextLength
+            //  we need to recalculate the designated textLength per line
+            // otherwise we can stick to the given 'nextTextLength' value
+            boolean noTextLengthPerLineCalcRequired = nextLength == null || isSingleLine;
+            final FontData fontData = noTextLengthPerLineCalcRequired ? null : getFontData(getFont());
+            
+            for (final String line : lines) {
+                content.append("\n" + indentation);
+                content.append("<text x=\"0\" y=\"").append(y).append("\"");
+                
+                final Double nextLineLength = noTextLengthPerLineCalcRequired ? nextLength :
+                    // need to box the result here as the type of the ternary operation would be 'double' otherwise
+                    //  yielding NPEs if 'nextLength' is 'null'
+                    Double.valueOf(PlacementUtil.estimateTextSize(fontData, line).getWidth());
+                
+                // text length
+                content.append(textLength(nextLineLength));
+                // semantic data
+                content.append(isSingleLine ? " "
+                    // style
+                    + style(style)
+                    // semantic data
+                    + attributes(false) : ""
+                );
+                content.append(textLineAttributes(line, i++));
+                content.append(">");
+                content.append(line);
+                content.append("</text>");
+            }
         } else {
             // without a display just use the pt size as line height for multiline text
 
@@ -1098,10 +1135,10 @@ private Properties getFontProperties(Font font) {
          * A font's size is specified in 'pt' which is a relative size where a height of 72pt
          * corresponds to 1 inch. When a font is rendered on a screen however, these pts are converted
          * to pixels and sizes of nodes and boxes are determined correspondingly. We thus use a px size
-         * if we are able to determine the device with wich the font was rendered (i.e. the device).
+         * if we are able to determine the device with which the font was rendered (i.e. the device).
          */
         Float size = (Float) attributes.get(TextAttribute.SIZE);
-        if (display != null) {
+        if (display != null || Klighd.simulateSwtFontSizeInAwt()) {
             result.put("font-size", fixedPrecision(size.floatValue() * pointToPxFactor) + "px");
         } else {
             result.put("font-size", fixedPrecision(size.floatValue()) + "pt");
diff --git a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/Klighd.java b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/Klighd.java
index 71f303e1a..702fc4691 100644
--- a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/Klighd.java
+++ b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/Klighd.java
@@ -46,6 +46,7 @@ public class Klighd {
     private static IKlighdStatusManager statusManager;
     
     private static boolean suppressDisplayScaleCompensationWhileHandlingText = false;
+    private static boolean simulateSwtFontSizeInAwt = true;
     
     static {
         boolean isLinux = false;
@@ -128,7 +129,31 @@ public static boolean isSuppressDisplayScaleCompensationWhileHandlingText() {
      *            <code>true</code> if compensation shall be suppressed, <code>false</code> reverts
      *            to the default.
      */
-    public static void setSuppressDisplayScaleCompensationWhileHandlingText(boolean suppress) {
+    public static void setSuppressDisplayScaleCompensationWhileHandlingText(final boolean suppress) {
         suppressDisplayScaleCompensationWhileHandlingText = suppress;
     }
+    
+    /**
+     * Returns the (global) setting on adjusting the font size estimation of AWT to match SWT sizes
+     * more closely. This also enables using px instead of pt when rendering SVGs.
+     * 
+     * @return <code>true</code> if simulation is active and fonts will be scaled (default),
+     *         <code>false</code> otherwise.
+     */
+    public static boolean simulateSwtFontSizeInAwt() {
+        return simulateSwtFontSizeInAwt;
+    }
+
+    /**
+     * Changes the (global) setting on adjusting adjusting the font size estimation of AWT to match SWT sizes
+     * more closely. This also enables using px instead of pt when rendering SVGs.
+     * This setting will only affect non-UI applications, but may be switched off if default AWT font sizes and values
+     * in pt are preferred.
+     *
+     * @param simulate
+     *            <code>true</code> if font sizes should be adjusted, <code>false</code> to deactivate this behavior.
+     */
+    public static void setSimulateSwtFontSizeInAwt(final boolean simulate) {
+        simulateSwtFontSizeInAwt = simulate;
+    }
 }
diff --git a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/microlayout/PlacementUtil.java b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/microlayout/PlacementUtil.java
index 3e41b3f15..5b5fdc883 100644
--- a/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/microlayout/PlacementUtil.java
+++ b/plugins/de.cau.cs.kieler.klighd/src/de/cau/cs/kieler/klighd/microlayout/PlacementUtil.java
@@ -245,7 +245,7 @@ public C getC() {
     // CHECKSTYLEON Visibility
 
     private static final KRenderingPackage KRENDERING_PACKAGE = KRenderingPackage.eINSTANCE;
-
+    private static final float PT_TO_PX_FACTOR = KlighdConstants.DEFAULT_DISPLAY_DPI / 72f;
 
     /**
      * Evaluates a position inside given parent bounds.
@@ -934,10 +934,11 @@ public static Bounds estimateTextSize(final KText kText) {
     public static Bounds estimateTextSize(final KText kText, final String text) {
         final Bounds testSize = getTestingTextSize(kText);
 
-        if (testSize != null)
+        if (testSize != null) {
             return testSize;
-        else
+        } else {
             return estimateTextSize(fontDataFor(kText, null), text);
+        }
     }
     
     /**
@@ -1092,6 +1093,11 @@ private static Bounds estimateTextSizeAWT(final FontData fontData, final String
             textBounds = new Bounds(fm.getStringBounds(text, fmg));
         }
         
+        if (Klighd.simulateSwtFontSizeInAwt()) {
+            textBounds.width  *= PT_TO_PX_FACTOR;
+            textBounds.height *= PT_TO_PX_FACTOR;
+        }
+        
         return textBounds;
     }