JEditorPane을 "도장"으로 사용하여 HTML 텍스트를 PDF로 렌더링합니다. 특정 너비로 줄 바꿈 텍스트가 필요하며 텍스트 뒤에 흰색 "강조 표시"를 적용해야합니다. 따라서 PDF 렌더링 스레드에서 JEditorPane을 만들고 텍스트 및 스타일 시트를 설정 한 다음 PDF 그래픽에 페인팅합니다. 그러나 HTML 편집기 키트의 내부에 간헐적 인 NullPointerException이 발생합니다. 이것은이 SSCCE에 재현 :비 EventDispatchThread에서 JEditorPane 사용
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.GlyphView.getFont()
가 null 반환 javax.swing.text.GlyphPainter1.sync()
,에) 실패합니다. 조건부 중단 점을 설정하면이 경우 GlyphView
이 javax.swing.text.html.InlineView
이라는 것을 알 수 있습니다. 중단 점이 중지 된 후 getFont()
을 호출하면 null이 아닌 글꼴이 반환되므로 시간에 초기화되지 않습니다.
스윙 구성 요소는 스레드로부터 안전하지 않지만 백그라운드 스레드에서 JEditorPane을 인스턴스화하고 해당 스레드에서 안전하게 조작 할 수 없어야합니다. 한 스레드 만 호출하는 한 구성 요소에?
실행중인 스윙 코드가 콜백을 등록하지 않는다고 확신합니까? 이러한 콜백은 EDT에서 실행됩니다. 내가 작성한 코드가 아니라 Swing 호출의 결과로 실행되는 모든 코드를 의미합니다. –
@Marko, 위에서 언급 한 스윙 코드에서 콜백을 찾을 수 없지만 꽤 복잡합니다. 나는 CSS 파싱이 스레드에서 일어나고 있다고 생각 했겠지만, 모든 것을 같은 스레드에서 모두 알 수있다. –
다른 방법으로 백그라운드 스레드의 그래픽 객체에 HTML 텍스트를 렌더링하는 방법에 대한 권장 사항을 환영합니다. 배경에 대한 강조 표시 지원과 HTML의 기본 글꼴을 지정하고 고정 폭을 지정할 수있는 기능이 필요합니다. –