0

I have a some Graphics2D area, where i paint geometrical primitives. It can be easily scaled with g2.scale(x, y). Now i want to paint some text in my scalable area and edit text right into it. It works too, but unfortunately there is one problem, that can be illustrated. This is how it looks at the monitor by 100%: enter image description here

And that's what happens at 300%:

enter image description here

The position of lette "A" is almost OK, but the positions of letter B jumps left-right. I suppose that JEditorPane just takes the nearest font to render scaled font. And space in different fonts could be slightly different size in pixels. That does not really makes problems at the beginning of the string, but at the end the error accumulates.

How can i achive that JEditorPane renders font in exact same positions at any g2.scale()? (For example OpenOffice works correct whit the same problem).

UPD: sscce

class Canvas extends JPanel {
    @Override
    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        g.translate(p.x, p.y);
        g2.scale(scale, scale);
        g2.setColor(Color.GRAY);
        g2.fill(g2.getClip());

        JEditorPane pane = new JEditorPane();
        pane.setFont(pane.getFont().deriveFont(22f));
        pane.setText("A                                                                                              B");
        pane.setBounds(new Rectangle(100, 100, 700, 30));
        g2.translate(100, 100);
        pane.paint(g2);
        g2.translate(-100, -100);

        g2.setColor(Color.BLACK);
        g2.draw(new Rectangle(100, 100, 700, 30));


        g2.scale(1 / scale, 1 / scale);
        g.translate(-p.x, -p.y);
        g2.setColor(Color.GREEN);
        g2.setFont(g2.getFont().deriveFont(33f));
        g2.drawString("Scale (mouse wheel): " + new DecimalFormat("#.##").format(scale * 100) + "%", 2, 768-66);
        g2.drawString("(Drag mouse to move)", 2, 768-33);
    }


// +++++++++++++++++++++++++++ GUI +++++++++++++++++++++++++++++++++++++

    public Canvas() {
        addMouseWheelListener(new MouseWheelListener() {
            @Override
            public void mouseWheelMoved(MouseWheelEvent e) {
                scale += e.getWheelRotation() * 1.0 / 10;
                repaint();
            }
        });

        addMouseMotionListener(new MouseMotionAdapter() {
            @Override
            public void mouseDragged(MouseEvent e) {
                p.x = e.getPoint().x - startDrag.x;
                p.y = e.getPoint().y - startDrag.y;
                repaint();
            }
        });

        addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                startDrag = e.getPoint();
                startDrag.x -= p.x;
                startDrag.y -= p.y;
            }
        });
    }

    public static double scale = 1.0;
    public static Point p = new Point();
    public static Point startDrag = new Point();
}

public class Main {
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.add(new Canvas());

        frame.setSize(1024, 768);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}
AvrDragon
  • 7,139
  • 4
  • 27
  • 42

2 Answers2

2

See there http://java-sl.com/articles.html articles abut JEditorPane scaling. The "Scaling with custom GlyphPainter and fractional measure support" article explains the font scaling and measuring issue. You can use TextLayout based or GlyphVector based painting to fix the fractional fnt measuring.

StanislavL
  • 56,971
  • 9
  • 68
  • 98
1

I was battling with the same one for some time. The problem is bigger when you try to right justify the text and use a small font size (hence many characters on the line). Scaling just multiplie the erros and make it easily visible.

Using the "Scaling with custom GlyphPainter and fractional measure support" article and code mentioned above will help as switching from int to float gets rid of the rounding error. It works almost okay, but not exactly, i.e. there is something else that is happening there.

In ScaledGlyphPainter.getTabbedTextWidth I replaced the actual mensurment

nextX += metrics.getStringBounds(txtStr, n - charCount, n, painterGr).getWidth();

With

FontRenderContext  frc = new FontRenderContext(new AffineTransform (), true, true);
TextLayout layout = new TextLayout (text, metrics.getFont(), frc);
nextX += layout.getBounds().getWidth();

Apparently, both measurements will return different results on the same text !!!! If measurement happen one way and drawing another, you will get drawing errors. Once I changed the code above to use the TextLayout measurement , all issues were gone and justification works as needed.