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

Graphics2D drawString methods draw text differently when font is rotated

    XMLWordPrintable

Details

    • 2d
    • 11
    • x86_64
    • windows_7

    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


      Attachments

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

        Issue Links

          Activity

            People

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

              Dates

                Created:
                Updated:
                Resolved: