Java 如何在docx4j生成的docx中呈现servlet中的图像

Java 如何在docx4j生成的docx中呈现servlet中的图像,java,servlets,xhtml,docx,docx4j,Java,Servlets,Xhtml,Docx,Docx4j,这个问题与docx4j非常相似,但与之相关,而不是飞碟 我使用docx4j通过一个servlet向docx呈现一个xhtml文档,该servlet返回生成的docx文档。xhtml文档具有一个从另一个servlet请求的图像。图像servlet在返回适当的图像之前检查谁已登录。下面的代码显示了如何请求图像: <img height="140" width="140" src="http://localhost:8080/myapp/servlet/DisplayPic" /> 在飞

这个问题与docx4j非常相似,但与之相关,而不是飞碟

我使用docx4j通过一个servlet向docx呈现一个xhtml文档,该servlet返回生成的docx文档。xhtml文档具有一个从另一个servlet请求的图像。图像servlet在返回适当的图像之前检查谁已登录。下面的代码显示了如何请求图像:

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

在飞碟中,我可以使用
ReplacedElementFactory
,但这似乎不是docx4j所使用的。有没有办法在转换过程中替换元素?

哦,我玩得多开心啊!我有一个复杂而疯狂的解决方案,我知道@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);
然后我就有了两个定制的类ProfileImageDocx4jUserAgent(它负责驴的工作):

公共类概要文件ImageDocx4JuserAgent扩展了Docx4jUserAgent{
/**
*替换调用DisplayUserPic servlet的图像。
*
*从重写的方法javadoc:
*
*{@inheritardoc}
*/
@凌驾
公共Docx4JFSImage getdocx4jimagerource(字符串uri){
if(StringUtils.contains(uri,“DisplayUserPic”)){
InputStream输入=null;
试一试{
输入=。。。;
byte[]bytes=IOUtils.toByteArray(输入);
返回新的Docx4JFSImage(字节);
}捕获(IOE异常){
getLogger().错误(ExceptionUtils.getStackTrace(e));
}捕获(服务异常e){
getLogger().错误(ExceptionUtils.getStackTrace(e));
}最后{
IOUtils.closequity(输入);
}
返回super.getdocx4jimagerource(uri);
}否则{
返回super.getdocx4jimagerource(uri);
}
}
}
和ProfileImageDocx4jReplacedElementFactory(此时会让iText内容忽略图像…否则会记录一个错误,但仍然可以正常工作):

公共类概要文件ImageDocx4jReplaceDelementFactory扩展了Docx4jReplacedElementFactory{
/**
*构造器。
* 
*@param输出设备
*输出设备
*/
公共配置文件ImageDocx4jReplaceDelementFactory(Docx4jDocxOutputDevice输出设备){
超级(输出设备);
}
/**
*强制忽略使用DisplayUserPic servlet的所有图像。
*
*从重写的方法javadoc:
*
*{@inheritardoc}
*/
@凌驾
public ReplacedElement createReplacedElement(LayoutContext LayoutContext、BlockBox、BlockBox、,
UserAgentCallback UserAgentCallback,int-cssWidth,int-cssHeight){
Element=blockBox.getElement();
if(元素==null){
返回null;
}
字符串nodeName=element.getNodeName();
字符串src=element.getAttribute(“src”);
if(“img”.equals(nodeName)&&src.contains(“DisplayUserPic”)){
返回null;
}
//违约行为
返回super.createReplacedElement(layoutContext、blockBox、userAgentCallback、cssWidth、cssHeight);
}
}

我猜docx4j的人可能会在docx4j中构建一些东西来处理这种情况,但目前(我认为)这似乎是一个很好的解决办法

我尝试过使用嵌入html中的Base64编码图像,因为我可以在转换之前替换html,但docx4j似乎不能使用Base64图像。我已经设法做了一些肮脏的反射来扩展Docx4jReplacedElementFactory,并让XHTMLImporter使用我的ReplacedElementFactory,但它不工作。我认为图像不是通过ReplacedElementFactory包含的,而是在base64编码图像转换导入的后一阶段添加的(请参见第976行的XHTMLImporter)。那里似乎没有任何与base64相关的内容。。。我使用的是2.8.0版,我看了一下,可以看到base64的内容,但是版本是2.8.1-SNAPSHOT,除非必要,否则我宁愿推迟使用SNAPSHOT版本。请添加您的建议作为答案,因为这似乎是更理想的解决方案
        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);
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;
}
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);
        }
    }
}
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);
    }
}