第二部分 - 当缩放JTextPane时,如何获得一致的渲染?

4
我之前提交了这个问题和一个示例程序:如何在缩放JTextPane时获得一致的渲染效果? 问题回顾:我想允许用户缩放不可编辑的JTextPane。运行早期问题中提交的示例程序,它只是缩放了Graphics对象,导致粗体文本和非粗体文本之间的间距不一致。
下面的示例程序尝试通过将文本窗格绘制到100%的BufferedImage,然后缩放图像来解决该问题。这解决了间距不一致的问题,但结果文本缺乏清晰度。是否有一些渲染提示的组合(或其他更改),可以产生漂亮清晰的文本?
提前感谢任何建议或对此方法可行性的评论。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;

import javax.swing.Box;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;

public class ScaledJTextPane extends JTextPane
{
    double scale_;
    BufferedImage raster_;

    public ScaledJTextPane()
    {
        scale_ = 1.0;
        raster_ = null;
    }

    public void draw(Graphics g)
    {
        if (raster_ == null)
        {
            // Draw this text pane to a BufferedImage at 100%
            raster_ = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2 = raster_.createGraphics();
            g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF);

            paint(g2);
        }

        Graphics2D g2 = (Graphics2D) g;

        // Experiment with different rendering hints
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
                RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        g2.setRenderingHint(RenderingHints.KEY_RENDERING,
                RenderingHints.VALUE_RENDER_QUALITY);

        // Scale the BufferedImage            
        g2.scale(scale_, scale_);
        g2.drawImage(raster_, 0, 0, null);
    }

    public void setScale(double scale)
    {
        scale_ = scale;
        raster_ = null;
    }

    private static void createAndShowGUI() 
    {
        // Create and set up the window.
        JFrame frame = new JFrame("ScaledJTextPane using BufferedImage");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final ScaledJTextPane scaledTextPane = new ScaledJTextPane();
        StyledDocument doc = scaledTextPane.getStyledDocument();
        Style defaultStyle = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
        Style boldStyle = doc.addStyle("bold", defaultStyle);
        StyleConstants.setBold(boldStyle, true);

        scaledTextPane.setFont(new Font("Dialog", Font.PLAIN, 14));
        String boldText = "Four score and seven years ago ";
        String plainText = "our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal.";
        try 
        {
            doc.insertString(doc.getLength(), boldText, boldStyle);
            doc.insertString(doc.getLength(), plainText, defaultStyle);
        } 
        catch (BadLocationException ble) 
        {
            System.err.println("Couldn't insert text into text pane.");
        }

        final JComboBox zoomCombo=new JComboBox(new String[] {"75%",
                "100%", "150%", "175%", "200%"});
        final JPanel panel = new JPanel()
        {
            protected void paintComponent(Graphics g)
            {
                super.paintComponent(g);
                scaledTextPane.draw(g);
            }
        };
        zoomCombo.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                String s = (String) zoomCombo.getSelectedItem();
                s = s.substring(0, s.length() - 1);
                double scale = new Double(s).doubleValue() / 100;
                scaledTextPane.setScale(scale);
                panel.invalidate();
                panel.repaint();
            }
        });
        zoomCombo.setSelectedItem("100%");

        JPanel optionsPanel = new JPanel(new GridBagLayout());
        GridBagConstraints c = new GridBagConstraints();

        c.gridx = 0;
        c.gridy = 0;
        c.anchor = GridBagConstraints.WEST;

        optionsPanel.add(zoomCombo, c);

        c.gridx++;
        c.weightx = 1;
        c.fill = GridBagConstraints.HORIZONTAL;
        optionsPanel.add(Box.createHorizontalGlue(), c);

        // Add content to the window.
        scaledTextPane.setBounds(0, 0, 450, 300);
        panel.setOpaque(true);
        panel.setBackground(Color.WHITE);
        frame.getContentPane().add(panel, BorderLayout.CENTER);
        frame.getContentPane().add(optionsPanel, BorderLayout.NORTH);
        frame.setSize(900, 300);

        //Display the window.
        frame.setVisible(true);
    }

    public static void main(String[] args) 
    {
        SwingUtilities.invokeLater(new Runnable() 
        {
            public void run() 
            {
                createAndShowGUI();
            }
        });
    }
}
3个回答

3

其实,那就是我开始的地方。我一直在使用你的ScaledEditorKit,但是有几个问题我一直没有解决:(文本窗格在某些宽度下光标定位不正确) - jht
继续那个评论...自从升级到Java 6 Update 14及更高版本后,渲染不一致。我希望通过仅允许在100%处进行编辑来简化事情。 - jht
问题描述太常见了。不幸的是我无法提供任何相关信息。需要详细描述错误行为。 - StanislavL
我重新审视了这个问题。您的ScaledGlyphPainter似乎解决了两个问题。第一个问题的详细报告请参见http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6736241。尽管在此时更改字形绘制器会导致严重的向后兼容性问题,因为文本布局非常不同,但我将接受这个解决方案。谢谢。 - jht
此外,您可以尝试基于TextLayout的GlyphPainter(例如,请参见SUN的GlyphPainter2类),但它速度较慢。 - StanislavL

2
遗憾的是,从固定分辨率扩展到更大尺寸总会导致一些锯齿状伪影。这里有一种替代方法,它可以缩放JTextPane使用的字体。
对于低级控制,请考虑TextLayout,它包括一个FontRenderContext,可以管理抗锯齿和小数度量设置,如此示例所示。 alt text
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;

/** @see https://dev59.com/1VPTa4cB1Zd3GeqPfgQo */
public class ScaledJTextPane {

    private static final int SIZE = 14;
    private static final String FONT = "Dialog";

    private static void createAndShowGUI() {
        JFrame frame = new JFrame("ScaledJTextPane using BufferedImage");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        final JTextPane tp = new JTextPane();
        tp.setFont(new Font(FONT, Font.PLAIN, SIZE));
        tp.setPreferredSize(new Dimension(400, 300));
        StyledDocument doc = tp.getStyledDocument();
        Style defaultStyle = StyleContext.getDefaultStyleContext()
            .getStyle(StyleContext.DEFAULT_STYLE);
        Style boldStyle = doc.addStyle("bold", defaultStyle);
        StyleConstants.setBold(boldStyle, true);
        String boldText = "Four score and seven years ago ";
        String plainText = "our fathers brought forth on this continent, "
            + "a new nation, conceived in Liberty, and dedicated to the "
            + "proposition that all men are created equal.";
        try {
            doc.insertString(doc.getLength(), boldText, boldStyle);
            doc.insertString(doc.getLength(), plainText, defaultStyle);
        } catch (BadLocationException ble) {
            ble.printStackTrace(System.err);
        }
        final JPanel panel = new JPanel();
        panel.add(tp);

        final JComboBox zoomCombo = new JComboBox(new String[]{
                "75%", "100%", "150%", "175%", "200%"});
        zoomCombo.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                String s = (String) zoomCombo.getSelectedItem();
                s = s.substring(0, s.length() - 1);
                double scale = new Double(s).doubleValue() / 100;
                int size = (int) (SIZE * scale);
                tp.setFont(new Font(FONT, Font.PLAIN, size));
            }
        });
        zoomCombo.setSelectedItem("100%");
        JPanel optionsPanel = new JPanel();
        optionsPanel.add(zoomCombo);
        panel.setBackground(Color.WHITE);
        frame.add(panel, BorderLayout.CENTER);
        frame.add(optionsPanel, BorderLayout.NORTH);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                createAndShowGUI();
            }
        });
    }
}

这是一种有趣的方法,可能是我可以使用的。在现实世界中,文本窗格可能包含多个字体大小,这意味着需要遍历文档元素。此外,我不想修改文本窗格,只需以缩放的方式显示它,因此我需要维护一个基本大小的文本窗格的副本。 - jht

0
我想允许用户对不可编辑的JTextPane进行缩放。

由于文本窗格是不可编辑的,您可以使用Screen Image类创建文本窗格的图像。然后,您可以使用适当的缩放因子在面板上绘制该图像。


谢谢您的建议,但这似乎归结为我在上面的代码中尝试的方法 - 即创建文本窗格的BufferedImage,然后使用适当的缩放因子绘制图像。我遇到的问题是,与直接缩放Graphics对象并直接绘制文本窗格创建的文本相比,结果文本要不清晰得多。 - jht

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接