2012-06-25 5 views
1

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(),에) 실패합니다. 조건부 중단 점을 설정하면이 경우 GlyphViewjavax.swing.text.html.InlineView이라는 것을 알 수 있습니다. 중단 점이 중지 된 후 getFont()을 호출하면 null이 아닌 글꼴이 반환되므로 시간에 초기화되지 않습니다.

스윙 구성 요소는 스레드로부터 안전하지 않지만 백그라운드 스레드에서 JEditorPane을 인스턴스화하고 해당 스레드에서 안전하게 조작 할 수 없어야합니다. 한 스레드 만 호출하는 한 구성 요소에?

+1

실행중인 스윙 코드가 콜백을 등록하지 않는다고 확신합니까? 이러한 콜백은 EDT에서 실행됩니다. 내가 작성한 코드가 아니라 Swing 호출의 결과로 실행되는 모든 코드를 의미합니다. –

+0

@Marko, 위에서 언급 한 스윙 코드에서 콜백을 찾을 수 없지만 꽤 복잡합니다. 나는 CSS 파싱이 스레드에서 일어나고 있다고 생각 했겠지만, 모든 것을 같은 스레드에서 모두 알 수있다. –

+0

다른 방법으로 백그라운드 스레드의 그래픽 객체에 HTML 텍스트를 렌더링하는 방법에 대한 권장 사항을 환영합니다. 배경에 대한 강조 표시 지원과 HTML의 기본 글꼴을 지정하고 고정 폭을 지정할 수있는 기능이 필요합니다. –

답변

1

가벼운 구성 요소 만 사용하기 때문에 headless mode을 옵션으로 사용할 수 있습니다. ProcessBuilder을 사용하여 GUI의 EDT에서 작업을 유지할 수 있습니다 (그림은 here). 제안에 대한 마르코에

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); 
      } 
     } 
    }); 
} 
+0

진행 막대가 페이지에서 틱하는 동안 백그라운드에서 PDF를 생성하는 스윙 앱입니다. Headless는 불행하게도 옵션이 아니며, EDT가 PDF 노트를 칠하도록하지도 않습니다. (진행 막대가 깨집니다) –

+0

SwingWorker에서 헤드리스 JVM을 생성하십시오. 'stdout/stdin'을 통해 진행 상황을 전달하고 그것을'PropertyChangeListener'에 전달해야합니다. – trashgod

+0

제안을 주셔서 감사합니다. JVM 프로세스와 메모리를 공유해야합니다. 해결 방법을 찾은 것 같아요. –

0

감사 이벤트 발송 쓰레드에 콜백을보고, 나는 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); 
    } 
} 
+0

슬프게도, 이것은 _ 스레드가 안전하지 않습니다. – trashgod

+0

수정되었지만 구성 요소에 대한 모든 액세스가 단일 (EDT가 아닌) 스레드에서 발생하는 경우에 작동합니다. 스타일 시트가 다른 스레드에서 변경된 경우에도 EDT의보기를 다시 작성하는 기본 구현과 비교합니다. 대부분 EDT에서 구성 요소가 렌더링된다고 가정 할 가능성이 높습니다. –