Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* $Id: PdfReader.java,v 1.51 2004/12/27 07:09:47 blowagie 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 com.lowagie.text.Rectangle;
import com.lowagie.text.PageSize;
import com.lowagie.text.StringCompare;
/** Reads a PDF document and prepares it to import pages to our
* document. This class is thread safe; this means that
* a single instance can serve as many output documents as needed and can even be static.
* @author Paulo Soares ([email protected])
* @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 boolean newXrefType;
protected PdfObject xrefObj[];
protected PdfDictionary trailer;
protected PdfDictionary pages[];
protected PdfDictionary catalog;
protected PRIndirectReference pageRefs[];
protected PRAcroForm acroForm = null;
protected boolean acroFormParsed = false;
protected ArrayList pageInh;
protected int pagesCount;
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;
/**
* 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);
}
/** 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.pagesCount = reader.pagesCount;
this.tokens = reader.tokens;
this.decrypt = reader.decrypt;
this.pValue = reader.pValue;
this.rValue = reader.rValue;
this.xrefObj = new PdfObject[reader.xrefObj.length];
for (int k = 0; k < reader.xrefObj.length; ++k) {
this.xrefObj[k] = duplicatePdfObject(reader.xrefObj[k], this);
}
this.pageRefs = new PRIndirectReference[reader.pageRefs.length];
this.pages = new PdfDictionary[reader.pages.length];
for (int k = 0; k < reader.pageRefs.length; ++k) {
this.pageRefs[k] = (PRIndirectReference)duplicatePdfObject(reader.pageRefs[k], this);
this.pages[k] = (PdfDictionary)getPdfObject(this.pageRefs[k]);
}
this.trailer = (PdfDictionary)duplicatePdfObject(reader.trailer, this);
this.catalog = (PdfDictionary)getPdfObject(trailer.get(PdfName.ROOT));
this.fileLength = reader.fileLength;
}
/** 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, xrefObj, pages);
}
/** Gets the number of pages in the document.
* @return the number of pages in the document
*/
public int getNumberOfPages() {
return pages.length;
}
/** 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 he 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) {
PdfDictionary page = pages[index - 1];
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) {
Rectangle rect = getPageSize(index);
int rotation = getPageRotation(index);
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) {
PdfDictionary page = pages[index - 1];
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 = pages[index - 1];
PdfArray cropBox = (PdfArray)getPdfObject(page.get(PdfName.CROPBOX));
if (cropBox == null)
return getPageSize(index);
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 = pages[index - 1];
PdfArray box = null;
if (boxName.equals("trim"))
box = (PdfArray)getPdfObject(page.get(PdfName.TRIMBOX));
else if (boxName.equals("art"))
box = (PdfArray)getPdfObject(page.get(PdfName.ARTBOX));
else if (boxName.equals("bleed"))
box = (PdfArray)getPdfObject(page.get(PdfName.BLEEDBOX));
else if (boxName.equals("crop"))
box = (PdfArray)getPdfObject(page.get(PdfName.CROPBOX));
else if (boxName.equals("media"))
box = (PdfArray)getPdfObject(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 = (PdfObject)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
}
}
}
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;
}
/**
*/
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();
decrypt = new PdfEncryption();
//check by user password
decrypt.setupByUserPassword(documentID, password, oValue, pValue, rValue == 3);
if (!equalsArray(uValue, decrypt.userKey, rValue == 3 ? 16 : 32)) {
//check by owner password
decrypt.setupByOwnerPassword(documentID, password, uValue, oValue, pValue, rValue == 3);
if (!Arrays.equals(uValue, decrypt.userKey)) {
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())
xrefObj[((PRIndirectReference)encDic).getNumber()] = null;
}
/** 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;
PRIndirectReference ref = (PRIndirectReference)obj;
int idx = ref.getNumber();
boolean appendable = ref.getReader().appendable;
obj = ref.getReader().xrefObj[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;
}
}
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);
}
protected 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);
}
protected void popPageAttributes() {
pageInh.remove(pageInh.size() - 1);
}
protected void iteratePages(PRIndirectReference rpage) throws IOException {
if (pagesCount >= pages.length) // just in case we have more pages than declared
return;
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);
}
pages[pagesCount] = page;
pageRefs[pagesCount++] = rpage;
}
else {
page.put(PdfName.TYPE, PdfName.PAGES);
pushPageAttributes(page);
ArrayList kids = kidsPR.getArrayList();
for (int k = 0; k < kids.size(); ++k){
iteratePages((PRIndirectReference)kids.get(k));
}
popPageAttributes();
}
}
protected void readPages() throws IOException {
pageInh = new ArrayList();
catalog = (PdfDictionary)getPdfObject(trailer.get(PdfName.ROOT));
PdfDictionary rootPages = (PdfDictionary)getPdfObject(catalog.get(PdfName.PAGES));
PdfNumber count = (PdfNumber)getPdfObject(rootPages.get(PdfName.COUNT));
pages = new PdfDictionary[count.intValue()];
pageRefs = new PRIndirectReference[pages.length];
pagesCount = 0;
iteratePages((PRIndirectReference)catalog.get(PdfName.PAGES));
pageInh = null;
}
protected void PRSimpleRecursive(PdfObject obj) throws IOException {
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();
PRSimpleRecursive(dic.get(key));
}
break;
case PdfObject.ARRAY:
ArrayList list = ((PdfArray)obj).getArrayList();
for (int k = 0; k < list.size(); ++k) {
PRSimpleRecursive((PdfObject)list.get(k));
}
break;
case PdfObject.INDIRECT:
PRIndirectReference ref = (PRIndirectReference)obj;
int num = ref.getNumber();
if (!visited[num]) {
visited[num] = true;
newHits.put(num, 1);
}
break;
}
}
protected void readDocObj() throws IOException {
ArrayList streams = new ArrayList();
xrefObj = new PdfObject[xref.length / 2];
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[k / 2] = obj;
}
int fileLength = tokens.length();
byte tline[] = new byte[16];
for (int k = 0; k < streams.size(); ++k) {
PRStream stream = (PRStream)streams.get(k);
PdfNumber length = (PdfNumber)getPdfObject(stream.get(PdfName.LENGTH));
int start = stream.getOffset();
int streamLength = length.intValue();
boolean calc = false;
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;
}
if (calc) {
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);
}
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[n], h);
xrefObj[n] = null;
}
objStmMark = null;
}
xref = null;
}
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[objNumber[k]] = obj;
}
}
}
finally {
tokens = saveTokens;
}
}
static PdfObject killIndirect(PdfObject obj) {
if (obj == null || obj.isNull())
return null;
PdfObject ret = getPdfObject(obj);
if (obj.isIndirect()) {
PRIndirectReference ref = (PRIndirectReference)obj;
ref.getReader().xrefObj[ref.getNumber()] = null;
}
return ret;
}
protected void readXref() throws IOException {
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);
int ch;
do {
ch = tokens.read();
} while (ch != -1 && ch != 't');
if (ch == -1)
throw new IOException("Unexpected end of file.");
tokens.backOnePosition(ch);
tokens.nextValidToken();
if (!tokens.getStringValue().equals("trailer"))
throw new IOException("trailer not found.");
trailer = (PdfDictionary)readPRObject();
PdfObject xrs = trailer.get(PdfName.XREFSTM);
if (xrs != null && xrs.isNumber()) {
int loc = ((PdfNumber)xrs).intValue();
try {
if (readXRefStream(loc)) {
newXrefType = true;
}
else
xref = null;
}
catch (Exception e) {
xref = null;
}
}
PdfNumber xrefSize = (PdfNumber)trailer.get(PdfName.SIZE);
int size = xrefSize.intValue() * 2;
if (xref == null)
xref = new int[size];
else {
if (xref.length < size) { // fix incorrect size
int xref2[] = new int[size];
System.arraycopy(xref, 0, xref2, 0, xref.length);
xref = xref2;
}
}
tokens.seek(startxref);
readXrefSection();
PdfDictionary trailer2 = trailer;
while (true) {
PdfNumber prev = (PdfNumber)trailer2.get(PdfName.PREV);
if (prev == null)
break;
tokens.seek(prev.intValue());
readXrefSection();
trailer2 = (PdfDictionary)readPRObject();
}
}
protected void 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);
}
if (xref.length < end * 2) { // fix incorrect size
int xref2[] = new int[end * 2];
System.arraycopy(xref, 0, xref2, 0, xref.length);
xref = xref2;
}
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");
}
}
}
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
if (xref == null)
xref = new int[size * 2];
else if (xref.length < size * 2) {
int x2[] = new int[size * 2];
System.arraycopy(xref, 0, x2, 0, xref.length);
xref = x2;
}
if (objStmMark == null)
objStmMark = new HashMap();
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();
if (xref.length < (start + length) * 2) {
int x2[] = new int[(start + length) * 2];
System.arraycopy(xref, 0, x2, 0, xref.length);
xref = x2;
}
while (length-- > 0) {
int total = 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;
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 {
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;
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());
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());
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;
}
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) {
if (pageNum > pages.length) return null;
if (appendable)
pages[pageNum - 1].setIndRef(pageRefs[pageNum - 1]);
return pages[pageNum - 1];
}
/** 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) {
if (pageNum > pageRefs.length) return null;
return pageRefs[pageNum - 1];
}
/** 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 = getPageN(pageNum);
if (page == null)
return null;
PdfObject contents = getPdfObject(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 = getPdfObject((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 = xrefObj[xr];
xrefObj[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) {
for (int k = xrefObj.length - 1; k > 0; --k) {
if (xrefObj[k] == null) {
freeXref = k;
break;
}
}
if (freeXref == -1) {
PdfObject temp[] = new PdfObject[xrefObj.length + 10];
System.arraycopy(xrefObj, 0, temp, 0, xrefObj.length);
freeXref = xrefObj.length;
xrefObj = temp;
}
}
page.put(PdfName.CONTENTS, new PRIndirectReference(this, freeXref));
xrefObj[freeXref] = new PRStream(this, content);
}
/** Get the content from a stream.
* @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 {
PdfReader reader = stream.getReader();
PdfObject filter = getPdfObject(stream.get(PdfName.FILTER));
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);
}
}
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 = getPdfObject(stream.get(PdfName.DECODEPARMS));
if (dpo == null || (!dpo.isDictionary() && !dpo.isArray()))
dpo = getPdfObject(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.getPdfObject((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.
* @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){}
}
}
/** Eliminates shared streams if they exist. */
public void eliminateSharedStreams() {
if (!sharedStreams)
return;
sharedStreams = false;
if (pages.length == 1)
return;
ArrayList newRefs = new ArrayList();
ArrayList newStreams = new ArrayList();
IntHashtable visited = new IntHashtable();
for (int k = 0; k < pages.length; ++k) {
PdfDictionary page = pages[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 {
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;
int start = 1;
for (int pass = 0; pass < 2; ++pass) {
for (int k = start; k < xrefObj.length; ++k) {
if (xrefObj[k] == null) {
int p = newStreams.size() - 1;
xrefObj[k] = (PRStream)newStreams.get(p);
PRIndirectReference ref = (PRIndirectReference)newRefs.get(p);
ref.setNumber(k, 0);
if (p == 0)
return;
newStreams.remove(p);
}
}
start = xrefObj.length;
PdfObject nxo[] = new PdfObject[xrefObj.length + newStreams.size()];
System.arraycopy(xrefObj, 0, nxo, 0, xrefObj.length);
xrefObj = nxo;
}
}
/** 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.length;
}
/**
* 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 = getPdfObject(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 = getPdfObject(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.length; ++k) {
PdfObject obj = xrefObj[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);
++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)));
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.length; ++k) {
PdfObject obj = xrefObj[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)getPdfObject(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;
PdfName newName = new PdfName(ns);
dic.put(PdfName.BASEFONT, newName);
fd.put(PdfName.FONTNAME, newName);
++total;
}
}
return total;
}
private static PdfArray getNameArray(PdfObject obj) {
if (obj == null)
return null;
obj = getPdfObject(obj);
if (obj.isArray())
return (PdfArray)obj;
else if (obj.isDictionary()) {
PdfObject arr2 = getPdfObject(((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)getPdfObject(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)getPdfObject(catalog.get(PdfName.NAMES));
dic = (PdfDictionary)getPdfObject(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 static void replaceNamedDestination(PdfObject obj, HashMap names) {
if (obj != null && obj.isDictionary()) {
PdfObject ob2 = getPdfObject(((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);
}
else if ((ob2 = getPdfObject(((PdfDictionary)obj).get(PdfName.A))) != null) {
PdfDictionary dic = (PdfDictionary)ob2;
PdfName type = (PdfName)getPdfObject(dic.get(PdfName.S));
if (PdfName.GOTO.equals(type)) {
ob2 = getPdfObject(dic.get(PdfName.D));
if (ob2.isName())
name = PdfName.decodeName(ob2.toString());
else if (ob2.isString())
name = ob2.toString();
PdfArray dest = (PdfArray)names.get(name);
if (dest != null)
dic.put(PdfName.D, dest);
}
}
}
}
/**
* Removes all the fields from the document.
*/
public void removeFields() {
for (int k = 0; k < pages.length; ++k) {
PdfDictionary page = pages[k];
PdfArray annots = (PdfArray)getPdfObject(page.get(PdfName.ANNOTS));
if (annots == null)
continue;
ArrayList arr = annots.getArrayList();
for (int j = 0; j < arr.size(); ++j) {
PdfDictionary annot = (PdfDictionary)getPdfObject((PdfObject)arr.get(j));
if (PdfName.WIDGET.equals(annot.get(PdfName.SUBTYPE)))
arr.remove(j--);
}
if (arr.isEmpty())
page.remove(PdfName.ANNOTS);
}
catalog.remove(PdfName.ACROFORM);
}
/**
* Removes all the annotations and fields from the document.
*/
public void removeAnnotations() {
for (int k = 0; k < pages.length; ++k) {
pages[k].remove(PdfName.ANNOTS);
}
catalog.remove(PdfName.ACROFORM);
}
private void iterateBookmarks(PdfDictionary outline, HashMap names) {
while (outline != null) {
replaceNamedDestination(outline, names);
PdfDictionary first = (PdfDictionary)getPdfObject(outline.get(PdfName.FIRST));
if (first != null) {
iterateBookmarks(first, names);
}
outline = (PdfDictionary)getPdfObject(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 = 0; k < pages.length; ++k) {
PdfArray arr = (PdfArray)getPdfObject(pages[k].get(PdfName.ANNOTS));
if (arr == null)
continue;
ArrayList list = arr.getArrayList();
for (int an = 0; an < list.size(); ++an) {
PdfObject obj = getPdfObject((PdfObject)list.get(an));
replaceNamedDestination(obj, names);
}
}
PdfDictionary outlines = (PdfDictionary)getPdfObject(catalog.get(PdfName.OUTLINES));
if (outlines == null)
return;
iterateBookmarks((PdfDictionary)getPdfObject(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;
}
}
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.length || xrefObj[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 (xrefObj[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(getPdfObject(ref), hits);
}
}
}
}
/** Removes all the unreachable objects.
* @return the number of indirect objects removed
*/
public int removeUnusedObjects() {
boolean hits[] = new boolean[xrefObj.length];
removeUnusedNode(trailer, hits);
int total = 0;
for (int k = 1; k < hits.length; ++k) {
if (!hits[k] && xrefObj[k] != null) {
xrefObj[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)getPdfObject(catalog.get(PdfName.NAMES));
if (names == null)
return null;
PdfDictionary js = (PdfDictionary)getPdfObject(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)getPdfObject((PdfIndirectReference)jscript.get(sortedNames[k]));
if (j == null)
continue;
PdfObject obj = getPdfObject(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.
* @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.
* @param pagesToKeep the pages to keep in the document
*/
public void selectPages(List pagesToKeep) {
IntHashtable pg = new IntHashtable();
ArrayList finalPages = new ArrayList();
for (Iterator it = pagesToKeep.iterator(); it.hasNext();) {
Integer pi = (Integer)it.next();
int p = pi.intValue();
if (p >= 1 && p <= pages.length && pg.put(p, 1) == 0)
finalPages.add(pi);
}
PRIndirectReference parent = (PRIndirectReference)catalog.get(PdfName.PAGES);
PdfDictionary topPages = (PdfDictionary)getPdfObject(parent);
PRIndirectReference newPageRefs[] = new PRIndirectReference[finalPages.size()];
PdfDictionary newPages[] = new PdfDictionary[finalPages.size()];
topPages.put(PdfName.COUNT, new PdfNumber(finalPages.size()));
PdfArray kids = new PdfArray();
for (int k = 0; k < finalPages.size(); ++k) {
int p = ((Integer)finalPages.get(k)).intValue() - 1;
kids.add(newPageRefs[k] = pageRefs[p]);
newPages[k] = pages[p];
newPages[k].put(PdfName.PARENT, parent);
pageRefs[p] = null;
}
topPages.put(PdfName.KIDS, kids);
AcroFields af = getAcroFields();
for (int k = 0; k < pageRefs.length; ++k) {
PRIndirectReference ref = pageRefs[k];
if (ref != null) {
af.removeFieldsFromPage(k + 1);
xrefObj[ref.getNumber()] = null;
}
}
pages = newPages;
pageRefs = newPageRefs;
removeUnusedObjects();
}
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);
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);
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);
catalog.put(PdfName.VIEWERPREFERENCES, vp);
}
public void setViewerPreferences(int preferences) {
setViewerPreferences(preferences, catalog);
}
public int getViewerPreferences() {
int prefs = 0;
PdfName name = null;
PdfObject obj = getPdfObject(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;
}
obj = getPdfObject(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;
}
obj = getPdfObject(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(catalog.get(vpnames[k]));
if (obj != null && "true".equals(obj.toString()))
prefs |= vpints[k];
}
obj = getPdfObject(catalog.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 = getPdfObject(catalog.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;
}
}