1

この質問はこの質問と非常に似ていますが、空飛ぶ円盤ではなくdocx4jに関連しています。

私はdocx4jを使用して、生成されたdocxドキュメントを返すサーブレットを介してxhtmlドキュメントをdocxにレンダリングしています。xhtmlドキュメントは、別のサーブレットから要求された画像を特徴としています。イメージサーブレットは、適切なイメージを返す前に、誰がログインしているかを確認します。以下のコードは、画像がどのようにリクエストされるかを示しています。

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

私の問題は、画像のhttpリクエストがXHTMLImporter(私が思うに)ログインしたユーザーからのものではないため、画像サーブレットが誰がログインしたかを知らないため、目的の画像が返されないことです。

私は現在、以下のコードを使用して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が使用しているものではないようです。変換プロセス中に要素を置き換える方法はありますか?

4

1 に答える 1

2

なんて楽しいんだ!私には複雑で複雑でクレイジーなソリューションがあり、@JasonPlutextが私が見落としていた非常にシンプルで明白なソリューションを提供することを知っています。

ここにあります。このコードは、単語ドキュメントを出力ストリームに生成します。

        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しますが、多くの反射を伴ってすべてを実行します。基本的に、いくつかのオブジェクトを挿入して、デフォルトのオブジェクトと(Importerインスタンスのフィールドである)オブジェクトをオーバーライドDocx4jUserAgentDocx4jReplacedElementFactoryますDocxRenderer。下記参照:

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

次に、2つのカスタマイズされたクラス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に何かを組み込むでしょうが、今のところ(私は思う)これは良い回避策のようです

于 2012-07-06T17:21:18.937 に答える