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}