JDK-8258984 : [macos] Graphics attributed string and "FontMetrics.stringWidth" inconsistency
  • Type: Bug
  • Component: client-libs
  • Sub-Component: 2d
  • Affected Version: 9,11,14,15,16,17
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: os_x
  • CPU: x86_64
  • Submitted: 2020-12-24
  • Updated: 2021-01-07
  • Resolved: 2021-01-07
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 17
17Resolved
Related Reports
Duplicate :  
Description
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



Comments
If the test is updated to use an OpenType font (eg Courier) instead of Monospaced which maps to an AAT font, then the metrics match up. Closing as a dup. of JDK-8144012
07-01-2021

This is reproducible on 10.14.6 so I don't think it is anything to do with macOS 11. Also I can reproduce with JDK 9 and 11 as well as 15 so it is not a recent regression either.
07-01-2021