1

HTMLテキストをPDFにレンダリングするための「ゴム印」としてJEditorPaneを使用しています。テキストを特定の幅で折り返す必要があり、テキストの後ろに白い「ハイライト」を適用する必要があります。そのため、PDF レンダリング スレッドで JEditorPane を作成し、テキストとスタイルシートを設定してから、それを PDF グラフィックにペイントしています。ただし、HTML エディター キットの奥深くで断続的に NullPointerException が発生します。これは、この SSCCE で再現可能です。

import javax.swing.*;
import javax.swing.text.View;
import javax.swing.text.html.HTMLDocument;
import java.awt.*;
import java.awt.image.BufferedImage;

/**
 * @author sbarnum
 */
public class TextMarkerUtilsTest {
    public static void main(String[] args) throws Exception {
        Rectangle bounds = new Rectangle(255, 255);
        BufferedImage image = new BufferedImage(bounds.width, bounds.height, BufferedImage.TYPE_INT_RGB);
        Graphics2D d = image.createGraphics();
        d.setClip(bounds);
        for (int i=0; i<1000; i++) {
            JEditorPane renderHelper = new JEditorPane("text/html", "<html><body>This is my text.</body></html>");
            HTMLDocument document = (HTMLDocument) renderHelper.getDocument();
            document.getStyleSheet().addRule("foo{color:black;}");
            View rootView = renderHelper.getUI().getRootView(renderHelper);
            rootView.paint(d, bounds);
        }
    }

}

上記を実行すると、通常、ループを数回通過した後、次の例外がスローされます。

java.lang.NullPointerException
    at sun.font.FontDesignMetrics$MetricsKey.init(FontDesignMetrics.java:199)
    at sun.font.FontDesignMetrics.getMetrics(FontDesignMetrics.java:267)
    at sun.swing.SwingUtilities2.getFontMetrics(SwingUtilities2.java:949)
    at javax.swing.JComponent.getFontMetrics(JComponent.java:1599)
    at javax.swing.text.LabelView.getFontMetrics(LabelView.java:154)
    at javax.swing.text.html.InlineView.calculateLongestWordSpanUseWhitespace(InlineView.java:246)
    at javax.swing.text.html.InlineView.calculateLongestWordSpan(InlineView.java:191)
    at javax.swing.text.html.InlineView.getLongestWordSpan(InlineView.java:177)
    at javax.swing.text.html.ParagraphView.calculateMinorAxisRequirements(ParagraphView.java:140)
    at javax.swing.text.BoxView.checkRequests(BoxView.java:918)
    at javax.swing.text.BoxView.getMinimumSpan(BoxView.java:551)
    at javax.swing.text.html.ParagraphView.getMinimumSpan(ParagraphView.java:261)
    at javax.swing.text.BoxView.calculateMinorAxisRequirements(BoxView.java:886)
    at javax.swing.text.html.BlockView.calculateMinorAxisRequirements(BlockView.java:129)
    at javax.swing.text.BoxView.checkRequests(BoxView.java:918)
    at javax.swing.text.BoxView.getMinimumSpan(BoxView.java:551)
    at javax.swing.text.html.BlockView.getMinimumSpan(BlockView.java:361)
    at javax.swing.text.BoxView.calculateMinorAxisRequirements(BoxView.java:886)
    at javax.swing.text.html.BlockView.calculateMinorAxisRequirements(BlockView.java:129)
    at javax.swing.text.BoxView.checkRequests(BoxView.java:918)
    at javax.swing.text.BoxView.setSpanOnAxis(BoxView.java:326)
    at javax.swing.text.BoxView.layout(BoxView.java:691)
    at javax.swing.text.BoxView.setSize(BoxView.java:380)
    at javax.swing.plaf.basic.BasicTextUI$RootView.setSize(BasicTextUI.java:1703)
    at javax.swing.plaf.basic.BasicTextUI$RootView.paint(BasicTextUI.java:1422)
    at com.prosc.msi.model.editor.TextMarkerUtilsTest$1.run(TextMarkerUtilsTest.java:40)
    at java.lang.Thread.run(Thread.java:680)

いくつかの興味深い発見:

  • 上記がイベントディスパッチスレッドで実行される場合、機能します
  • への呼び出しを取り出すとaddRule("foo{color:black;}")、機能します (ルールを指定する必要がありますが、ルールが何であるかは問題ではないようです。ルールが追加されると失敗します)

問題は にjavax.swing.text.GlyphPainter1.sync()あり、javax.swing.text.GlyphView.getFont()は null を返します。GlyphView条件付きブレークポイントを設定すると、この場合の が であることがわかりますjavax.swing.text.html.InlineView。ブレークポイントが停止した後に呼び出すgetFont()と、null 以外のフォントが返されるため、何かが時間内に初期化されていません。

Swing コンポーネントがスレッドセーフではないことはわかっていますが、バックグラウンド スレッドで JEditorPane をインスタンス化し、そのバックグラウンド スレッドで安全に操作できるようにすべきではありませんか?

4

2 に答える 2

1

軽量コンポーネントのみを使用しているため、ヘッドレス モードがオプションになる場合があります。ここProcessBuilderに示されているを使用して、GUI の EDT の作業を省くことができます。

public static void main(String[] args) throws Exception {
    System.setProperty("java.awt.headless", "true"); 
    EventQueue.invokeLater(new Runnable() {

        @Override
        public void run() {
            Rectangle bounds = new Rectangle(255, 255);
            BufferedImage image = new BufferedImage(
                bounds.width, bounds.height, BufferedImage.TYPE_INT_RGB);
            Graphics2D d = image.createGraphics();
            d.setClip(bounds);
            for (int i = 0; i < 1000; i++) {
                JEditorPane renderHelper = new JEditorPane(
                    "text/html", "<html><body>This is my text.</body></html>");
                HTMLDocument document = (HTMLDocument) renderHelper.getDocument();
                document.getStyleSheet().addRule("foo{color:black;}");
                View rootView = renderHelper.getUI().getRootView(renderHelper);
                rootView.paint(d, bounds);
            }
        }
    });
}
于 2012-06-25T23:35:23.723 に答える
0

イベント ディスパッチ スレッドへのコールバックを探すよう提案してくれた Marko に感謝しますHTMLDocument.styleChanged()。私のサブクラス:

public class ThreadFriendlyHTMLDocument extends HTMLDocument {
    @Override
    protected void styleChanged(final Style style) {
        // to fix GlyphPainter1.sync NullPointerException, we call this in the current thread, instead of the EDT
        DefaultDocumentEvent dde = new DefaultDocumentEvent(0,
                          this.getLength(),
                          DocumentEvent.EventType.CHANGE);
        dde.end();
        fireChangedUpdate(dde);
    }
}
于 2012-06-26T21:04:30.820 に答える