Font ligatures and kerning break under selection in Swings text based components
Asked Answered
N

0

8

Ligatures

Playing around with swing on java 17 I encountered some funky glitches: When enabling font ligatures and switching to a ligature heavy font, like Fira Code, selecting part of the ligated Text has following effect:

  • The selected text seems to be re-rendered out of context, however the parts outside the ligature still are unchanged.

Example of broken ligature

  • When exiting the selection the caret (text cursor) leaves some unrendered space between letters.

Unselecting part of ligature leaves unrendered space

  • Both problems can also happen simultaneous, possibly leading to unreadable content.

Both problems showing simultaneously

Kerning

When considering a kerning intensive font like Segoe UI or Segoe UI Variable, the new default font of Microsoft Windows 11, following glitches are happening:

  • Re-rendering of selected text without kerning being calculated in the context of the full text.

Animation of kerned rerendered selection

  • Trying to place the caret in a long line of kerned text (in this case the text is rendered more condensed) is nearly impossible - it seems like the internal selection logic is calculating the target based on the unkerned version.

Animation of faulty selection offset for kerned text

  • Kerned text does also seem to have the problem of not being correctly re rendered when selections are canceled.

Animation of Unselecting kerned text leaves artefacts of unrendered space

Analysis

The behaviour seems to show from at least Java 1.8 on, I spared my time and harddrive space checking previous versions. Also different LAF's do not seem to influence the glitches in any way. I think the culprit seems to be java.desktop/javax.swing.text.PlainView, especially its way in treating text as splittable segments, re rendering only 'damaged' text in updateDamage#668 and its calculations of change bounding boxes.

Workaround

The only consistent workaround I found working all the time was to deactivate kerning and ligatures. I also tried to hook my own PlainView class into swing, but its architecture actively tries to discourage such fixes.

Questions

Finally I want to ask some questions, it would be very helpful in understanding the problematic:

  • Did you also encounter such problems or is my fabricated example too synthetic?
  • Are other rendering pipelines also affected? I assume so but I only was able to test on Windows 11.
  • Do you know if it is the fault of the JVM standard library or are the stored font metrics non-standard?
  • Are there any workarounds you know of improving the situation?
  • Is there a way in exchanging the default font rendering pipeline in swing?

Thank you for your help! I have attached the source code as well as my current machine configuration below.


Edit: The Problem also seems to be present on Mac OSX Big Sur and Java 17 leading to artefacts when using kerning and ligatures. To reproduce I used the default Helvetica font. This may hint on the cause for the glitches being the jdk.

import javax.swing.*;
import java.awt.*;
import java.awt.font.*;
import java.util.*;


/**
        Tested on: Edition Windows 11 Pro
        Version    22H2
        OS build   22623.875
        Experience Windows Feature Experience Pack 1000.22636.1000.0
        
        openjdk 17.0.4.1 2022-08-12
        OpenJDK Runtime Environment Temurin-17.0.4.1+1 (build 17.0.4.1+1)
        OpenJDK 64-Bit Server VM Temurin-17.0.4.1+1 (build 17.0.4.1+1, mixed mode, sharing)
**/
public class Main {
    private static final boolean TEST_KERNING = true;
    private static final boolean TEST_LIGATURES = true;
    private static final String TEST_FONT = "Segoe UI Variable";
    private static final Number TEST_FONT_SIZE = 40;
    private static final Number TEST_FONT_WIDTH = TextAttribute.WEIGHT_BOLD;

    public static void main(String[] args) throws Exception {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

        Map<TextAttribute, Object> textAttributes = new HashMap<>();
        textAttributes.put(TextAttribute.FAMILY, TEST_FONT);
        textAttributes.put(TextAttribute.SIZE, TEST_FONT_SIZE);
        textAttributes.put(TextAttribute.WEIGHT, TEST_FONT_WIDTH);
        if(TEST_KERNING)
            textAttributes.put(TextAttribute.KERNING, TextAttribute.KERNING_ON);
        if(TEST_LIGATURES)
            textAttributes.put(TextAttribute.LIGATURES, TextAttribute.LIGATURES_ON);
        Font font = Font.getFont(textAttributes);

        JTextField txtInput = new JTextField();
        txtInput.setFont(font);

        JFrame frame = new JFrame("Demo");
        frame.setSize(400, 100);
        frame.add(txtInput);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        SwingUtilities.invokeAndWait(() ->
            frame.setVisible(true)
        );
    }
}
Nettienetting answered 31/10, 2022 at 17:53 Comment(4)
It would be interesting to repeat the test with JavaFX or Apache Pivot. I use the Dialog font with Java 1.8 on Windows 10, but I don't do a lot of text processing with Swing.Belostok
@GilbertLeBlanc AFAIK does JavaFX not support font kerning and ligatures in its default components.Nettienetting
I don't know. Font kerning is a subject I've never studied.Belostok
Swing text components don't support ligatures, kerning, or fractional metrics. You can, however, implement your own ViewFactory to return custom views which perform all the calculations taking ligatures and kerning into account. You can also submit a bug at bugreport.java.com.Downwards

© 2022 - 2024 — McMap. All rights reserved.