001/* 002 * $Id: PdfPages.java 4784 2011-03-15 08:33:00Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.pdf; 045 046import java.io.IOException; 047import java.util.ArrayList; 048 049import com.itextpdf.text.Document; 050import com.itextpdf.text.DocumentException; 051import com.itextpdf.text.ExceptionConverter; 052import com.itextpdf.text.error_messages.MessageLocalization; 053 054/** 055 * <CODE>PdfPages</CODE> is the PDF Pages-object. 056 * <P> 057 * The Pages of a document are accessible through a tree of nodes known as the Pages tree. 058 * This tree defines the ordering of the pages in the document.<BR> 059 * This object is described in the 'Portable Document Format Reference Manual version 1.3' 060 * section 6.3 (page 71-73) 061 * 062 * @see PdfPage 063 */ 064 065public class PdfPages { 066 067 private ArrayList<PdfIndirectReference> pages = new ArrayList<PdfIndirectReference>(); 068 private ArrayList<PdfIndirectReference> parents = new ArrayList<PdfIndirectReference>(); 069 private int leafSize = 10; 070 private PdfWriter writer; 071 private PdfIndirectReference topParent; 072 073 // constructors 074 075/** 076 * Constructs a <CODE>PdfPages</CODE>-object. 077 */ 078 079 PdfPages(PdfWriter writer) { 080 this.writer = writer; 081 } 082 083 void addPage(PdfDictionary page) { 084 try { 085 if (pages.size() % leafSize == 0) 086 parents.add(writer.getPdfIndirectReference()); 087 PdfIndirectReference parent = parents.get(parents.size() - 1); 088 page.put(PdfName.PARENT, parent); 089 PdfIndirectReference current = writer.getCurrentPage(); 090 writer.addToBody(page, current); 091 pages.add(current); 092 } 093 catch (Exception e) { 094 throw new ExceptionConverter(e); 095 } 096 } 097 098 PdfIndirectReference addPageRef(PdfIndirectReference pageRef) { 099 try { 100 if (pages.size() % leafSize == 0) 101 parents.add(writer.getPdfIndirectReference()); 102 pages.add(pageRef); 103 return parents.get(parents.size() - 1); 104 } 105 catch (Exception e) { 106 throw new ExceptionConverter(e); 107 } 108 } 109 110 // returns the top parent to include in the catalog 111 PdfIndirectReference writePageTree() throws IOException { 112 if (pages.isEmpty()) 113 throw new IOException(MessageLocalization.getComposedMessage("the.document.has.no.pages")); 114 int leaf = 1; 115 ArrayList<PdfIndirectReference> tParents = parents; 116 ArrayList<PdfIndirectReference> tPages = pages; 117 ArrayList<PdfIndirectReference> nextParents = new ArrayList<PdfIndirectReference>(); 118 while (true) { 119 leaf *= leafSize; 120 int stdCount = leafSize; 121 int rightCount = tPages.size() % leafSize; 122 if (rightCount == 0) 123 rightCount = leafSize; 124 for (int p = 0; p < tParents.size(); ++p) { 125 int count; 126 int thisLeaf = leaf; 127 if (p == tParents.size() - 1) { 128 count = rightCount; 129 thisLeaf = pages.size() % leaf; 130 if (thisLeaf == 0) 131 thisLeaf = leaf; 132 } 133 else 134 count = stdCount; 135 PdfDictionary top = new PdfDictionary(PdfName.PAGES); 136 top.put(PdfName.COUNT, new PdfNumber(thisLeaf)); 137 PdfArray kids = new PdfArray(); 138 ArrayList<PdfObject> internal = kids.getArrayList(); 139 internal.addAll(tPages.subList(p * stdCount, p * stdCount + count)); 140 top.put(PdfName.KIDS, kids); 141 if (tParents.size() > 1) { 142 if (p % leafSize == 0) 143 nextParents.add(writer.getPdfIndirectReference()); 144 top.put(PdfName.PARENT, nextParents.get(p / leafSize)); 145 } 146 else { 147 top.put(PdfName.ITXT, new PdfString(Document.getRelease())); 148 } 149 writer.addToBody(top, tParents.get(p)); 150 } 151 if (tParents.size() == 1) { 152 topParent = tParents.get(0); 153 return topParent; 154 } 155 tPages = tParents; 156 tParents = nextParents; 157 nextParents = new ArrayList<PdfIndirectReference>(); 158 } 159 } 160 161 PdfIndirectReference getTopParent() { 162 return topParent; 163 } 164 165 void setLinearMode(PdfIndirectReference topParent) { 166 if (parents.size() > 1) 167 throw new RuntimeException(MessageLocalization.getComposedMessage("linear.page.mode.can.only.be.called.with.a.single.parent")); 168 if (topParent != null) { 169 this.topParent = topParent; 170 parents.clear(); 171 parents.add(topParent); 172 } 173 leafSize = 10000000; 174 } 175 176 void addPage(PdfIndirectReference page) { 177 pages.add(page); 178 } 179 180 int reorderPages(int order[]) throws DocumentException { 181 if (order == null) 182 return pages.size(); 183 if (parents.size() > 1) 184 throw new DocumentException(MessageLocalization.getComposedMessage("page.reordering.requires.a.single.parent.in.the.page.tree.call.pdfwriter.setlinearmode.after.open")); 185 if (order.length != pages.size()) 186 throw new DocumentException(MessageLocalization.getComposedMessage("page.reordering.requires.an.array.with.the.same.size.as.the.number.of.pages")); 187 int max = pages.size(); 188 boolean temp[] = new boolean[max]; 189 for (int k = 0; k < max; ++k) { 190 int p = order[k]; 191 if (p < 1 || p > max) 192 throw new DocumentException(MessageLocalization.getComposedMessage("page.reordering.requires.pages.between.1.and.1.found.2", String.valueOf(max), String.valueOf(p))); 193 if (temp[p - 1]) 194 throw new DocumentException(MessageLocalization.getComposedMessage("page.reordering.requires.no.page.repetition.page.1.is.repeated", p)); 195 temp[p - 1] = true; 196 } 197 PdfIndirectReference copy[] = pages.toArray(new PdfIndirectReference[pages.size()]); 198 for (int k = 0; k < max; ++k) { 199 pages.set(k, copy[order[k] - 1]); 200 } 201 return max; 202 } 203}