Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8235821

Graphics2D drawString methods draw text differently when font is rotated

XMLWordPrintable

    • 2d
    • 11
    • x86_64
    • windows_7

      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


        1. 14-result.JPG
          22 kB
          Pardeep Sharma
        2. 8u231-result.JPG
          22 kB
          Pardeep Sharma
        3. font-rotation.png
          19 kB
          Pardeep Sharma
        4. FontRotationTest.java
          2 kB
          Pardeep Sharma

            prr Philip Race
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Created:
              Updated:
              Resolved: