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

[macos] Graphics attributed string and "FontMetrics.stringWidth" inconsistency

XMLWordPrintable

    • 2d
    • x86_64
    • os_x

      ADDITIONAL SYSTEM INFORMATION :
      Mac OS 11 (Big Sur), Java OpenJDK 15.0.1

      A DESCRIPTION OF THE PROBLEM :
      On Mac OS with Java 15 I'm using the method "java.awt.Graphics.drawString(AttributedCharacterIterator, int, int)" to draw a string with a certain font.
      I'm using the method "java.awt.FontMetrics.stringWidth(String)" to compute the width of the string, but the string is actually drawn in a larger width.
      I cannot reproduce the problem with Java 1.8

      REGRESSION : Last worked in version 8

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run the source code that I'm attaching.
      The first line of text is drawn using "java.awt.Graphics.drawString(String, int, int)".
      The second and third lines are drawn using Graphics.drawString(AttributedCharacterIterator) and "java.awt.font.TextLayout".

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      All three lines of text should look exactly the same.
      ACTUAL -
      The second and third lines of text are drawn on a larger horizontal span.

      ---------- BEGIN SOURCE ----------
      public class FontMetricsVsTextLayout {
        static JPanel area =null;
        public static void main(String[] args) throws InterruptedException {
          area = new JPanel() {
            

            @Override
            protected void paintComponent(Graphics g) {
              super.paintComponent(g);
              int x = 0;
              int y = 20;
              
              String str = "ABCDEFGHIJKLABCDEFGHIJKL";
              Font baseFont = g.getFont();
              baseFont = baseFont.deriveFont(16f);
              Font monospaceFont = new Font("Monospaced", Font.PLAIN, 14);
              
              //Use drawString
              g.setFont(monospaceFont);
              g.drawString(str, x, y);
              //Compute width using "stringWidth"
              int w = area.getFontMetrics(g.getFont()).stringWidth(str);

              //Add some suffix after the content
              x += w;
              g.setFont(baseFont);
              g.drawString("ZZZZ", x, y);
              
              y += 20;
              x = 0;
              //Use draw attributed string
              g.setFont(monospaceFont);
              AttributedString astr = new AttributedString(str);
              astr.addAttribute(java.awt.font.TextAttribute.FONT, monospaceFont);
              g.drawString(astr.getIterator(), x, y);
              //Compute width using "stringWidth"
              w = area.getFontMetrics(g.getFont()).stringWidth(str);

              //Add some suffix after
              x += w;
              astr = new AttributedString("ZZZZ");
              astr.addAttribute(java.awt.font.TextAttribute.FONT, baseFont);
              g.drawString(astr.getIterator(), x, y);
              
              //Use textlayout to draw the string
              y += 20;
              x = 0;
              Graphics2D g2d = ((Graphics2D)g);
              
              TextLayout tl = new TextLayout(str, monospaceFont, g2d.getFontRenderContext());
              tl.draw(g2d, x, y);
              //Compute the width using tl.getAdvance()
              x+= tl.getAdvance();
              
              //Add some suffix after it.
              tl = new TextLayout("ZZZZ", baseFont, g2d.getFontRenderContext());
              tl.draw(g2d, x, y);
            }
          };
          area.setOpaque(true);
          JFrame frame = new JFrame();
          frame.getContentPane().add(new JScrollPane(area));
          frame.setVisible(true);
          frame.setSize(400, 600);
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          Thread.sleep(10000);
        }
      }

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

      CUSTOMER SUBMITTED WORKAROUND :
      Use "java.awt.font.TextLayout.getAdvance()" instead of FontMetrics.stringWidth to establish the length of the content drawn using Graphics.drawString(AttributedCharacterIterator).

      FREQUENCY : always


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

              Created:
              Updated:
              Resolved: