0

iTextを含むテンプレート PDF を入力するために使用していますAcroForm。このテンプレートを使用して、動的にページを含む新しい PDF を作成したいと考えています。私の考えは、テンプレート PDF を入力し、ページを書き込み済みのフィールドでコピーし、それを新しいファイルに追加することです。彼らの主な問題は、顧客がテンプレートを自分でデザインしたいということです。したがって、この問題を解決する正しい方法を試しているかどうかはわかりません。

だから私は今動作しないこのコードを作成しましたが、エラーが発生しましたcom.itextpdf.io.IOException: PDF header not found.

マイコード

 x = 1;
try (PdfDocument finalDoc = new PdfDocument(new PdfWriter("C:\\Users\\...Final.pdf"))) {
        for (HashMap<String, String> map : testValues) {
            String path1 = "C:\\Users\\.....Temp.pdf"
            InputStream template = templateValues.get("Template");

            PdfWriter writer = new PdfWriter(path1);

            try (PdfDocument pdfDoc = new PdfDocument(new PdfReader(template), writer)) {
                PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
                for (HashMap.Entry<String, String> map2 : map.entrySet()) {

                    if (form.getField(map2.getKey()) != null) {
                        Map<String, PdfFormField> fields = form.getFormFields();
                        fields.get(map2.getKey()).setValue(map2.getValue());


                    }

                }
            } catch (IOException | PdfException ex) {
                System.err.println("Ex2: " + ex.getMessage());

            }
            if (x != 0 && (x % 5) == 0) {
                try (PdfDocument tempDoc = new PdfDocument(new PdfReader(path1))) {
                    PdfPage page = tempDoc.getFirstPage();
                    finalDoc.addPage(page.copyTo(finalDoc));

                } catch (IOException | PdfException ex) {
                    System.err.println("Ex3: " + ex.getMessage());

                }

            }
             x++;
       }
    } catch (IOException | PdfException ex) {
        System.err.println("Ex: " + ex.getMessage());
    }
4

2 に答える 2

4

パート 1 - PDF ヘッダーがありません

これは、すでに読み取られている (そして、PdfReader の構成によっては閉じられている) ループで InputStream を再読み取りしようとしたことが原因であると思われます。これを解決する方法は、使用されている InputStream の特定のタイプに依存します。単純な InputStream のままにしたい場合 (より具体的でありながらより有能な InputStream タイプに対して)、最初にストリームからバイトを丸呑みする必要があります。メモリ (たとえば、ByteArrayOutputStream) を作成し、それらのバイトに基づいて PDFReaders を作成します。

すなわち

ByteArrayOutputStream templateBuffer = new ByteArrayOutputStream();
while ((int c = template.read()) > 0) templateBuffer.write(c);
for (/* your loop */) {
    ...
    PdfDocument filledInAcroFormTemplate = new PdfDocument(new PdfReader(new ByteArrayInputStream(templateBuffer.toByteArray())), new PdfWriter(tmp))
   ...

パート 2 - その他の問題

いくつかのこと

  1. 最近リリースされた iText の 7.0.1 バージョンを入手してください。これには、AcroForm 処理に関するいくつかの修正が含まれているためです。
  2. おそらく、一時的な PDF に ByteArrayOutputStreams を使用することで (ファイルに書き出すのではなく) 回避できます。以下の例では、このアプローチを使用します。
  3. PdfDocument/PdfPage は「カーネル」モジュールにありますが、AcroForms は「フォーム」モジュールにあります (つまり、PdfPage は意図的に AcroForms を認識していません) - IPdfPageExtraCopier は、モジュール間のブリッジのようなものです。AcroForms を適切にコピーするには、引数が 2 つの copyTo() バージョンを使用して、PdfPageFormCopier のインスタンスを渡す必要があります。
  4. フィールド名はドキュメント内で一意である必要があります (「絶対」フィールド名 - 今のところフィールド階層はスキップします)。テンプレートからフィールドを何度もループして追加しているため、一意性を確保するためにフィールドの名前を変更する戦略を立てる必要があります (現在の API は、実際にはこの領域で少し不格好です)。

    File acroFormTemplate = new File("someTemplate.pdf");
    Map<String, String> someMapOfFieldToValues = new HashMap<>();
    try (
        PdfDocument  finalOutput = new PdfDocument(new PdfWriter(new FileOutputStream(new File("finalOutput.pdf")));
    ) {
        for (/* some looping condition */int x = 0; x < 5; x++) {
            // for each iteration of the loop, create a temporary in-memory
            // PDF to handle form field edits.
            ByteArrayOutputStream tmp = new ByteArrayOutputStream();
            try (
                PdfDocument filledInAcroFormTemplate = new PdfDocument(new PdfReader(new FileInputStream(acroFormTemplate)), new PdfWriter(tmp));
            ) {
                PdfAcroForm acroForm = PdfAcroForm.getAcroForm(filledInAcroFormTemplate, true);
                for (PdfFormField field : acroForm.getFormFields().values()) {
                    if (someMapOfFieldToValues.containsKey(field.getFieldName())) {
                        field.setValue(someMapOfFieldToValues.get(field.getFieldName()));
                    }
                }
                // NOTE that because we're adding the template multiple times
                // we need to adopt a field renaming strategy to ensure field
                // uniqueness in the final document.  For demonstration's sake
                // we'll just rename them prefixed w/ our loop counter
                List<String> fieldNames = new ArrayList<>();
                fieldNames.addAll(acroForm.getFormFields().keySet()); // avoid ConfurrentModification
                for (String fieldName : fieldNames) {
                    acroForm.renameField(fieldName, x+"_"+fieldName);
                }
            }
    
            // the temp PDF needs to be "closed" for all the PDF finalization
            // magic to happen...so open up new read-only version to act as
            // the source for the merging from our in-memory bucket-o-bytes
            try (
                PdfDocument readOnlyFilledInAcroFormTemplate = new PdfDocument(new PdfReader(new ByteArrayInputStream(tmp.toByteArray())));
            ) {
                // although PdfPage.copyTo will probably work for simple pages, PdfDocument.copyPagesTo
                // is a more comprehensive copy (wider support for copying Outlines and Tagged content)
                // so it's more suitable for general page-copy use.  Also, since we're copying AcroForm
                // content, we need to use the PdfPageFormCopier
                readOnlyFilledInAcroFormTemplate.copyPagesTo(1, 1, finalOutput, new PdfPageFormCopier());
            }
        }
    }
    
于 2016-09-22T15:19:49.907 に答える
0

コンテンツの追加が完了したら、PdfDocuments を閉じます。

于 2016-09-22T12:30:49.163 に答える