2012-07-06 82 views
1

這個問題與this question非常相似,但涉及到docx4j而不是飛碟。如何從docx4j生成docx中的servlet呈現圖像docx

我正在使用docx4j將xhtml文檔通過一個返回生成的docx文檔的servlet呈現給docx。 xhtml文檔具有從另一個servlet請求的圖像。在返回適當的圖像之前,圖像小服務程序檢查誰已登錄。下面的代碼顯示的圖像是如何要求:

<img height="140" width="140" src="http://localhost:8080/myapp/servlet/DisplayPic" /> 

我的問題是,對於圖像的http請求從XHTMLImporter(我認爲),而不是登錄的用戶,因此圖像的servlet不知道是誰的登錄,因此不會返回所需的圖像。

我目前使用下面的代碼來呈現XHTML文檔:

XHTMLImporter.setHyperlinkStyle("Hyperlink"); 
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage(); 

NumberingDefinitionsPart ndp = new NumberingDefinitionsPart(); 
wordMLPackage.getMainDocumentPart().addTargetPart(ndp); 
ndp.unmarshalDefaultNumbering(); 

wordMLPackage.getMainDocumentPart().getContent().addAll(XHTMLImporter.convert(xhtmlDocAsString, null, wordMLPackage)); 

在飛碟我能夠使用ReplacedElementFactory但是這似乎並沒有被什麼東西docx4j使用。在轉換過程中是否有替代元素的方法?

+0

我已經去使用嵌入在HTML中的base 64編碼圖像,因爲我可以在轉換前做html替換,但docx4j似乎不適用於基礎64圖像 – Edd 2012-07-06 13:41:35

+0

我已經設法做一些骯髒的反射來擴展Docx4jReplacedElementFactory並讓XHTMLImporter使用我的ReplacedElementFactory,但它不起作用。我認爲圖像不是通過ReplacedElementFactory包含的,而是在轉換的後期階段添加的 – Edd 2012-07-06 15:16:47

+0

導入base64編碼圖像應該可以工作(請參閱第976行的XHTMLImporter) – JasonPlutext 2012-07-09 12:52:08

回答

2

哦,我有什麼樂趣!我有一個複雜而複雜的解決方案,我知道@JasonPlutext將提供一個我忽略的非常簡單明顯的解決方案。

這是它。此代碼生成word文檔輸出流:

 outputStream = response.getOutputStream(); 

     XHTMLImporter.setHyperlinkStyle("Hyperlink"); 

     // Create an empty docx package 
     WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.createPackage(); 

     NumberingDefinitionsPart ndp = new NumberingDefinitionsPart(); 
     wordMLPackage.getMainDocumentPart().addTargetPart(ndp); 
     ndp.unmarshalDefaultNumbering(); 

     // Convert the XHTML, and add it into the empty docx we made 
     List<Object> wmlObjects = getWmlObjects(wordMLPackage, xhtmlDocumentAsString); 
     wordMLPackage.getMainDocumentPart().getContent().addAll(wmlObjects); 

     SaveToZipFile saver = new SaveToZipFile(wordMLPackage); 
     saver.save(outputStream); 

方法getWmlObjects是我自己該模擬XHTMLImporter.convert方法,但做每件事本身有很多反思。它基本上注入了幾個對象來覆蓋DocxRenderer(這是導入器實例的一個字段)中的默認對象Docx4jUserAgentDocx4jReplacedElementFactory。請看下圖:

private List<Object> getWmlObjects(WordprocessingMLPackage wordMLPackage, String xhtmlDocumentAsString) { 

    try { 
     DocxRenderer renderer = new DocxRenderer(); 

     // override the user agent 
     FieldAccessUtils.setField(renderer, "userAgent", new ProfileImageDocx4jUserAgent()); 

     // override the replaced element factory 
     Docx4jDocxOutputDevice outputDevice = (Docx4jDocxOutputDevice) FieldAccessUtils.getField(renderer, 
       "_outputDevice"); 
     renderer.getSharedContext().setReplacedElementFactory(
       new ProfileImageDocx4jReplacedElementFactory(outputDevice)); 

     // build the XHTMLImporter instance as it does in XHTMLImporter.convert but with our new renderer 

     XHTMLImporter importer; // = new XHTMLImporter(wordMLPackage); 
     Constructor<XHTMLImporter> constructor = XHTMLImporter.class 
       .getDeclaredConstructor(WordprocessingMLPackage.class); 
     constructor.setAccessible(true); 
     importer = constructor.newInstance(wordMLPackage); 
     constructor.setAccessible(false); 

     FieldAccessUtils.setField(importer, "renderer", renderer); 

     InputSource is = new InputSource(new BufferedReader(new StringReader(xhtmlDocumentAsString))); 
     Document dom = XMLResource.load(is).getDocument(); 

     renderer.setDocument(dom, null); 
     renderer.layout(); 

     // use reflection to do: importer.traverse(renderer.getRootBox(), FieldAccessUtils.getField(importer, "imports"), null); 
     Method traverseMethod = importer.getClass().getDeclaredMethod("traverse", Box.class, List.class, 
       TableProperties.class); 
     traverseMethod.setAccessible(true); 
     traverseMethod.invoke(importer, renderer.getRootBox(), FieldAccessUtils.getField(importer, "imports"), null); 
     traverseMethod.setAccessible(false); 

     return (List<Object>) FieldAccessUtils.getField(importer, "imports"); 

    } catch (SecurityException e) { 
     getLogger().error(ExceptionUtils.getStackTrace(e)); 
    } catch (NoSuchMethodException e) { 
     getLogger().error(ExceptionUtils.getStackTrace(e)); 
    } catch (IllegalArgumentException e) { 
     getLogger().error(ExceptionUtils.getStackTrace(e)); 
    } catch (IllegalAccessException e) { 
     getLogger().error(ExceptionUtils.getStackTrace(e)); 
    } catch (InvocationTargetException e) { 
     getLogger().error(ExceptionUtils.getStackTrace(e)); 
    } catch (InstantiationException e) { 
     getLogger().error(ExceptionUtils.getStackTrace(e)); 
    } 

    try { 
     // plan B 
     return XHTMLImporter.convert(xhtmlDocumentAsString, null, wordMLPackage); 
    } catch (Docx4JException e) { 
     getLogger().error(ExceptionUtils.getStackTrace(e)); 
    } 

    return null; 
} 

然後我有我的兩個自定義類ProfileImageDocx4jUserAgent(它繁瑣的工作):

public class ProfileImageDocx4jUserAgent extends Docx4jUserAgent { 

    /** 
    * Replace the image where the DisplayUserPic servlet is being called. 
    * <p> 
    * From overridden method javadoc: 
    * <p> 
    * {@inheritDoc} 
    */ 
    @Override 
    public Docx4JFSImage getDocx4JImageResource(String uri) { 

     if (StringUtils.contains(uri, "DisplayUserPic")) { 

      InputStream input = null; 
      try { 

       input = ...; 
       byte[] bytes = IOUtils.toByteArray(input); 
       return new Docx4JFSImage(bytes); 

      } catch (IOException e) { 
       getLogger().error(ExceptionUtils.getStackTrace(e)); 
      } catch (ServiceException e) { 
       getLogger().error(ExceptionUtils.getStackTrace(e)); 
      } finally { 
       IOUtils.closeQuietly(input); 
      } 

      return super.getDocx4JImageResource(uri); 

     } else { 
      return super.getDocx4JImageResource(uri); 
     } 
    } 
} 

而且ProfileImageDocx4jReplacedElementFactory(它得到了iText的東西,在這一點上忽略圖像。 ..否則,有記錄一個錯誤,但它仍然能正常工作):

public class ProfileImageDocx4jReplacedElementFactory extends Docx4jReplacedElementFactory { 

    /** 
    * Constructor. 
    * 
    * @param outputDevice 
    *   the output device 
    */ 
    public ProfileImageDocx4jReplacedElementFactory(Docx4jDocxOutputDevice outputDevice) { 
     super(outputDevice); 
    } 

    /** 
    * Forces any images which use the DisplayUserPic servlet to be ignored. 
    * <p> 
    * From overridden method javadoc: 
    * <p> 
    * {@inheritDoc} 
    */ 
    @Override 
    public ReplacedElement createReplacedElement(LayoutContext layoutContext, BlockBox blockBox, 
      UserAgentCallback userAgentCallback, int cssWidth, int cssHeight) { 

     Element element = blockBox.getElement(); 
     if (element == null) { 
      return null; 
     } 

     String nodeName = element.getNodeName(); 
     String src = element.getAttribute("src"); 
     if ("img".equals(nodeName) && src.contains("DisplayUserPic")) { 
      return null; 
     } 

     // default behaviour 
     return super.createReplacedElement(layoutContext, blockBox, userAgentCallback, cssWidth, cssHeight); 
    } 
} 

我猜docx4j傢伙可能會建立的東西到docx4j手這種情況下,但目前(我認爲)這似乎是一個很好的工作

相關問題