3.1 Introduction to the concept of direct content
4 levels of pdf content
Level 1: Under text and graphics, PdfWriter.getDirectContentUnder()
Level 2: graphics layer, Chunk, Images background, PdfPCell boundary, etc.
Level 3: text layer, Chunks, Phrases, Paragraphs content, etc.
Level 4: On top of text and graphics, PdfWriter.getDirectContent()
import java.io.FileOutputStream; import java.io.IOException; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Element; import com.itextpdf.text.Font; import com.itextpdf.text.Image; import com.itextpdf.text.PageSize; import com.itextpdf.text.Paragraph; import com.itextpdf.text.Font.FontFamily; import com.itextpdf.text.pdf.BaseFont; import com.itextpdf.text.pdf.PdfContentByte; import com.itextpdf.text.pdf.PdfWriter; public class FestivalOpening { /** The resulting PDF. */ public static final String RESULT = "D:/data/iText/inAction/chapter03/festival_opening.pdf"; /** The movie poster. */ public static final String RESOURCE = "E:/study/PDF/SourceCodeiText/itext-book/book/resources/img/loa.jpg"; /** * Main method. * @param args no arguments needed * @throws DocumentException * @throwsIOException */ public static void main(String[] args) throws IOException, DocumentException { // step 1 Document document = new Document(PageSize.POSTCARD, 30, 30, 30, 30); // step 2 PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(RESULT)); // step 3 document.open(); // step 4 //Create and add a Paragraph Paragraph p = new Paragraph("Foobar Film Festival", new Font(FontFamily.HELVETICA, 22)); p.setAlignment(Element.ALIGN_CENTER); document.add(p); //Create and add an Image Image img = Image.getInstance(RESOURCE); img.setAbsolutePosition( (PageSize.POSTCARD.getWidth() - img.getScaledWidth()) / 2, (PageSize.POSTCARD.getHeight() - img.getScaledHeight()) / 2); document.add(img); // Now we go to the next page document.newPage(); document.add(p); document.add(img); // Add text on top of the image PdfContentByte over = writer.getDirectContent(); over.saveState(); float sinus = (float)Math.sin(Math.PI / 60); float cosinus = (float)Math.cos(Math.PI / 60); BaseFont bf = BaseFont.createFont(); over.beginText(); over.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE); over.setLineWidth(1.5f); over.setRGBColorStroke(0xFF, 0x00, 0x00); over.setRGBColorFill(0xFF, 0xFF, 0xFF); over.setFontAndSize(bf, 36); over.setTextMatrix(cosinus, sinus, -sinus, cosinus, 50, 324); over.showText("SOLD OUT"); over.endText(); over.restoreState(); //Add a rectangle under the image PdfContentByte under = writer.getDirectContentUnder(); under.saveState(); under.setRGBColorFill(0xFF, 0xD7, 0x00); under.rectangle(5, 5, PageSize.POSTCARD.getWidth() - 10, PageSize.POSTCARD.getHeight() - 10); under.fill(); under.restoreState(); // step 5 document.close(); } }
Graphics status
1. fill() fills the square and fills it according to setRGBColorFill(). There is no border by default.
2. fillStroke() fills the square and draws the border according to setLineWidth() and the default black
3. setRGBColorStroke() sets the border color
4. current transformation matrix (CTM) current transformation matrix
5. stroke() only draws borders
6. saveState/rrestoreState corresponds to push and pop.
import java.io.FileOutputStream; import java.io.IOException; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Rectangle; import com.itextpdf.text.pdf.PdfContentByte; import com.itextpdf.text.pdf.PdfWriter; public class GraphicsStateStack { /** The resulting PDF. */ public static final String RESULT = "D:/data/iText/inAction/chapter03/graphics_state.pdf"; /** * Main method. * * @param args no arguments needed * @throws DocumentException * @throwsIOException */ public static void main(String[] args) throws IOException, DocumentException { // step 1 Document document = new Document(new Rectangle(200, 120)); // step 2 PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(RESULT)); // step 3 document.open(); // step 4 PdfContentByte canvas = writer.getDirectContent(); // state 1: canvas.setRGBColorFill(0xFF, 0x45, 0x00); // fill a rectangle in state 1 canvas.rectangle(10, 10, 60, 60); canvas.fill(); canvas.saveState(); // state 2; canvas.setLineWidth(3); canvas.setRGBColorFill(0x8B, 0x00, 0x00); // fill and stroke a rectangle in state 2 canvas.rectangle(40, 20, 60, 60); canvas.fillStroke(); canvas.saveState(); // state 3: canvas.concatCTM(1, 0, 0.1f, 1, 0, 0); canvas.setRGBColorStroke(0xFF, 0x45, 0x00); canvas.setRGBColorFill(0xFF, 0xD7, 0x00); // fill and stroke a rectangle in state 3 canvas.rectangle(70, 30, 60, 60); canvas.fillStroke(); canvas.restoreState(); //stroke a rectangle in state 2 canvas.rectangle(100, 40, 60, 60); canvas.stroke(); canvas.restoreState(); // fill and stroke a rectangle in state 1 canvas.rectangle(130, 50, 60, 60); canvas.fillStroke(); // step 5 document.close(); } }
Text status
1. showText(), set the displayed text
2. setTextRenderingMode() sets the border mode, setLineWidth() sets the border width, and the default is no border
3. setFontAndSize() sets the font and size
4. setTextMatrix() sets the font matrix
5. setRGBColorStoke() sets the border color
6. setRGBColorFill() sets the fill color
3.2 Add Text based on absolute positioning
PdfContentByte.showTextAligned()
Measure string
Chunk c; String foobar = "Foobar Film Festival"; // Measuring a String in Helvetica Font helvetica = new Font(FontFamily.HELVETICA, 12); BaseFont bf_helv = helvetica.getCalculatedBaseFont(false); float width_helv = bf_helv.getWidthPoint(foobar, 12); c = new Chunk(foobar + ": " + width_helv, helvetica); document.add(new Paragraph(c)); document.add(new Paragraph(String.format("Chunk width: %f", c.getWidthPoint()))); // Measuring a String in Times BaseFont bf_times = BaseFont.createFont( "c:/windows/fonts/times.ttf", BaseFont.WINANSI, BaseFont.EMBEDDED); Font times = new Font(bf_times, 12); float width_times = bf_times.getWidthPoint(foobar, 12); c = new Chunk(foobar + ": " + width_times, times); document.add(new Paragraph(c)); document.add(new Paragraph(String.format("Chunk width: %f", c.getWidthPoint()))); document.add(Chunk.NEWLINE);
String upward and downward space
Note: font size is not the height of a character, but the vertical space of a line
// Ascent and descent of the String document.add(new Paragraph("Ascent Helvetica: " + bf_helv.getAscentPoint(foobar, 12))); document.add(new Paragraph("Ascent Times: " + bf_times.getAscentPoint(foobar, 12))); document.add(new Paragraph("Descent Helvetica: " + bf_helv.getDescentPoint(foobar, 12))); document.add(new Paragraph("Descent Times: " + bf_times.getDescentPoint(foobar, 12)));
String positioning
PdfContentByte canvas = writer.getDirectContent(); // Adding text with PdfContentByte.showTextAligned() canvas.beginText(); canvas.setFontAndSize(bf_helv, 12); canvas.showTextAligned(Element.ALIGN_LEFT, foobar, 400, 788, 0); canvas.showTextAligned(Element.ALIGN_RIGHT, foobar, 400, 752, 0); canvas.showTextAligned(Element.ALIGN_CENTER, foobar, 400, 716, 0); canvas.showTextAligned(Element.ALIGN_CENTER, foobar, 400, 680, 30); canvas.showTextAlignedKerned(Element.ALIGN_LEFT, foobar, 400, 644, 0); canvas.endText();
Kerning (KERNING)
// Kerned text width_helv = bf_helv.getWidthPointKerned(foobar, 12); c = new Chunk(foobar + ": " + width_helv, helvetica); document.add(new Paragraph(c));
ColumnText.showTextAligned()
// Adding text with ColumnText.showTextAligned() Phrase phrase = new Phrase(foobar, times); ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT, phrase, 200, 572, 0); ColumnText.showTextAligned(canvas, Element.ALIGN_RIGHT, phrase, 200, 536, 0); ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER, phrase, 200, 500, 0); ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER, phrase, 200, 464, 30); ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER, phrase, 200, 428, -30);
Phrase positioning
A phrase can contain a series of chunks. Add a phrase (Phrase) to absolute positioning, and you can easily select the font, font size and color. iText will automatically calculate the spacing of each chunk (Chunk) in the phrase (Phrase).
Chunks: Scaling, Skewing, Rendering Mode
1. setScaling(float scale) sets the scaling ratio
2. setSkew(float arg1, float arg2) sets the tilt, arg1: the angle of the baseline, arg2: the angle of the character to the baseline
3. setTextRenderMode() sets the text rendering mode
PdfContentByte.TEXT_RENDER_MODE_FILL, default filled character shape, no border
PdfContentByte.TEXT_RENDER_MODE_STROKE, no padding characters, only borders
PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE, fill character, with border
PdfContentByte.TEXT_RENDER_MODE_INVISIBLE, text is not visible
// Chunk attributes c = new Chunk(foobar, times); c.setHorizontalScaling(0.5f); phrase = new Phrase(c); ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT, phrase, 400, 572, 0); c = new Chunk(foobar, times); c.setSkew(15, 15); phrase = new Phrase(c); ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT, phrase, 400, 536, 0); c = new Chunk(foobar, times); c.setSkew(0, 25); phrase = new Phrase(c); ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT, phrase, 400, 500, 0); c = new Chunk(foobar, times); c.setTextRenderMode(PdfContentByte.TEXT_RENDER_MODE_STROKE, 0.1f, BaseColor.RED); phrase = new Phrase(c); ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT, phrase, 400, 464, 0); c = new Chunk(foobar, times); c.setTextRenderMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE, 1, null); phrase = new Phrase(c); ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT, phrase, 400, 428, -0);
Complete example
import java.io.FileOutputStream; import java.io.IOException; import com.itextpdf.text.Chunk; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Element; import com.itextpdf.text.Font; import com.itextpdf.text.Paragraph; import com.itextpdf.text.Phrase; import com.itextpdf.text.Font.FontFamily; import com.itextpdf.text.pdf.BaseFont; import com.itextpdf.text.pdf.ColumnText; import com.itextpdf.text.pdf.PdfContentByte; import com.itextpdf.text.pdf.PdfWriter; import com.itextpdf.text.BaseColor; public class FoobarFilmFestival { public static final String RESULT = "D:/data/iText/inAction/chapter03/foobar_film_festival.pdf"; /** * Main method. * * @param args no arguments needed * @throws DocumentException * @throwsIOException */ public static void main(String[] args) throws IOException, DocumentException { // step 1 Document document = new Document(); // step 2 PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(RESULT)); // step 3 document.open(); // step 4 Chunk c; String foobar = "Foobar Film Festival"; // Measuring a String in Helvetica Font helvetica = new Font(FontFamily.HELVETICA, 12); BaseFont bf_helv = helvetica.getCalculatedBaseFont(false); float width_helv = bf_helv.getWidthPoint(foobar, 12); c = new Chunk(foobar + ": " + width_helv, helvetica); document.add(new Paragraph(c)); document.add(new Paragraph(String.format("Chunk width: %f", c.getWidthPoint()))); // Measuring a String in Times BaseFont bf_times = BaseFont.createFont( "c:/windows/fonts/times.ttf", BaseFont.WINANSI, BaseFont.EMBEDDED); Font times = new Font(bf_times, 12); float width_times = bf_times.getWidthPoint(foobar, 12); c = new Chunk(foobar + ": " + width_times, times); document.add(new Paragraph(c)); document.add(new Paragraph(String.format("Chunk width: %f", c.getWidthPoint()))); document.add(Chunk.NEWLINE); // Ascent and descent of the String document.add(new Paragraph("Ascent Helvetica: " + bf_helv.getAscentPoint(foobar, 12))); document.add(new Paragraph("Ascent Times: " + bf_times.getAscentPoint(foobar, 12))); document.add(new Paragraph("Descent Helvetica: " + bf_helv.getDescentPoint(foobar, 12))); document.add(new Paragraph("Descent Times: " + bf_times.getDescentPoint(foobar, 12))); document.add(Chunk.NEWLINE); // Kerned text width_helv = bf_helv.getWidthPointKerned(foobar, 12); c = new Chunk(foobar + ": " + width_helv, helvetica); document.add(new Paragraph(c)); // Drawing lines to see where the text is added PdfContentByte canvas = writer.getDirectContent(); canvas.saveState(); canvas.setLineWidth(0.05f); canvas.moveTo(400, 806); canvas.lineTo(400, 626); canvas.moveTo(508.7f, 806); canvas.lineTo(508.7f, 626); canvas.moveTo(280, 788); canvas.lineTo(520, 788); canvas.moveTo(280, 752); canvas.lineTo(520, 752); canvas.moveTo(280, 716); canvas.lineTo(520, 716); canvas.moveTo(280, 680); canvas.lineTo(520, 680); canvas.moveTo(280, 644); canvas.lineTo(520, 644); canvas.stroke(); canvas.restoreState(); // Adding text with PdfContentByte.showTextAligned() canvas.beginText(); canvas.setFontAndSize(bf_helv, 12); canvas.showTextAligned(Element.ALIGN_LEFT, foobar, 400, 788, 0); canvas.showTextAligned(Element.ALIGN_RIGHT, foobar, 400, 752, 0); canvas.showTextAligned(Element.ALIGN_CENTER, foobar, 400, 716, 0); canvas.showTextAligned(Element.ALIGN_CENTER, foobar, 400, 680, 30); canvas.showTextAlignedKerned(Element.ALIGN_LEFT, foobar, 400, 644, 0); canvas.endText(); // More lines to see where the text is added canvas.saveState(); canvas.setLineWidth(0.05f); canvas.moveTo(200, 590); canvas.lineTo(200, 410); canvas.moveTo(400, 590); canvas.lineTo(400, 410); canvas.moveTo(80, 572); canvas.lineTo(520, 572); canvas.moveTo(80, 536); canvas.lineTo(520, 536); canvas.moveTo(80, 500); canvas.lineTo(520, 500); canvas.moveTo(80, 464); canvas.lineTo(520, 464); canvas.moveTo(80, 428); canvas.lineTo(520, 428); canvas.stroke(); canvas.restoreState(); // Adding text with ColumnText.showTextAligned() Phrase phrase = new Phrase(foobar, times); ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT, phrase, 200, 572, 0); ColumnText.showTextAligned(canvas, Element.ALIGN_RIGHT, phrase, 200, 536, 0); ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER, phrase, 200, 500, 0); ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER, phrase, 200, 464, 30); ColumnText.showTextAligned(canvas, Element.ALIGN_CENTER, phrase, 200, 428, -30); // Chunk attributes c = new Chunk(foobar, times); c.setHorizontalScaling(0.5f); phrase = new Phrase(c); ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT, phrase, 400, 572, 0); c = new Chunk(foobar, times); c.setSkew(15, 15); phrase = new Phrase(c); ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT, phrase, 400, 536, 0); c = new Chunk(foobar, times); c.setSkew(0, 25); phrase = new Phrase(c); ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT, phrase, 400, 500, 0); c = new Chunk(foobar, times); c.setTextRenderMode(PdfContentByte.TEXT_RENDER_MODE_STROKE, 0.1f, BaseColor.RED); phrase = new Phrase(c); ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT, phrase, 400, 464, 0); c = new Chunk(foobar, times); c.setTextRenderMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE, 1, null); phrase = new Phrase(c); ColumnText.showTextAligned(canvas, Element.ALIGN_LEFT, phrase, 400, 428, -0); // step 5 document.close(); } }
3.3 Using ColumnText object
Using ColumnText in text mode
Using ColumnText in composite mode
3.4 Create reusable content
How to repeatedly add an image to a document? The bytes of PDF images are stored in a separate area, and the page contains a reference to the image, pointing to an external object (XObject)
Image XObjects
Add images to the top level
The top image covers the text: Foobar Film Festival
import java.io.FileOutputStream; import java.io.IOException; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Element; import com.itextpdf.text.Font; import com.itextpdf.text.Image; import com.itextpdf.text.PageSize; import com.itextpdf.text.Paragraph; import com.itextpdf.text.Font.FontFamily; import com.itextpdf.text.pdf.PdfWriter; public class ImageDirect { /** The resulting PDF. */ public static final String RESULT = "D:/data/iText/inAction/chapter03/image_direct.pdf"; /** The movie poster. */ public static final String RESOURCE = "E:/study/PDF/SourceCodeiText/itext-book/book/resources/img/loa.jpg"; public static void main(String[] args) throws IOException, DocumentException { // step 1 Document document = new Document(PageSize.POSTCARD, 30, 30, 30, 30); // step 2 PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(RESULT)); writer.setCompressionLevel(0); // step 3 document.open(); // step 4 Image img = Image.getInstance(RESOURCE); img.setAbsolutePosition((PageSize.POSTCARD.getWidth() - img.getScaledWidth()) / 2, (PageSize.POSTCARD.getHeight() - img.getScaledHeight()) / 2); writer.getDirectContent().addImage(img); Paragraph p = new Paragraph("Foobar Film Festival", new Font(FontFamily.HELVETICA, 22)); p.setAlignment(Element.ALIGN_CENTER); document.add(p); // step 5 document.close(); } }
Tilt image
import java.io.FileOutputStream; import java.io.IOException; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Image; import com.itextpdf.text.PageSize; import com.itextpdf.text.pdf.PdfWriter; public class ImageSkew { /** The resulting PDF. */ public static final String RESULT = "D:/data/iText/inAction/chapter03/image_skew.pdf"; /** The movie poster. */ public static final String RESOURCE = "E:/study/PDF/SourceCodeiText/itext-book/book/resources/img/loa.jpg"; /** * Main method. * * @param args no arguments needed * @throws DocumentException * @throwsIOException */ public static void main(String[] args) throws IOException, DocumentException { // step 1 Document document = new Document(PageSize.POSTCARD.rotate()); // step 2 PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(RESULT)); writer.setCompressionLevel(0); // step 3 document.open(); // step 4 Image img = Image.getInstance(RESOURCE); // Add the image to the upper layer writer.getDirectContent().addImage(img, img.getWidth(), 0, 0.35f * img.getHeight(), 0.65f * img.getHeight(), 30, 30); // step 5 document.close(); } }
Embedded images
PdfWriter.getDirectContent().addImage(img, true)
import java.io.FileOutputStream; import java.io.IOException; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentException; import com.itextpdf.text.Image; import com.itextpdf.text.PageSize; import com.itextpdf.text.pdf.PdfWriter; public class ImageInline { /** The resulting PDF. */ public static final String RESULT = "D:/data/iText/inAction/chapter03/image_inline.pdf"; /** The movie poster. */ public static final String RESOURCE = "E:/study/PDF/SourceCodeiText/itext-book/book/resources/img/loa.jpg"; /** * Main method. * * @param args no arguments needed * @throws DocumentException * @throwsIOException */ public static void main(String[] args) throws IOException, DocumentException { // step 1 Document document = new Document(PageSize.POSTCARD, 30, 30, 30, 30); // step 2 PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(RESULT)); writer.setCompressionLevel(0); // step 3 document.open(); // step 4 Image img = Image.getInstance(RESOURCE); img.setAbsolutePosition( (PageSize.POSTCARD.getWidth() - img.getScaledWidth()) / 2, (PageSize.POSTCARD.getHeight() - img.getScaledHeight()) / 2); writer.getDirectContent().addImage(img, true); // step 5 document.close(); } }
PDFTemplate object
PdfTemplate: Another name for XObject
PdfTemplate is a PDF content stream, which is a sequence of any graphic objects. PdfTemplate extends PdfContentByte and inherits all its methods.
PdfContentByte canvas = writer.getDirectContent(); //Create the XObject PdfTemplate celluloid = canvas.createTemplate(595, 84.2f); celluloid.rectangle(8, 8, 579, 68); for (float f = 8.25f; f < 581; f + = 6.5f) { celluloid.roundRectangle(f, 8.5f, 6, 3, 1.5f); celluloid.roundRectangle(f, 72.5f, 6, 3, 1.5f); } celluloid.setGrayFill(0.1f); celluloid.eoFill(); // Write the XObject to the OutputStream writer.releaseTemplate(celluloid); // Add the XObject 10 times for (int i = 0; i < 10; i + + ) { canvas.addTemplate(celluloid, 0, i * 84.2f); } // Go to the next page document.newPage(); // Add the XObject 10 times for (int i = 0; i < 10; i + + ) { canvas.addTemplate(celluloid, 0, i * 84.2f); }