JDK-8235821 : Graphics2D drawString methods draw text differently when font is rotated
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 11,13,14
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_7
  • CPU: x86_64
  • Submitted: 2019-12-01
  • Updated: 2019-12-13
  • Resolved: 2019-12-13
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 15
15Resolved
Related Reports
Duplicate :  
Description
ADDITIONAL SYSTEM INFORMATION :
openjdk version "14-ea" 2020-03-17
OpenJDK Runtime Environment (build 14-ea+25-1178)
OpenJDK 64-Bit Server VM (build 14-ea+25-1178, mixed mode, sharing)

Microsoft Windows [Version 6.1.7601]


A DESCRIPTION OF THE PROBLEM :
When using a font that has been rotated and scaled using deriveFont(AffineTransform), text will draw differently depending on whether you use drawString(AttributedCharacterIterator, int, int) or drawString(String, int, int).

More specifically, when you use drawString(String, int, int), and the Graphics2D object uses a rotated + scaled font, and fractional metrics are enabled on the Graphics2D object, the glyph advances for the string appear to be calculated incorrectly.

The issue has been observed in OpenJDK 14-ea25, OpenJDK 13-b33, and OpenJDK 11.0.4.

The issue does not exist in Oracle JDK 1.8.0_152.


REGRESSION : Last worked in version 8

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached code.


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The characters in all four strings ("Hello World 1", "Hello World 2", "Hello World 3", "Hello World 4") are spaced correctly; spacing in all four samples should be equivalent across samples.

ACTUAL -
The characters in "Hello World 2" are not spaced correctly (the glyphs overlap).


---------- BEGIN SOURCE ----------
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.TextAttribute;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.text.AttributedString;
import java.util.Collections;

import javax.imageio.ImageIO;

public class FontRotationTest {

    public static void main(String... args) throws Exception {

        BufferedImage img = new BufferedImage(700, 700, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = img.createGraphics();

        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setColor(Color.WHITE);
        g2d.fillRect(0, 0, img.getWidth(), img.getHeight());
        g2d.setColor(Color.BLACK);

        AffineTransform at = AffineTransform.getRotateInstance(Math.PI / 2);
        at.scale(3.65, 2.44); // x and y scales must be different to reproduce
        Font font = new Font(Font.SANS_SERIF, Font.PLAIN, 24).deriveFont(at);

        drawString(g2d, "Hello World 1", 100, 50, font, false, false); // <- OK
        drawString(g2d, "Hello World 2", 200, 50, font, false, true);  // <- problem here
        drawString(g2d, "Hello World 3", 300, 50, font, true, false);  // <- OK
        drawString(g2d, "Hello World 4", 400, 50, font, true, true);   // <- OK

        g2d.dispose();

        ImageIO.write(img, "png", new File("font-rotation.png"));
    }

    private static void drawString(Graphics2D g2d, String s, int x, int y, Font font, boolean attributed, boolean fm) {

        Object onOrOff = (fm ? RenderingHints.VALUE_FRACTIONALMETRICS_ON : RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, onOrOff);

        if (attributed) {
            AttributedString as = new AttributedString(s, Collections.singletonMap(TextAttribute.FONT, font));
            g2d.drawString(as.getIterator(), x, y);
        } else {
            Font oldFont = g2d.getFont();
            g2d.setFont(font);
            g2d.drawString(s, x, y);
            g2d.setFont(oldFont);
        }
    }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Use the drawString(AttributedCharacterIterator, int, int) method instead of the drawString(String, int, int) method, or disable fractional metrics.


FREQUENCY : always



Comments
Same fractional metrics issue as 8224109.
13-12-2019

Reported as a regression, Graphics2D drawString methods draw text differently when font is rotated. Checked this with reported version and could confirm the issue. Results: ======== 8u231: OK 9: OK 10.0.2: OK 11: Fail 12: Fail 13: Fail 14 ea b25: Fail This is a regression in JDK 11 (see screenshots as quick reference). Run the attached test case to verify same.
12-12-2019