web-dev-qa-db-ja.com

iTextを使用してHTMLをPDFに変換

多くの開発者がさまざまな形式でほぼ同じ質問をするため、この質問を投稿しています。私はこの質問に自分で答えます(私はiText Groupの創立者/ CTOです)。これは「Wikiの答え」になることができます。 Stack Overflowの「ドキュメント」機能がまだ存在している場合、これはドキュメントトピックの適切な候補となります。

ソースファイル:

次のHTMLファイルをPDFに変換しようとしています。

<html>
    <head>
        <title>Colossal (movie)</title>
        <style>
            .poster { width: 120px;float: right; }
            .director { font-style: italic; }
            .description { font-family: serif; }
            .imdb { font-size: 0.8em; }
            a { color: red; }
        </style>
    </head>
    <body>
        <img src="img/colossal.jpg" class="poster" />
        <h1>Colossal (2016)</h1>
        <div class="director">Directed by Nacho Vigalondo</div>
        <div class="description">Gloria is an out-of-work party girl
            forced to leave her life in New York City, and move back home.
            When reports surface that a giant creature is destroying Seoul,
            she gradually comes to the realization that she is somehow connected
            to this phenomenon.
        </div>
        <div class="imdb">Read more about this movie on
            <a href="www.imdb.com/title/tt4680182">IMDB</a>
        </div>
    </body>
</html>

ブラウザでは、このHTMLは次のようになります。

enter image description here

私が遭遇した問題:

HTMLWorkerはCSSをまったく考慮しません

HTMLWorkerを使用した場合、ImageProviderを作成して、画像が見つからないことを通知するエラーを回避する必要があります。また、いくつかのスタイルを変更するためにStyleSheetインスタンスを作成する必要があります。

public static class MyImageFactory implements ImageProvider {
    public Image getImage(String src, Map<String, String> h,
            ChainedProperties cprops, DocListener doc) {
        try {
            return Image.getInstance(
                String.format("resources/html/img/%s",
                    src.substring(src.lastIndexOf("/") + 1)));
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }    
}

public static void main(String[] args) throws IOException, DocumentException {
    Document document = new Document();
    PdfWriter.getInstance(document, new FileOutputStream("results/htmlworker.pdf"));
    document.open();
    StyleSheet styles = new StyleSheet();   
    styles.loadStyle("imdb", "size", "-3");
    HTMLWorker htmlWorker = new HTMLWorker(document, null, styles);
    HashMap<String,Object> providers = new HashMap<String, Object>();
    providers.put(HTMLWorker.IMG_PROVIDER, new MyImageFactory());
    htmlWorker.setProviders(providers);
    htmlWorker.parse(new FileReader("resources/html/sample.html"));
    document.close();   
}

結果は次のようになります。

enter image description here

何らかの理由で、HTMLWorker<title>タグのコンテンツも表示します。これを回避する方法がわかりません。ヘッダーのCSSはまったく解析されません。StyleSheetオブジェクトを使用して、コード内のすべてのスタイルを定義する必要があります。

コードを見ると、使用している多くのオブジェクトとメソッドが非推奨になっていることがわかります。

enter image description here

そこで、XML Workerを使用するようにアップグレードすることにしました。


XMLワーカーを使用すると画像が見つかりません

私は次のコードを試しました:

public static final String DEST = "results/xmlworker1.pdf";
public static final String HTML = "resources/html/sample.html";
public void createPdf(String file) throws IOException, DocumentException {
    Document document = new Document();
    PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));
    document.open();
    XMLWorkerHelper.getInstance().parseXHtml(writer, document,
            new FileInputStream(HTML));
    document.close();
}

これにより、次のPDFが作成されました。

enter image description here

Times-Romanの代わりに、デフォルトのフォントHelveticaが使用されます。これはiTextの典型です(HTMLでフォントを明示的に定義する必要があります)。そうでなければ、CSSは尊重されているように見えますが、画像が欠落しており、エラーメッセージは表示されませんでした。

HTMLWorkerを使用すると、例外がスローされ、ImageProviderを導入することで問題を解決できました。これがXML Workerで機能するかどうか見てみましょう。

すべてのCSSスタイルがXMLワーカーでサポートされているわけではありません

私はこのようにコードを適合させました:

public static final String DEST = "results/xmlworker2.pdf";
public static final String HTML = "resources/html/sample.html";
public static final String IMG_PATH = "resources/html/";
public void createPdf(String file) throws IOException, DocumentException {
    Document document = new Document();
    PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(file));
    document.open();

    CSSResolver cssResolver =
            XMLWorkerHelper.getInstance().getDefaultCssResolver(true);
    HtmlPipelineContext htmlContext = new HtmlPipelineContext(null);
    htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
    htmlContext.setImageProvider(new AbstractImageProvider() {
        public String getImageRootPath() {
            return IMG_PATH;
        }
    });

    PdfWriterPipeline pdf = new PdfWriterPipeline(document, writer);
    HtmlPipeline html = new HtmlPipeline(htmlContext, pdf);
    CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);

    XMLWorker worker = new XMLWorker(css, true);
    XMLParser p = new XMLParser(worker);
    p.parse(new FileInputStream(HTML));

    document.close();
}

私のコードははるかに長いですが、今では画像がレンダリングされます:

enter image description here

HTMLWorkerクラスのCSS属性widthが考慮されるが、poster属性は無視されることを示すfloatを使用してレンダリングしたときよりも画像が大きくなります。 。どうすれば修正できますか?

残りの質問:

したがって、質問はこれに要約されます:PDFに変換しようとするspecificHTMLファイルがあります。私は多くの作業を行って、次々に問題を修正しましたが、解決できない特定の問題が1つあります。 float: right?などの要素の位置

追加の質問:

HTMLにフォーム要素(<input>など)が含まれている場合、それらのフォーム要素は無視されます。

10
Bruno Lowagie

コードが機能しない理由

HTML to PDF tutorial の紹介で説明したように、HTMLWorkerは何年も前に廃止されました。完全なHTMLを変換することは意図されていませんでしたHTMLページに<head>セクションと<body>セクションがあることはわかりませんが、すべてのコンテンツを解析するだけです。小さなHTMLスニペットを解析することを目的としており、 StyleSheetクラス;実際のCSSはサポートされていません。

その後、XML Workerが登場しました。 XMLワーカーは、XMLを解析するための汎用フレームワークとして意図されていました。概念実証として、XHTMLをPDF機能に書き込むことを決めましたが、すべてのHTMLタグをサポートしていませんでした。たとえば、フォームはまったくサポートされていませんでした。 HTMLのフォームはPDFのフォームとは大きく異なり、iTextアーキテクチャとHTML + CSSのアーキテクチャとの間には不一致もありました。しかし、XML Workerは多くの触手を持つモンスターになりました。

最終的に、HTML + CSS変換の要件を考慮して、iTextをゼロから書き直すことにしました。その結果、 iText 7 になりました。 iText 7の上に、いくつかのアドオンを作成しました。このコンテキストで最も重要なアドオンは pdfHTML です。

問題を解決する方法

ITextの最新バージョン(iText 7.1.0 + pdfHTML 2.0.0)を使用して、質問からHTMLをPDFに変換するコードはこのスニペットに縮小されます。

public static final String SRC = "src/main/resources/html/sample.html";
public static final String DEST = "target/results/sample.pdf";
public void createPdf(String src, String dest) throws IOException {
    HtmlConverter.convertToPdf(new File(src), new File(dest));
}

結果は次のようになります。

enter image description here

ご覧のとおり、これはほぼ期待どおりの結果です。 iText 7.1.0/pdfHTML 2.0.0以降、デフォルトのフォントはTimes-Romanです。 CSSは尊重されています。画像は右側に浮いています。

いくつかの追加の考え。

開発者は、iText 7/pdfHTML 2へのアップグレードのアドバイスをするときに、新しいiTextバージョンへのアップグレードに反対することがよくあります。聞いている議論の上位3つに答えさせてください。

無料のiTextを使用する必要がありますが、iText 7は無料ではありません/ pdfHTMLアドオンはクローズドソースです。

iText 7は、iText 5やXML Workerと同様にAGPLを使用してリリースされます。 AGPLは、オープンソースプロジェクトのコンテキストで無料の意味でfree useを許可します。クローズドソース/プロプライエタリ製品を配布する場合(たとえば、SaaSコンテキスト)でiTextを使用する場合)、iTextを無料で使用することはできません。これはiText 5にはすでに当てはまりましたが、これはiText 7にも当てはまります。iText5より前のバージョンに関しては、 これらをまったく使用すべきではありません 。私たちはiText Group内で激しい議論を行ってきました:一方では、開発者の意見を聞いていない企業による大規模な悪用を避けたいと思っていた人々がいました。開発者は、上司が間違ったことをするように強制し、上司に商用ライセンスを購入するよう説得することはできないと言っていました。開発者を上司の間違った振る舞いで罰するべきではないと主張しました。 nオープンソースのpdfHTMLの支持、つまりiTextの開発者が議論に勝ちました。それらが間違っていなかったことを証明し、iTextを正しく使用してください。iTextを無料で使用している場合はAGPLを尊重してください;クローズドソースコンテキストでiTextを使用している場合は、上司が商用ライセンスを購入していることを確認してください。

レガシーシステムを維持する必要があり、古いiTextバージョンを使用する必要があります。

マジ?メンテナンスには、アップグレードの適用と、使用しているソフトウェアの新しいバージョンへの移行も含まれます。ご覧のとおり、iText 7とpdfHTMLを使用する際に必要なコードは非常にシンプルで、以前必要だったコードよりもエラーが発生しにくいです。移行プロジェクトに時間がかかりすぎないようにしてください。

私はまだ始まったばかりで、iText 7については知りませんでした。私は自分のプロジェクトを終えて初めて発見しました。

それが、私がこの質問と回答を投稿している理由です。自分をeXtremeプログラマーと考えてください。すべてのコードを捨てて、新たに始めましょう。想像したほどの作業ではないことに気付くでしょう。また、iText 5が段階的に廃止されているため、プロジェクトが将来にわたって使用できるようになったことを知って、よりよく眠れます。有料のお客様には引き続きサポートを提供していますが、最終的にはiText 5のサポートを完全に停止します。

13
Bruno Lowagie

iText 7とこのコードを使用:

public void generatePDF(String htmlFile) {
    try {

        //HTML String
        String htmlString = htmlFile;
        //Setting destination 
        FileOutputStream fileOutputStream = new FileOutputStream(new File(dirPath + "/USER-16-PF-Report.pdf"));

        PdfWriter pdfWriter = new PdfWriter(fileOutputStream);
        ConverterProperties converterProperties = new ConverterProperties();
        PdfDocument pdfDocument = new PdfDocument(pdfWriter);

        //For setting the PAGE SIZE
        pdfDocument.setDefaultPageSize(new PageSize(PageSize.A3));

        Document document = HtmlConverter.convertToDocument(htmlFile, pdfDocument, converterProperties);
        document.close();
    } 
    catch (Exception e) {
         e.printStackTrace();
    }

}

0