Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* $Id: PdfWriter.java 5477 2012-10-12 13:54:05Z eugenemark $
*
* This file is part of the iText (R) project.
* Copyright (c) 1998-2012 1T3XT BVBA
* Authors: Bruno Lowagie, Paulo Soares, et al.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3
* as published by the Free Software Foundation with the addition of the
* following permission added to Section 15 as permitted in Section 7(a):
* FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT,
* 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, see http://www.gnu.org/licenses or write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA, 02110-1301 USA, or download the license from the following URL:
* http://itextpdf.com/terms-of-use/
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License.
*
* In accordance with Section 7(b) of the GNU Affero General Public License,
* a covered work must retain the producer line in every PDF that is created
* or manipulated using iText.
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial activities involving the iText software without
* disclosing the source code of your own applications.
* These activities include: offering paid services to customers as an ASP,
* serving PDFs on the fly in a web application, shipping iText with a closed
* source product.
*
* For more information, please contact iText Software Corp. at this
* address: [email protected]
*/
package com.itextpdf.text.pdf;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.cert.Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.DocListener;
import com.itextpdf.text.DocWriter;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.ExceptionConverter;
import com.itextpdf.text.Image;
import com.itextpdf.text.ImgJBIG2;
import com.itextpdf.text.ImgWMF;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.Version;
import com.itextpdf.text.error_messages.MessageLocalization;
import com.itextpdf.text.pdf.collection.PdfCollection;
import com.itextpdf.text.pdf.events.PdfPageEventForwarder;
import com.itextpdf.text.pdf.interfaces.*;
import com.itextpdf.text.pdf.internal.PdfIsoKeys;
import com.itextpdf.text.pdf.internal.PdfVersionImp;
import com.itextpdf.text.pdf.internal.PdfXConformanceImp;
import com.itextpdf.text.xml.xmp.XmpWriter;
/**
* A DocWriter class for PDF.
*
* When this PdfWriter is added
* to a certain PdfDocument, the PDF representation of every Element
* added to this Document will be written to the outputstream.
*/
public class PdfWriter extends DocWriter implements
PdfViewerPreferences,
PdfEncryptionSettings,
PdfVersion,
PdfDocumentActions,
PdfPageActions,
PdfIsoConformance,
PdfRunDirection,
PdfAnnotations {
/**
* The highest generation number possible.
* @since iText 2.1.6
*/
public static final int GENERATION_MAX = 65535;
// INNER CLASSES
/**
* This class generates the structure of a PDF document.
*
* This class covers the third section of Chapter 5 in the 'Portable Document Format
* Reference Manual version 1.3' (page 55-60). It contains the body of a PDF document
* (section 5.14) and it can also generate a Cross-reference Table (section 5.15).
*
* @see PdfWriter
* @see PdfObject
* @see PdfIndirectObject
*/
public static class PdfBody {
// inner classes
/**
* PdfCrossReference is an entry in the PDF Cross-Reference table.
*/
static public class PdfCrossReference implements Comparable {
// membervariables
private final int type;
/** Byte offset in the PDF file. */
private final long offset;
private final int refnum;
/** generation of the object. */
private final int generation;
// constructors
/**
* Constructs a cross-reference element for a PdfIndirectObject.
* @param refnum
* @param offset byte offset of the object
* @param generation generation number of the object
*/
public PdfCrossReference(final int refnum, final long offset, final int generation) {
type = 0;
this.offset = offset;
this.refnum = refnum;
this.generation = generation;
}
/**
* Constructs a cross-reference element for a PdfIndirectObject.
* @param refnum
* @param offset byte offset of the object
*/
public PdfCrossReference(final int refnum, final long offset) {
type = 1;
this.offset = offset;
this.refnum = refnum;
this.generation = 0;
}
public PdfCrossReference(final int type, final int refnum, final long offset, final int generation) {
this.type = type;
this.offset = offset;
this.refnum = refnum;
this.generation = generation;
}
public int getRefnum() {
return refnum;
}
/**
* Returns the PDF representation of this PdfObject.
* @param os
* @throws IOException
*/
public void toPdf(final OutputStream os) throws IOException {
StringBuffer off = new StringBuffer("0000000000").append(offset);
off.delete(0, off.length() - 10);
StringBuffer gen = new StringBuffer("00000").append(generation);
gen.delete(0, gen.length() - 5);
off.append(' ').append(gen).append(generation == GENERATION_MAX ? " f \n" : " n \n");
os.write(getISOBytes(off.toString()));
}
/**
* Writes PDF syntax to the OutputStream
* @param midSize
* @param os
* @throws IOException
*/
public void toPdf(int midSize, final OutputStream os) throws IOException {
os.write((byte)type);
while (--midSize >= 0)
os.write((byte)(offset >>> 8 * midSize & 0xff));
os.write((byte)(generation >>> 8 & 0xff));
os.write((byte)(generation & 0xff));
}
/**
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(final PdfCrossReference other) {
return refnum < other.refnum ? -1 : refnum==other.refnum ? 0 : 1;
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(final Object obj) {
if (obj instanceof PdfCrossReference) {
PdfCrossReference other = (PdfCrossReference)obj;
return refnum == other.refnum;
}
else
return false;
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return refnum;
}
}
private static final int OBJSINSTREAM = 200;
// membervariables
/** array containing the cross-reference table of the normal objects. */
protected final TreeSet xrefs;
protected int refnum;
/** the current byte position in the body. */
protected long position;
protected final PdfWriter writer;
protected ByteBuffer index;
protected ByteBuffer streamObjects;
protected int currentObjNum;
protected int numObj = 0;
// constructors
/**
* Constructs a new PdfBody.
* @param writer
*/
protected PdfBody(final PdfWriter writer) {
xrefs = new TreeSet();
xrefs.add(new PdfCrossReference(0, 0, GENERATION_MAX));
position = writer.getOs().getCounter();
refnum = 1;
this.writer = writer;
}
// methods
void setRefnum(final int refnum) {
this.refnum = refnum;
}
protected PdfWriter.PdfBody.PdfCrossReference addToObjStm(final PdfObject obj, final int nObj) throws IOException {
if (numObj >= OBJSINSTREAM)
flushObjStm();
if (index == null) {
index = new ByteBuffer();
streamObjects = new ByteBuffer();
currentObjNum = getIndirectReferenceNumber();
numObj = 0;
}
int p = streamObjects.size();
int idx = numObj++;
PdfEncryption enc = writer.crypto;
writer.crypto = null;
obj.toPdf(writer, streamObjects);
writer.crypto = enc;
streamObjects.append(' ');
index.append(nObj).append(' ').append(p).append(' ');
return new PdfWriter.PdfBody.PdfCrossReference(2, nObj, currentObjNum, idx);
}
public void flushObjStm() throws IOException {
if (numObj == 0)
return;
int first = index.size();
index.append(streamObjects);
PdfStream stream = new PdfStream(index.toByteArray());
stream.flateCompress(writer.getCompressionLevel());
stream.put(PdfName.TYPE, PdfName.OBJSTM);
stream.put(PdfName.N, new PdfNumber(numObj));
stream.put(PdfName.FIRST, new PdfNumber(first));
add(stream, currentObjNum);
index = null;
streamObjects = null;
numObj = 0;
}
/**
* Adds a PdfObject to the body.
*
* This methods creates a PdfIndirectObject with a
* certain number, containing the given PdfObject.
* It also adds a PdfCrossReference for this object
* to an ArrayList that will be used to build the
* Cross-reference Table.
*
* @param object a PdfObject
* @return a PdfIndirectObject
* @throws IOException
*/
PdfIndirectObject add(final PdfObject object) throws IOException {
return add(object, getIndirectReferenceNumber());
}
PdfIndirectObject add(final PdfObject object, final boolean inObjStm) throws IOException {
return add(object, getIndirectReferenceNumber(), inObjStm);
}
/**
* Gets a PdfIndirectReference for an object that will be created in the future.
* @return a PdfIndirectReference
*/
public PdfIndirectReference getPdfIndirectReference() {
return new PdfIndirectReference(0, getIndirectReferenceNumber());
}
protected int getIndirectReferenceNumber() {
int n = refnum++;
xrefs.add(new PdfCrossReference(n, 0, GENERATION_MAX));
return n;
}
/**
* Adds a PdfObject to the body given an already existing
* PdfIndirectReference.
*
* This methods creates a PdfIndirectObject with the number given by
* ref, containing the given PdfObject.
* It also adds a PdfCrossReference for this object
* to an ArrayList that will be used to build the
* Cross-reference Table.
*
* @param object a PdfObject
* @param ref a PdfIndirectReference
* @return a PdfIndirectObject
* @throws IOException
*/
PdfIndirectObject add(final PdfObject object, final PdfIndirectReference ref) throws IOException {
return add(object, ref.getNumber());
}
PdfIndirectObject add(final PdfObject object, final PdfIndirectReference ref, final boolean inObjStm) throws IOException {
return add(object, ref.getNumber(), inObjStm);
}
PdfIndirectObject add(final PdfObject object, final int refNumber) throws IOException {
return add(object, refNumber, true); // to false
}
protected PdfIndirectObject add(final PdfObject object, final int refNumber, final boolean inObjStm) throws IOException {
if (inObjStm && object.canBeInObjStm() && writer.isFullCompression()) {
PdfCrossReference pxref = addToObjStm(object, refNumber);
PdfIndirectObject indirect = new PdfIndirectObject(refNumber, object, writer);
if (!xrefs.add(pxref)) {
xrefs.remove(pxref);
xrefs.add(pxref);
}
return indirect;
}
else {
PdfIndirectObject indirect = new PdfIndirectObject(refNumber, object, writer);
PdfCrossReference pxref = new PdfCrossReference(refNumber, position);
if (!xrefs.add(pxref)) {
xrefs.remove(pxref);
xrefs.add(pxref);
}
indirect.writeTo(writer.getOs());
position = writer.getOs().getCounter();
return indirect;
}
}
/**
* Returns the offset of the Cross-Reference table.
*
* @return an offset
*/
public long offset() {
return position;
}
/**
* Returns the total number of objects contained in the CrossReferenceTable of this Body.
*
* @return a number of objects
*/
public int size() {
return Math.max(xrefs.last().getRefnum() + 1, refnum);
}
/**
* Returns the CrossReferenceTable of the Body.
* @param os
* @param root
* @param info
* @param encryption
* @param fileID
* @param prevxref
* @throws IOException
*/
public void writeCrossReferenceTable(final OutputStream os, final PdfIndirectReference root, final PdfIndirectReference info, final PdfIndirectReference encryption, final PdfObject fileID, final long prevxref) throws IOException {
int refNumber = 0;
if (writer.isFullCompression()) {
flushObjStm();
refNumber = getIndirectReferenceNumber();
xrefs.add(new PdfCrossReference(refNumber, position));
}
PdfCrossReference entry = xrefs.first();
int first = entry.getRefnum();
int len = 0;
ArrayList sections = new ArrayList();
for (PdfCrossReference pdfCrossReference : xrefs) {
entry = pdfCrossReference;
if (first + len == entry.getRefnum())
++len;
else {
sections.add(Integer.valueOf(first));
sections.add(Integer.valueOf(len));
first = entry.getRefnum();
len = 1;
}
}
sections.add(Integer.valueOf(first));
sections.add(Integer.valueOf(len));
if (writer.isFullCompression()) {
int mid = 5;
long mask = 0xff00000000L;
for (; mid > 1; --mid) {
if ((mask & position) != 0)
break;
mask >>>= 8;
}
ByteBuffer buf = new ByteBuffer();
for (Object element : xrefs) {
entry = (PdfCrossReference) element;
entry.toPdf(mid, buf);
}
PdfStream xr = new PdfStream(buf.toByteArray());
buf = null;
xr.flateCompress(writer.getCompressionLevel());
xr.put(PdfName.SIZE, new PdfNumber(size()));
xr.put(PdfName.ROOT, root);
if (info != null) {
xr.put(PdfName.INFO, info);
}
if (encryption != null)
xr.put(PdfName.ENCRYPT, encryption);
if (fileID != null)
xr.put(PdfName.ID, fileID);
xr.put(PdfName.W, new PdfArray(new int[]{1, mid, 2}));
xr.put(PdfName.TYPE, PdfName.XREF);
PdfArray idx = new PdfArray();
for (int k = 0; k < sections.size(); ++k)
idx.add(new PdfNumber(sections.get(k).intValue()));
xr.put(PdfName.INDEX, idx);
if (prevxref > 0)
xr.put(PdfName.PREV, new PdfNumber(prevxref));
PdfEncryption enc = writer.crypto;
writer.crypto = null;
PdfIndirectObject indirect = new PdfIndirectObject(refNumber, xr, writer);
indirect.writeTo(writer.getOs());
writer.crypto = enc;
}
else {
os.write(getISOBytes("xref\n"));
Iterator i = xrefs.iterator();
for (int k = 0; k < sections.size(); k += 2) {
first = sections.get(k).intValue();
len = sections.get(k + 1).intValue();
os.write(getISOBytes(String.valueOf(first)));
os.write(getISOBytes(" "));
os.write(getISOBytes(String.valueOf(len)));
os.write('\n');
while (len-- > 0) {
entry = i.next();
entry.toPdf(os);
}
}
}
}
}
/**
* PdfTrailer is the PDF Trailer object.
*
* This object is described in the 'Portable Document Format Reference Manual version 1.3'
* section 5.16 (page 59-60).
*/
static public class PdfTrailer extends PdfDictionary {
// membervariables
long offset;
// constructors
/**
* Constructs a PDF-Trailer.
*
* @param size the number of entries in the PdfCrossReferenceTable
* @param offset offset of the PdfCrossReferenceTable
* @param root an indirect reference to the root of the PDF document
* @param info an indirect reference to the info object of the PDF document
* @param encryption
* @param fileID
* @param prevxref
*/
public PdfTrailer(final int size, final long offset, final PdfIndirectReference root, final PdfIndirectReference info, final PdfIndirectReference encryption, final PdfObject fileID, final long prevxref) {
this.offset = offset;
put(PdfName.SIZE, new PdfNumber(size));
put(PdfName.ROOT, root);
if (info != null) {
put(PdfName.INFO, info);
}
if (encryption != null)
put(PdfName.ENCRYPT, encryption);
if (fileID != null)
put(PdfName.ID, fileID);
if (prevxref > 0)
put(PdfName.PREV, new PdfNumber(prevxref));
}
/**
* Returns the PDF representation of this PdfObject.
* @param writer
* @param os
* @throws IOException
*/
@Override
public void toPdf(final PdfWriter writer, final OutputStream os) throws IOException {
os.write(getISOBytes("trailer\n"));
super.toPdf(null, os);
os.write('\n');
writeKeyInfo(os);
os.write(getISOBytes("startxref\n"));
os.write(getISOBytes(String.valueOf(offset)));
os.write(getISOBytes("\n%%EOF\n"));
}
}
// ESSENTIALS
// Construct a PdfWriter instance
/**
* Constructs a PdfWriter.
*/
protected PdfWriter() {
}
/**
* Constructs a PdfWriter.
*
* Remark: a PdfWriter can only be constructed by calling the method
* getInstance(Document document, OutputStream os).
*
* @param document The PdfDocument that has to be written
* @param os The OutputStream the writer has to write to.
*/
protected PdfWriter(final PdfDocument document, final OutputStream os) {
super(document, os);
pdf = document;
directContent = new PdfContentByte(this);
directContentUnder = new PdfContentByte(this);
}
/**
* Use this method to get an instance of the PdfWriter.
*
* @param document The Document that has to be written
* @param os The OutputStream the writer has to write to.
* @return a new PdfWriter
*
* @throws DocumentException on error
*/
public static PdfWriter getInstance(final Document document, final OutputStream os)
throws DocumentException {
PdfDocument pdf = new PdfDocument();
document.addDocListener(pdf);
PdfWriter writer = new PdfWriter(pdf, os);
pdf.addWriter(writer);
return writer;
}
/**
* Use this method to get an instance of the PdfWriter.
*
* @return a new PdfWriter
* @param document The Document that has to be written
* @param os The OutputStream the writer has to write to.
* @param listener A DocListener to pass to the PdfDocument.
* @throws DocumentException on error
*/
public static PdfWriter getInstance(final Document document, final OutputStream os, final DocListener listener)
throws DocumentException {
PdfDocument pdf = new PdfDocument();
pdf.addDocListener(listener);
document.addDocListener(pdf);
PdfWriter writer = new PdfWriter(pdf, os);
pdf.addWriter(writer);
return writer;
}
// the PdfDocument instance
/** the pdfdocument object. */
protected PdfDocument pdf;
/**
* Gets the PdfDocument associated with this writer.
* @return the PdfDocument
*/
PdfDocument getPdfDocument() {
return pdf;
}
/**
* Use this method to get the info dictionary if you want to
* change it directly (add keys and values to the info dictionary).
* @return the info dictionary
*/
public PdfDictionary getInfo() {
return pdf.getInfo();
}
/**
* Use this method to get the current vertical page position.
* @param ensureNewLine Tells whether a new line shall be enforced. This may cause side effects
* for elements that do not terminate the lines they've started because those lines will get
* terminated.
* @return The current vertical page position.
*/
public float getVerticalPosition(final boolean ensureNewLine) {
return pdf.getVerticalPosition(ensureNewLine);
}
/**
* Sets the initial leading for the PDF document.
* This has to be done before the document is opened.
* @param leading the initial leading
* @since 2.1.6
* @throws DocumentException if you try setting the leading after the document was opened.
*/
public void setInitialLeading(final float leading) throws DocumentException {
if (open)
throw new DocumentException(MessageLocalization.getComposedMessage("you.can.t.set.the.initial.leading.if.the.document.is.already.open"));
pdf.setLeading(leading);
}
// the PdfDirectContentByte instances
/*
* You should see Direct Content as a canvas on which you can draw
* graphics and text. One canvas goes on top of the page (getDirectContent),
* the other goes underneath (getDirectContentUnder).
* You can always the same object throughout your document,
* even if you have moved to a new page. Whatever you add on
* the canvas will be displayed on top or under the current page.
*/
/** The direct content in this document. */
protected PdfContentByte directContent;
/** The direct content under in this document. */
protected PdfContentByte directContentUnder;
/**
* Use this method to get the direct content for this document.
* There is only one direct content, multiple calls to this method
* will allways retrieve the same object.
* @return the direct content
*/
public PdfContentByte getDirectContent() {
if (!open)
throw new RuntimeException(MessageLocalization.getComposedMessage("the.document.is.not.open"));
return directContent;
}
/**
* Use this method to get the direct content under for this document.
* There is only one direct content, multiple calls to this method
* will always retrieve the same object.
* @return the direct content
*/
public PdfContentByte getDirectContentUnder() {
if (!open)
throw new RuntimeException(MessageLocalization.getComposedMessage("the.document.is.not.open"));
return directContentUnder;
}
/**
* Resets all the direct contents to empty.
* This happens when a new page is started.
*/
void resetContent() {
directContent.reset();
directContentUnder.reset();
}
// PDF body
/*
* A PDF file has 4 parts: a header, a body, a cross-reference table, and a trailer.
* The body contains all the PDF objects that make up the PDF document.
* Each element gets a reference (a set of numbers) and the byte position of
* every object is stored in the cross-reference table.
* Use these methods only if you know what you're doing.
*/
/** body of the PDF document */
protected PdfBody body;
/**
* Adds the local destinations to the body of the document.
* @param desto the HashMap containing the destinations
* @throws IOException on error
*/
void addLocalDestinations(final TreeMap desto) throws IOException {
for (Map.Entry entry : desto.entrySet()) {
String name = entry.getKey();
PdfDocument.Destination dest = entry.getValue();
PdfDestination destination = dest.destination;
if (dest.reference == null)
dest.reference = getPdfIndirectReference();
if (destination == null)
addToBody(new PdfString("invalid_" + name), dest.reference);
else
addToBody(destination, dest.reference);
}
}
/**
* Use this method to add a PDF object to the PDF body.
* Use this method only if you know what you're doing!
* @param object
* @return a PdfIndirectObject
* @throws IOException
*/
public PdfIndirectObject addToBody(final PdfObject object) throws IOException {
PdfIndirectObject iobj = body.add(object);
return iobj;
}
/**
* Use this method to add a PDF object to the PDF body.
* Use this method only if you know what you're doing!
* @param object
* @param inObjStm
* @return a PdfIndirectObject
* @throws IOException
*/
public PdfIndirectObject addToBody(final PdfObject object, final boolean inObjStm) throws IOException {
PdfIndirectObject iobj = body.add(object, inObjStm);
return iobj;
}
/**
* Use this method to add a PDF object to the PDF body.
* Use this method only if you know what you're doing!
* @param object
* @param ref
* @return a PdfIndirectObject
* @throws IOException
*/
public PdfIndirectObject addToBody(final PdfObject object, final PdfIndirectReference ref) throws IOException {
PdfIndirectObject iobj = body.add(object, ref);
return iobj;
}
/**
* Use this method to add a PDF object to the PDF body.
* Use this method only if you know what you're doing!
* @param object
* @param ref
* @param inObjStm
* @return a PdfIndirectObject
* @throws IOException
*/
public PdfIndirectObject addToBody(final PdfObject object, final PdfIndirectReference ref, final boolean inObjStm) throws IOException {
PdfIndirectObject iobj = body.add(object, ref, inObjStm);
return iobj;
}
/**
* Use this method to add a PDF object to the PDF body.
* Use this method only if you know what you're doing!
* @param object
* @param refNumber
* @return a PdfIndirectObject
* @throws IOException
*/
public PdfIndirectObject addToBody(final PdfObject object, final int refNumber) throws IOException {
PdfIndirectObject iobj = body.add(object, refNumber);
return iobj;
}
/**
* Use this method to add a PDF object to the PDF body.
* Use this method only if you know what you're doing!
* @param object
* @param refNumber
* @param inObjStm
* @return a PdfIndirectObject
* @throws IOException
*/
public PdfIndirectObject addToBody(final PdfObject object, final int refNumber, final boolean inObjStm) throws IOException {
PdfIndirectObject iobj = body.add(object, refNumber, inObjStm);
return iobj;
}
/**
* Use this to get an PdfIndirectReference for an object that
* will be created in the future.
* Use this method only if you know what you're doing!
* @return the PdfIndirectReference
*/
public PdfIndirectReference getPdfIndirectReference() {
return body.getPdfIndirectReference();
}
protected int getIndirectReferenceNumber() {
return body.getIndirectReferenceNumber();
}
/**
* Returns the outputStreamCounter.
* @return the outputStreamCounter
*/
public OutputStreamCounter getOs() {
return os;
}
// PDF Catalog
/*
* The Catalog is also called the root object of the document.
* Whereas the Cross-Reference maps the objects number with the
* byte offset so that the viewer can find the objects, the
* Catalog tells the viewer the numbers of the objects needed
* to render the document.
*/
protected PdfDictionary getCatalog(final PdfIndirectReference rootObj) {
PdfDictionary catalog = pdf.getCatalog(rootObj);
// [F12] tagged PDF
buildStructTreeRootForTagged(catalog);
// [F13] OCG
if (!documentOCG.isEmpty()) {
fillOCProperties(false);
catalog.put(PdfName.OCPROPERTIES, OCProperties);
}
return catalog;
}
protected void buildStructTreeRootForTagged(PdfDictionary catalog) {
if (tagged) {
try {
getStructureTreeRoot().buildTree();
}
catch (Exception e) {
throw new ExceptionConverter(e);
}
catalog.put(PdfName.STRUCTTREEROOT, structureTreeRoot.getReference());
PdfDictionary mi = new PdfDictionary();
mi.put(PdfName.MARKED, PdfBoolean.PDFTRUE);
if (userProperties)
mi.put(PdfName.USERPROPERTIES, PdfBoolean.PDFTRUE);
catalog.put(PdfName.MARKINFO, mi);
}
}
/** Holds value of property extraCatalog this is used for Output Intents. */
protected PdfDictionary extraCatalog;
/**
* Sets extra keys to the catalog.
* @return the catalog to change
*/
public PdfDictionary getExtraCatalog() {
if (extraCatalog == null)
extraCatalog = new PdfDictionary();
return this.extraCatalog;
}
// PdfPages
/*
* The page root keeps the complete page tree of the document.
* There's an entry in the Catalog that refers to the root
* of the page tree, the page tree contains the references
* to pages and other page trees.
*/
/** The root of the page tree. */
protected PdfPages root = new PdfPages(this);
/** The PdfIndirectReference to the pages. */
protected ArrayList pageReferences = new ArrayList();
/** The current page number. */
protected int currentPageNumber = 1;
/**
* The value of the Tabs entry in the page dictionary.
* @since 2.1.5
*/
protected PdfName tabs = null;
/**
* Additional page dictionary entries.
* @since 5.1.0
*/
protected PdfDictionary pageDictEntries = new PdfDictionary();
/**
* Adds an additional entry for the page dictionary.
* @param key the key
* @param object the PdfObject for the given key
* @since 5.1.0
*/
public void addPageDictEntry(final PdfName key, final PdfObject object) {
pageDictEntries.put(key, object);
}
/**
* Gets the additional pageDictEntries.
* @return the page dictionary entries
* @since 5.1.0
*/
public PdfDictionary getPageDictEntries() {
return pageDictEntries;
}
/**
* Resets the additional pageDictEntries.
* @since 5.1.0
*/
public void resetPageDictEntries() {
pageDictEntries = new PdfDictionary();
}
/**
* Use this method to make sure the page tree has a linear structure
* (every leave is attached directly to the root).
* Use this method to allow page reordering with method reorderPages.
*/
public void setLinearPageMode() {
root.setLinearMode(null);
}
/**
* Use this method to reorder the pages in the document.
* A null argument value only returns the number of pages to process.
* It is advisable to issue a Document.newPage() before using this method.
* @return the total number of pages
* @param order an array with the new page sequence. It must have the
* same size as the number of pages.
* @throws DocumentException if all the pages are not present in the array
*/
public int reorderPages(final int order[]) throws DocumentException {
return root.reorderPages(order);
}
/**
* Use this method to get a reference to a page existing or not.
* If the page does not exist yet the reference will be created
* in advance. If on closing the document, a page number greater
* than the total number of pages was requested, an exception
* is thrown.
* @param page the page number. The first page is 1
* @return the reference to the page
*/
public PdfIndirectReference getPageReference(int page) {
--page;
if (page < 0)
throw new IndexOutOfBoundsException(MessageLocalization.getComposedMessage("the.page.number.must.be.gt.eq.1"));
PdfIndirectReference ref;
if (page < pageReferences.size()) {
ref = pageReferences.get(page);
if (ref == null) {
ref = body.getPdfIndirectReference();
pageReferences.set(page, ref);
}
}
else {
int empty = page - pageReferences.size();
for (int k = 0; k < empty; ++k)
pageReferences.add(null);
ref = body.getPdfIndirectReference();
pageReferences.add(ref);
}
return ref;
}
/**
* Gets the pagenumber of this document.
* This number can be different from the real pagenumber,
* if you have (re)set the page number previously.
* @return a page number
*/
public int getPageNumber() {
return pdf.getPageNumber();
}
PdfIndirectReference getCurrentPage() {
return getPageReference(currentPageNumber);
}
public int getCurrentPageNumber() {
return currentPageNumber;
}
/**
* Sets the Viewport for the next page.
* @param vp an array consisting of Viewport dictionaries.
* @since 5.1.0
*/
public void setPageViewport(final PdfArray vp) {
addPageDictEntry(PdfName.VP, vp);
}
/**
* Sets the value for the Tabs entry in the page tree.
* @param tabs Can be PdfName.R, PdfName.C or PdfName.S.
* Since the Adobe Extensions Level 3, it can also be PdfName.A
* or PdfName.W
* @since 2.1.5
*/
public void setTabs(final PdfName tabs) {
this.tabs = tabs;
}
/**
* Returns the value to be used for the Tabs entry in the page tree.
* @return the Tabs PdfName
* @since 2.1.5
*/
public PdfName getTabs() {
return tabs;
}
/**
* Adds some PdfContents to this Writer.
*
* The document has to be open before you can begin to add content
* to the body of the document.
*
* @return a PdfIndirectReference
* @param page the PdfPage to add
* @param contents the PdfContents of the page
* @throws PdfException on error
*/
PdfIndirectReference add(final PdfPage page, final PdfContents contents) throws PdfException {
if (!open) {
throw new PdfException(MessageLocalization.getComposedMessage("the.document.is.not.open"));
}
PdfIndirectObject object;
try {
object = addToBody(contents);
}
catch(IOException ioe) {
throw new ExceptionConverter(ioe);
}
page.add(object.getIndirectReference());
// [U5]
if (group != null) {
page.put(PdfName.GROUP, group);
group = null;
}
else if (rgbTransparencyBlending) {
PdfDictionary pp = new PdfDictionary();
pp.put(PdfName.TYPE, PdfName.GROUP);
pp.put(PdfName.S, PdfName.TRANSPARENCY);
pp.put(PdfName.CS, PdfName.DEVICERGB);
page.put(PdfName.GROUP, pp);
}
root.addPage(page);
currentPageNumber++;
return null;
}
// page events
/*
* Page events are specific for iText, not for PDF.
* Upon specific events (for instance when a page starts
* or ends), the corresponding method in the page event
* implementation that is added to the writer is invoked.
*/
/** The PdfPageEvent for this document. */
private PdfPageEvent pageEvent;
/**
* Sets the PdfPageEvent for this document.
* @param event the PdfPageEvent for this document
*/
public void setPageEvent(final PdfPageEvent event) {
if (event == null) this.pageEvent = null;
else if (this.pageEvent == null) this.pageEvent = event;
else if (this.pageEvent instanceof PdfPageEventForwarder) ((PdfPageEventForwarder)this.pageEvent).addPageEvent(event);
else {
PdfPageEventForwarder forward = new PdfPageEventForwarder();
forward.addPageEvent(this.pageEvent);
forward.addPageEvent(event);
this.pageEvent = forward;
}
}
/**
* Gets the PdfPageEvent for this document or null
* if none is set.
* @return the PdfPageEvent for this document or null
* if none is set
*/
public PdfPageEvent getPageEvent() {
return pageEvent;
}
// Open and Close methods + method that create the PDF
/** A number referring to the previous Cross-Reference Table. */
protected long prevxref = 0;
/**
* Signals that the Document has been opened and that
* Elements can be added.
*
* When this method is called, the PDF-document header is
* written to the outputstream.
* @see com.itextpdf.text.DocWriter#open()
*/
@Override
public void open() {
super.open();
try {
pdf_version.writeHeader(os);
body = new PdfBody(this);
if (isPdfX() && ((PdfXConformanceImp)pdfIsoConformance).isPdfX32002()) {
PdfDictionary sec = new PdfDictionary();
sec.put(PdfName.GAMMA, new PdfArray(new float[]{2.2f,2.2f,2.2f}));
sec.put(PdfName.MATRIX, new PdfArray(new float[]{0.4124f,0.2126f,0.0193f,0.3576f,0.7152f,0.1192f,0.1805f,0.0722f,0.9505f}));
sec.put(PdfName.WHITEPOINT, new PdfArray(new float[]{0.9505f,1f,1.089f}));
PdfArray arr = new PdfArray(PdfName.CALRGB);
arr.add(sec);
setDefaultColorspace(PdfName.DEFAULTRGB, addToBody(arr).getIndirectReference());
}
}
catch(IOException ioe) {
throw new ExceptionConverter(ioe);
}
}
/**
* Signals that the Document was closed and that no other
* Elements will be added.
*
* The pages-tree is built and written to the outputstream.
* A Catalog is constructed, as well as an Info-object,
* the reference table is composed and everything is written
* to the outputstream embedded in a Trailer.
* @see com.itextpdf.text.DocWriter#close()
*/
@Override
public void close() {
if (open) {
if (currentPageNumber - 1 != pageReferences.size())
throw new RuntimeException("The page " + pageReferences.size() +
" was requested but the document has only " + (currentPageNumber - 1) + " pages.");
pdf.close();
try {
addSharedObjectsToBody();
for (PdfOCG layer : documentOCG) {
addToBody(layer.getPdfObject(), layer.getRef());
}
// add the root to the body
PdfIndirectReference rootRef = root.writePageTree();
// make the catalog-object and add it to the body
PdfDictionary catalog = getCatalog(rootRef);
// [C9] if there is XMP data to add: add it
if (xmpMetadata != null) {
PdfStream xmp = new PdfStream(xmpMetadata);
xmp.put(PdfName.TYPE, PdfName.METADATA);
xmp.put(PdfName.SUBTYPE, PdfName.XML);
if (crypto != null && !crypto.isMetadataEncrypted()) {
PdfArray ar = new PdfArray();
ar.add(PdfName.CRYPT);
xmp.put(PdfName.FILTER, ar);
}
catalog.put(PdfName.METADATA, body.add(xmp).getIndirectReference());
}
// [C10] make pdfx conformant
if (isPdfX()) {
completeInfoDictionary(getInfo());
completeExtraCatalog(getExtraCatalog());
}
// [C11] Output Intents
if (extraCatalog != null) {
catalog.mergeDifferent(extraCatalog);
}
writeOutlines(catalog, false);
// add the Catalog to the body
PdfIndirectObject indirectCatalog = addToBody(catalog, false);
// add the info-object to the body
PdfIndirectObject infoObj = addToBody(getInfo(), false);
// [F1] encryption
PdfIndirectReference encryption = null;
PdfObject fileID = null;
body.flushObjStm();
if (crypto != null) {
PdfIndirectObject encryptionObject = addToBody(crypto.getEncryptionDictionary(), false);
encryption = encryptionObject.getIndirectReference();
fileID = crypto.getFileID();
}
else
fileID = PdfEncryption.createInfoId(PdfEncryption.createDocumentId());
// write the cross-reference table of the body
body.writeCrossReferenceTable(os, indirectCatalog.getIndirectReference(),
infoObj.getIndirectReference(), encryption, fileID, prevxref);
// make the trailer
// [F2] full compression
if (fullCompression) {
writeKeyInfo(os);
os.write(getISOBytes("startxref\n"));
os.write(getISOBytes(String.valueOf(body.offset())));
os.write(getISOBytes("\n%%EOF\n"));
}
else {
PdfTrailer trailer = new PdfTrailer(body.size(),
body.offset(),
indirectCatalog.getIndirectReference(),
infoObj.getIndirectReference(),
encryption,
fileID, prevxref);
trailer.toPdf(this, os);
}
super.close();
}
catch(IOException ioe) {
throw new ExceptionConverter(ioe);
}
}
}
protected void addXFormsToBody() throws IOException {
for (Object objs[] : formXObjects.values()) {
PdfTemplate template = (PdfTemplate)objs[1];
if (template != null && template.getIndirectReference() instanceof PRIndirectReference)
continue;
if (template != null && template.getType() == PdfTemplate.TYPE_TEMPLATE) {
addToBody(template.getFormXObject(compressionLevel), template.getIndirectReference());
}
}
}
protected void addSharedObjectsToBody() throws IOException {
// [F3] add the fonts
for (FontDetails details : documentFonts.values()) {
details.writeFont(this);
}
// [F4] add the form XObjects
addXFormsToBody();
// [F5] add all the dependencies in the imported pages
for (PdfReaderInstance element : readerInstances.values()) {
currentPdfReaderInstance= element;
currentPdfReaderInstance.writeAllPages();
}
currentPdfReaderInstance = null;
// [F6] add the spotcolors
for (ColorDetails color : documentColors.values()) {
addToBody(color.getSpotColor(this), color.getIndirectReference());
}
// [F7] add the pattern
for (PdfPatternPainter pat : documentPatterns.keySet()) {
addToBody(pat.getPattern(compressionLevel), pat.getIndirectReference());
}
// [F8] add the shading patterns
for (PdfShadingPattern shadingPattern : documentShadingPatterns) {
shadingPattern.addToBody();
}
// [F9] add the shadings
for (PdfShading shading : documentShadings) {
shading.addToBody();
}
// [F10] add the extgstate
for (Map.Entryentry : documentExtGState.entrySet()) {
PdfDictionary gstate = entry.getKey();
PdfObject obj[] = entry.getValue();
addToBody(gstate, (PdfIndirectReference)obj[1]);
}
// [F11] add the properties
for (Map.Entry