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

Graphics.drawString() renders certain unicode chars incorrectly, when the font is scaled

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: P4 P4
    • tbd
    • 9, 10, 11
    • client-libs
    • None
    • java --version
      openjdk 11.0.1 2018-10-16
      OpenJDK Runtime Environment 18.9 (build 11.0.1+13)
      OpenJDK 64-Bit Server VM 18.9 (build 11.0.1+13, mixed mode)

       

      macOS 10.14.0

    • 2d
    • os_x

      When drawing a large version of ♫ (\u266B) with g.drawString() the font changes: the connecting beam points down, instead of up and the glyph is suddenly smaller. FontMetrics still reflect the width and height of what a properly scaled glyph would have looked like. This last fact makes this especially problematic, as the glyphs cannot be positioned properly anymore.

      Demo Code:
      ==========

      import javax.swing.*;
      import java.awt.*;
      import java.awt.geom.Rectangle2D;
      import java.awt.image.BufferedImage;

      public class UnicodeFontMetricsIssues {

          public static void main(final String[] args) {
              final String note = "\u266B";
              final String xNoteY = "\u266BY";
              final String xY = "XY";

              final JFrame frame = new JFrame();
              frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
              frame.getContentPane().setLayout(new BorderLayout());
              final JPanel iconPanel = new JPanel(new FlowLayout());
              frame.getContentPane().add(iconPanel, BorderLayout.CENTER);
              final TextIcon noteIcon = new TextIcon(note);
              final TextIcon xNoteYIcon = new TextIcon(xNoteY);
              final TextIcon xYIcon = new TextIcon(xY);
              iconPanel.add(noteIcon);
              iconPanel.add(xNoteYIcon);
              iconPanel.add(xYIcon);
              final JSlider slider = new JSlider(1, 2000, 1);
              slider.setSnapToTicks(false);
              final JPanel sliderPanel = new JPanel(new FlowLayout());
              sliderPanel.add(slider);
              final JLabel scaleFactorLabel = new JLabel("1.0");
              sliderPanel.add(scaleFactorLabel);
              frame.getContentPane().add(sliderPanel, BorderLayout.SOUTH);

              slider.addChangeListener(e -> {
                  final float scaleFactor = slider.getValue()/100f;
                  noteIcon.setScaleFactor(scaleFactor);
                  xNoteYIcon.setScaleFactor(scaleFactor);
                  xYIcon.setScaleFactor(scaleFactor);
                  scaleFactorLabel.setText("" + scaleFactor);
                  frame.invalidate();
              });

              SwingUtilities.invokeLater(() -> {
                  frame.setSize(500, 800);
                  frame.setVisible(true);
              });
          }

          private static class TextIcon extends JLabel {

              private final String text;

              public TextIcon(final String text) {
                  this.text = text;
                  setIcon(createIconWithText(1f));
              }

              public void setScaleFactor(final float scaleFactor) {
                  setIcon(createIconWithText(scaleFactor));
              }

              private ImageIcon createIconWithText(final float scaleFactor) {
                  final int width = 400;
                  final int height = 200;
                  final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
                  final Graphics2D g2d = (Graphics2D)image.getGraphics();

                  g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

                  final float originalSize = g2d.getFont().getSize2D();
                  g2d.setFont(g2d.getFont().deriveFont(originalSize * scaleFactor));

                  final FontMetrics metrics = g2d.getFontMetrics();

                  // fill whole background
                  g2d.setColor(Color.GREEN);
                  g2d.fillRect(0, 0, width, height);

                  // draw string bounds as RED background
                  final Rectangle2D rect = metrics.getStringBounds(text, g2d);
                  g2d.setColor(Color.RED);
                  g2d.fillRect((width - (int) rect.getWidth()) / 2, 0, (int)rect.getWidth(), (int)rect.getHeight());

                  // draw string
                  g2d.setColor(Color.BLACK);
                  g2d.drawString(text, (width - (int) rect.getWidth()) / 2, metrics.getAscent());
                  g2d.dispose();
                  return new ImageIcon(image);
              }
          }
      }


      The demo code lets you change the size of the font, showing a scale factor. The RED background shows the bounding box for the string.

      The issue is illustrated in the attached screenshots and movie.

        1. unicode_scale.mov
          1.17 MB
        2. small.png
          small.png
          437 kB
        3. large.png
          large.png
          446 kB

            prr Philip Race
            hschreiber Hendrik Schreiber
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: