itext7研究ノート-第3章

序文

    このシリーズの最初の章で、特定のページサイズ、特定のページマージン(明示的または暗黙的に定義)を作成Documentし、DocumentオブジェクトにParagraphsやなどの基本的な描画ブロック追加したことを覚えていますか。List■、iTextは、コンテンツがページ内で適切に整理されることを保証します。同時に、TableCSVファイルの内容を表示するオブジェクトも作成しましたが、結果はすでに非常によく表示されています。しかし、上記のすべてが効率的に実行されない場合はどうなりますか?Webページのコンテンツのレイアウトをより適切に制御したい場合はどうなりますか?Tableクラスによって描画された長方形の境界線に満足できない場合はどうなりますか?作成されたページ数に関係なく、各ページの特定の場所にコンテンツを追加するとどうなりますか?

    第2章で述べた絶対位置にすべてを描く方法でこの問題を解決できますか?第2章のスターウォーズの冒頭のテキストを描く例を通して、これが非常に複雑なコードにつながる可能性があることに気づきました(コードを維持するのは難しいです)。もちろん、基本コンポーネントの高レベルAPIと低レベルAPIを組み合わせて、レイアウトをさらに制御できるようにする方法が必要です。これは、この章の第3章で説明する内容です。

ドキュメントレンダラーの紹介

Documentテキストと画像    を1つに追加したいが、テキストがドキュメントの幅全体を占めることは望ましくないとします。代わりに、図1に示すように、コンテンツを3つの列に編成します。

iText3-1

図1.テキストと画像は列に整理されています

    この例では、次のコードを使用できます(次のコードはすべてNewYorkTimesクラスの一部です)。

PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
PageSize ps = PageSize.A5;
Document document = new Document(pdf, ps);
//Set column parameters
float offSet = 36;
float columnWidth = (ps.getWidth() - offSet * 2 + 10) / 3;
float columnHeight = ps.getHeight() - offSet * 2;
//Define column areas
Rectangle[] columns = {
    new Rectangle(offSet - 5, offSet, columnWidth, columnHeight),
    new Rectangle(offSet + columnWidth, offSet, columnWidth, columnHeight),
    new Rectangle(
        offSet + columnWidth * 2 + 5, offSet, columnWidth, columnHeight)};
document.setRenderer(new ColumnDocumentRenderer(document, columns));
// adding content
Image inst = new Image(ImageDataFactory.getImage(INST_IMG)).setWidth(columnWidth);
String articleInstagram = new String(
    Files.readAllBytes(Paths.get(INST_TXT)), StandardCharsets.UTF_8);
NewYorkTimes.addArticle(document,
    "Instagram May Change Your Feed, Personalizing It With an Algorithm",
    "By MIKE ISAAC MARCH 15, 2016", inst, articleInstagram);
doc.close();

    誰もが最初の3行に精通している必要があります。これらは第1章で紹介されました。5、6、および7行目は、一連のパラメーターを定義しています。

  • offset変数。この変数を使用して、上下左右の境界線の幅を定義します。
  • 各列の幅columnWidth。これは、計算可能なページを3つの部分に分割して計算されます(目標は3列です)。計算可能なページのサイズは、ページ全体の幅-2 * offset+10であり、+ 10の関数は次のことを保証します。各列の間にギャップがあります
  • columnHeightサイズは単にページ全体の高さ-2 *offset

columnsこの配列変数    を使用して、3つのRectangleオブジェクトを格納します(確認する前に、デフォルトの座標系が左下隅にあり、x軸が右、y軸が右にあることに注意してください)。

  • 最初のものRectangle:左下隅の座標は(offset-5offset)、幅はcolumnWidth、、高さはcolumnHeight
  • 2つ目Rectangle:左下隅の座標は(offset+columnWidthoffset)、幅はcolumnWidth、、高さはcolumnHeight
  • 3番目Rectangle:左下隅の座標は(offset+2*columnWidth+5offset)、幅はcolumnWidth、、高さはcolumnHeight

    私たちは、その後、使用columns作成するには、このオブジェクトをColumnDocumentRenderer、一度この宣言ColumnDocumentRendererのように私たちがして、我々はすべてのコンテンツが追加されます定義する3つに従うレイアウト表示。DocumentDocumentRendererDocumentRectangle

    16行目でImageオブジェクトを作成し、長方形の幅に合わせて画像を比例的に拡大縮小しました。17行目と18​​行目では、テキストファイルを読み取って保存Stringします。次にaddArticle()示すように、これらの変数をパラメーターとして使用します。

public static void addArticle(
    Document doc, String title, String author, Image img, String text)
    throws IOException {
    Paragraph p1 = new Paragraph(title)
            .setFont(timesNewRomanBold)
            .setFontSize(14);
    doc.add(p1);
    doc.add(img);
    Paragraph p2 = new Paragraph()
            .setFont(timesNewRoman)
            .setFontSize(7)
            .setFontColor(Color.GRAY)
            .add(author);
    doc.add(p2);
    Paragraph p3 = new Paragraph()
            .setFont(timesNewRoman)
            .setFontSize(10)
            .add(text);
    doc.add(p3);
}

    timesNewRomanまた、timesNewRomanBoldオブジェクトはNewYorkTimesクラスの静的メンバー変数あり、タイプはPdfFontです。一般に、この例は前の章の例よりも単純です。次に、もう少し複雑な例を見てみましょう。

ブロックレンダラーを使用する

    最初の章では、米国の各大陸の情報のcsvファイルの内容をPDFに表示するときに、一連のCellオブジェクトを作成してからオブジェクトに追加しましたTable。オブジェクトCellの背景色と境界線のサイズは定義しませんでした。デフォルト値を使用しています。

デフォルト設定:Cellオブジェクトには背景色がなく、境界線のサイズは0.5ユーザー単位です。

    次にTable、次の図2に示すように、別のデータソースを使用してそのデータソースに配置します。

itext3-2

図2.角が丸く色付きのセルがあるテーブル

    ここで、コードの記述方法を説明します。最初のコードは前のコードと似ていますが、注目に値するのは次のコードだけです(クラス全体がクラスですPremierLeague)。

PageSize ps = new PageSize(842, 680);

    以前は、PageSize.A4サイズなどの標準的な紙を使用していましたこの例では、自分で定義した用紙サイズを使用します:842x680ユーザーユニット(1インチは72ユーザーユニット、つまり11.7x9.4インチに相当します)。PremierLeagueクラスのメインコードは次のとおりです。

PdfFont font = PdfFontFactory.createFont(FontConstants.HELVETICA);
PdfFont bold = PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD);
Table table = new Table(new float[]{1.5f, 7, 2, 2, 2, 2, 3, 4, 4, 2});
table.setWidthPercent(100)
        .setTextAlignment(Property.TextAlignment.CENTER)
        .setHorizontalAlignment(Property.HorizontalAlignment.CENTER);
BufferedReader br = new BufferedReader(new FileReader(DATA));
String line = br.readLine();
process(table, line, bold, true);
while ((line = br.readLine()) != null) {
    process(table, line, font, false);
}
br.close();
document.add(table);

    米国の状態を示す第1章の前の例との違いはわずかです。この例では、使用方法setTextAlignmentsetHorizontalAlignment方法を設定して、コンテンツをテーブルの中央に配置し、テーブル自体を中央に配置します(これは、テーブルが使用可能な幅の100%を占めることとは関係ありません)。 。次に、process()このより興味深い関数を見てみましょう

public void process(Table table, String line, PdfFont font, boolean isHeader) {
    StringTokenizer tokenizer = new StringTokenizer(line, ";");
    int columnNumber = 0;
    while (tokenizer.hasMoreTokens()) {
        if (isHeader) {
            Cell cell = new Cell().add(new Paragraph(tokenizer.nextToken()));
            cell.setNextRenderer(new RoundedCornersCellRenderer(cell));
            cell.setPadding(5).setBorder(null);
            table.addHeaderCell(cell);
        } else {
            columnNumber++;
            Cell cell = new Cell().add(new Paragraph(tokenizer.nextToken()));
            cell.setFont(font).setBorder(new SolidBorder(Color.BLACK, 0.5f));
            switch (columnNumber) {
                case 4:
                    cell.setBackgroundColor(greenColor);
                    break;
                case 5:
                    cell.setBackgroundColor(yellowColor);
                    break;
                case 6:
                    cell.setBackgroundColor(redColor);
                    break;
                default:
                    cell.setBackgroundColor(blueColor);
                    break;
            }
            table.addCell(cell);
        }
    }
}

    まず普通の文章を見てみましょう。行16、19、22、および25では、列番号に応じて背景色を変更します。13行目ではCell、フォントを設定し、setBorder()関数を使用してデフォルトの境界線オーバーライドします。境界線を、幅0.5ユーザー単位の黒い実線の境界線として定義します。

SolidBorder継承されBorderたクラス、それは、次のような多くの同様の兄弟、持っているDashedBorderDottedBorderDoubleBorderなど iTextで選択した境界線が提供されない場合は、Borderクラスを拡張したり、既存の実装を使用してインスピレーションを得たり、独自の実装を作成したりできますCellRenderer

    7行目と8行目でカスタムを使用しRoundedCornersCellRenderer()、パディングサイズを指定して、境界線をに設定しましたnulそうでsetBorder(null)ない場合は、2つの境界線が描画されます。1つはiText自体によって描画され、もう1つは作成するコンテンツレンダラーによって描画される境界線です。定義したコンテンツレンダラーを見てみましょう。

private class RoundedCornersCellRenderer extends CellRenderer {
    public RoundedCornersCellRenderer(Cell modelElement) {
        super(modelElement);
    }
 
    @Override
    public void drawBorder(DrawContext drawContext) {
        Rectangle rectangle = getOccupiedAreaBBox();
        float llx = rectangle.getX() + 1;
        float lly = rectangle.getY() + 1;
        float urx = rectangle.getX() + getOccupiedAreaBBox().getWidth() - 1;
        float ury = rectangle.getY() + getOccupiedAreaBBox().getHeight() - 1;
        PdfCanvas canvas = drawContext.getCanvas();
        float r = 4;
        float b = 0.4477f;
        canvas.moveTo(llx, lly).lineTo(urx, lly).lineTo(urx, ury - r)
                .curveTo(urx, ury - r * b, urx - r * b, ury, urx - r, ury)
                .lineTo(llx + r, ury)
                .curveTo(llx + r * b, ury, llx, ury - r * b, llx, ury - r)
                .lineTo(llx, lly).stroke();
        super.drawBorder(drawContext);
    }
}

    CellRendererクラスはBlockRendererクラスの特別な実装です。

BlockRendererやリストBlockElementsなどのクラスを使用できますParagraphこれらのレンダラークラスを使用するとdraw()、メソッドをオーバーライドしてカスタム関数を作成できます例:Paragraphカスタム背景を作成できます。方法CellRendererもありdrawBorder()ます。

drawBorder()メソッド    をオーバーライドして、上部に角が丸い長方形を描画します(6〜21行目)。getOccupiedAreaBBox()このメソッドはRectangleオブジェクトを返します。これを使用しBlockElementて境界ボックスを見つけることができます(8行目)。我々が使用しgetX()getY()getWidth()およびgetHeight()方法は、細胞の左下隅と右上隅の座標(ライン9~12)を定義します。
    drawContextこのパラメーターを使用すると、PdfCanvasインスタンスにアクセスできます(13行目)。一連の線と曲線(線14〜20)の形で境界線を描画します。この例は、高レベルのapi(でCell構成されるTable)と低レベルのapi(ニーズを満たす境界線を描画するためにほぼ手動でPDF構文を作成する)を緊密に統合して使用する方法を示しています

曲線を描くためのコードには数学の知識が必要ですが、ロケット科学ほど難しくはありません。最も一般的なタイプの境界はiTextにあるため、iTextエンジンで数式がどのように計算されるかを心配する必要はありません。

BlockRendererその実装クラス    についてはまだ多くの知識があります。別のチュートリアルで詳しく説明します。最後に、この章の最後に例を示し、作成された各ページに背景、ヘッダー(またはフッター)、透かし、ページ番号を自動的に追加する方法を示します。

イベントの処理(イベントの処理、背景、ページフッター、ウォーターマークの追加)

    多くの行があるドキュメントに追加するとTable、このテーブルはさまざまなページに分散される可能性があります。以下の図3に、に格納されてufo.csvいるUFOディレクトリのリストを示します。奇数ページの背景は緑と黄色、偶数ページの背景は青です。各ページにはヘッダー“THE TRUTH IS OUT THERE”があり、ページの実際のコンテンツに透かしがあり、“CONFIDENTIAL”各ページの下部が中央にあり、ページ番号があります

itext3-3

図3.背景色と透かしの繰り返し

    以下は、UFOディレクトリテーブルを生成するためのコードです。これは、第1章のテーブル表示コードと非常によく似ています。

PdfDocument pdf = new PdfDocument(new PdfWriter(dest));
pdf.addEventHandler(PdfDocumentEvent.END_PAGE, new MyEventHandler());
Document document = new Document(pdf);
Paragraph p = new Paragraph("List of reported UFO sightings in 20th century")
        .setTextAlignment(Property.TextAlignment.CENTER)
        .setFont(helveticaBold).setFontSize(14);
document.add(p);
Table table = new Table(new float[]{3, 5, 7, 4});
table.setWidthPercent(100);
BufferedReader br = new BufferedReader(new FileReader(DATA));
String line = br.readLine();
process(table, line, helveticaBold, true);
while ((line = br.readLine()) != null) {
    process(table, line, helvetica, false);
}
br.close();
document.add(table);
document.close();

    コードでは、次のProperty.TextAlignment.CENTERように、テキスト配置プロパティをに設定してcentered追加し、Paragraphufo.csvをループしてコンテンツを表示します。

public void process(Table table, String line, PdfFont font, boolean isHeader) {
        StringTokenizer tokenizer = new StringTokenizer(line, ";");
        while (tokenizer.hasMoreTokens()) {
            if (isHeader) {
                table.addHeaderCell(new Cell().add(new Paragraph(tokenizer.nextToken()).setFont(font)).setFontSize(9).setBorder(new SolidBorder(Color.BLACK, 0.5f)));
            } else {
                table.addCell(new Cell().add(new Paragraph(tokenizer.nextToken()).setFont(font)).setFontSize(9).setBorder(new SolidBorder(Color.BLACK, 0.5f)));
            }
        }
    }

    pdf.addEventHandler(PdfDocumentEvent.END_PAGE, new MyEventHandler());、見たことがありませんか?ここでPdfDocumentは、イベントハンドラー追加しますMyEventHandler。このMyEventHandler実現(実装)は、IEventHandler一方向のみインターフェイスですhandleEvent()このPdfDocumentEvent.END_PAGEタイプのイベントが発生するたびに、このタイプのイベントがトリガーされます。このタイプのイベントとは、iTextがページへのコンテンツの追加を終了したとき、新しいページが作成されたためか、最後のページが到着して完了したためかを示します。
    次にIEventHandler、実装を見てみましょう

protected class MyEventHandler implements IEventHandler {
    public void handleEvent(Event event) {
        PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
        PdfDocument pdfDoc = docEvent.getDocument();
        PdfPage page = docEvent.getPage();
        int pageNumber = pdfDoc.getPageNumber(page);
        Rectangle pageSize = page.getPageSize();
        PdfCanvas pdfCanvas = new PdfCanvas(
            page.newContentStreamBefore(), page.getResources(), pdfDoc);
 
        //Set background
        Color limeColor = new DeviceCmyk(0.208f, 0, 0.584f, 0);
        Color blueColor = new DeviceCmyk(0.445f, 0.0546f, 0, 0.0667f);
        pdfCanvas.saveState()
                .setFillColor(pageNumber % 2 == 1 ? limeColor : blueColor)
                .rectangle(pageSize.getLeft(), pageSize.getBottom(),
                    pageSize.getWidth(), pageSize.getHeight())
                .fill().restoreState();
        //Add header and footer
        pdfCanvas.beginText()
                .setFontAndSize(helvetica, 9)
                .moveText(pageSize.getWidth() / 2 - 60, pageSize.getTop() - 20)
                .showText("THE TRUTH IS OUT THERE")
                .moveText(60, -pageSize.getTop() + 30)
                .showText(String.valueOf(pageNumber))
                .endText();
        //Add watermark
        Canvas canvas = new Canvas(pdfCanvas, pdfDoc, page.getPageSize());
        canvas.setProperty(Property.FONT_COLOR, Color.WHITE);
        canvas.setProperty(Property.FONT_SIZE, 60);
        canvas.setProperty(Property.FONT, helveticaBold);
        canvas.showTextAligned(new Paragraph("CONFIDENTIAL"),
            298, 421, pdfDoc.getPageNumber(page),
            TextAlignment.CENTER, VerticalAlignment.MIDDLE, 45);
 
        pdfCanvas.release();
    }
}

    3行目では、最初eventにこの関数パラメーターをに変換しPdfDocumentEvent、次にそれgetDocument()を取得するために呼び出しPdfDocumentます。これらの変数を介して現在のページのページ番号を取得しPdfCanvasページサイズのインスタンスがあります

異なるパスまたはシェイプがオーバーラップする可能性があります。最初に描画されたパスまたはシェイプ(コンテンツストリームに保存されます)が最初にキャンバスに描画され、後で描画されるグラフィックが前のコンテンツをカバーします(オーバーラップする部分がある場合)。ページのコンテンツが完全にレンダリングされるたびに、背景、つまり各PdfPageトラッキングコンテンツフローの配列を追加する必要があります。インデックスをパラメータとして使用して、getContentStream()個々のコンテンツストリームを取得できます。最初と最後のコンテンツストリームを使用getFirstContentStream()してgetLastContentStream()取得できます。newContentStreamBefore()andnewContentStreamAfter()メソッドを使用して新しいコンテンツストリームを作成することもできます。

    8行目では、次の3つのパラメーターを使用して1つを作成しますPdfCanvas

  • page.newContentStreamBefore():ページのレンダリング後に不透明な長方形を描画すると、その長方形は既存のすべてのコンテンツをカバーします。背景と透かしがテーブルのコンテンツを覆わないように、ページコンテンツの前に追加されるコンテンツストリームにアクセスする必要があります。
  • page.getResources():すべてのコンテンツストリームには、フォントや画像などの外部リソースが必要です。ページに新しいコンテンツを追加する場合、iTextがページのリソースディレクトリにアクセスできることが重要です。
  • pdfDocPdfDocument新しく追加したコンテンツストリームをオブジェクトに追加できるように、オブジェクトを取得できる必要がありますPdfDocument

    次に、canvasオブジェクトに追加したもの:

  • 11〜18行目:定義limeColorblueColor2色。最初に現在の画像の状態を保存し(詳細は第2章の詳細な説明を参照)、次に奇数ページと偶数ページの数に応じて塗りつぶしペンの色を設定し、奇数ページを緑と黄色、偶数ページを青で、ページ全体のサイズの長方形を作成します。最後に、後でコンテンツの色に影響を与えることなく、前の画像の状態を復元します。
  • 20〜26行目:テキストの書き込みを開始し、フォントスタイルとフォントサイズを設定してから、ページの上部中央に移動し、書き込みを開始して"THE TRUTH IS OUT THERE"から、カーソルを下部に移動し、ページ番号を入力します。ヘッダーとフッターはOKです。
  • 28〜31行目:ここでCanvasタイプのインスタンスを作成しますcanvas高レベルが同じことを示すのと同じようにCanvasこれはPdfCanvas高レベルの表現です。ここでは、pdf構文(第2章の構文)を使用して、フォント、フォントサイズ、フォントの色、およびその他の属性を変更しません。メソッドを使用します。同様、デフォルトのフォントを変更するなど、ここでメソッドを使用できますこれは、オブジェクトのような、同じ目的のために使用することができるですDocumentPdfDocumentsetProperty()DocumentsetProperty()ParagraphListTable
  • 32〜34行目:showTextAligned()メソッドを使用してParagraph、中央に表示されたディスプレイを1つ追加します。座標は(298,421)で、45度傾斜しています。

    背景、ヘッダー、フッター、ウォーターマークを追加したら、PdfCanvasオブジェクトを解放します。
    この例では、2つの異なる方法を使用して、絶対位置にテキストを追加します。ヘッダーとフッターの描画に関しては、前の章で出会った低レベルのAPI(テキストの状態を含む)を使用しました。同様の方法を使用して、透かしを追加できます。ただし、テキストを回転してページの中央に配置する必要があるため、多くの数学的な計算が必要になります。テキストを必要な座標に配置する変換行列の計算を回避するために、便利な方法を使用しますshowTextAligned()。iTextを使用すると、ここで多くの操作を節約できます。

総括する

    この章の例を組み合わせると、前の章で説明した低レベルAPIの重要性を理解できます。この機能を基本的な構成要素と組み合わせて、カスタム機能を作成できます。これにより、セルオブジェクトのカスタム境界線が作成され、ページに背景色、ヘッダー、フッターが追加されます。最後に、透かしを追加すると、ここで使用できるPDF構文のすべての入力と出力を知る必要がないことがわかります。テキストを回転および中央揃えするための変換マトリックスの定義を処理する便利な方法。
    次の章では、コンテンツのさまざまな形式である注釈について学習します。インタラクティブな形式を作成できる特定の種類の注釈に焦点を当てます。今後ともよろしくお願いいたします。

PS:iText7シリーズには全部で7つの章があります。最初の数章の内容は比較的基本的で退屈です。次の数章でより実用的になります!これらの7つの章を書いた後、私たちは公式ウェブサイトでサンプルを更新し続けます!

PS2:これらの記事を翻訳するのは難しいので、特に学生のパーティーのために、それらが正しいかどうかを確認するために練習する必要があります。それが良いと思うなら、賞賛してください私へのあなたのサポートは、更新を続ける私の動機です!他のタイプミスを理解していただければ幸いです。

おすすめ

転載: blog.csdn.net/u012397189/article/details/77540464