2012-07-06 50 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手这种情况下,但目前(我认为)这似乎是一个很好的工作

相关问题