From 13d6dc3a6a5e8bd3c17997351a0e6f087eb301a2 Mon Sep 17 00:00:00 2001 From: tknall Date: Tue, 25 Nov 2008 12:04:30 +0000 Subject: Removing itext from source. git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@302 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c --- src/main/java/com/lowagie/text/pdf/PdfReader.java | 3172 --------------------- 1 file changed, 3172 deletions(-) delete mode 100644 src/main/java/com/lowagie/text/pdf/PdfReader.java (limited to 'src/main/java/com/lowagie/text/pdf/PdfReader.java') diff --git a/src/main/java/com/lowagie/text/pdf/PdfReader.java b/src/main/java/com/lowagie/text/pdf/PdfReader.java deleted file mode 100644 index da46174..0000000 --- a/src/main/java/com/lowagie/text/pdf/PdfReader.java +++ /dev/null @@ -1,3172 +0,0 @@ -/* - * $Id: PdfReader.java,v 1.76 2006/05/31 16:23:26 psoares33 Exp $ - * $Name: $ - * - * Copyright 2001, 2002 Paulo Soares - * - * The contents of this file are subject to the Mozilla Public License Version 1.1 - * (the "License"); you may not use this file except in compliance with the License. - * You may obtain a copy of the License at http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the License. - * - * The Original Code is 'iText, a free JAVA-PDF library'. - * - * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by - * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie. - * All Rights Reserved. - * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer - * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved. - * - * Contributor(s): all the names of the contributors are added in the source code - * where applicable. - * - * Alternatively, the contents of this file may be used under the terms of the - * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the - * provisions of LGPL are applicable instead of those above. If you wish to - * allow use of your version of this file only under the terms of the LGPL - * License and not to allow others to use your version of this file under - * the MPL, indicate your decision by deleting the provisions above and - * replace them with the notice and other provisions required by the LGPL. - * If you do not delete the provisions above, a recipient may use your version - * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE. - * - * This library is free software; you can redistribute it and/or modify it - * under the terms of the MPL as stated above or under the terms of the GNU - * Library General Public License as published by the Free Software Foundation; - * either version 2 of the License, or any later version. - * - * This library 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 Library general Public License for more - * details. - * - * If you didn't download this code from the following link, you should check if - * you aren't using an obsolete version: - * http://www.lowagie.com/iText/ - */ - -package com.lowagie.text.pdf; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.List; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.Iterator; -import java.util.zip.InflaterInputStream; -import java.util.Arrays; -import java.util.Collections; - -import com.lowagie.text.Rectangle; -import com.lowagie.text.PageSize; -import com.lowagie.text.StringCompare; -import com.lowagie.text.ExceptionConverter; - -/** Reads a PDF document. - * @author Paulo Soares (psoares@consiste.pt) - * @author Kazuya Ujihara - */ -public class PdfReader { - - static final PdfName pageInhCandidates[] = { - PdfName.MEDIABOX, PdfName.ROTATE, PdfName.RESOURCES, PdfName.CROPBOX - }; - - static final PdfName vpnames[] = {PdfName.HIDETOOLBAR, PdfName.HIDEMENUBAR, - PdfName.HIDEWINDOWUI, PdfName.FITWINDOW, PdfName.CENTERWINDOW, PdfName.DISPLAYDOCTITLE}; - static final int vpints[] = {PdfWriter.HideToolbar, PdfWriter.HideMenubar, - PdfWriter.HideWindowUI, PdfWriter.FitWindow, PdfWriter.CenterWindow, PdfWriter.DisplayDocTitle}; - - static final byte endstream[] = PdfEncodings.convertToBytes("endstream", null); - static final byte endobj[] = PdfEncodings.convertToBytes("endobj", null); - protected PRTokeniser tokens; - // Each xref pair is a position - // type 0 -> -1, 0 - // type 1 -> offset, 0 - // type 2 -> index, obj num - protected int xref[]; - protected HashMap objStmMark; - protected IntHashtable objStmToOffset; - protected boolean newXrefType; - private ArrayList xrefObj; - PdfDictionary rootPages; - protected PdfDictionary trailer; - //protected ArrayList pages; - protected PdfDictionary catalog; - protected PageRefs pageRefs; - protected PRAcroForm acroForm = null; - protected boolean acroFormParsed = false; - protected ArrayList pageInh; - protected boolean encrypted = false; - protected boolean rebuilt = false; - protected int freeXref; - protected boolean tampered = false; - protected int lastXref; - protected int eofPos; - protected char pdfVersion; - protected PdfEncryption decrypt; - protected byte password[] = null; //added by ujihara for decryption - protected ArrayList strings = new ArrayList(); - protected boolean sharedStreams = true; - protected boolean consolidateNamedDestinations = false; - protected int rValue; - protected int pValue; - private int objNum; - private int objGen; - private boolean visited[]; - private IntHashtable newHits; - private int fileLength; - private boolean hybridXref; - private int lastXrefPartial = -1; - private boolean partial; - private PRIndirectReference cryptoRef; - - /** - * Holds value of property appendable. - */ - private boolean appendable; - - protected PdfReader() { - } - - /** Reads and parses a PDF document. - * @param filename the file name of the document - * @throws IOException on error - */ - public PdfReader(String filename) throws IOException { - this(filename, null); - } - - /** Reads and parses a PDF document. - * @param filename the file name of the document - * @param ownerPassword the password to read the document - * @throws IOException on error - */ - public PdfReader(String filename, byte ownerPassword[]) throws IOException { - password = ownerPassword; - tokens = new PRTokeniser(filename); - readPdf(); - } - - /** Reads and parses a PDF document. - * @param pdfIn the byte array with the document - * @throws IOException on error - */ - public PdfReader(byte pdfIn[]) throws IOException { - this(pdfIn, null); - } - - /** Reads and parses a PDF document. - * @param pdfIn the byte array with the document - * @param ownerPassword the password to read the document - * @throws IOException on error - */ - public PdfReader(byte pdfIn[], byte ownerPassword[]) throws IOException { - password = ownerPassword; - tokens = new PRTokeniser(pdfIn); - readPdf(); - } - - /** Reads and parses a PDF document. - * @param url the URL of the document - * @throws IOException on error - */ - public PdfReader(URL url) throws IOException { - this(url, null); - } - - /** Reads and parses a PDF document. - * @param url the URL of the document - * @param ownerPassword the password to read the document - * @throws IOException on error - */ - public PdfReader(URL url, byte ownerPassword[]) throws IOException { - password = ownerPassword; - tokens = new PRTokeniser(new RandomAccessFileOrArray(url)); - readPdf(); - } - - /** - * Reads and parses a PDF document. - * @param is the InputStream containing the document. The stream is read to the - * end but is not closed - * @param ownerPassword the password to read the document - * @throws IOException on error - */ - public PdfReader(InputStream is, byte ownerPassword[]) throws IOException { - password = ownerPassword; - tokens = new PRTokeniser(new RandomAccessFileOrArray(is)); - readPdf(); - } - - /** - * Reads and parses a PDF document. - * @param is the InputStream containing the document. The stream is read to the - * end but is not closed - * @throws IOException on error - */ - public PdfReader(InputStream is) throws IOException { - this(is, null); - } - - /** - * Reads and parses a pdf document. Contrary to the other constructors only the xref is read - * into memory. The reader is said to be working in "partial" mode as only parts of the pdf - * are read as needed. The pdf is left open but may be closed at any time with - * PdfReader.close(), reopen is automatic. - * @param raf the document location - * @param ownerPassword the password or null for no password - * @throws IOException on error - */ - public PdfReader(RandomAccessFileOrArray raf, byte ownerPassword[]) throws IOException { - password = ownerPassword; - partial = true; - tokens = new PRTokeniser(raf); - readPdfPartial(); - } - - /** Creates an independent duplicate. - * @param reader the PdfReader to duplicate - */ - public PdfReader(PdfReader reader) { - this.appendable = reader.appendable; - this.consolidateNamedDestinations = reader.consolidateNamedDestinations; - this.encrypted = reader.encrypted; - this.rebuilt = reader.rebuilt; - this.sharedStreams = reader.sharedStreams; - this.tampered = reader.tampered; - this.password = reader.password; - this.pdfVersion = reader.pdfVersion; - this.eofPos = reader.eofPos; - this.freeXref = reader.freeXref; - this.lastXref = reader.lastXref; - this.tokens = new PRTokeniser(reader.tokens.getSafeFile()); - if (reader.decrypt != null) - this.decrypt = new PdfEncryption(reader.decrypt); - this.pValue = reader.pValue; - this.rValue = reader.rValue; - this.xrefObj = new ArrayList(reader.xrefObj); - for (int k = 0; k < reader.xrefObj.size(); ++k) { - this.xrefObj.set(k, duplicatePdfObject((PdfObject)reader.xrefObj.get(k), this)); - } - this.pageRefs = new PageRefs(reader.pageRefs, this); - this.trailer = (PdfDictionary)duplicatePdfObject(reader.trailer, this); - this.catalog = (PdfDictionary)getPdfObject(trailer.get(PdfName.ROOT)); - this.rootPages = (PdfDictionary)getPdfObject(catalog.get(PdfName.PAGES)); - this.fileLength = reader.fileLength; - this.partial = reader.partial; - this.hybridXref = reader.hybridXref; - this.objStmToOffset = reader.objStmToOffset; - this.xref = reader.xref; - this.cryptoRef = (PRIndirectReference)duplicatePdfObject(reader.cryptoRef, this); - } - - /** Gets a new file instance of the original PDF - * document. - * @return a new file instance of the original PDF document - */ - public RandomAccessFileOrArray getSafeFile() { - return tokens.getSafeFile(); - } - - protected PdfReaderInstance getPdfReaderInstance(PdfWriter writer) { - return new PdfReaderInstance(this, writer); - } - - /** Gets the number of pages in the document. - * @return the number of pages in the document - */ - public int getNumberOfPages() { - return pageRefs.size(); - } - - /** Returns the document's catalog. This dictionary is not a copy, - * any changes will be reflected in the catalog. - * @return the document's catalog - */ - public PdfDictionary getCatalog() { - return catalog; - } - - /** Returns the document's acroform, if it has one. - * @return the document's acroform - */ - public PRAcroForm getAcroForm() { - if (!acroFormParsed) { - acroFormParsed = true; - PdfObject form = catalog.get(PdfName.ACROFORM); - if (form != null) { - try { - acroForm = new PRAcroForm(this); - acroForm.readAcroForm((PdfDictionary)getPdfObject(form)); - } - catch (Exception e) { - acroForm = null; - } - } - } - return acroForm; - } - /** - * Gets the page rotation. This value can be 0, 90, 180 or 270. - * @param index the page number. The first page is 1 - * @return the page rotation - */ - public int getPageRotation(int index) { - return getPageRotation(pageRefs.getPageNRelease(index)); - } - - int getPageRotation(PdfDictionary page) { - PdfNumber rotate = (PdfNumber)getPdfObject(page.get(PdfName.ROTATE)); - if (rotate == null) - return 0; - else { - int n = rotate.intValue(); - n %= 360; - return n < 0 ? n + 360 : n; - } - } - /** Gets the page size, taking rotation into account. This - * is a Rectangle with the value of the /MediaBox and the /Rotate key. - * @param index the page number. The first page is 1 - * @return a Rectangle - */ - public Rectangle getPageSizeWithRotation(int index) { - return getPageSizeWithRotation(pageRefs.getPageNRelease(index)); - } - - /** - * Gets the rotated page from a page dictionary. - * @param page the page dictionary - * @return the rotated page - */ - public Rectangle getPageSizeWithRotation(PdfDictionary page) { - Rectangle rect = getPageSize(page); - int rotation = getPageRotation(page); - while (rotation > 0) { - rect = rect.rotate(); - rotation -= 90; - } - return rect; - } - - /** Gets the page size without taking rotation into account. This - * is the value of the /MediaBox key. - * @param index the page number. The first page is 1 - * @return the page size - */ - public Rectangle getPageSize(int index) { - return getPageSize(pageRefs.getPageNRelease(index)); - } - - /** - * Gets the page from a page dictionary - * @param page the page dictionary - * @return the page - */ - public Rectangle getPageSize(PdfDictionary page) { - PdfArray mediaBox = (PdfArray)getPdfObject(page.get(PdfName.MEDIABOX)); - return getNormalizedRectangle(mediaBox); - } - - /** Gets the crop box without taking rotation into account. This - * is the value of the /CropBox key. The crop box is the part - * of the document to be displayed or printed. It usually is the same - * as the media box but may be smaller. If the page doesn't have a crop - * box the page size will be returned. - * @param index the page number. The first page is 1 - * @return the crop box - */ - public Rectangle getCropBox(int index) { - PdfDictionary page = pageRefs.getPageNRelease(index); - PdfArray cropBox = (PdfArray)getPdfObjectRelease(page.get(PdfName.CROPBOX)); - if (cropBox == null) - return getPageSize(page); - return getNormalizedRectangle(cropBox); - } - - /** Gets the box size. Allowed names are: "crop", "trim", "art", "bleed" and "media". - * @param index the page number. The first page is 1 - * @param boxName the box name - * @return the box rectangle or null - */ - public Rectangle getBoxSize(int index, String boxName) { - PdfDictionary page = pageRefs.getPageNRelease(index); - PdfArray box = null; - if (boxName.equals("trim")) - box = (PdfArray)getPdfObjectRelease(page.get(PdfName.TRIMBOX)); - else if (boxName.equals("art")) - box = (PdfArray)getPdfObjectRelease(page.get(PdfName.ARTBOX)); - else if (boxName.equals("bleed")) - box = (PdfArray)getPdfObjectRelease(page.get(PdfName.BLEEDBOX)); - else if (boxName.equals("crop")) - box = (PdfArray)getPdfObjectRelease(page.get(PdfName.CROPBOX)); - else if (boxName.equals("media")) - box = (PdfArray)getPdfObjectRelease(page.get(PdfName.MEDIABOX)); - if (box == null) - return null; - return getNormalizedRectangle(box); - } - - /** Returns the content of the document information dictionary as a HashMap - * of String. - * @return content of the document information dictionary - */ - public HashMap getInfo() { - HashMap map = new HashMap(); - PdfDictionary info = (PdfDictionary)getPdfObject(trailer.get(PdfName.INFO)); - if (info == null) - return map; - for (Iterator it = info.getKeys().iterator(); it.hasNext();) { - PdfName key = (PdfName)it.next(); - PdfObject obj = getPdfObject(info.get(key)); - if (obj == null) - continue; - String value = obj.toString(); - switch (obj.type()) { - case PdfObject.STRING: { - value = ((PdfString)obj).toUnicodeString(); - break; - } - case PdfObject.NAME: { - value = PdfName.decodeName(value); - break; - } - } - map.put(PdfName.decodeName(key.toString()), value); - } - return map; - } - - /** Normalizes a Rectangle so that llx and lly are smaller than urx and ury. - * @param box the original rectangle - * @return a normalized Rectangle - */ - public static Rectangle getNormalizedRectangle(PdfArray box) { - ArrayList rect = box.getArrayList(); - float llx = ((PdfNumber)rect.get(0)).floatValue(); - float lly = ((PdfNumber)rect.get(1)).floatValue(); - float urx = ((PdfNumber)rect.get(2)).floatValue(); - float ury = ((PdfNumber)rect.get(3)).floatValue(); - return new Rectangle(Math.min(llx, urx), Math.min(lly, ury), - Math.max(llx, urx), Math.max(lly, ury)); - } - - protected void readPdf() throws IOException { - try { - fileLength = tokens.getFile().length(); - pdfVersion = tokens.checkPdfHeader(); - try { - readXref(); - } - catch (Exception e) { - try { - rebuilt = true; - rebuildXref(); - lastXref = -1; - } - catch (Exception ne) { - throw new IOException("Rebuild failed: " + ne.getMessage() + "; Original message: " + e.getMessage()); - } - } - try { - readDocObj(); - } - catch (IOException ne) { - if (rebuilt) - throw ne; - rebuilt = true; - encrypted = false; - rebuildXref(); - lastXref = -1; - readDocObj(); - } - - strings.clear(); - readPages(); - eliminateSharedStreams(); - removeUnusedObjects(); - } - finally { - try { - tokens.close(); - } - catch (Exception e) { - // empty on purpose - } - } - } - - protected void readPdfPartial() throws IOException { - try { - fileLength = tokens.getFile().length(); - pdfVersion = tokens.checkPdfHeader(); - try { - readXref(); - } - catch (Exception e) { - try { - rebuilt = true; - rebuildXref(); - lastXref = -1; - } - catch (Exception ne) { - throw new IOException("Rebuild failed: " + ne.getMessage() + "; Original message: " + e.getMessage()); - } - } - readDocObjPartial(); - readPages(); - } - catch (IOException e) { - try{tokens.close();}catch(Exception ee){} - throw e; - } - } - - private boolean equalsArray(byte ar1[], byte ar2[], int size) { - for (int k = 0; k < size; ++k) { - if (ar1[k] != ar2[k]) - return false; - } - return true; - } - - /** - * @throws IOException - */ - private void readDecryptedDocObj() throws IOException { - if (encrypted) - return; - PdfObject encDic = trailer.get(PdfName.ENCRYPT); - if (encDic == null || encDic.toString().equals("null")) - return; - encrypted = true; - PdfDictionary enc = (PdfDictionary)getPdfObject(encDic); - - String s; - PdfObject o; - - PdfArray documentIDs = (PdfArray)getPdfObject(trailer.get(PdfName.ID)); - byte documentID[] = null; - if (documentIDs != null) { - o = (PdfObject)documentIDs.getArrayList().get(0); - s = o.toString(); - documentID = com.lowagie.text.DocWriter.getISOBytes(s); - } - - s = enc.get(PdfName.U).toString(); - byte uValue[] = com.lowagie.text.DocWriter.getISOBytes(s); - s = enc.get(PdfName.O).toString(); - byte oValue[] = com.lowagie.text.DocWriter.getISOBytes(s); - - o = enc.get(PdfName.R); - if (!o.isNumber()) throw new IOException("Illegal R value."); - rValue = ((PdfNumber)o).intValue(); - if (rValue != 2 && rValue != 3) throw new IOException("Unknown encryption type (" + rValue + ")"); - - o = enc.get(PdfName.P); - if (!o.isNumber()) throw new IOException("Illegal P value."); - pValue = ((PdfNumber)o).intValue(); - - // get the Keylength if Revision is 3 - int lengthValue; - if ( rValue == 3 ){ - o = enc.get(PdfName.LENGTH); - if (!o.isNumber()) - throw new IOException("Illegal Length value."); - lengthValue = ( (PdfNumber) o).intValue(); - if (lengthValue > 128 || lengthValue < 40 || lengthValue % 8 != 0) - throw new IOException("Illegal Length value."); - } else { - // Keylength is 40 bit in revision 2 - lengthValue=40; - } - - - - decrypt = new PdfEncryption(); - - //check by user password - decrypt.setupByUserPassword(documentID, password, oValue, pValue, lengthValue, rValue); - if (!equalsArray(uValue, decrypt.userKey, rValue == 3 ? 16 : 32)) { - //check by owner password - decrypt.setupByOwnerPassword(documentID, password, uValue, oValue, pValue, lengthValue, rValue); - if (!equalsArray(uValue, decrypt.userKey, rValue == 3 ? 16 : 32)) { - throw new IOException("Bad user password"); - } - } - for (int k = 0; k < strings.size(); ++k) { - PdfString str = (PdfString)strings.get(k); - str.decrypt(this); - } - if (encDic.isIndirect()) { - cryptoRef = (PRIndirectReference)encDic; - xrefObj.set(cryptoRef.getNumber(), null); - } - } - - /** - * @param obj - * @return a PdfObject - */ - public static PdfObject getPdfObjectRelease(PdfObject obj) { - PdfObject obj2 = getPdfObject(obj); - releaseLastXrefPartial(obj); - return obj2; - } - - - /** - * Reads a PdfObject resolving an indirect reference - * if needed. - * @param obj the PdfObject to read - * @return the resolved PdfObject - */ - public static PdfObject getPdfObject(PdfObject obj) { - if (obj == null) - return null; - if (!obj.isIndirect()) - return obj; - try { - PRIndirectReference ref = (PRIndirectReference)obj; - int idx = ref.getNumber(); - boolean appendable = ref.getReader().appendable; - obj = ref.getReader().getPdfObject(idx); - if (obj == null) { - if (appendable) { - obj = new PdfNull(); - obj.setIndRef(ref); - return obj; - } - else - return PdfNull.PDFNULL; - } - else { - if (appendable) { - switch (obj.type()) { - case PdfObject.NULL: - obj = new PdfNull(); - break; - case PdfObject.BOOLEAN: - obj = new PdfBoolean(((PdfBoolean)obj).booleanValue()); - break; - case PdfObject.NAME: - obj = new PdfName(obj.getBytes()); - break; - } - obj.setIndRef(ref); - } - return obj; - } - } - catch (Exception e) { - throw new ExceptionConverter(e); - } - } - - /** - * Reads a PdfObject resolving an indirect reference - * if needed. If the reader was opened in partial mode the object will be released - * to save memory. - * @param obj the PdfObject to read - * @param parent - * @return a PdfObject - */ - public static PdfObject getPdfObjectRelease(PdfObject obj, PdfObject parent) { - PdfObject obj2 = getPdfObject(obj, parent); - releaseLastXrefPartial(obj); - return obj2; - } - - /** - * @param obj - * @param parent - * @return a PdfObject - */ - public static PdfObject getPdfObject(PdfObject obj, PdfObject parent) { - if (obj == null) - return null; - if (!obj.isIndirect()) { - PRIndirectReference ref = null; - if (parent != null && (ref = parent.getIndRef()) != null && ref.getReader().isAppendable()) { - switch (obj.type()) { - case PdfObject.NULL: - obj = new PdfNull(); - break; - case PdfObject.BOOLEAN: - obj = new PdfBoolean(((PdfBoolean)obj).booleanValue()); - break; - case PdfObject.NAME: - obj = new PdfName(obj.getBytes()); - break; - } - obj.setIndRef(ref); - } - return obj; - } - return getPdfObject(obj); - } - - /** - * @param idx - * @return a PdfObject - */ - public PdfObject getPdfObjectRelease(int idx) { - PdfObject obj = getPdfObject(idx); - releaseLastXrefPartial(); - return obj; - } - - /** - * @param idx - * @return aPdfObject - */ - public PdfObject getPdfObject(int idx) { - try { - lastXrefPartial = -1; - if (idx < 0 || idx >= xrefObj.size()) - return null; - PdfObject obj = (PdfObject)xrefObj.get(idx); - if (!partial || obj != null) - return obj; - if (idx * 2 >= xref.length) - return null; - obj = readSingleObject(idx); - lastXrefPartial = -1; - if (obj != null) - lastXrefPartial = idx; - return obj; - } - catch (Exception e) { - throw new ExceptionConverter(e); - } - } - - /** - * - */ - public void resetLastXrefPartial() { - lastXrefPartial = -1; - } - - /** - * - */ - public void releaseLastXrefPartial() { - if (partial && lastXrefPartial != -1) { - xrefObj.set(lastXrefPartial, null); - lastXrefPartial = -1; - } - } - - /** - * @param obj - */ - public static void releaseLastXrefPartial(PdfObject obj) { - if (obj == null) - return; - if (!obj.isIndirect()) - return; - PRIndirectReference ref = (PRIndirectReference)obj; - PdfReader reader = ref.getReader(); - if (reader.partial && reader.lastXrefPartial != -1 && reader.lastXrefPartial == ref.getNumber()) { - reader.xrefObj.set(reader.lastXrefPartial, null); - } - reader.lastXrefPartial = -1; - } - - private void setXrefPartialObject(int idx, PdfObject obj) { - if (!partial || idx < 0) - return; - xrefObj.set(idx, obj); - } - - /** - * @param obj - * @return an indirect reference - */ - public PRIndirectReference addPdfObject(PdfObject obj) { - xrefObj.add(obj); - return new PRIndirectReference(this, xrefObj.size() - 1); - } - - protected void readPages() throws IOException { - pageInh = new ArrayList(); - catalog = (PdfDictionary)getPdfObject(trailer.get(PdfName.ROOT)); - rootPages = (PdfDictionary)getPdfObject(catalog.get(PdfName.PAGES)); - pageRefs = new PageRefs(this); - } - - protected void readDocObjPartial() throws IOException { - xrefObj = new ArrayList(xref.length / 2); - xrefObj.addAll(Collections.nCopies(xref.length / 2, null)); - readDecryptedDocObj(); - if (objStmToOffset != null) { - int keys[] = objStmToOffset.getKeys(); - for (int k = 0; k < keys.length; ++k) { - int n = keys[k]; - objStmToOffset.put(n, xref[n * 2]); - xref[n * 2] = -1; - } - } - } - - protected PdfObject readSingleObject(int k) throws IOException { - strings.clear(); - int k2 = k * 2; - int pos = xref[k2]; - if (pos < 0) - return null; - if (xref[k2 + 1] > 0) - pos = objStmToOffset.get(xref[k2 + 1]); - tokens.seek(pos); - tokens.nextValidToken(); - if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) - tokens.throwError("Invalid object number."); - objNum = tokens.intValue(); - tokens.nextValidToken(); - if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) - tokens.throwError("Invalid generation number."); - objGen = tokens.intValue(); - tokens.nextValidToken(); - if (!tokens.getStringValue().equals("obj")) - tokens.throwError("Token 'obj' expected."); - PdfObject obj; - try { - obj = readPRObject(); - for (int j = 0; j < strings.size(); ++j) { - PdfString str = (PdfString)strings.get(j); - str.decrypt(this); - } - if (obj.isStream()) { - checkPRStreamLength((PRStream)obj); - } - } - catch (Exception e) { - obj = null; - } - if (xref[k2 + 1] > 0) { - obj = readOneObjStm((PRStream)obj, xref[k2]); - } - xrefObj.set(k, obj); - return obj; - } - - protected PdfObject readOneObjStm(PRStream stream, int idx) throws IOException { - int first = ((PdfNumber)getPdfObject(stream.get(PdfName.FIRST))).intValue(); - byte b[] = getStreamBytes(stream, tokens.getFile()); - PRTokeniser saveTokens = tokens; - tokens = new PRTokeniser(b); - try { - int address = 0; - boolean ok = true; - ++idx; - for (int k = 0; k < idx; ++k) { - ok = tokens.nextToken(); - if (!ok) - break; - if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) { - ok = false; - break; - } - ok = tokens.nextToken(); - if (!ok) - break; - if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) { - ok = false; - break; - } - address = tokens.intValue() + first; - } - if (!ok) - throw new IOException("Error reading ObjStm"); - tokens.seek(address); - return readPRObject(); - } - finally { - tokens = saveTokens; - } - } - - /** - * @return the percentage of the cross reference table that has been read - */ - public double dumpPerc() { - int total = 0; - for (int k = 0; k < xrefObj.size(); ++k) { - if (xrefObj.get(k) != null) - ++total; - } - return (total * 100.0 / xrefObj.size()); - } - - protected void readDocObj() throws IOException { - ArrayList streams = new ArrayList(); - xrefObj = new ArrayList(xref.length / 2); - xrefObj.addAll(Collections.nCopies(xref.length / 2, null)); - for (int k = 2; k < xref.length; k += 2) { - int pos = xref[k]; - if (pos <= 0 || xref[k + 1] > 0) - continue; - tokens.seek(pos); - tokens.nextValidToken(); - if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) - tokens.throwError("Invalid object number."); - objNum = tokens.intValue(); - tokens.nextValidToken(); - if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) - tokens.throwError("Invalid generation number."); - objGen = tokens.intValue(); - tokens.nextValidToken(); - if (!tokens.getStringValue().equals("obj")) - tokens.throwError("Token 'obj' expected."); - PdfObject obj; - try { - obj = readPRObject(); - if (obj.isStream()) { - streams.add(obj); - } - } - catch (Exception e) { - obj = null; - } - xrefObj.set(k / 2, obj); - } - for (int k = 0; k < streams.size(); ++k) { - checkPRStreamLength((PRStream)streams.get(k)); - } - readDecryptedDocObj(); - if (objStmMark != null) { - for (Iterator i = objStmMark.entrySet().iterator(); i.hasNext();) { - Map.Entry entry = (Map.Entry)i.next(); - int n = ((Integer)entry.getKey()).intValue(); - IntHashtable h = (IntHashtable)entry.getValue(); - readObjStm((PRStream)xrefObj.get(n), h); - xrefObj.set(n, null); - } - objStmMark = null; - } - xref = null; - } - - private void checkPRStreamLength(PRStream stream) throws IOException { - int fileLength = tokens.length(); - int start = stream.getOffset(); - boolean calc = false; - int streamLength = 0; - PdfObject obj = getPdfObjectRelease(stream.get(PdfName.LENGTH)); - if (obj != null && obj.type() == PdfObject.NUMBER) { - streamLength = ((PdfNumber)obj).intValue(); - if (streamLength + start > fileLength - 20) - calc = true; - else { - tokens.seek(start + streamLength); - String line = tokens.readString(20); - if (!line.startsWith("\nendstream") && - !line.startsWith("\r\nendstream") && - !line.startsWith("\rendstream") && - !line.startsWith("endstream")) - calc = true; - } - } - else - calc = true; - if (calc) { - byte tline[] = new byte[16]; - tokens.seek(start); - while (true) { - int pos = tokens.getFilePointer(); - if (!tokens.readLineSegment(tline)) - break; - if (equalsn(tline, endstream)) { - streamLength = pos - start; - break; - } - if (equalsn(tline, endobj)) { - tokens.seek(pos - 16); - String s = tokens.readString(16); - int index = s.indexOf("endstream"); - if (index >= 0) - pos = pos - 16 + index; - streamLength = pos - start; - break; - } - } - } - stream.setLength(streamLength); - } - - protected void readObjStm(PRStream stream, IntHashtable map) throws IOException { - int first = ((PdfNumber)getPdfObject(stream.get(PdfName.FIRST))).intValue(); - int n = ((PdfNumber)getPdfObject(stream.get(PdfName.N))).intValue(); - byte b[] = getStreamBytes(stream, tokens.getFile()); - PRTokeniser saveTokens = tokens; - tokens = new PRTokeniser(b); - try { - int address[] = new int[n]; - int objNumber[] = new int[n]; - boolean ok = true; - for (int k = 0; k < n; ++k) { - ok = tokens.nextToken(); - if (!ok) - break; - if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) { - ok = false; - break; - } - objNumber[k] = tokens.intValue(); - ok = tokens.nextToken(); - if (!ok) - break; - if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) { - ok = false; - break; - } - address[k] = tokens.intValue() + first; - } - if (!ok) - throw new IOException("Error reading ObjStm"); - for (int k = 0; k < n; ++k) { - if (map.containsKey(k)) { - tokens.seek(address[k]); - PdfObject obj = readPRObject(); - xrefObj.set(objNumber[k], obj); - } - } - } - finally { - tokens = saveTokens; - } - } - - /** - * Eliminates the reference to the object freeing the memory used by it and clearing - * the xref entry. - * @param obj the object. If it's an indirect reference it will be eliminated - * @return the object or the already erased dereferenced object - */ - public static PdfObject killIndirect(PdfObject obj) { - if (obj == null || obj.isNull()) - return null; - PdfObject ret = getPdfObjectRelease(obj); - if (obj.isIndirect()) { - PRIndirectReference ref = (PRIndirectReference)obj; - PdfReader reader = ref.getReader(); - int n = ref.getNumber(); - reader.xrefObj.set(n, null); - if (reader.partial) - reader.xref[n * 2] = -1; - } - return ret; - } - - private void ensureXrefSize(int size) { - if (size == 0) - return; - if (xref == null) - xref = new int[size]; - else { - if (xref.length < size) { - int xref2[] = new int[size]; - System.arraycopy(xref, 0, xref2, 0, xref.length); - xref = xref2; - } - } - } - - protected void readXref() throws IOException { - hybridXref = false; - newXrefType = false; - tokens.seek(tokens.getStartxref()); - tokens.nextToken(); - if (!tokens.getStringValue().equals("startxref")) - throw new IOException("startxref not found."); - tokens.nextToken(); - if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) - throw new IOException("startxref is not followed by a number."); - int startxref = tokens.intValue(); - lastXref = startxref; - eofPos = tokens.getFilePointer(); - try { - if (readXRefStream(startxref)) { - newXrefType = true; - return; - } - } - catch (Exception e) {} - xref = null; - tokens.seek(startxref); - trailer = readXrefSection(); - PdfDictionary trailer2 = trailer; - while (true) { - PdfNumber prev = (PdfNumber)trailer2.get(PdfName.PREV); - if (prev == null) - break; - tokens.seek(prev.intValue()); - trailer2 = readXrefSection(); - } - } - - protected PdfDictionary readXrefSection() throws IOException { - tokens.nextValidToken(); - if (!tokens.getStringValue().equals("xref")) - tokens.throwError("xref subsection not found"); - int start = 0; - int end = 0; - int pos = 0; - int gen = 0; - while (true) { - tokens.nextValidToken(); - if (tokens.getStringValue().equals("trailer")) - break; - if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) - tokens.throwError("Object number of the first object in this xref subsection not found"); - start = tokens.intValue(); - tokens.nextValidToken(); - if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) - tokens.throwError("Number of entries in this xref subsection not found"); - end = tokens.intValue() + start; - if (start == 1) { // fix incorrect start number - int back = tokens.getFilePointer(); - tokens.nextValidToken(); - pos = tokens.intValue(); - tokens.nextValidToken(); - gen = tokens.intValue(); - if (pos == 0 && gen == 65535) { - --start; - --end; - } - tokens.seek(back); - } - ensureXrefSize(end * 2); - for (int k = start; k < end; ++k) { - tokens.nextValidToken(); - pos = tokens.intValue(); - tokens.nextValidToken(); - gen = tokens.intValue(); - tokens.nextValidToken(); - int p = k * 2; - if (tokens.getStringValue().equals("n")) { - if (xref[p] == 0 && xref[p + 1] == 0) { -// if (pos == 0) -// tokens.throwError("File position 0 cross-reference entry in this xref subsection"); - xref[p] = pos; - } - } - else if (tokens.getStringValue().equals("f")) { - if (xref[p] == 0 && xref[p + 1] == 0) - xref[p] = -1; - } - else - tokens.throwError("Invalid cross-reference entry in this xref subsection"); - } - } - PdfDictionary trailer = (PdfDictionary)readPRObject(); - PdfNumber xrefSize = (PdfNumber)trailer.get(PdfName.SIZE); - ensureXrefSize(xrefSize.intValue() * 2); - PdfObject xrs = trailer.get(PdfName.XREFSTM); - if (xrs != null && xrs.isNumber()) { - int loc = ((PdfNumber)xrs).intValue(); - try { - readXRefStream(loc); - newXrefType = true; - hybridXref = true; - } - catch (IOException e) { - xref = null; - throw e; - } - } - return trailer; - } - - protected boolean readXRefStream(int ptr) throws IOException { - tokens.seek(ptr); - int thisStream = 0; - if (!tokens.nextToken()) - return false; - if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) - return false; - thisStream = tokens.intValue(); - if (!tokens.nextToken() || tokens.getTokenType() != PRTokeniser.TK_NUMBER) - return false; - if (!tokens.nextToken() || !tokens.getStringValue().equals("obj")) - return false; - PdfObject object = readPRObject(); - PRStream stm = null; - if (object.isStream()) { - stm = (PRStream)object; - if (!PdfName.XREF.equals(stm.get(PdfName.TYPE))) - return false; - } - if (trailer == null) { - trailer = new PdfDictionary(); - trailer.putAll(stm); - } - stm.setLength(((PdfNumber)stm.get(PdfName.LENGTH)).intValue()); - int size = ((PdfNumber)stm.get(PdfName.SIZE)).intValue(); - PdfArray index; - PdfObject obj = stm.get(PdfName.INDEX); - if (obj == null) { - index = new PdfArray(); - index.add(new int[]{0, size}); - } - else - index = (PdfArray)obj; - PdfArray w = (PdfArray)stm.get(PdfName.W); - int prev = -1; - obj = stm.get(PdfName.PREV); - if (obj != null) - prev = ((PdfNumber)obj).intValue(); - // Each xref pair is a position - // type 0 -> -1, 0 - // type 1 -> offset, 0 - // type 2 -> index, obj num - ensureXrefSize(size * 2); - if (objStmMark == null && !partial) - objStmMark = new HashMap(); - if (objStmToOffset == null && partial) - objStmToOffset = new IntHashtable(); - byte b[] = getStreamBytes(stm, tokens.getFile()); - int bptr = 0; - ArrayList wa = w.getArrayList(); - int wc[] = new int[3]; - for (int k = 0; k < 3; ++k) - wc[k] = ((PdfNumber)wa.get(k)).intValue(); - ArrayList sections = index.getArrayList(); - for (int idx = 0; idx < sections.size(); idx += 2) { - int start = ((PdfNumber)sections.get(idx)).intValue(); - int length = ((PdfNumber)sections.get(idx + 1)).intValue(); - ensureXrefSize((start + length) * 2); - while (length-- > 0) { - int type = 1; - if (wc[0] > 0) { - type = 0; - for (int k = 0; k < wc[0]; ++k) - type = (type << 8) + (b[bptr++] & 0xff); - } - int field2 = 0; - for (int k = 0; k < wc[1]; ++k) - field2 = (field2 << 8) + (b[bptr++] & 0xff); - int field3 = 0; - for (int k = 0; k < wc[2]; ++k) - field3 = (field3 << 8) + (b[bptr++] & 0xff); - int base = start * 2; - if (xref[base] == 0 && xref[base + 1] == 0) { - switch (type) { - case 0: - xref[base] = -1; - break; - case 1: - xref[base] = field2; - break; - case 2: - xref[base] = field3; - xref[base + 1] = field2; - if (partial) { - objStmToOffset.put(field2, 0); - } - else { - Integer on = new Integer(field2); - IntHashtable seq = (IntHashtable)objStmMark.get(on); - if (seq == null) { - seq = new IntHashtable(); - seq.put(field3, 1); - objStmMark.put(on, seq); - } - else - seq.put(field3, 1); - } - break; - } - } - ++start; - } - } - thisStream *= 2; - if (thisStream < xref.length) - xref[thisStream] = -1; - - if (prev == -1) - return true; - return readXRefStream(prev); - } - - protected void rebuildXref() throws IOException { - hybridXref = false; - newXrefType = false; - tokens.seek(0); - int xr[][] = new int[1024][]; - int top = 0; - trailer = null; - byte line[] = new byte[64]; - for (;;) { - int pos = tokens.getFilePointer(); - if (!tokens.readLineSegment(line)) - break; - if (line[0] == 't') { - if (!PdfEncodings.convertToString(line, null).startsWith("trailer")) - continue; - tokens.seek(pos); - tokens.nextToken(); - pos = tokens.getFilePointer(); - try { - PdfDictionary dic = (PdfDictionary)readPRObject(); - if (dic.get(PdfName.ROOT) != null) - trailer = dic; - else - tokens.seek(pos); - } - catch (Exception e) { - tokens.seek(pos); - } - } - else if (line[0] >= '0' && line[0] <= '9') { - int obj[] = PRTokeniser.checkObjectStart(line); - if (obj == null) - continue; - int num = obj[0]; - int gen = obj[1]; - if (num >= xr.length) { - int newLength = num * 2; - int xr2[][] = new int[newLength][]; - System.arraycopy(xr, 0, xr2, 0, top); - xr = xr2; - } - if (num >= top) - top = num + 1; - if (xr[num] == null || gen >= xr[num][1]) { - obj[0] = pos; - xr[num] = obj; - } - } - } - if (trailer == null) - throw new IOException("trailer not found."); - xref = new int[top * 2]; - for (int k = 0; k < top; ++k) { - int obj[] = xr[k]; - if (obj != null) - xref[k * 2] = obj[0]; - } - } - - protected PdfDictionary readDictionary() throws IOException { - PdfDictionary dic = new PdfDictionary(); - while (true) { - tokens.nextValidToken(); - if (tokens.getTokenType() == PRTokeniser.TK_END_DIC) - break; - if (tokens.getTokenType() != PRTokeniser.TK_NAME) - tokens.throwError("Dictionary key is not a name."); - PdfName name = new PdfName(tokens.getStringValue(), false); - PdfObject obj = readPRObject(); - int type = obj.type(); - if (-type == PRTokeniser.TK_END_DIC) - tokens.throwError("Unexpected '>>'"); - if (-type == PRTokeniser.TK_END_ARRAY) - tokens.throwError("Unexpected ']'"); - dic.put(name, obj); - } - return dic; - } - - protected PdfArray readArray() throws IOException { - PdfArray array = new PdfArray(); - while (true) { - PdfObject obj = readPRObject(); - int type = obj.type(); - if (-type == PRTokeniser.TK_END_ARRAY) - break; - if (-type == PRTokeniser.TK_END_DIC) - tokens.throwError("Unexpected '>>'"); - array.add(obj); - } - return array; - } - - protected PdfObject readPRObject() throws IOException { - tokens.nextValidToken(); - int type = tokens.getTokenType(); - switch (type) { - case PRTokeniser.TK_START_DIC: { - PdfDictionary dic = readDictionary(); - int pos = tokens.getFilePointer(); - // be careful in the trailer. May not be a "next" token. - if (tokens.nextToken() && tokens.getStringValue().equals("stream")) { - int ch = tokens.read(); - if (ch != '\n') - ch = tokens.read(); - if (ch != '\n') - tokens.backOnePosition(ch); - PRStream stream = new PRStream(this, tokens.getFilePointer()); - stream.putAll(dic); - stream.setObjNum(objNum, objGen); - return stream; - } - else { - tokens.seek(pos); - return dic; - } - } - case PRTokeniser.TK_START_ARRAY: - return readArray(); - case PRTokeniser.TK_NUMBER: - return new PdfNumber(tokens.getStringValue()); - case PRTokeniser.TK_STRING: - PdfString str = new PdfString(tokens.getStringValue(), null).setHexWriting(tokens.isHexString()); - str.setObjNum(objNum, objGen); - if (strings != null) - strings.add(str); - return str; - case PRTokeniser.TK_NAME: - return new PdfName(tokens.getStringValue(), false); - case PRTokeniser.TK_REF: - int num = tokens.getReference(); - PRIndirectReference ref = new PRIndirectReference(this, num, tokens.getGeneration()); - if (visited != null && !visited[num]) { - visited[num] = true; - newHits.put(num, 1); - } - return ref; - default: - return new PdfLiteral(-type, tokens.getStringValue()); - } - } - - /** Decodes a stream that has the FlateDecode filter. - * @param in the input data - * @return the decoded data - */ - public static byte[] FlateDecode(byte in[]) { - byte b[] = FlateDecode(in, true); - if (b == null) - return FlateDecode(in, false); - return b; - } - - /** - * @param in - * @param dicPar - * @return a byte array - */ - public static byte[] decodePredictor(byte in[], PdfObject dicPar) { - if (dicPar == null || !dicPar.isDictionary()) - return in; - PdfDictionary dic = (PdfDictionary)dicPar; - PdfObject obj = getPdfObject(dic.get(PdfName.PREDICTOR)); - if (obj == null || !obj.isNumber()) - return in; - int predictor = ((PdfNumber)obj).intValue(); - if (predictor < 10) - return in; - int width = 1; - obj = getPdfObject(dic.get(PdfName.COLUMNS)); - if (obj != null && obj.isNumber()) - width = ((PdfNumber)obj).intValue(); - int colors = 1; - obj = getPdfObject(dic.get(PdfName.COLORS)); - if (obj != null && obj.isNumber()) - colors = ((PdfNumber)obj).intValue(); - int bpc = 8; - obj = getPdfObject(dic.get(PdfName.BITSPERCOMPONENT)); - if (obj != null && obj.isNumber()) - bpc = ((PdfNumber)obj).intValue(); - DataInputStream dataStream = new DataInputStream(new ByteArrayInputStream(in)); - ByteArrayOutputStream fout = new ByteArrayOutputStream(in.length); - int bytesPerPixel = colors * bpc / 8; - int bytesPerRow = (colors*width*bpc + 7)/8; - byte[] curr = new byte[bytesPerRow]; - byte[] prior = new byte[bytesPerRow]; - - // Decode the (sub)image row-by-row - while (true) { - // Read the filter type byte and a row of data - int filter = 0; - try { - filter = dataStream.read(); - if (filter < 0) { - return fout.toByteArray(); - } - dataStream.readFully(curr, 0, bytesPerRow); - } catch (Exception e) { - return fout.toByteArray(); - } - - switch (filter) { - case 0: //PNG_FILTER_NONE - break; - case 1: //PNG_FILTER_SUB - for (int i = bytesPerPixel; i < bytesPerRow; i++) { - curr[i] += curr[i - bytesPerPixel]; - } - break; - case 2: //PNG_FILTER_UP - for (int i = 0; i < bytesPerRow; i++) { - curr[i] += prior[i]; - } - break; - case 3: //PNG_FILTER_AVERAGE - for (int i = 0; i < bytesPerPixel; i++) { - curr[i] += prior[i] / 2; - } - for (int i = bytesPerPixel; i < bytesPerRow; i++) { - curr[i] += ((curr[i - bytesPerPixel] & 0xff) + (prior[i] & 0xff))/2; - } - break; - case 4: //PNG_FILTER_PAETH - for (int i = 0; i < bytesPerPixel; i++) { - curr[i] += prior[i]; - } - - for (int i = bytesPerPixel; i < bytesPerRow; i++) { - int a = curr[i - bytesPerPixel] & 0xff; - int b = prior[i] & 0xff; - int c = prior[i - bytesPerPixel] & 0xff; - - int p = a + b - c; - int pa = Math.abs(p - a); - int pb = Math.abs(p - b); - int pc = Math.abs(p - c); - - int ret; - - if ((pa <= pb) && (pa <= pc)) { - ret = a; - } else if (pb <= pc) { - ret = b; - } else { - ret = c; - } - curr[i] += (byte)(ret); - } - break; - default: - // Error -- uknown filter type - throw new RuntimeException("PNG filter unknown."); - } - try { - fout.write(curr); - } - catch (IOException ioe) { - // Never happens - } - - // Swap curr and prior - byte[] tmp = prior; - prior = curr; - curr = tmp; - } - } - - /** A helper to FlateDecode. - * @param in the input data - * @param strict true to read a correct stream. false - * to try to read a corrupted stream - * @return the decoded data - */ - public static byte[] FlateDecode(byte in[], boolean strict) { - ByteArrayInputStream stream = new ByteArrayInputStream(in); - InflaterInputStream zip = new InflaterInputStream(stream); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte b[] = new byte[strict ? 4092 : 1]; - try { - int n; - while ((n = zip.read(b)) >= 0) { - out.write(b, 0, n); - } - zip.close(); - out.close(); - return out.toByteArray(); - } - catch (Exception e) { - if (strict) - return null; - return out.toByteArray(); - } - } - - /** Decodes a stream that has the ASCIIHexDecode filter. - * @param in the input data - * @return the decoded data - */ - public static byte[] ASCIIHexDecode(byte in[]) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - boolean first = true; - int n1 = 0; - for (int k = 0; k < in.length; ++k) { - int ch = in[k] & 0xff; - if (ch == '>') - break; - if (PRTokeniser.isWhitespace(ch)) - continue; - int n = PRTokeniser.getHex(ch); - if (n == -1) - throw new RuntimeException("Illegal character in ASCIIHexDecode."); - if (first) - n1 = n; - else - out.write((byte)((n1 << 4) + n)); - first = !first; - } - if (!first) - out.write((byte)(n1 << 4)); - return out.toByteArray(); - } - - /** Decodes a stream that has the ASCII85Decode filter. - * @param in the input data - * @return the decoded data - */ - public static byte[] ASCII85Decode(byte in[]) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - int state = 0; - int chn[] = new int[5]; - for (int k = 0; k < in.length; ++k) { - int ch = in[k] & 0xff; - if (ch == '~') - break; - if (PRTokeniser.isWhitespace(ch)) - continue; - if (ch == 'z' && state == 0) { - out.write(0); - out.write(0); - out.write(0); - out.write(0); - continue; - } - if (ch < '!' || ch > 'u') - throw new RuntimeException("Illegal character in ASCII85Decode."); - chn[state] = ch - '!'; - ++state; - if (state == 5) { - state = 0; - int r = 0; - for (int j = 0; j < 5; ++j) - r = r * 85 + chn[j]; - out.write((byte)(r >> 24)); - out.write((byte)(r >> 16)); - out.write((byte)(r >> 8)); - out.write((byte)r); - } - } - int r = 0; - if (state == 1) - throw new RuntimeException("Illegal length in ASCII85Decode."); - if (state == 2) { - r = chn[0] * 85 * 85 * 85 * 85 + chn[1] * 85 * 85 * 85; - out.write((byte)(r >> 24)); - } - else if (state == 3) { - r = chn[0] * 85 * 85 * 85 * 85 + chn[1] * 85 * 85 * 85 + chn[2] * 85 * 85; - out.write((byte)(r >> 24)); - out.write((byte)(r >> 16)); - } - else if (state == 4) { - r = chn[0] * 85 * 85 * 85 * 85 + chn[1] * 85 * 85 * 85 + chn[2] * 85 * 85 + chn[3] * 85 ; - out.write((byte)(r >> 24)); - out.write((byte)(r >> 16)); - out.write((byte)(r >> 8)); - } - return out.toByteArray(); - } - - /** Decodes a stream that has the LZWDecode filter. - * @param in the input data - * @return the decoded data - */ - public static byte[] LZWDecode(byte in[]) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - LZWDecoder lzw = new LZWDecoder(); - lzw.decode(in, out); - return out.toByteArray(); - } - - /** Checks if the document had errors and was rebuilt. - * @return true if rebuilt. - * - */ - public boolean isRebuilt() { - return this.rebuilt; - } - - /** Gets the dictionary that represents a page. - * @param pageNum the page number. 1 is the first - * @return the page dictionary - */ - public PdfDictionary getPageN(int pageNum) { - PdfDictionary dic = pageRefs.getPageN(pageNum); - if (dic == null) - return null; - if (appendable) - dic.setIndRef(pageRefs.getPageOrigRef(pageNum)); - return dic; - } - - /** - * @param pageNum - * @return a Dictionary object - */ - public PdfDictionary getPageNRelease(int pageNum) { - PdfDictionary dic = getPageN(pageNum); - pageRefs.releasePage(pageNum); - return dic; - } - - /** - * @param pageNum - */ - public void releasePage(int pageNum) { - pageRefs.releasePage(pageNum); - } - - /** - * - */ - public void resetReleasePage() { - pageRefs.resetReleasePage(); - } - - /** Gets the page reference to this page. - * @param pageNum the page number. 1 is the first - * @return the page reference - */ - public PRIndirectReference getPageOrigRef(int pageNum) { - return pageRefs.getPageOrigRef(pageNum); - } - - /** Gets the contents of the page. - * @param pageNum the page number. 1 is the first - * @param file the location of the PDF document - * @throws IOException on error - * @return the content - */ - public byte[] getPageContent(int pageNum, RandomAccessFileOrArray file) throws IOException{ - PdfDictionary page = getPageNRelease(pageNum); - if (page == null) - return null; - PdfObject contents = getPdfObjectRelease(page.get(PdfName.CONTENTS)); - if (contents == null) - return new byte[0]; - ByteArrayOutputStream bout = null; - if (contents.isStream()) { - return getStreamBytes((PRStream)contents, file); - } - else if (contents.isArray()) { - PdfArray array = (PdfArray)contents; - ArrayList list = array.getArrayList(); - bout = new ByteArrayOutputStream(); - for (int k = 0; k < list.size(); ++k) { - PdfObject item = getPdfObjectRelease((PdfObject)list.get(k)); - if (item == null || !item.isStream()) - continue; - byte[] b = getStreamBytes((PRStream)item, file); - bout.write(b); - if (k != list.size() - 1) - bout.write('\n'); - } - return bout.toByteArray(); - } - else - return new byte[0]; - } - - /** Gets the contents of the page. - * @param pageNum the page number. 1 is the first - * @throws IOException on error - * @return the content - */ - public byte[] getPageContent(int pageNum) throws IOException{ - RandomAccessFileOrArray rf = getSafeFile(); - try { - rf.reOpen(); - return getPageContent(pageNum, rf); - } - finally { - try{rf.close();}catch(Exception e){} - } - } - - protected void killXref(PdfObject obj) { - if (obj == null) - return; - if ((obj instanceof PdfIndirectReference) && !obj.isIndirect()) - return; - switch (obj.type()) { - case PdfObject.INDIRECT: { - int xr = ((PRIndirectReference)obj).getNumber(); - obj = (PdfObject)xrefObj.get(xr); - xrefObj.set(xr, null); - freeXref = xr; - killXref(obj); - break; - } - case PdfObject.ARRAY: { - ArrayList t = ((PdfArray)obj).getArrayList(); - for (int i = 0; i < t.size(); ++i) - killXref((PdfObject)t.get(i)); - break; - } - case PdfObject.STREAM: - case PdfObject.DICTIONARY: { - PdfDictionary dic = (PdfDictionary)obj; - for (Iterator i = dic.getKeys().iterator(); i.hasNext();){ - killXref(dic.get((PdfName)i.next())); - } - break; - } - } - } - - /** Sets the contents of the page. - * @param content the new page content - * @param pageNum the page number. 1 is the first - * @throws IOException on error - */ - public void setPageContent(int pageNum, byte content[]) throws IOException{ - PdfDictionary page = getPageN(pageNum); - if (page == null) - return; - PdfObject contents = page.get(PdfName.CONTENTS); - freeXref = -1; - killXref(contents); - if (freeXref == -1) { - xrefObj.add(null); - freeXref = xrefObj.size() - 1; - } - page.put(PdfName.CONTENTS, new PRIndirectReference(this, freeXref)); - xrefObj.set(freeXref, new PRStream(this, content)); - } - - /** Get the content from a stream applying the required filters. - * @param stream the stream - * @param file the location where the stream is - * @throws IOException on error - * @return the stream content - */ - public static byte[] getStreamBytes(PRStream stream, RandomAccessFileOrArray file) throws IOException { - PdfObject filter = getPdfObjectRelease(stream.get(PdfName.FILTER)); - byte[] b = getStreamBytesRaw(stream, file); - ArrayList filters = new ArrayList(); - if (filter != null) { - if (filter.isName()) - filters.add(filter); - else if (filter.isArray()) - filters = ((PdfArray)filter).getArrayList(); - } - ArrayList dp = new ArrayList(); - PdfObject dpo = getPdfObjectRelease(stream.get(PdfName.DECODEPARMS)); - if (dpo == null || (!dpo.isDictionary() && !dpo.isArray())) - dpo = getPdfObjectRelease(stream.get(PdfName.DP)); - if (dpo != null) { - if (dpo.isDictionary()) - dp.add(dpo); - else if (dpo.isArray()) - dp = ((PdfArray)dpo).getArrayList(); - } - String name; - for (int j = 0; j < filters.size(); ++j) { - name = ((PdfName)PdfReader.getPdfObjectRelease((PdfObject)filters.get(j))).toString(); - if (name.equals("/FlateDecode") || name.equals("/Fl")) { - b = PdfReader.FlateDecode(b); - PdfObject dicParam = null; - if (j < dp.size()) { - dicParam = (PdfObject)dp.get(j); - b = decodePredictor(b, dicParam); - } - } - else if (name.equals("/ASCIIHexDecode") || name.equals("/AHx")) - b = PdfReader.ASCIIHexDecode(b); - else if (name.equals("/ASCII85Decode") || name.equals("/A85")) - b = PdfReader.ASCII85Decode(b); - else if (name.equals("/LZWDecode")) { - b = PdfReader.LZWDecode(b); - PdfObject dicParam = null; - if (j < dp.size()) { - dicParam = (PdfObject)dp.get(j); - b = decodePredictor(b, dicParam); - } - } - else - throw new IOException("The filter " + name + " is not supported."); - } - return b; - } - - /** Get the content from a stream applying the required filters. - * @param stream the stream - * @throws IOException on error - * @return the stream content - */ - public static byte[] getStreamBytes(PRStream stream) throws IOException { - RandomAccessFileOrArray rf = stream.getReader().getSafeFile(); - try { - rf.reOpen(); - return PdfReader.getStreamBytes(stream, rf); - } - finally { - try{rf.close();}catch(Exception e){} - } - } - - /** Get the content from a stream as it is without applying any filter. - * @param stream the stream - * @param file the location where the stream is - * @throws IOException on error - * @return the stream content - */ - public static byte[] getStreamBytesRaw(PRStream stream, RandomAccessFileOrArray file) throws IOException { - PdfReader reader = stream.getReader(); - byte b[]; - if (stream.getOffset() < 0) - b = stream.getBytes(); - else { - b = new byte[stream.getLength()]; - file.seek(stream.getOffset()); - file.readFully(b); - PdfEncryption decrypt = reader.getDecrypt(); - if (decrypt != null) { - decrypt.setHashKey(stream.getObjNum(), stream.getObjGen()); - decrypt.prepareKey(); - decrypt.encryptRC4(b); - } - } - return b; - } - - /** Get the content from a stream as it is without applying any filter. - * @param stream the stream - * @throws IOException on error - * @return the stream content - */ - public static byte[] getStreamBytesRaw(PRStream stream) throws IOException { - RandomAccessFileOrArray rf = stream.getReader().getSafeFile(); - try { - rf.reOpen(); - return PdfReader.getStreamBytesRaw(stream, rf); - } - finally { - try{rf.close();}catch(Exception e){} - } - } - - /** Eliminates shared streams if they exist. */ - public void eliminateSharedStreams() { - if (!sharedStreams) - return; - sharedStreams = false; - if (pageRefs.size() == 1) - return; - ArrayList newRefs = new ArrayList(); - ArrayList newStreams = new ArrayList(); - IntHashtable visited = new IntHashtable(); - for (int k = 1; k <= pageRefs.size(); ++k) { - PdfDictionary page = pageRefs.getPageN(k); - if (page == null) - continue; - PdfObject contents = getPdfObject(page.get(PdfName.CONTENTS)); - if (contents == null) - continue; - if (contents.isStream()) { - PRIndirectReference ref = (PRIndirectReference)page.get(PdfName.CONTENTS); - if (visited.containsKey(ref.getNumber())) { - // need to duplicate - newRefs.add(ref); - newStreams.add(new PRStream((PRStream)contents, null)); - } - else - visited.put(ref.getNumber(), 1); - } - else if (contents.isArray()) { - PdfArray array = (PdfArray)contents; - ArrayList list = array.getArrayList(); - for (int j = 0; j < list.size(); ++j) { - PRIndirectReference ref = (PRIndirectReference)list.get(j); - if (visited.containsKey(ref.getNumber())) { - // need to duplicate - newRefs.add(ref); - newStreams.add(new PRStream((PRStream)getPdfObject(ref), null)); - } - else - visited.put(ref.getNumber(), 1); - } - } - } - if (newStreams.size() == 0) - return; - for (int k = 0; k < newStreams.size(); ++k) { - xrefObj.add(newStreams.get(k)); - PRIndirectReference ref = (PRIndirectReference)newRefs.get(k); - ref.setNumber(xrefObj.size() - 1, 0); - } - } - - /** Checks if the document was changed. - * @return true if the document was changed, - * false otherwise - */ - public boolean isTampered() { - return tampered; - } - - /** - * Sets the tampered state. A tampered PdfReader cannot be reused in PdfStamper. - * @param tampered the tampered state - */ - public void setTampered(boolean tampered) { - this.tampered = tampered; - } - - /** Gets the XML metadata. - * @throws IOException on error - * @return the XML metadata - */ - public byte[] getMetadata() throws IOException { - PdfObject obj = getPdfObject(catalog.get(PdfName.METADATA)); - if (!(obj instanceof PRStream)) - return null; - RandomAccessFileOrArray rf = getSafeFile(); - byte b[] = null; - try { - rf.reOpen(); - b = getStreamBytes((PRStream)obj, rf); - } - finally { - try { - rf.close(); - } - catch (Exception e) { - // empty on purpose - } - } - return b; - } - - /** - * Gets the byte address of the last xref table. - * @return the byte address of the last xref table - */ - public int getLastXref() { - return lastXref; - } - - /** - * Gets the number of xref objects. - * @return the number of xref objects - */ - public int getXrefSize() { - return xrefObj.size(); - } - - /** - * Gets the byte address of the %%EOF marker. - * @return the byte address of the %%EOF marker - */ - public int getEofPos() { - return eofPos; - } - - /** - * Gets the PDF version. Only the last version char is returned. For example - * version 1.4 is returned as '4'. - * @return the PDF version - */ - public char getPdfVersion() { - return pdfVersion; - } - - /** - * Returns true if the PDF is encrypted. - * @return true if the PDF is encrypted - */ - public boolean isEncrypted() { - return encrypted; - } - - /** - * Gets the encryption permissions. It can be used directly in - * PdfWriter.setEncryption(). - * @return the encryption permissions - */ - public int getPermissions() { - return pValue; - } - - /** - * Returns true if the PDF has a 128 bit key encryption. - * @return true if the PDF has a 128 bit key encryption - */ - public boolean is128Key() { - return rValue == 3; - } - - /** - * Gets the trailer dictionary - * @return the trailer dictionary - */ - public PdfDictionary getTrailer() { - return trailer; - } - - PdfEncryption getDecrypt() { - return decrypt; - } - - static boolean equalsn(byte a1[], byte a2[]) { - int length = a2.length; - for (int k = 0; k < length; ++k) { - if (a1[k] != a2[k]) - return false; - } - return true; - } - - static boolean existsName(PdfDictionary dic, PdfName key, PdfName value) { - PdfObject type = getPdfObjectRelease(dic.get(key)); - if (type == null || !type.isName()) - return false; - PdfName name = (PdfName)type; - return name.equals(value); - } - - static String getFontName(PdfDictionary dic) { - PdfObject type = getPdfObjectRelease(dic.get(PdfName.BASEFONT)); - if (type == null || !type.isName()) - return null; - return PdfName.decodeName(type.toString()); - } - - static String getSubsetPrefix(PdfDictionary dic) { - String s = getFontName(dic); - if (s == null) - return null; - if (s.length() < 8 || s.charAt(6) != '+') - return null; - for (int k = 0; k < 6; ++k) { - char c = s.charAt(k); - if (c < 'A' || c > 'Z') - return null; - } - return s; - } - - /** Finds all the font subsets and changes the prefixes to some - * random values. - * @return the number of font subsets altered - */ - public int shuffleSubsetNames() { - int total = 0; - for (int k = 1; k < xrefObj.size(); ++k) { - PdfObject obj = getPdfObjectRelease(k); - if (obj == null || !obj.isDictionary()) - continue; - PdfDictionary dic = (PdfDictionary)obj; - if (!existsName(dic, PdfName.TYPE, PdfName.FONT)) - continue; - if (existsName(dic, PdfName.SUBTYPE, PdfName.TYPE1) - || existsName(dic, PdfName.SUBTYPE, PdfName.MMTYPE1) - || existsName(dic, PdfName.SUBTYPE, PdfName.TRUETYPE)) { - String s = getSubsetPrefix(dic); - if (s == null) - continue; - String ns = BaseFont.createSubsetPrefix() + s.substring(7); - PdfName newName = new PdfName(ns); - dic.put(PdfName.BASEFONT, newName); - setXrefPartialObject(k, dic); - ++total; - PdfDictionary fd = (PdfDictionary)getPdfObject(dic.get(PdfName.FONTDESCRIPTOR)); - if (fd == null) - continue; - fd.put(PdfName.FONTNAME, newName); - } - else if (existsName(dic, PdfName.SUBTYPE, PdfName.TYPE0)) { - String s = getSubsetPrefix(dic); - PdfArray arr = (PdfArray)getPdfObject(dic.get(PdfName.DESCENDANTFONTS)); - if (arr == null) - continue; - ArrayList list = arr.getArrayList(); - if (list.size() == 0) - continue; - PdfDictionary desc = (PdfDictionary)getPdfObject((PdfObject)list.get(0)); - String sde = getSubsetPrefix(desc); - if (sde == null) - continue; - String ns = BaseFont.createSubsetPrefix(); - if (s != null) - dic.put(PdfName.BASEFONT, new PdfName(ns + s.substring(7))); - setXrefPartialObject(k, dic); - PdfName newName = new PdfName(ns + sde.substring(7)); - desc.put(PdfName.BASEFONT, newName); - ++total; - PdfDictionary fd = (PdfDictionary)getPdfObject(desc.get(PdfName.FONTDESCRIPTOR)); - if (fd == null) - continue; - fd.put(PdfName.FONTNAME, newName); - } - } - return total; - } - - /** Finds all the fonts not subset but embedded and marks them as subset. - * @return the number of fonts altered - */ - public int createFakeFontSubsets() { - int total = 0; - for (int k = 1; k < xrefObj.size(); ++k) { - PdfObject obj = getPdfObjectRelease(k); - if (obj == null || !obj.isDictionary()) - continue; - PdfDictionary dic = (PdfDictionary)obj; - if (!existsName(dic, PdfName.TYPE, PdfName.FONT)) - continue; - if (existsName(dic, PdfName.SUBTYPE, PdfName.TYPE1) - || existsName(dic, PdfName.SUBTYPE, PdfName.MMTYPE1) - || existsName(dic, PdfName.SUBTYPE, PdfName.TRUETYPE)) { - String s = getSubsetPrefix(dic); - if (s != null) - continue; - s = getFontName(dic); - if (s == null) - continue; - String ns = BaseFont.createSubsetPrefix() + s; - PdfDictionary fd = (PdfDictionary)getPdfObjectRelease(dic.get(PdfName.FONTDESCRIPTOR)); - if (fd == null) - continue; - if (fd.get(PdfName.FONTFILE) == null && fd.get(PdfName.FONTFILE2) == null - && fd.get(PdfName.FONTFILE3) == null) - continue; - fd = (PdfDictionary)getPdfObject(dic.get(PdfName.FONTDESCRIPTOR)); - PdfName newName = new PdfName(ns); - dic.put(PdfName.BASEFONT, newName); - fd.put(PdfName.FONTNAME, newName); - setXrefPartialObject(k, dic); - ++total; - } - } - return total; - } - - private static PdfArray getNameArray(PdfObject obj) { - if (obj == null) - return null; - obj = getPdfObjectRelease(obj); - if (obj.isArray()) - return (PdfArray)obj; - else if (obj.isDictionary()) { - PdfObject arr2 = getPdfObjectRelease(((PdfDictionary)obj).get(PdfName.D)); - if (arr2 != null && arr2.isArray()) - return (PdfArray)arr2; - } - return null; - } - - /** - * Gets all the named destinations as an HashMap. The key is the name - * and the value is the destinations array. - * @return gets all the named destinations - */ - public HashMap getNamedDestination() { - HashMap names = getNamedDestinationFromNames(); - names.putAll(getNamedDestinationFromStrings()); - return names; - } - - /** - * Gets the named destinations from the /Dests key in the catalog as an HashMap. The key is the name - * and the value is the destinations array. - * @return gets the named destinations - */ - public HashMap getNamedDestinationFromNames() { - HashMap names = new HashMap(); - if (catalog.get(PdfName.DESTS) != null) { - PdfDictionary dic = (PdfDictionary)getPdfObjectRelease(catalog.get(PdfName.DESTS)); - Set keys = dic.getKeys(); - for (Iterator it = keys.iterator(); it.hasNext();) { - PdfName key = (PdfName)it.next(); - String name = PdfName.decodeName(key.toString()); - PdfArray arr = getNameArray(dic.get(key)); - if (arr != null) - names.put(name, arr); - } - } - return names; - } - - /** - * Gets the named destinations from the /Names key in the catalog as an HashMap. The key is the name - * and the value is the destinations array. - * @return gets the named destinations - */ - public HashMap getNamedDestinationFromStrings() { - if (catalog.get(PdfName.NAMES) != null) { - PdfDictionary dic = (PdfDictionary)getPdfObjectRelease(catalog.get(PdfName.NAMES)); - dic = (PdfDictionary)getPdfObjectRelease(dic.get(PdfName.DESTS)); - if (dic != null) { - HashMap names = PdfNameTree.readTree(dic); - for (Iterator it = names.entrySet().iterator(); it.hasNext();) { - Map.Entry entry = (Map.Entry)it.next(); - PdfArray arr = getNameArray((PdfObject)entry.getValue()); - if (arr != null) - entry.setValue(arr); - else - it.remove(); - } - return names; - } - } - return new HashMap(); - } - - private boolean replaceNamedDestination(PdfObject obj, HashMap names) { - obj = getPdfObject(obj); - int objIdx = lastXrefPartial; - releaseLastXrefPartial(); - if (obj != null && obj.isDictionary()) { - PdfObject ob2 = getPdfObjectRelease(((PdfDictionary)obj).get(PdfName.DEST)); - String name = null; - if (ob2 != null) { - if (ob2.isName()) - name = PdfName.decodeName(ob2.toString()); - else if (ob2.isString()) - name = ob2.toString(); - PdfArray dest = (PdfArray)names.get(name); - if (dest != null) { - ((PdfDictionary)obj).put(PdfName.DEST, dest); - setXrefPartialObject(objIdx, obj); - return true; - } - } - else if ((ob2 = getPdfObject(((PdfDictionary)obj).get(PdfName.A))) != null) { - int obj2Idx = lastXrefPartial; - releaseLastXrefPartial(); - PdfDictionary dic = (PdfDictionary)ob2; - PdfName type = (PdfName)getPdfObjectRelease(dic.get(PdfName.S)); - if (PdfName.GOTO.equals(type)) { - PdfObject ob3 = getPdfObjectRelease(dic.get(PdfName.D)); - if (ob3.isName()) - name = PdfName.decodeName(ob3.toString()); - else if (ob3.isString()) - name = ob3.toString(); - PdfArray dest = (PdfArray)names.get(name); - if (dest != null) { - dic.put(PdfName.D, dest); - setXrefPartialObject(obj2Idx, ob2); - setXrefPartialObject(objIdx, obj); - return true; - } - } - } - } - return false; - } - - /** - * Removes all the fields from the document. - */ - public void removeFields() { - pageRefs.resetReleasePage(); - for (int k = 1; k <= pageRefs.size(); ++k) { - PdfDictionary page = pageRefs.getPageN(k); - PdfArray annots = (PdfArray)getPdfObject(page.get(PdfName.ANNOTS)); - if (annots == null) { - pageRefs.releasePage(k); - continue; - } - ArrayList arr = annots.getArrayList(); - for (int j = 0; j < arr.size(); ++j) { - PdfDictionary annot = (PdfDictionary)getPdfObjectRelease((PdfObject)arr.get(j)); - if (PdfName.WIDGET.equals(annot.get(PdfName.SUBTYPE))) - arr.remove(j--); - } - if (arr.isEmpty()) - page.remove(PdfName.ANNOTS); - else - pageRefs.releasePage(k); - } - catalog.remove(PdfName.ACROFORM); - pageRefs.resetReleasePage(); - } - - /** - * Removes all the annotations and fields from the document. - */ - public void removeAnnotations() { - pageRefs.resetReleasePage(); - for (int k = 1; k <= pageRefs.size(); ++k) { - PdfDictionary page = pageRefs.getPageN(k); - if (page.get(PdfName.ANNOTS) == null) - pageRefs.releasePage(k); - else - page.remove(PdfName.ANNOTS); - } - catalog.remove(PdfName.ACROFORM); - pageRefs.resetReleasePage(); - } - - private void iterateBookmarks(PdfObject outlineRef, HashMap names) { - while (outlineRef != null) { - replaceNamedDestination(outlineRef, names); - PdfDictionary outline = (PdfDictionary)getPdfObjectRelease(outlineRef); - PdfObject first = outline.get(PdfName.FIRST); - if (first != null) { - iterateBookmarks(first, names); - } - outlineRef = outline.get(PdfName.NEXT); - } - } - - /** Replaces all the local named links with the actual destinations. */ - public void consolidateNamedDestinations() { - if (consolidateNamedDestinations) - return; - consolidateNamedDestinations = true; - HashMap names = getNamedDestination(); - if (names.size() == 0) - return; - for (int k = 1; k <= pageRefs.size(); ++k) { - PdfDictionary page = pageRefs.getPageN(k); - PdfObject annotsRef; - PdfArray annots = (PdfArray)getPdfObject(annotsRef = page.get(PdfName.ANNOTS)); - int annotIdx = lastXrefPartial; - releaseLastXrefPartial(); - if (annots == null) { - pageRefs.releasePage(k); - continue; - } - ArrayList list = annots.getArrayList(); - boolean commitAnnots = false; - for (int an = 0; an < list.size(); ++an) { - PdfObject objRef = (PdfObject)list.get(an); - if (replaceNamedDestination(objRef, names) && !objRef.isIndirect()) - commitAnnots = true; - } - if (commitAnnots) - setXrefPartialObject(annotIdx, annots); - if (!commitAnnots || annotsRef.isIndirect()) - pageRefs.releasePage(k); - } - PdfDictionary outlines = (PdfDictionary)getPdfObjectRelease(catalog.get(PdfName.OUTLINES)); - if (outlines == null) - return; - iterateBookmarks(outlines.get(PdfName.FIRST), names); - } - - protected static PdfDictionary duplicatePdfDictionary(PdfDictionary original, PdfDictionary copy, PdfReader newReader) { - if (copy == null) - copy = new PdfDictionary(); - for (Iterator it = original.getKeys().iterator(); it.hasNext();) { - PdfName key = (PdfName)it.next(); - copy.put(key, duplicatePdfObject(original.get(key), newReader)); - } - return copy; - } - - protected static PdfObject duplicatePdfObject(PdfObject original, PdfReader newReader) { - if (original == null) - return null; - switch (original.type()) { - case PdfObject.DICTIONARY: { - return duplicatePdfDictionary((PdfDictionary)original, null, newReader); - } - case PdfObject.STREAM: { - PRStream org = (PRStream)original; - PRStream stream = new PRStream(org, null, newReader); - duplicatePdfDictionary(org, stream, newReader); - return stream; - } - case PdfObject.ARRAY: { - ArrayList list = ((PdfArray)original).getArrayList(); - PdfArray arr = new PdfArray(); - for (Iterator it = list.iterator(); it.hasNext();) { - arr.add(duplicatePdfObject((PdfObject)it.next(), newReader)); - } - return arr; - } - case PdfObject.INDIRECT: { - PRIndirectReference org = (PRIndirectReference)original; - return new PRIndirectReference(newReader, org.getNumber(), org.getGeneration()); - } - default: - return original; - } - } - - /** - * Closes the reader - */ - public void close() { - if (!partial) - return; - try { - tokens.close(); - } - catch (IOException e) { - throw new ExceptionConverter(e); - } - } - - protected void removeUnusedNode(PdfObject obj, boolean hits[]) { - if (obj == null) - return; - switch (obj.type()) { - case PdfObject.DICTIONARY: - case PdfObject.STREAM: { - PdfDictionary dic = (PdfDictionary)obj; - for (Iterator it = dic.getKeys().iterator(); it.hasNext();) { - PdfName key = (PdfName)it.next(); - PdfObject v = dic.get(key); - if (v.isIndirect()) { - int num = ((PRIndirectReference)v).getNumber(); - if (num >= xrefObj.size() || (!partial && xrefObj.get(num) == null)) { - dic.put(key, PdfNull.PDFNULL); - continue; - } - } - removeUnusedNode(v, hits); - } - break; - } - case PdfObject.ARRAY: { - ArrayList list = ((PdfArray)obj).getArrayList(); - for (int k = 0; k < list.size(); ++k) { - PdfObject v = (PdfObject)list.get(k); - if (v.isIndirect()) { - int num = ((PRIndirectReference)v).getNumber(); - if (num >= xrefObj.size() || (!partial && xrefObj.get(num) == null)) { - list.set(k, PdfNull.PDFNULL); - continue; - } - } - removeUnusedNode(v, hits); - } - break; - } - case PdfObject.INDIRECT: { - PRIndirectReference ref = (PRIndirectReference)obj; - int num = ref.getNumber(); - if (!hits[num]) { - hits[num] = true; - removeUnusedNode(getPdfObjectRelease(ref), hits); - } - } - } - } - - /** Removes all the unreachable objects. - * @return the number of indirect objects removed - */ - public int removeUnusedObjects() { - boolean hits[] = new boolean[xrefObj.size()]; - removeUnusedNode(trailer, hits); - int total = 0; - if (partial) { - for (int k = 1; k < hits.length; ++k) { - if (!hits[k]) { - xref[k * 2] = -1; - xref[k * 2 + 1] = 0; - xrefObj.set(k, null); - ++total; - } - } - } - else { - for (int k = 1; k < hits.length; ++k) { - if (!hits[k]) { - xrefObj.set(k, null); - ++total; - } - } - } - return total; - } - - /** Gets a read-only version of AcroFields. - * @return a read-only version of AcroFields - */ - public AcroFields getAcroFields() { - return new AcroFields(this, null); - } - - /** - * Gets the global document JavaScript. - * @param file the document file - * @throws IOException on error - * @return the global document JavaScript - */ - public String getJavaScript(RandomAccessFileOrArray file) throws IOException { - PdfDictionary names = (PdfDictionary)getPdfObjectRelease(catalog.get(PdfName.NAMES)); - if (names == null) - return null; - PdfDictionary js = (PdfDictionary)getPdfObjectRelease(names.get(PdfName.JAVASCRIPT)); - if (js == null) - return null; - HashMap jscript = PdfNameTree.readTree(js); - String sortedNames[] = new String[jscript.size()]; - sortedNames = (String[])jscript.keySet().toArray(sortedNames); - Arrays.sort(sortedNames, new StringCompare()); - StringBuffer buf = new StringBuffer(); - for (int k = 0; k < sortedNames.length; ++k) { - PdfDictionary j = (PdfDictionary)getPdfObjectRelease((PdfIndirectReference)jscript.get(sortedNames[k])); - if (j == null) - continue; - PdfObject obj = getPdfObjectRelease(j.get(PdfName.JS)); - if (obj.isString()) - buf.append(((PdfString)obj).toUnicodeString()).append('\n'); - else if (obj.isStream()) { - byte bytes[] = getStreamBytes((PRStream)obj, file); - if (bytes.length >= 2 && bytes[0] == (byte)254 && bytes[1] == (byte)255) - buf.append(PdfEncodings.convertToString(bytes, PdfObject.TEXT_UNICODE)); - else - buf.append(PdfEncodings.convertToString(bytes, PdfObject.TEXT_PDFDOCENCODING)); - buf.append('\n'); - } - } - return buf.toString(); - } - - /** - * Gets the global document JavaScript. - * @throws IOException on error - * @return the global document JavaScript - */ - public String getJavaScript() throws IOException { - RandomAccessFileOrArray rf = getSafeFile(); - try { - rf.reOpen(); - return getJavaScript(rf); - } - finally { - try{rf.close();}catch(Exception e){} - } - } - - /** - * Selects the pages to keep in the document. The pages are described as - * ranges. The page ordering can be changed but - * no page repetitions are allowed. Note that it may be very slow in partial mode. - * @param ranges the comma separated ranges as described in {@link SequenceList} - */ - public void selectPages(String ranges) { - selectPages(SequenceList.expand(ranges, getNumberOfPages())); - } - - /** - * Selects the pages to keep in the document. The pages are described as a - * List of Integer. The page ordering can be changed but - * no page repetitions are allowed. Note that it may be very slow in partial mode. - * @param pagesToKeep the pages to keep in the document - */ - public void selectPages(List pagesToKeep) { - pageRefs.selectPages(pagesToKeep); - removeUnusedObjects(); - } - - /** - * @param preferences - * @param catalog - */ - public static void setViewerPreferences(int preferences, PdfDictionary catalog) { - catalog.remove(PdfName.PAGELAYOUT); - catalog.remove(PdfName.PAGEMODE); - catalog.remove(PdfName.VIEWERPREFERENCES); - if ((preferences & PdfWriter.PageLayoutSinglePage) != 0) - catalog.put(PdfName.PAGELAYOUT, PdfName.SINGLEPAGE); - else if ((preferences & PdfWriter.PageLayoutOneColumn) != 0) - catalog.put(PdfName.PAGELAYOUT, PdfName.ONECOLUMN); - else if ((preferences & PdfWriter.PageLayoutTwoColumnLeft) != 0) - catalog.put(PdfName.PAGELAYOUT, PdfName.TWOCOLUMNLEFT); - else if ((preferences & PdfWriter.PageLayoutTwoColumnRight) != 0) - catalog.put(PdfName.PAGELAYOUT, PdfName.TWOCOLUMNRIGHT); - else if ((preferences & PdfWriter.PageLayoutTwoPageLeft) != 0) - catalog.put(PdfName.PAGELAYOUT, PdfName.TWOPAGELEFT); - else if ((preferences & PdfWriter.PageLayoutTwoPageRight) != 0) - catalog.put(PdfName.PAGELAYOUT, PdfName.TWOPAGERIGHT); - if ((preferences & PdfWriter.PageModeUseNone) != 0) - catalog.put(PdfName.PAGEMODE, PdfName.USENONE); - else if ((preferences & PdfWriter.PageModeUseOutlines) != 0) - catalog.put(PdfName.PAGEMODE, PdfName.USEOUTLINES); - else if ((preferences & PdfWriter.PageModeUseThumbs) != 0) - catalog.put(PdfName.PAGEMODE, PdfName.USETHUMBS); - else if ((preferences & PdfWriter.PageModeFullScreen) != 0) - catalog.put(PdfName.PAGEMODE, PdfName.FULLSCREEN); - else if ((preferences & PdfWriter.PageModeUseOC) != 0) - catalog.put(PdfName.PAGEMODE, PdfName.USEOC); - else if ((preferences & PdfWriter.PageModeUseAttachments) != 0) - catalog.put(PdfName.PAGEMODE, PdfName.USEATTACHMENTS); - if ((preferences & PdfWriter.ViewerPreferencesMask) == 0) - return; - PdfDictionary vp = new PdfDictionary(); - if ((preferences & PdfWriter.HideToolbar) != 0) - vp.put(PdfName.HIDETOOLBAR, PdfBoolean.PDFTRUE); - if ((preferences & PdfWriter.HideMenubar) != 0) - vp.put(PdfName.HIDEMENUBAR, PdfBoolean.PDFTRUE); - if ((preferences & PdfWriter.HideWindowUI) != 0) - vp.put(PdfName.HIDEWINDOWUI, PdfBoolean.PDFTRUE); - if ((preferences & PdfWriter.FitWindow) != 0) - vp.put(PdfName.FITWINDOW, PdfBoolean.PDFTRUE); - if ((preferences & PdfWriter.CenterWindow) != 0) - vp.put(PdfName.CENTERWINDOW, PdfBoolean.PDFTRUE); - if ((preferences & PdfWriter.DisplayDocTitle) != 0) - vp.put(PdfName.DISPLAYDOCTITLE, PdfBoolean.PDFTRUE); - if ((preferences & PdfWriter.NonFullScreenPageModeUseNone) != 0) - vp.put(PdfName.NONFULLSCREENPAGEMODE, PdfName.USENONE); - else if ((preferences & PdfWriter.NonFullScreenPageModeUseOutlines) != 0) - vp.put(PdfName.NONFULLSCREENPAGEMODE, PdfName.USEOUTLINES); - else if ((preferences & PdfWriter.NonFullScreenPageModeUseThumbs) != 0) - vp.put(PdfName.NONFULLSCREENPAGEMODE, PdfName.USETHUMBS); - else if ((preferences & PdfWriter.NonFullScreenPageModeUseOC) != 0) - vp.put(PdfName.NONFULLSCREENPAGEMODE, PdfName.USEOC); - if ((preferences & PdfWriter.DirectionL2R) != 0) - vp.put(PdfName.DIRECTION, PdfName.L2R); - else if ((preferences & PdfWriter.DirectionR2L) != 0) - vp.put(PdfName.DIRECTION, PdfName.R2L); - if ((preferences & PdfWriter.PrintScalingNone) != 0) - vp.put(PdfName.PRINTSCALING, PdfName.NONE); - catalog.put(PdfName.VIEWERPREFERENCES, vp); - } - - /** - * @param preferences - */ - public void setViewerPreferences(int preferences) { - setViewerPreferences(preferences, catalog); - } - - /** - * @return an int that contains the Viewer Preferences. - */ - public int getViewerPreferences() { - int prefs = 0; - PdfName name = null; - PdfObject obj = getPdfObjectRelease(catalog.get(PdfName.PAGELAYOUT)); - if (obj != null && obj.isName()) { - name = (PdfName)obj; - if (name.equals(PdfName.SINGLEPAGE)) - prefs |= PdfWriter.PageLayoutSinglePage; - else if (name.equals(PdfName.ONECOLUMN)) - prefs |= PdfWriter.PageLayoutOneColumn; - else if (name.equals(PdfName.TWOCOLUMNLEFT)) - prefs |= PdfWriter.PageLayoutTwoColumnLeft; - else if (name.equals(PdfName.TWOCOLUMNRIGHT)) - prefs |= PdfWriter.PageLayoutTwoColumnRight; - else if (name.equals(PdfName.TWOPAGELEFT)) - prefs |= PdfWriter.PageLayoutTwoPageLeft; - else if (name.equals(PdfName.TWOPAGERIGHT)) - prefs |= PdfWriter.PageLayoutTwoPageRight; - } - obj = getPdfObjectRelease(catalog.get(PdfName.PAGEMODE)); - if (obj != null && obj.isName()) { - name = (PdfName)obj; - if (name.equals(PdfName.USENONE)) - prefs |= PdfWriter.PageModeUseNone; - else if (name.equals(PdfName.USEOUTLINES)) - prefs |= PdfWriter.PageModeUseOutlines; - else if (name.equals(PdfName.USETHUMBS)) - prefs |= PdfWriter.PageModeUseThumbs; - else if (name.equals(PdfName.USEOC)) - prefs |= PdfWriter.PageModeUseOC; - else if (name.equals(PdfName.USEATTACHMENTS)) - prefs |= PdfWriter.PageModeUseAttachments; - } - obj = getPdfObjectRelease(catalog.get(PdfName.VIEWERPREFERENCES)); - if (obj == null || !obj.isDictionary()) - return prefs; - PdfDictionary vp = (PdfDictionary)obj; - for (int k = 0; k < vpnames.length; ++k) { - obj = getPdfObject(vp.get(vpnames[k])); - if (obj != null && "true".equals(obj.toString())) - prefs |= vpints[k]; - } - obj = getPdfObjectRelease(vp.get(PdfName.PRINTSCALING)); - if (PdfName.NONE.equals(obj)) - prefs |= PdfWriter.PrintScalingNone; - obj = getPdfObjectRelease(vp.get(PdfName.NONFULLSCREENPAGEMODE)); - if (obj != null && obj.isName()) { - name = (PdfName)obj; - if (name.equals(PdfName.USENONE)) - prefs |= PdfWriter.NonFullScreenPageModeUseNone; - else if (name.equals(PdfName.USEOUTLINES)) - prefs |= PdfWriter.NonFullScreenPageModeUseOutlines; - else if (name.equals(PdfName.USETHUMBS)) - prefs |= PdfWriter.NonFullScreenPageModeUseThumbs; - else if (name.equals(PdfName.USEOC)) - prefs |= PdfWriter.NonFullScreenPageModeUseOC; - } - obj = getPdfObjectRelease(vp.get(PdfName.DIRECTION)); - if (obj != null && obj.isName()) { - name = (PdfName)obj; - if (name.equals(PdfName.L2R)) - prefs |= PdfWriter.DirectionL2R; - else if (name.equals(PdfName.R2L)) - prefs |= PdfWriter.DirectionR2L; - } - return prefs; - } - - /** - * Getter for property appendable. - * @return Value of property appendable. - */ - public boolean isAppendable() { - return this.appendable; - } - - /** - * Setter for property appendable. - * @param appendable New value of property appendable. - */ - public void setAppendable(boolean appendable) { - this.appendable = appendable; - if (appendable) - getPdfObject(trailer.get(PdfName.ROOT)); - } - - /** - * Getter for property newXrefType. - * @return Value of property newXrefType. - */ - public boolean isNewXrefType() { - return newXrefType; - } - - /** - * Getter for property fileLength. - * @return Value of property fileLength. - */ - public int getFileLength() { - return fileLength; - } - - /** - * Getter for property hybridXref. - * @return Value of property hybridXref. - */ - public boolean isHybridXref() { - return hybridXref; - } - - static class PageRefs { - private PdfReader reader; - private IntHashtable refsp; - private ArrayList refsn; - private ArrayList pageInh; - private int lastPageRead = -1; - private int sizep; - - private PageRefs(PdfReader reader) throws IOException { - this.reader = reader; - if (reader.partial) { - refsp = new IntHashtable(); - PdfNumber npages = (PdfNumber)PdfReader.getPdfObjectRelease(reader.rootPages.get(PdfName.COUNT)); - sizep = npages.intValue(); - } - else { - readPages(); - } - } - - PageRefs(PageRefs other, PdfReader reader) { - this.reader = reader; - this.sizep = other.sizep; - if (other.refsn != null) { - refsn = new ArrayList(other.refsn); - for (int k = 0; k < refsn.size(); ++k) { - refsn.set(k, duplicatePdfObject((PdfObject)refsn.get(k), reader)); - } - } - else - this.refsp = (IntHashtable)other.refsp.clone(); - } - - int size() { - if (refsn != null) - return refsn.size(); - else - return sizep; - } - - void readPages() throws IOException { - if (refsn != null) - return; - refsp = null; - refsn = new ArrayList(); - pageInh = new ArrayList(); - iteratePages((PRIndirectReference)reader.catalog.get(PdfName.PAGES)); - pageInh = null; - reader.rootPages.put(PdfName.COUNT, new PdfNumber(refsn.size())); - } - - void reReadPages() throws IOException { - refsn = null; - readPages(); - } - - /** Gets the dictionary that represents a page. - * @param pageNum the page number. 1 is the first - * @return the page dictionary - */ - public PdfDictionary getPageN(int pageNum) { - PRIndirectReference ref = getPageOrigRef(pageNum); - return (PdfDictionary)PdfReader.getPdfObject(ref); - } - - /** - * @param pageNum - * @return a dictionary object - */ - public PdfDictionary getPageNRelease(int pageNum) { - PdfDictionary page = getPageN(pageNum); - releasePage(pageNum); - return page; - } - - /** - * @param pageNum - * @return an indirect reference - */ - public PRIndirectReference getPageOrigRefRelease(int pageNum) { - PRIndirectReference ref = getPageOrigRef(pageNum); - releasePage(pageNum); - return ref; - } - - /** Gets the page reference to this page. - * @param pageNum the page number. 1 is the first - * @return the page reference - */ - public PRIndirectReference getPageOrigRef(int pageNum) { - try { - --pageNum; - if (pageNum < 0 || pageNum >= size()) - return null; - if (refsn != null) - return (PRIndirectReference)refsn.get(pageNum); - else { - int n = refsp.get(pageNum); - if (n == 0) { - PRIndirectReference ref = getSinglePage(pageNum); - if (reader.lastXrefPartial == -1) - lastPageRead = -1; - else - lastPageRead = pageNum; - reader.lastXrefPartial = -1; - refsp.put(pageNum, ref.getNumber()); - return ref; - } - else { - if (lastPageRead != pageNum) - lastPageRead = -1; - return new PRIndirectReference(reader, n); - } - } - } - catch (Exception e) { - throw new ExceptionConverter(e); - } - } - - /** - * @param pageNum - */ - public void releasePage(int pageNum) { - if (refsp == null) - return; - --pageNum; - if (pageNum < 0 || pageNum >= size()) - return; - if (pageNum != lastPageRead) - return; - lastPageRead = -1; - reader.lastXrefPartial = refsp.get(pageNum); - reader.releaseLastXrefPartial(); - refsp.remove(pageNum); - } - - /** - * - */ - public void resetReleasePage() { - if (refsp == null) - return; - lastPageRead = -1; - } - - void insertPage(int pageNum, PRIndirectReference ref) { - --pageNum; - if (refsn != null) { - if (pageNum >= refsn.size()) - refsn.add(ref); - else - refsn.add(pageNum, ref); - } - else { - ++sizep; - lastPageRead = -1; - if (pageNum >= size()) { - refsp.put(size(), ref.getNumber()); - } - else { - IntHashtable refs2 = new IntHashtable((refsp.size() + 1) * 2); - for (Iterator it = refsp.getEntryIterator(); it.hasNext();) { - IntHashtable.IntHashtableEntry entry = (IntHashtable.IntHashtableEntry)it.next(); - int p = entry.getKey(); - refs2.put(p >= pageNum ? p + 1 : p, entry.getValue()); - } - refs2.put(pageNum, ref.getNumber()); - refsp = refs2; - } - } - } - - private void pushPageAttributes(PdfDictionary nodePages) { - PdfDictionary dic = new PdfDictionary(); - if (pageInh.size() != 0) { - dic.putAll((PdfDictionary)pageInh.get(pageInh.size() - 1)); - } - for (int k = 0; k < pageInhCandidates.length; ++k) { - PdfObject obj = nodePages.get(pageInhCandidates[k]); - if (obj != null) - dic.put(pageInhCandidates[k], obj); - } - pageInh.add(dic); - } - - private void popPageAttributes() { - pageInh.remove(pageInh.size() - 1); - } - - private void iteratePages(PRIndirectReference rpage) throws IOException { - PdfDictionary page = (PdfDictionary)getPdfObject(rpage); - PdfArray kidsPR = (PdfArray)getPdfObject(page.get(PdfName.KIDS)); - if (kidsPR == null) { - page.put(PdfName.TYPE, PdfName.PAGE); - PdfDictionary dic = (PdfDictionary)pageInh.get(pageInh.size() - 1); - PdfName key; - for (Iterator i = dic.getKeys().iterator(); i.hasNext();) { - key = (PdfName)i.next(); - if (page.get(key) == null) - page.put(key, dic.get(key)); - } - if (page.get(PdfName.MEDIABOX) == null) { - PdfArray arr = new PdfArray(new float[]{0,0,PageSize.LETTER.right(),PageSize.LETTER.top()}); - page.put(PdfName.MEDIABOX, arr); - } - refsn.add(rpage); - } - else { - page.put(PdfName.TYPE, PdfName.PAGES); - pushPageAttributes(page); - ArrayList kids = kidsPR.getArrayList(); - for (int k = 0; k < kids.size(); ++k){ - PdfObject obj = (PdfObject)kids.get(k); - if (!obj.isIndirect()) { - while (k < kids.size()) - kids.remove(k); - break; - } - iteratePages((PRIndirectReference)obj); - } - popPageAttributes(); - } - } - - protected PRIndirectReference getSinglePage(int n) throws IOException { - PdfDictionary acc = new PdfDictionary(); - PdfDictionary top = reader.rootPages; - int base = 0; - while (true) { - for (int k = 0; k < pageInhCandidates.length; ++k) { - PdfObject obj = top.get(pageInhCandidates[k]); - if (obj != null) - acc.put(pageInhCandidates[k], obj); - } - PdfArray kids = (PdfArray)PdfReader.getPdfObjectRelease(top.get(PdfName.KIDS)); - for (Iterator it = kids.listIterator(); it.hasNext();) { - PRIndirectReference ref = (PRIndirectReference)it.next(); - PdfDictionary dic = (PdfDictionary)getPdfObject(ref); - int last = reader.lastXrefPartial; - PdfObject count = getPdfObjectRelease(dic.get(PdfName.COUNT)); - reader.lastXrefPartial = last; - int acn = 1; - if (count != null && count.type() == PdfObject.NUMBER) - acn = ((PdfNumber)count).intValue(); - if (n < base + acn) { - if (count == null) { - dic.mergeDifferent(acc); - return ref; - } - reader.releaseLastXrefPartial(); - top = dic; - break; - } - reader.releaseLastXrefPartial(); - base += acn; - } - } - } - - private void selectPages(List pagesToKeep) { - IntHashtable pg = new IntHashtable(); - ArrayList finalPages = new ArrayList(); - int psize = size(); - for (Iterator it = pagesToKeep.iterator(); it.hasNext();) { - Integer pi = (Integer)it.next(); - int p = pi.intValue(); - if (p >= 1 && p <= psize && pg.put(p, 1) == 0) - finalPages.add(pi); - } - if (reader.partial) { - for (int k = 1; k <= psize; ++k) { - getPageOrigRef(k); - resetReleasePage(); - } - } - PRIndirectReference parent = (PRIndirectReference)reader.catalog.get(PdfName.PAGES); - PdfDictionary topPages = (PdfDictionary)PdfReader.getPdfObject(parent); - ArrayList newPageRefs = new ArrayList(finalPages.size()); - PdfArray kids = new PdfArray(); - for (int k = 0; k < finalPages.size(); ++k) { - int p = ((Integer)finalPages.get(k)).intValue(); - PRIndirectReference pref = getPageOrigRef(p); - resetReleasePage(); - kids.add(pref); - newPageRefs.add(pref); - getPageN(p).put(PdfName.PARENT, parent); - } - AcroFields af = reader.getAcroFields(); - boolean removeFields = (af.getFields().size() > 0); - for (int k = 1; k <= psize; ++k) { - if (!pg.containsKey(k)) { - if (removeFields) - af.removeFieldsFromPage(k); - PRIndirectReference pref = getPageOrigRef(k); - int nref = pref.getNumber(); - reader.xrefObj.set(nref, null); - if (reader.partial) { - reader.xref[nref * 2] = -1; - reader.xref[nref * 2 + 1] = 0; - } - } - } - topPages.put(PdfName.COUNT, new PdfNumber(finalPages.size())); - topPages.put(PdfName.KIDS, kids); - refsp = null; - refsn = newPageRefs; - } - } - - PdfIndirectReference getCryptoRef() { - if (cryptoRef == null) - return null; - return new PdfIndirectReference(0, cryptoRef.getNumber(), cryptoRef.getGeneration()); - } -} \ No newline at end of file -- cgit v1.2.3