18

Flying Saucer を使用して XHTML を PDF ドキュメントに変換しています。コードは基本的な HTML とインライン CSS だけで動作するようになりましたが、今は画像を一種のヘッダーとして PDF に追加しようとしています。私が疑問に思っているのは、画像ファイルをJava Imageオブジェクトとして読み込み、それを何らかの方法でPDF(またはXHTML-仮想「url」を取得するように)に追加することで、画像を追加する方法があるかどうかです。 PDF のレンダリングに使用できる Image オブジェクトを表します)。誰かがこのようなことをしたことがありますか?

ご協力いただきありがとうございます。

4

3 に答える 3

37

私は先週それをしなければならなかったので、うまくいけばすぐにあなたに答えることができるでしょう。

空飛ぶ円盤

最も簡単な方法は、Flying Saucerでレンダリングする前に、必要な画像をHTMLテンプレートにマークアップとして追加することです。Flying Saucer内でReplacedElementFactory、レンダリングする前に任意のマークアップを画像データで置き換えることができるように、を実装する必要があります。

/**
 * Replaced element in order to replace elements like 
 * <tt>&lt;div class="media" data-src="image.png" /></tt> with the real
 * media content.
 */
public class MediaReplacedElementFactory implements ReplacedElementFactory {
    private final ReplacedElementFactory superFactory;

    public MediaReplacedElementFactory(ReplacedElementFactory superFactory) {
        this.superFactory = superFactory;
    }

    @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 className = element.getAttribute("class");
        // Replace any <div class="media" data-src="image.png" /> with the
        // binary data of `image.png` into the PDF.
        if ("div".equals(nodeName) && "media".equals(className)) {
            if (!element.hasAttribute("data-src")) {
                throw new RuntimeException("An element with class `media` is missing a `data-src` attribute indicating the media file.");
            }
            InputStream input = null;
            try {
                input = new FileInputStream("/base/folder/" + element.getAttribute("data-src"));
                final byte[] bytes = IOUtils.toByteArray(input);
                final Image image = Image.getInstance(bytes);
                final FSImage fsImage = new ITextFSImage(image);
                if (fsImage != null) {
                    if ((cssWidth != -1) || (cssHeight != -1)) {
                        fsImage.scale(cssWidth, cssHeight);
                    }
                    return new ITextImageElement(fsImage);
                }
            } catch (Exception e) {
                throw new RuntimeException("There was a problem trying to read a template embedded graphic.", e);
            } finally {
                IOUtils.closeQuietly(input);
            }
        }
        return this.superFactory.createReplacedElement(layoutContext, blockBox, userAgentCallback, cssWidth, cssHeight);
    }

    @Override
    public void reset() {
        this.superFactory.reset();
    }

    @Override
    public void remove(Element e) {
        this.superFactory.remove(e);
    }

    @Override
    public void setFormSubmissionListener(FormSubmissionListener listener) {
        this.superFactory.setFormSubmissionListener(listener);
    }
}

/base/folderメディアを解決するためのFlyingSaucerのルートURLになるため、HTMLファイルが配置されているフォルダーをここにハードコーディングしていることに気付くでしょう。どこからでも(プロパティなど)、正しい場所に変更できます。

HTML

HTMLマークアップ内で、次の<div class="media" data-src="somefile.png" />ような場所を示します。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>My document</title>
        <style type="text/css">
        #logo { /* something if needed */ }
        </style>
    </head>
    <body>
        <!-- Header -->
        <div id="logo" class="media" data-src="media/logo.png" style="width: 177px; height: 60px" />
        ...
    </body>
</html>

レンダリング

ReplacedElementFactoryそして最後に、レンダリング時にFlying-Saucerに自分を示す必要があります。

String content = loadHtml();
ITextRenderer renderer = new ITextRenderer();
renderer.getSharedContext().setReplacedElementFactory(new MediaReplacedElementFactory(renderer.getSharedContext().getReplacedElementFactory()));
renderer.setDocumentFromString(content.toString());
renderer.layout();
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
renderer.createPDF(baos);
// baos.toByteArray();

Freemarkerを使用してテンプレートからHTMLを生成し、その結果をFlyingSaucerにフィードして大成功を収めています。これはかなりきちんとしたライブラリです。

于 2012-07-13T19:59:56.380 に答える
5

詳細な解決策を提供してくれたアレックスに感謝します。私はこのソリューションを使用していますが、それを機能させるために追加する別の行があることがわかりました。

public ReplacedElement createReplacedElement(LayoutContext layoutContext, BlockBox blockBox, UserAgentCallback userAgentCallback, int cssWidth, int cssHeight) {
  Element element = blockBox.getElement();
  ....
  ....
  final Image image = Image.getInstance(bytes);
  final int factor = ((ITextUserAgent)userAgentCallback).getSharedContext().getDotsPerPixel(); //Need to add this line
  image.scaleAbsolute(image.getPlainWidth() * factor, image.getPlainHeight() * factor) //Need to add this line
  final FSImage fsImage = new ITextFSImage(image);
  ....
  ....

から DPP を読み取り、SharedContext画像をスケーリングして、画像を PDF にレンダリングして表示する必要があります。

ITextReplacedElement別の提案: を実装する代わりに、直接拡張できますReplacedElementFactory。その場合、次ReplacedElementFactoryのようにを設定できます。SharedContext

renderer.getSharedContext().setReplacedElementFactory(new MediaReplacedElementFactory(renderer.getOutputDevice()); 
于 2016-05-31T13:29:42.993 に答える