如何在docx4j生成的docx中从servlet渲染图像



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

我正在使用docx4j通过返回生成的docx文档的servlet将xhtml文档呈现为docx。xhtml 文档具有从另一个 servlet 请求的图像。映像 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使用的东西。有没有办法在转换过程中替换元素?

哦,

我玩得很开心!我有一个错综复杂、复杂和疯狂的解决方案,我知道@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方法,但自己做了大量反射。它基本上注入了几个对象来覆盖默认Docx4jUserAgent,并在DocxRenderer(这是导入器实例的一个字段)中Docx4jReplacedElementFactory对象。见下文:

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

和 ProfileImageDocx4jReplacementElementFactory(此时让 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中构建一些东西来处理这种情况,但目前(我认为)这似乎是一个很好的解决方法

相关内容

  • 没有找到相关文章

最新更新