iTextSharp-LGPL/src/core/iTextSharp/text/pdf/PdfReader.cs

3464 lines
139 KiB
C#

using System;
using System.Collections;
using System.Security.Cryptography;
using System.Net;
using System.Text;
using System.IO;
using iTextSharp.text;
using iTextSharp.text.exceptions;
using iTextSharp.text.pdf.intern;
using iTextSharp.text.pdf.interfaces;
using System.util;
using System.util.zlib;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Cms;
using Org.BouncyCastle.X509;
/*
* $Id: PdfReader.cs,v 1.50 2008/05/13 11:25:23 psoares33 Exp $
*
*
* 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/
*/
namespace iTextSharp.text.pdf {
/** Reads a PDF document.
* @author Paulo Soares (psoares@consiste.pt)
* @author Kazuya Ujihara
*/
public class PdfReader : IPdfViewerPreferences {
static PdfName[] pageInhCandidates = {
PdfName.MEDIABOX, PdfName.ROTATE, PdfName.RESOURCES, PdfName.CROPBOX
};
static byte[] endstream = PdfEncodings.ConvertToBytes("endstream", null);
static byte[] endobj = PdfEncodings.ConvertToBytes("endobj", null);
protected internal PRTokeniser tokens;
// Each xref pair is a position
// type 0 -> -1, 0
// type 1 -> offset, 0
// type 2 -> index, obj num
protected internal int[] xref;
protected internal Hashtable objStmMark;
protected internal IntHashtable objStmToOffset;
protected internal bool newXrefType;
private ArrayList xrefObj;
PdfDictionary rootPages;
protected internal PdfDictionary trailer;
protected internal PdfDictionary catalog;
protected internal PageRefs pageRefs;
protected internal PRAcroForm acroForm = null;
protected internal bool acroFormParsed = false;
protected internal bool encrypted = false;
protected internal bool rebuilt = false;
protected internal int freeXref;
protected internal bool tampered = false;
protected internal int lastXref;
protected internal int eofPos;
protected internal char pdfVersion;
protected internal PdfEncryption decrypt;
protected internal byte[] password = null; //added by ujihara for decryption
protected ICipherParameters certificateKey = null; //added by Aiken Sam for certificate decryption
protected X509Certificate certificate = null; //added by Aiken Sam for certificate decryption
private bool ownerPasswordUsed;
protected internal ArrayList strings = new ArrayList();
protected internal bool sharedStreams = true;
protected internal bool consolidateNamedDestinations = false;
protected internal int rValue;
protected internal int pValue;
private int objNum;
private int objGen;
private int fileLength;
private bool hybridXref;
private int lastXrefPartial = -1;
private bool partial;
private PRIndirectReference cryptoRef;
private PdfViewerPreferencesImp viewerPreferences = new PdfViewerPreferencesImp();
private bool encryptionError;
/**
* Holds value of property appendable.
*/
private bool appendable;
protected internal PdfReader() {
}
/** Reads and parses a PDF document.
* @param filename the file name of the document
* @throws IOException on error
*/
public PdfReader(String filename) : 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) {
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) : 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) {
password = ownerPassword;
tokens = new PRTokeniser(pdfIn);
ReadPdf();
}
/** Reads and parses a PDF document.
* @param filename the file name of the document
* @param certificate the certificate to read the document
* @param certificateKey the private key of the certificate
* @param certificateKeyProvider the security provider for certificateKey
* @throws IOException on error
*/
public PdfReader(String filename, X509Certificate certificate, ICipherParameters certificateKey) {
this.certificate = certificate;
this.certificateKey = certificateKey;
tokens = new PRTokeniser(filename);
ReadPdf();
}
/** Reads and parses a PDF document.
* @param url the Uri of the document
* @throws IOException on error
*/
public PdfReader(Uri url) : this(url, null) {
}
/** Reads and parses a PDF document.
* @param url the Uri of the document
* @param ownerPassword the password to read the document
* @throws IOException on error
*/
public PdfReader(Uri url, byte[] ownerPassword) {
password = ownerPassword;
tokens = new PRTokeniser(new RandomAccessFileOrArray(url));
ReadPdf();
}
/**
* Reads and parses a PDF document.
* @param is the <CODE>InputStream</CODE> 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(Stream isp, byte[] ownerPassword) {
password = ownerPassword;
tokens = new PRTokeniser(new RandomAccessFileOrArray(isp));
ReadPdf();
}
/**
* Reads and parses a PDF document.
* @param isp the <CODE>InputStream</CODE> containing the document. The stream is read to the
* end but is not closed
* @throws IOException on error
*/
public PdfReader(Stream isp) : this(isp, 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
* <CODE>PdfReader.Close()</CODE>, reopen is automatic.
* @param raf the document location
* @param ownerPassword the password or <CODE>null</CODE> for no password
* @throws IOException on error
*/
public PdfReader(RandomAccessFileOrArray raf, byte[] ownerPassword) {
password = ownerPassword;
partial = true;
tokens = new PRTokeniser(raf);
ReadPdfPartial();
}
/** Creates an independent duplicate.
* @param reader the <CODE>PdfReader</CODE> 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.SafeFile);
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.Count; ++k) {
this.xrefObj[k] = DuplicatePdfObject((PdfObject)reader.xrefObj[k], this);
}
this.pageRefs = new PageRefs(reader.pageRefs, this);
this.trailer = (PdfDictionary)DuplicatePdfObject(reader.trailer, this);
this.catalog = trailer.GetAsDict(PdfName.ROOT);
this.rootPages = catalog.GetAsDict(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);
this.ownerPasswordUsed = reader.ownerPasswordUsed;
}
/** Gets a new file instance of the original PDF
* document.
* @return a new file instance of the original PDF document
*/
public RandomAccessFileOrArray SafeFile {
get {
return tokens.SafeFile;
}
}
protected internal 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 NumberOfPages {
get {
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 Catalog {
get {
return catalog;
}
}
/** Returns the document's acroform, if it has one.
* @return the document's acroform
*/
public PRAcroForm AcroForm {
get {
if (!acroFormParsed) {
acroFormParsed = true;
PdfObject form = catalog.Get(PdfName.ACROFORM);
if (form != null) {
try {
acroForm = new PRAcroForm(this);
acroForm.ReadAcroForm((PdfDictionary)GetPdfObject(form));
}
catch {
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));
}
internal int GetPageRotation(PdfDictionary page) {
PdfNumber rotate = page.GetAsNumber(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 <CODE>Rectangle</CODE> with the value of the /MediaBox and the /Rotate key.
* @param index the page number. The first page is 1
* @return a <CODE>Rectangle</CODE>
*/
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 = page.GetAsArray(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 <CODE>Hashtable</CODE>
* of <CODE>String</CODE>.
* @return content of the document information dictionary
*/
public Hashtable Info {
get {
Hashtable map = new Hashtable();
PdfDictionary info = trailer.GetAsDict(PdfName.INFO);
if (info == null)
return map;
foreach (PdfName key in info.Keys) {
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[PdfName.DecodeName(key.ToString())] = value;
}
return map;
}
}
/** Normalizes a <CODE>Rectangle</CODE> so that llx and lly are smaller than urx and ury.
* @param box the original rectangle
* @return a normalized <CODE>Rectangle</CODE>
*/
public static Rectangle GetNormalizedRectangle(PdfArray box) {
float llx = ((PdfNumber)GetPdfObjectRelease(box[0])).FloatValue;
float lly = ((PdfNumber)GetPdfObjectRelease(box[1])).FloatValue;
float urx = ((PdfNumber)GetPdfObjectRelease(box[2])).FloatValue;
float ury = ((PdfNumber)GetPdfObjectRelease(box[3])).FloatValue;
return new Rectangle(Math.Min(llx, urx), Math.Min(lly, ury),
Math.Max(llx, urx), Math.Max(lly, ury));
}
protected internal virtual void ReadPdf() {
try {
fileLength = tokens.File.Length;
pdfVersion = tokens.CheckPdfHeader();
try {
ReadXref();
}
catch (Exception e) {
try {
rebuilt = true;
RebuildXref();
lastXref = -1;
}
catch (Exception ne) {
throw new InvalidPdfException("Rebuild failed: " + ne.Message + "; Original message: " + e.Message);
}
}
try {
ReadDocObj();
}
catch (Exception ne) {
if (ne is BadPasswordException)
throw new BadPasswordException(ne.Message);
if (rebuilt || encryptionError)
throw new InvalidPdfException(ne.Message);
rebuilt = true;
encrypted = false;
RebuildXref();
lastXref = -1;
ReadDocObj();
}
strings.Clear();
ReadPages();
EliminateSharedStreams();
RemoveUnusedObjects();
}
finally {
try {
tokens.Close();
}
catch {
// empty on purpose
}
}
}
protected internal void ReadPdfPartial() {
try {
fileLength = tokens.File.Length;
pdfVersion = tokens.CheckPdfHeader();
try {
ReadXref();
}
catch (Exception e) {
try {
rebuilt = true;
RebuildXref();
lastXref = -1;
}
catch (Exception ne) {
throw new InvalidPdfException("Rebuild failed: " + ne.Message + "; Original message: " + e.Message);
}
}
ReadDocObjPartial();
ReadPages();
}
catch (IOException e) {
try{tokens.Close();}catch{}
throw e;
}
}
private bool 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() {
if (encrypted)
return;
PdfObject encDic = trailer.Get(PdfName.ENCRYPT);
if (encDic == null || encDic.ToString().Equals("null"))
return;
encryptionError = true;
byte[] encryptionKey = null;
encrypted = true;
PdfDictionary enc = (PdfDictionary)GetPdfObject(encDic);
String s;
PdfObject o;
PdfArray documentIDs = trailer.GetAsArray(PdfName.ID);
byte[] documentID = null;
if (documentIDs != null) {
o = documentIDs[0];
strings.Remove(o);
s = o.ToString();
documentID = DocWriter.GetISOBytes(s);
if (documentIDs.Size > 1)
strings.Remove(documentIDs[1]);
}
// just in case we have a broken producer
if (documentID == null)
documentID = new byte[0];
byte[] uValue = null;
byte[] oValue = null;
int cryptoMode = PdfWriter.STANDARD_ENCRYPTION_40;
int lengthValue = 0;
PdfObject filter = GetPdfObjectRelease(enc.Get(PdfName.FILTER));
if (filter.Equals(PdfName.STANDARD)) {
s = enc.Get(PdfName.U).ToString();
strings.Remove(enc.Get(PdfName.U));
uValue = DocWriter.GetISOBytes(s);
s = enc.Get(PdfName.O).ToString();
strings.Remove(enc.Get(PdfName.O));
oValue = DocWriter.GetISOBytes(s);
o = enc.Get(PdfName.P);
if (!o.IsNumber())
throw new InvalidPdfException("Illegal P value.");
pValue = ((PdfNumber)o).IntValue;
o = enc.Get(PdfName.R);
if (!o.IsNumber())
throw new InvalidPdfException("Illegal R value.");
rValue = ((PdfNumber)o).IntValue;
switch (rValue) {
case 2:
cryptoMode = PdfWriter.STANDARD_ENCRYPTION_40;
break;
case 3:
o = enc.Get(PdfName.LENGTH);
if (!o.IsNumber())
throw new InvalidPdfException("Illegal Length value.");
lengthValue = ( (PdfNumber) o).IntValue;
if (lengthValue > 128 || lengthValue < 40 || lengthValue % 8 != 0)
throw new InvalidPdfException("Illegal Length value.");
cryptoMode = PdfWriter.STANDARD_ENCRYPTION_128;
break;
case 4:
PdfDictionary dic = (PdfDictionary)enc.Get(PdfName.CF);
if (dic == null)
throw new InvalidPdfException("/CF not found (encryption)");
dic = (PdfDictionary)dic.Get(PdfName.STDCF);
if (dic == null)
throw new InvalidPdfException("/StdCF not found (encryption)");
if (PdfName.V2.Equals(dic.Get(PdfName.CFM)))
cryptoMode = PdfWriter.STANDARD_ENCRYPTION_128;
else if (PdfName.AESV2.Equals(dic.Get(PdfName.CFM)))
cryptoMode = PdfWriter.ENCRYPTION_AES_128;
else
throw new UnsupportedPdfException("No compatible encryption found");
PdfObject em = enc.Get(PdfName.ENCRYPTMETADATA);
if (em != null && em.ToString().Equals("false"))
cryptoMode |= PdfWriter.DO_NOT_ENCRYPT_METADATA;
break;
default:
throw new UnsupportedPdfException("Unknown encryption type R = " + rValue);
}
} else if (filter.Equals(PdfName.PUBSEC)) {
bool foundRecipient = false;
byte[] envelopedData = null;
PdfArray recipients = null;
o = enc.Get(PdfName.V);
if (!o.IsNumber())
throw new InvalidPdfException("Illegal V value.");
int vValue = ((PdfNumber)o).IntValue;
switch(vValue) {
case 1:
cryptoMode = PdfWriter.STANDARD_ENCRYPTION_40;
lengthValue = 40;
recipients = (PdfArray)enc.Get(PdfName.RECIPIENTS);
break;
case 2:
o = enc.Get(PdfName.LENGTH);
if (!o.IsNumber())
throw new InvalidPdfException("Illegal Length value.");
lengthValue = ( (PdfNumber) o).IntValue;
if (lengthValue > 128 || lengthValue < 40 || lengthValue % 8 != 0)
throw new InvalidPdfException("Illegal Length value.");
cryptoMode = PdfWriter.STANDARD_ENCRYPTION_128;
recipients = (PdfArray)enc.Get(PdfName.RECIPIENTS);
break;
case 4:
PdfDictionary dic = (PdfDictionary)enc.Get(PdfName.CF);
if (dic == null)
throw new InvalidPdfException("/CF not found (encryption)");
dic = (PdfDictionary)dic.Get(PdfName.DEFAULTCRYPTFILTER);
if (dic == null)
throw new InvalidPdfException("/DefaultCryptFilter not found (encryption)");
if (PdfName.V2.Equals(dic.Get(PdfName.CFM))) {
cryptoMode = PdfWriter.STANDARD_ENCRYPTION_128;
lengthValue = 128;
}
else if (PdfName.AESV2.Equals(dic.Get(PdfName.CFM))) {
cryptoMode = PdfWriter.ENCRYPTION_AES_128;
lengthValue = 128;
}
else
throw new UnsupportedPdfException("No compatible encryption found");
PdfObject em = dic.Get(PdfName.ENCRYPTMETADATA);
if (em != null && em.ToString().Equals("false"))
cryptoMode |= PdfWriter.DO_NOT_ENCRYPT_METADATA;
recipients = (PdfArray)dic.Get(PdfName.RECIPIENTS);
break;
default:
throw new UnsupportedPdfException("Unknown encryption type V = " + rValue);
}
for (int i = 0; i<recipients.Size; i++)
{
PdfObject recipient = recipients[i];
strings.Remove(recipient);
CmsEnvelopedData data = null;
data = new CmsEnvelopedData(recipient.GetBytes());
foreach (RecipientInformation recipientInfo in data.GetRecipientInfos().GetRecipients()) {
if (recipientInfo.RecipientID.Match(certificate) && !foundRecipient) {
envelopedData = recipientInfo.GetContent(certificateKey);
foundRecipient = true;
}
}
}
if (!foundRecipient || envelopedData == null)
{
throw new UnsupportedPdfException("Bad certificate and key.");
}
SHA1 sh = new SHA1CryptoServiceProvider();
sh.TransformBlock(envelopedData, 0, 20, envelopedData, 0);
for (int i=0; i<recipients.Size; i++) {
byte[] encodedRecipient = recipients[i].GetBytes();
sh.TransformBlock(encodedRecipient, 0, encodedRecipient.Length, encodedRecipient, 0);
}
if ((cryptoMode & PdfWriter.DO_NOT_ENCRYPT_METADATA) != 0)
sh.TransformBlock(PdfEncryption.metadataPad, 0, PdfEncryption.metadataPad.Length, PdfEncryption.metadataPad, 0);
sh.TransformFinalBlock(envelopedData, 0, 0);
encryptionKey = sh.Hash;
}
decrypt = new PdfEncryption();
decrypt.SetCryptoMode(cryptoMode, lengthValue);
if (filter.Equals(PdfName.STANDARD))
{
//check by owner password
decrypt.SetupByOwnerPassword(documentID, password, uValue, oValue, pValue);
if (!EqualsArray(uValue, decrypt.userKey, (rValue == 3 || rValue == 4) ? 16 : 32)) {
//check by user password
decrypt.SetupByUserPassword(documentID, password, oValue, pValue);
if (!EqualsArray(uValue, decrypt.userKey, (rValue == 3 || rValue == 4) ? 16 : 32)) {
throw new BadPasswordException("Bad user password");
}
}
else
ownerPasswordUsed = true;
} else if (filter.Equals(PdfName.PUBSEC)) {
decrypt.SetupByEncryptionKey(encryptionKey, lengthValue);
ownerPasswordUsed = true;
}
for (int k = 0; k < strings.Count; ++k) {
PdfString str = (PdfString)strings[k];
str.Decrypt(this);
}
if (encDic.IsIndirect()) {
cryptoRef = (PRIndirectReference)encDic;
xrefObj[cryptoRef.Number] = null;
}
encryptionError = false;
}
/**
* @param obj
* @return a PdfObject
*/
public static PdfObject GetPdfObjectRelease(PdfObject obj) {
PdfObject obj2 = GetPdfObject(obj);
ReleaseLastXrefPartial(obj);
return obj2;
}
/**
* Reads a <CODE>PdfObject</CODE> resolving an indirect reference
* if needed.
* @param obj the <CODE>PdfObject</CODE> to read
* @return the resolved <CODE>PdfObject</CODE>
*/
public static PdfObject GetPdfObject(PdfObject obj) {
if (obj == null)
return null;
if (!obj.IsIndirect())
return obj;
PRIndirectReference refi = (PRIndirectReference)obj;
int idx = refi.Number;
bool appendable = refi.Reader.appendable;
obj = refi.Reader.GetPdfObject(idx);
if (obj == null) {
return null;
}
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.IndRef = refi;
}
return obj;
}
}
/**
* Reads a <CODE>PdfObject</CODE> 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 <CODE>PdfObject</CODE> 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 refi = null;
if (parent != null && (refi = parent.IndRef) != null && refi.Reader.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.IndRef = refi;
}
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) {
lastXrefPartial = -1;
if (idx < 0 || idx >= xrefObj.Count)
return null;
PdfObject obj = (PdfObject)xrefObj[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;
}
/**
*
*/
public void ResetLastXrefPartial() {
lastXrefPartial = -1;
}
/**
*
*/
public void ReleaseLastXrefPartial() {
if (partial && lastXrefPartial != -1) {
xrefObj[lastXrefPartial] = null;
lastXrefPartial = -1;
}
}
/**
* @param obj
*/
public static void ReleaseLastXrefPartial(PdfObject obj) {
if (obj == null)
return;
if (!obj.IsIndirect())
return;
if (!(obj is PRIndirectReference))
return;
PRIndirectReference refi = (PRIndirectReference)obj;
PdfReader reader = refi.Reader;
if (reader.partial && reader.lastXrefPartial != -1 && reader.lastXrefPartial == refi.Number) {
reader.xrefObj[reader.lastXrefPartial] = null;
}
reader.lastXrefPartial = -1;
}
private void SetXrefPartialObject(int idx, PdfObject obj) {
if (!partial || idx < 0)
return;
xrefObj[idx] = obj;
}
/**
* @param obj
* @return an indirect reference
*/
public PRIndirectReference AddPdfObject(PdfObject obj) {
xrefObj.Add(obj);
return new PRIndirectReference(this, xrefObj.Count - 1);
}
protected internal void ReadPages() {
catalog = trailer.GetAsDict(PdfName.ROOT);
rootPages = catalog.GetAsDict(PdfName.PAGES);
pageRefs = new PageRefs(this);
}
protected internal void ReadDocObjPartial() {
xrefObj = ArrayList.Repeat(null, xref.Length / 2);
ReadDecryptedDocObj();
if (objStmToOffset != null) {
int[] keys = objStmToOffset.GetKeys();
for (int k = 0; k < keys.Length; ++k) {
int n = keys[k];
objStmToOffset[n] = xref[n * 2];
xref[n * 2] = -1;
}
}
}
protected internal PdfObject ReadSingleObject(int k) {
strings.Clear();
int k2 = k * 2;
int pos = xref[k2];
if (pos < 0)
return null;
if (xref[k2 + 1] > 0)
pos = objStmToOffset[xref[k2 + 1]];
if (pos == 0)
return null;
tokens.Seek(pos);
tokens.NextValidToken();
if (tokens.TokenType != PRTokeniser.TK_NUMBER)
tokens.ThrowError("Invalid object number.");
objNum = tokens.IntValue;
tokens.NextValidToken();
if (tokens.TokenType != PRTokeniser.TK_NUMBER)
tokens.ThrowError("Invalid generation number.");
objGen = tokens.IntValue;
tokens.NextValidToken();
if (!tokens.StringValue.Equals("obj"))
tokens.ThrowError("Token 'obj' expected.");
PdfObject obj;
try {
obj = ReadPRObject();
for (int j = 0; j < strings.Count; ++j) {
PdfString str = (PdfString)strings[j];
str.Decrypt(this);
}
if (obj.IsStream()) {
CheckPRStreamLength((PRStream)obj);
}
}
catch {
obj = null;
}
if (xref[k2 + 1] > 0) {
obj = ReadOneObjStm((PRStream)obj, xref[k2]);
}
xrefObj[k] = obj;
return obj;
}
protected internal PdfObject ReadOneObjStm(PRStream stream, int idx) {
int first = stream.GetAsNumber(PdfName.FIRST).IntValue;
byte[] b = GetStreamBytes(stream, tokens.File);
PRTokeniser saveTokens = tokens;
tokens = new PRTokeniser(b);
try {
int address = 0;
bool ok = true;
++idx;
for (int k = 0; k < idx; ++k) {
ok = tokens.NextToken();
if (!ok)
break;
if (tokens.TokenType != PRTokeniser.TK_NUMBER) {
ok = false;
break;
}
ok = tokens.NextToken();
if (!ok)
break;
if (tokens.TokenType != PRTokeniser.TK_NUMBER) {
ok = false;
break;
}
address = tokens.IntValue + first;
}
if (!ok)
throw new InvalidPdfException("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.Count; ++k) {
if (xrefObj[k] != null)
++total;
}
return (total * 100.0 / xrefObj.Count);
}
protected internal void ReadDocObj() {
ArrayList streams = new ArrayList();
xrefObj = ArrayList.Repeat(null, 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.TokenType != PRTokeniser.TK_NUMBER)
tokens.ThrowError("Invalid object number.");
objNum = tokens.IntValue;
tokens.NextValidToken();
if (tokens.TokenType != PRTokeniser.TK_NUMBER)
tokens.ThrowError("Invalid generation number.");
objGen = tokens.IntValue;
tokens.NextValidToken();
if (!tokens.StringValue.Equals("obj"))
tokens.ThrowError("Token 'obj' expected.");
PdfObject obj;
try {
obj = ReadPRObject();
if (obj.IsStream()) {
streams.Add(obj);
}
}
catch {
obj = null;
}
xrefObj[k / 2] = obj;
}
for (int k = 0; k < streams.Count; ++k) {
CheckPRStreamLength((PRStream)streams[k]);
}
ReadDecryptedDocObj();
if (objStmMark != null) {
foreach (DictionaryEntry entry in objStmMark) {
int n = (int)entry.Key;
IntHashtable h = (IntHashtable)entry.Value;
ReadObjStm((PRStream)xrefObj[n], h);
xrefObj[n] = null;
}
objStmMark = null;
}
xref = null;
}
private void CheckPRStreamLength(PRStream stream) {
int fileLength = tokens.Length;
int start = stream.Offset;
bool 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.FilePointer;
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.Length = streamLength;
}
protected internal void ReadObjStm(PRStream stream, IntHashtable map) {
int first = stream.GetAsNumber(PdfName.FIRST).IntValue;
int n = stream.GetAsNumber(PdfName.N).IntValue;
byte[] b = GetStreamBytes(stream, tokens.File);
PRTokeniser saveTokens = tokens;
tokens = new PRTokeniser(b);
try {
int[] address = new int[n];
int[] objNumber = new int[n];
bool ok = true;
for (int k = 0; k < n; ++k) {
ok = tokens.NextToken();
if (!ok)
break;
if (tokens.TokenType != PRTokeniser.TK_NUMBER) {
ok = false;
break;
}
objNumber[k] = tokens.IntValue;
ok = tokens.NextToken();
if (!ok)
break;
if (tokens.TokenType != PRTokeniser.TK_NUMBER) {
ok = false;
break;
}
address[k] = tokens.IntValue + first;
}
if (!ok)
throw new InvalidPdfException("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;
}
}
/**
* 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 refi = (PRIndirectReference)obj;
PdfReader reader = refi.Reader;
int n = refi.Number;
reader.xrefObj[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];
Array.Copy(xref, 0, xref2, 0, xref.Length);
xref = xref2;
}
}
}
protected internal void ReadXref() {
hybridXref = false;
newXrefType = false;
tokens.Seek(tokens.Startxref);
tokens.NextToken();
if (!tokens.StringValue.Equals("startxref"))
throw new InvalidPdfException("startxref not found.");
tokens.NextToken();
if (tokens.TokenType != PRTokeniser.TK_NUMBER)
throw new InvalidPdfException("startxref is not followed by a number.");
int startxref = tokens.IntValue;
lastXref = startxref;
eofPos = tokens.FilePointer;
try {
if (ReadXRefStream(startxref)) {
newXrefType = true;
return;
}
}
catch {}
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 internal PdfDictionary ReadXrefSection() {
tokens.NextValidToken();
if (!tokens.StringValue.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.StringValue.Equals("trailer"))
break;
if (tokens.TokenType != PRTokeniser.TK_NUMBER)
tokens.ThrowError("Object number of the first object in this xref subsection not found");
start = tokens.IntValue;
tokens.NextValidToken();
if (tokens.TokenType != 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.FilePointer;
tokens.NextValidToken();
pos = tokens.IntValue;
tokens.NextValidToken();
gen = tokens.IntValue;
if (pos == 0 && gen == PdfWriter.GENERATION_MAX) {
--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.StringValue.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.StringValue.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 internal bool ReadXRefStream(int ptr) {
tokens.Seek(ptr);
int thisStream = 0;
if (!tokens.NextToken())
return false;
if (tokens.TokenType != PRTokeniser.TK_NUMBER)
return false;
thisStream = tokens.IntValue;
if (!tokens.NextToken() || tokens.TokenType != PRTokeniser.TK_NUMBER)
return false;
if (!tokens.NextToken() || !tokens.StringValue.Equals("obj"))
return false;
PdfObject objecto = ReadPRObject();
PRStream stm = null;
if (objecto.IsStream()) {
stm = (PRStream)objecto;
if (!PdfName.XREF.Equals(stm.Get(PdfName.TYPE)))
return false;
}
else
return false;
if (trailer == null) {
trailer = new PdfDictionary();
trailer.Merge(stm);
}
stm.Length = ((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 Hashtable();
if (objStmToOffset == null && partial)
objStmToOffset = new IntHashtable();
byte[] b = GetStreamBytes(stm, tokens.File);
int bptr = 0;
int[] wc = new int[3];
for (int k = 0; k < 3; ++k)
wc[k] = w.GetAsNumber(k).IntValue;
for (int idx = 0; idx < index.Size; idx += 2) {
int start = index.GetAsNumber(idx).IntValue;
int length = index.GetAsNumber(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 baseb = start * 2;
if (xref[baseb] == 0 && xref[baseb + 1] == 0) {
switch (type) {
case 0:
xref[baseb] = -1;
break;
case 1:
xref[baseb] = field2;
break;
case 2:
xref[baseb] = field3;
xref[baseb + 1] = field2;
if (partial) {
objStmToOffset[field2] = 0;
}
else {
IntHashtable seq = (IntHashtable)objStmMark[field2];
if (seq == null) {
seq = new IntHashtable();
seq[field3] = 1;
objStmMark[field2] = seq;
}
else
seq[field3] = 1;
}
break;
}
}
++start;
}
}
thisStream *= 2;
if (thisStream < xref.Length)
xref[thisStream] = -1;
if (prev == -1)
return true;
return ReadXRefStream(prev);
}
protected internal void RebuildXref() {
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.FilePointer;
if (!tokens.ReadLineSegment(line))
break;
if (line[0] == 't') {
if (!PdfEncodings.ConvertToString(line, null).StartsWith("trailer"))
continue;
tokens.Seek(pos);
tokens.NextToken();
pos = tokens.FilePointer;
try {
PdfDictionary dic = (PdfDictionary)ReadPRObject();
if (dic.Get(PdfName.ROOT) != null)
trailer = dic;
else
tokens.Seek(pos);
}
catch {
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][];
Array.Copy(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 InvalidPdfException("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 internal PdfDictionary ReadDictionary() {
PdfDictionary dic = new PdfDictionary();
while (true) {
tokens.NextValidToken();
if (tokens.TokenType == PRTokeniser.TK_END_DIC)
break;
if (tokens.TokenType != PRTokeniser.TK_NAME)
tokens.ThrowError("Dictionary key is not a name.");
PdfName name = new PdfName(tokens.StringValue, 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 internal PdfArray ReadArray() {
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;
}
// Track how deeply nested the current object is, so
// we know when to return an individual null or boolean, or
// reuse one of the static ones.
private int readDepth = 0;
protected internal PdfObject ReadPRObject() {
tokens.NextValidToken();
int type = tokens.TokenType;
switch (type) {
case PRTokeniser.TK_START_DIC: {
++readDepth;
PdfDictionary dic = ReadDictionary();
--readDepth;
int pos = tokens.FilePointer;
// be careful in the trailer. May not be a "next" token.
bool hasNext;
do {
hasNext = tokens.NextToken();
} while (hasNext && tokens.TokenType == PRTokeniser.TK_COMMENT);
if (hasNext && tokens.StringValue.Equals("stream")) {
//skip whitespaces
int ch;
do {
ch = tokens.Read();
} while (ch == 32 || ch == 9 || ch == 0 || ch == 12);
if (ch != '\n')
ch = tokens.Read();
if (ch != '\n')
tokens.BackOnePosition(ch);
PRStream stream = new PRStream(this, tokens.FilePointer);
stream.Merge(dic);
stream.ObjNum = objNum;
stream.ObjGen = objGen;
return stream;
}
else {
tokens.Seek(pos);
return dic;
}
}
case PRTokeniser.TK_START_ARRAY: {
++readDepth;
PdfArray arr = ReadArray();
--readDepth;
return arr;
}
case PRTokeniser.TK_NUMBER:
return new PdfNumber(tokens.StringValue);
case PRTokeniser.TK_STRING:
PdfString str = new PdfString(tokens.StringValue, null).SetHexWriting(tokens.IsHexString());
str.SetObjNum(objNum, objGen);
if (strings != null)
strings.Add(str);
return str;
case PRTokeniser.TK_NAME: {
PdfName cachedName = (PdfName)PdfName.staticNames[tokens.StringValue];
if (readDepth > 0 && cachedName != null) {
return cachedName;
} else {
// an indirect name (how odd...), or a non-standard one
return new PdfName(tokens.StringValue, false);
}
}
case PRTokeniser.TK_REF:
int num = tokens.Reference;
PRIndirectReference refi = new PRIndirectReference(this, num, tokens.Generation);
return refi;
default:
String sv = tokens.StringValue;
if ("null".Equals(sv)) {
if (readDepth == 0) {
return new PdfNull();
} //else
return PdfNull.PDFNULL;
}
else if ("true".Equals(sv)) {
if (readDepth == 0) {
return new PdfBoolean( true );
} //else
return PdfBoolean.PDFTRUE;
}
else if ("false".Equals(sv)) {
if (readDepth == 0) {
return new PdfBoolean( false );
} //else
return PdfBoolean.PDFFALSE;
}
return new PdfLiteral(-type, tokens.StringValue);
}
}
/** Decodes a stream that has the FlateDecode filter.
* @param in the input data
* @return the decoded data
*/
public static byte[] FlateDecode(byte[] inp) {
byte[] b = FlateDecode(inp, true);
if (b == null)
return FlateDecode(inp, false);
return b;
}
/**
* @param in
* @param dicPar
* @return a byte array
*/
public static byte[] DecodePredictor(byte[] inp, PdfObject dicPar) {
if (dicPar == null || !dicPar.IsDictionary())
return inp;
PdfDictionary dic = (PdfDictionary)dicPar;
PdfObject obj = GetPdfObject(dic.Get(PdfName.PREDICTOR));
if (obj == null || !obj.IsNumber())
return inp;
int predictor = ((PdfNumber)obj).IntValue;
if (predictor < 10)
return inp;
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;
MemoryStream dataStream = new MemoryStream(inp);
MemoryStream fout = new MemoryStream(inp.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.ReadByte();
if (filter < 0) {
return fout.ToArray();
}
int tot = 0;
while (tot < bytesPerRow) {
int n = dataStream.Read(curr, tot, bytesPerRow - tot);
if (n <= 0)
return fout.ToArray();
tot += n;
}
} catch {
return fout.ToArray();
}
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] += (byte)(prior[i] / 2);
}
for (int i = bytesPerPixel; i < bytesPerRow; i++) {
curr[i] += (byte)(((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 Exception("PNG filter unknown.");
}
fout.Write(curr, 0, curr.Length);
// Swap curr and prior
byte[] tmp = prior;
prior = curr;
curr = tmp;
}
}
/** A helper to FlateDecode.
* @param in the input data
* @param strict <CODE>true</CODE> to read a correct stream. <CODE>false</CODE>
* to try to read a corrupted stream
* @return the decoded data
*/
public static byte[] FlateDecode(byte[] inp, bool strict) {
MemoryStream stream = new MemoryStream(inp);
ZInflaterInputStream zip = new ZInflaterInputStream(stream);
MemoryStream outp = new MemoryStream();
byte[] b = new byte[strict ? 4092 : 1];
try {
int n;
while ((n = zip.Read(b, 0, b.Length)) > 0) {
outp.Write(b, 0, n);
}
zip.Close();
outp.Close();
return outp.ToArray();
}
catch {
if (strict)
return null;
return outp.ToArray();
}
}
/** Decodes a stream that has the ASCIIHexDecode filter.
* @param in the input data
* @return the decoded data
*/
public static byte[] ASCIIHexDecode(byte[] inp) {
MemoryStream outp = new MemoryStream();
bool first = true;
int n1 = 0;
for (int k = 0; k < inp.Length; ++k) {
int ch = inp[k] & 0xff;
if (ch == '>')
break;
if (PRTokeniser.IsWhitespace(ch))
continue;
int n = PRTokeniser.GetHex(ch);
if (n == -1)
throw new ArgumentException("Illegal character in ASCIIHexDecode.");
if (first)
n1 = n;
else
outp.WriteByte((byte)((n1 << 4) + n));
first = !first;
}
if (!first)
outp.WriteByte((byte)(n1 << 4));
return outp.ToArray();
}
/** Decodes a stream that has the ASCII85Decode filter.
* @param in the input data
* @return the decoded data
*/
public static byte[] ASCII85Decode(byte[] inp) {
MemoryStream outp = new MemoryStream();
int state = 0;
int[] chn = new int[5];
for (int k = 0; k < inp.Length; ++k) {
int ch = inp[k] & 0xff;
if (ch == '~')
break;
if (PRTokeniser.IsWhitespace(ch))
continue;
if (ch == 'z' && state == 0) {
outp.WriteByte(0);
outp.WriteByte(0);
outp.WriteByte(0);
outp.WriteByte(0);
continue;
}
if (ch < '!' || ch > 'u')
throw new ArgumentException("Illegal character in ASCII85Decode.");
chn[state] = ch - '!';
++state;
if (state == 5) {
state = 0;
int rx = 0;
for (int j = 0; j < 5; ++j)
rx = rx * 85 + chn[j];
outp.WriteByte((byte)(rx >> 24));
outp.WriteByte((byte)(rx >> 16));
outp.WriteByte((byte)(rx >> 8));
outp.WriteByte((byte)rx);
}
}
int r = 0;
// We'll ignore the next two lines for the sake of perpetuating broken PDFs
// if (state == 1)
// throw new ArgumentException("Illegal length in ASCII85Decode.");
if (state == 2) {
r = chn[0] * 85 * 85 * 85 * 85 + chn[1] * 85 * 85 * 85 + 85 * 85 * 85 + 85 * 85 + 85;
outp.WriteByte((byte)(r >> 24));
}
else if (state == 3) {
r = chn[0] * 85 * 85 * 85 * 85 + chn[1] * 85 * 85 * 85 + chn[2] * 85 * 85 + 85 * 85 + 85;
outp.WriteByte((byte)(r >> 24));
outp.WriteByte((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 + 85;
outp.WriteByte((byte)(r >> 24));
outp.WriteByte((byte)(r >> 16));
outp.WriteByte((byte)(r >> 8));
}
return outp.ToArray();
}
/** Decodes a stream that has the LZWDecode filter.
* @param in the input data
* @return the decoded data
*/
public static byte[] LZWDecode(byte[] inp) {
MemoryStream outp = new MemoryStream();
LZWDecoder lzw = new LZWDecoder();
lzw.Decode(inp, outp);
return outp.ToArray();
}
/** Checks if the document had errors and was rebuilt.
* @return true if rebuilt.
*
*/
public bool 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.IndRef = 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) {
PdfDictionary page = GetPageNRelease(pageNum);
if (page == null)
return null;
PdfObject contents = GetPdfObjectRelease(page.Get(PdfName.CONTENTS));
if (contents == null)
return new byte[0];
MemoryStream bout = null;
if (contents.IsStream()) {
return GetStreamBytes((PRStream)contents, file);
}
else if (contents.IsArray()) {
PdfArray array = (PdfArray)contents;
bout = new MemoryStream();
for (int k = 0; k < array.Size; ++k) {
PdfObject item = GetPdfObjectRelease(array[k]);
if (item == null || !item.IsStream())
continue;
byte[] b = GetStreamBytes((PRStream)item, file);
bout.Write(b, 0, b.Length);
if (k != array.Size - 1)
bout.WriteByte((byte)'\n');
}
return bout.ToArray();
}
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) {
RandomAccessFileOrArray rf = SafeFile;
try {
rf.ReOpen();
return GetPageContent(pageNum, rf);
}
finally {
try{rf.Close();}catch{}
}
}
protected internal void KillXref(PdfObject obj) {
if (obj == null)
return;
if ((obj is PdfIndirectReference) && !obj.IsIndirect())
return;
switch (obj.Type) {
case PdfObject.INDIRECT: {
int xr = ((PRIndirectReference)obj).Number;
obj = (PdfObject)xrefObj[xr];
xrefObj[xr] = null;
freeXref = xr;
KillXref(obj);
break;
}
case PdfObject.ARRAY: {
PdfArray t = (PdfArray)obj;
for (int i = 0; i < t.Size; ++i)
KillXref(t[i]);
break;
}
case PdfObject.STREAM:
case PdfObject.DICTIONARY: {
PdfDictionary dic = (PdfDictionary)obj;
foreach (PdfName key in dic.Keys){
KillXref(dic.Get(key));
}
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) {
SetPageContent(pageNum, content, PdfStream.DEFAULT_COMPRESSION);
}
/** Sets the contents of the page.
* @param content the new page content
* @param pageNum the page number. 1 is the first
* @since 2.1.3 (the method already existed without param compressionLevel)
*/
public void SetPageContent(int pageNum, byte[] content, int compressionLevel) {
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.Count - 1;
}
page.Put(PdfName.CONTENTS, new PRIndirectReference(this, freeXref));
xrefObj[freeXref] = new PRStream(this, content, compressionLevel);
}
/** 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) {
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).ArrayList;
}
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).ArrayList;
}
String name;
for (int j = 0; j < filters.Count; ++j) {
name = ((PdfName)GetPdfObjectRelease((PdfObject)filters[j])).ToString();
if (name.Equals("/FlateDecode") || name.Equals("/Fl")) {
b = FlateDecode(b);
PdfObject dicParam = null;
if (j < dp.Count) {
dicParam = (PdfObject)dp[j];
b = DecodePredictor(b, dicParam);
}
}
else if (name.Equals("/ASCIIHexDecode") || name.Equals("/AHx"))
b = ASCIIHexDecode(b);
else if (name.Equals("/ASCII85Decode") || name.Equals("/A85"))
b = ASCII85Decode(b);
else if (name.Equals("/LZWDecode")) {
b = LZWDecode(b);
PdfObject dicParam = null;
if (j < dp.Count) {
dicParam = (PdfObject)dp[j];
b = DecodePredictor(b, dicParam);
}
}
else if (name.Equals("/Crypt")) {
}
else
throw new UnsupportedPdfException("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) {
RandomAccessFileOrArray rf = stream.Reader.SafeFile;
try {
rf.ReOpen();
return GetStreamBytes(stream, rf);
}
finally {
try{rf.Close();}catch{}
}
}
/** 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) {
PdfReader reader = stream.Reader;
byte[] b;
if (stream.Offset < 0)
b = stream.GetBytes();
else {
b = new byte[stream.Length];
file.Seek(stream.Offset);
file.ReadFully(b);
PdfEncryption decrypt = reader.Decrypt;
if (decrypt != null) {
PdfObject filter = GetPdfObjectRelease(stream.Get(PdfName.FILTER));
ArrayList filters = new ArrayList();
if (filter != null) {
if (filter.IsName())
filters.Add(filter);
else if (filter.IsArray())
filters = ((PdfArray)filter).ArrayList;
}
bool skip = false;
for (int k = 0; k < filters.Count; ++k) {
PdfObject obj = GetPdfObjectRelease((PdfObject)filters[k]);
if (obj != null && obj.ToString().Equals("/Crypt")) {
skip = true;
break;
}
}
if (!skip) {
decrypt.SetHashKey(stream.ObjNum, stream.ObjGen);
b = decrypt.DecryptByteArray(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) {
RandomAccessFileOrArray rf = stream.Reader.SafeFile;
try {
rf.ReOpen();
return GetStreamBytesRaw(stream, rf);
}
finally {
try{rf.Close();}catch{}
}
}
/** 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 refi = (PRIndirectReference)page.Get(PdfName.CONTENTS);
if (visited.ContainsKey(refi.Number)) {
// need to duplicate
newRefs.Add(refi);
newStreams.Add(new PRStream((PRStream)contents, null));
}
else
visited[refi.Number] = 1;
}
else if (contents.IsArray()) {
PdfArray array = (PdfArray)contents;
for (int j = 0; j < array.Size; ++j) {
PRIndirectReference refi = (PRIndirectReference)array[j];
if (visited.ContainsKey(refi.Number)) {
// need to duplicate
newRefs.Add(refi);
newStreams.Add(new PRStream((PRStream)GetPdfObject(refi), null));
}
else
visited[refi.Number] = 1;
}
}
}
if (newStreams.Count == 0)
return;
for (int k = 0; k < newStreams.Count; ++k) {
xrefObj.Add(newStreams[k]);
PRIndirectReference refi = (PRIndirectReference)newRefs[k];
refi.SetNumber(xrefObj.Count - 1, 0);
}
}
/**
* Sets the tampered state. A tampered PdfReader cannot be reused in PdfStamper.
* @param tampered the tampered state
*/
public bool Tampered {
get {
return tampered;
}
set {
tampered = value;
pageRefs.KeepPages(); }
}
/** Gets the XML metadata.
* @throws IOException on error
* @return the XML metadata
*/
public byte[] Metadata {
get {
PdfObject obj = GetPdfObject(catalog.Get(PdfName.METADATA));
if (!(obj is PRStream))
return null;
RandomAccessFileOrArray rf = SafeFile;
byte[] b = null;
try {
rf.ReOpen();
b = GetStreamBytes((PRStream)obj, rf);
}
finally {
try {
rf.Close();
}
catch{
// 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 LastXref {
get {
return lastXref;
}
}
/**
* Gets the number of xref objects.
* @return the number of xref objects
*/
public int XrefSize {
get {
return xrefObj.Count;
}
}
/**
* Gets the byte address of the %%EOF marker.
* @return the byte address of the %%EOF marker
*/
public int EofPos {
get {
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 PdfVersion {
get {
return pdfVersion;
}
}
/**
* Returns <CODE>true</CODE> if the PDF is encrypted.
* @return <CODE>true</CODE> if the PDF is encrypted
*/
public bool IsEncrypted() {
return encrypted;
}
/**
* Gets the encryption permissions. It can be used directly in
* <CODE>PdfWriter.SetEncryption()</CODE>.
* @return the encryption permissions
*/
public int Permissions {
get {
return pValue;
}
}
/**
* Returns <CODE>true</CODE> if the PDF has a 128 bit key encryption.
* @return <CODE>true</CODE> if the PDF has a 128 bit key encryption
*/
public bool Is128Key() {
return rValue == 3;
}
/**
* Gets the trailer dictionary
* @return the trailer dictionary
*/
public PdfDictionary Trailer {
get {
return trailer;
}
}
internal PdfEncryption Decrypt {
get {
return decrypt;
}
}
internal static bool 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;
}
internal static bool 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);
}
internal static String GetFontName(PdfDictionary dic) {
if (dic == null)
return null;
PdfObject type = GetPdfObjectRelease(dic.Get(PdfName.BASEFONT));
if (type == null || !type.IsName())
return null;
return PdfName.DecodeName(type.ToString());
}
internal static String GetSubsetPrefix(PdfDictionary dic) {
if (dic == null)
return null;
String s = GetFontName(dic);
if (s == null)
return null;
if (s.Length < 8 || s[6] != '+')
return null;
for (int k = 0; k < 6; ++k) {
char c = s[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.Count; ++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 = dic.GetAsDict(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 = dic.GetAsArray(PdfName.DESCENDANTFONTS);
if (arr == null)
continue;
if (arr.IsEmpty())
continue;
PdfDictionary desc = arr.GetAsDict(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 = desc.GetAsDict(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.Count; ++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 = dic.GetAsDict(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 == null)
return null;
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 <CODE>Hashtable</CODE>. The key is the name
* and the value is the destinations array.
* @return gets all the named destinations
*/
public Hashtable GetNamedDestination() {
return GetNamedDestination(false);
}
/**
* Gets all the named destinations as an <CODE>HashMap</CODE>. The key is the name
* and the value is the destinations array.
* @param keepNames true if you want the keys to be real PdfNames instead of Strings
* @return gets all the named destinations
* @since 2.1.6
*/
public Hashtable GetNamedDestination(bool keepNames) {
Hashtable names = GetNamedDestinationFromNames(keepNames);
Hashtable names2 = GetNamedDestinationFromStrings();
foreach (DictionaryEntry ie in names2)
names[ie.Key] = ie.Value;
return names;
}
/**
* Gets the named destinations from the /Dests key in the catalog as an <CODE>Hashtable</CODE>. The key is the name
* and the value is the destinations array.
* @return gets the named destinations
*/
public Hashtable GetNamedDestinationFromNames() {
return GetNamedDestinationFromNames(false);
}
/**
* Gets the named destinations from the /Dests key in the catalog as an <CODE>HashMap</CODE>. The key is the name
* and the value is the destinations array.
* @param keepNames true if you want the keys to be real PdfNames instead of Strings
* @return gets the named destinations
* @since 2.1.6
*/
public Hashtable GetNamedDestinationFromNames(bool keepNames) {
Hashtable names = new Hashtable();
if (catalog.Get(PdfName.DESTS) != null) {
PdfDictionary dic = (PdfDictionary)GetPdfObjectRelease(catalog.Get(PdfName.DESTS));
if (dic == null)
return names;
foreach (PdfName key in dic.Keys) {
PdfArray arr = GetNameArray(dic.Get(key));
if (arr == null)
continue;
if (keepNames) {
names[key] = arr;
}
else {
String name = PdfName.DecodeName(key.ToString());
names[name] = arr;
}
}
}
return names;
}
/**
* Gets the named destinations from the /Names key in the catalog as an <CODE>Hashtable</CODE>. The key is the name
* and the value is the destinations array.
* @return gets the named destinations
*/
public Hashtable GetNamedDestinationFromStrings() {
if (catalog.Get(PdfName.NAMES) != null) {
PdfDictionary dic = (PdfDictionary)GetPdfObjectRelease(catalog.Get(PdfName.NAMES));
if (dic != null) {
dic = (PdfDictionary)GetPdfObjectRelease(dic.Get(PdfName.DESTS));
if (dic != null) {
Hashtable names = PdfNameTree.ReadTree(dic);
object[] keys = new object[names.Count];
names.Keys.CopyTo(keys, 0);
foreach (object key in keys) {
PdfArray arr = GetNameArray((PdfObject)names[key]);
if (arr != null)
names[key] = arr;
else
names.Remove(key);
}
return names;
}
}
}
return new Hashtable();
}
private bool ReplaceNamedDestination(PdfObject obj, Hashtable names) {
obj = GetPdfObject(obj);
int objIdx = lastXrefPartial;
ReleaseLastXrefPartial();
if (obj != null && obj.IsDictionary()) {
PdfObject ob2 = GetPdfObjectRelease(((PdfDictionary)obj).Get(PdfName.DEST));
Object name = null;
if (ob2 != null) {
if (ob2.IsName())
name = ob2;
else if (ob2.IsString())
name = ob2.ToString();
if (name != null) {
PdfArray dest = (PdfArray)names[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 != null) {
if (ob3.IsName())
name = ob3;
else if (ob3.IsString())
name = ob3.ToString();
}
if (name != null) {
PdfArray dest = (PdfArray)names[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 = page.GetAsArray(PdfName.ANNOTS);
if (annots == null) {
pageRefs.ReleasePage(k);
continue;
}
for (int j = 0; j < annots.Size; ++j) {
PdfObject obj = GetPdfObjectRelease((PdfObject)annots[j]);
if (obj == null || !obj.IsDictionary())
continue;
PdfDictionary annot = (PdfDictionary)obj;
if (PdfName.WIDGET.Equals(annot.Get(PdfName.SUBTYPE)))
annots.Remove(j--);
}
if (annots.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();
}
public ArrayList GetLinks(int page) {
pageRefs.ResetReleasePage();
ArrayList result = new ArrayList();
PdfDictionary pageDic = pageRefs.GetPageN(page);
if (pageDic.Get(PdfName.ANNOTS) != null) {
PdfArray annots = pageDic.GetAsArray(PdfName.ANNOTS);
for (int j = 0; j < annots.Size; ++j) {
PdfDictionary annot = (PdfDictionary)GetPdfObjectRelease(annots[j]);
if (PdfName.LINK.Equals(annot.Get(PdfName.SUBTYPE))) {
result.Add(new PdfAnnotation.PdfImportedLink(annot));
}
}
}
pageRefs.ReleasePage(page);
pageRefs.ResetReleasePage();
return result;
}
private void IterateBookmarks(PdfObject outlineRef, Hashtable 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;
Hashtable names = GetNamedDestination(true);
if (names.Count == 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;
}
bool commitAnnots = false;
for (int an = 0; an < annots.Size; ++an) {
PdfObject objRef = annots[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 internal static PdfDictionary DuplicatePdfDictionary(PdfDictionary original, PdfDictionary copy, PdfReader newReader) {
if (copy == null)
copy = new PdfDictionary();
foreach (PdfName key in original.Keys) {
copy.Put(key, DuplicatePdfObject(original.Get(key), newReader));
}
return copy;
}
protected internal 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: {
PdfArray arr = new PdfArray();
for (ListIterator it = ((PdfArray)original).GetListIterator(); it.HasNext();) {
arr.Add(DuplicatePdfObject((PdfObject)it.Next(), newReader));
}
return arr;
}
case PdfObject.INDIRECT: {
PRIndirectReference org = (PRIndirectReference)original;
return new PRIndirectReference(newReader, org.Number, org.Generation);
}
default:
return original;
}
}
/**
* Closes the reader
*/
public void Close() {
if (!partial)
return;
tokens.Close();
}
protected internal void RemoveUnusedNode(PdfObject obj, bool[] hits) {
Stack state = new Stack();
state.Push(obj);
while (state.Count != 0) {
Object current = state.Pop();
if (current == null)
continue;
ArrayList ar = null;
PdfDictionary dic = null;
PdfName[] keys = null;
Object[] objs = null;
int idx = 0;
if (current is PdfObject) {
obj = (PdfObject)current;
switch (obj.Type) {
case PdfObject.DICTIONARY:
case PdfObject.STREAM:
dic = (PdfDictionary)obj;
keys = new PdfName[dic.Size];
dic.Keys.CopyTo(keys, 0);
break;
case PdfObject.ARRAY:
ar = ((PdfArray)obj).ArrayList;
break;
case PdfObject.INDIRECT:
PRIndirectReference refi = (PRIndirectReference)obj;
int num = refi.Number;
if (!hits[num]) {
hits[num] = true;
state.Push(GetPdfObjectRelease(refi));
}
continue;
default:
continue;
}
}
else {
objs = (Object[])current;
if (objs[0] is ArrayList) {
ar = (ArrayList)objs[0];
idx = (int)objs[1];
}
else {
keys = (PdfName[])objs[0];
dic = (PdfDictionary)objs[1];
idx = (int)objs[2];
}
}
if (ar != null) {
for (int k = idx; k < ar.Count; ++k) {
PdfObject v = (PdfObject)ar[k];
if (v.IsIndirect()) {
int num = ((PRIndirectReference)v).Number;
if (num >= xrefObj.Count || (!partial && xrefObj[num] == null)) {
ar[k] = PdfNull.PDFNULL;
continue;
}
}
if (objs == null)
state.Push(new Object[]{ar, k + 1});
else {
objs[1] = k + 1;
state.Push(objs);
}
state.Push(v);
break;
}
}
else {
for (int k = idx; k < keys.Length; ++k) {
PdfName key = keys[k];
PdfObject v = dic.Get(key);
if (v.IsIndirect()) {
int num = ((PRIndirectReference)v).Number;
if (num >= xrefObj.Count || (!partial && xrefObj[num] == null)) {
dic.Put(key, PdfNull.PDFNULL);
continue;
}
}
if (objs == null)
state.Push(new Object[]{keys, dic, k + 1});
else {
objs[2] = k + 1;
state.Push(objs);
}
state.Push(v);
break;
}
}
}
}
/** Removes all the unreachable objects.
* @return the number of indirect objects removed
*/
public int RemoveUnusedObjects() {
bool[] hits = new bool[xrefObj.Count];
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[k] = null;
++total;
}
}
}
else {
for (int k = 1; k < hits.Length; ++k) {
if (!hits[k]) {
xrefObj[k] = null;
++total;
}
}
}
return total;
}
/** Gets a read-only version of <CODE>AcroFields</CODE>.
* @return a read-only version of <CODE>AcroFields</CODE>
*/
public AcroFields AcroFields {
get {
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) {
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;
Hashtable jscript = PdfNameTree.ReadTree(js);
String[] sortedNames = new String[jscript.Count];
jscript.Keys.CopyTo(sortedNames, 0);
Array.Sort(sortedNames);
StringBuilder buf = new StringBuilder();
for (int k = 0; k < sortedNames.Length; ++k) {
PdfDictionary j = (PdfDictionary)GetPdfObjectRelease((PdfIndirectReference)jscript[sortedNames[k]]);
if (j == null)
continue;
PdfObject obj = GetPdfObjectRelease(j.Get(PdfName.JS));
if (obj != null) {
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 JavaScript {
get {
RandomAccessFileOrArray rf = SafeFile;
try {
rf.ReOpen();
return GetJavaScript(rf);
}
finally {
try{rf.Close();}catch{}
}
}
}
/**
* 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, NumberOfPages));
}
/**
* Selects the pages to keep in the document. The pages are described as a
* <CODE>List</CODE> of <CODE>Integer</CODE>. 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(ArrayList pagesToKeep) {
pageRefs.SelectPages(pagesToKeep);
RemoveUnusedObjects();
}
/** Sets the viewer preferences as the sum of several constants.
* @param preferences the viewer preferences
* @see PdfViewerPreferences#setViewerPreferences
*/
public virtual int ViewerPreferences {
set {
this.viewerPreferences.ViewerPreferences = value;
SetViewerPreferences(this.viewerPreferences);
}
}
/** Adds a viewer preference
* @param key a key for a viewer preference
* @param value a value for the viewer preference
* @see PdfViewerPreferences#addViewerPreference
*/
public virtual void AddViewerPreference(PdfName key, PdfObject value) {
this.viewerPreferences.AddViewerPreference(key, value);
SetViewerPreferences(this.viewerPreferences);
}
internal virtual void SetViewerPreferences(PdfViewerPreferencesImp vp) {
vp.AddToCatalog(catalog);
}
/**
* Returns a bitset representing the PageMode and PageLayout viewer preferences.
* Doesn't return any information about the ViewerPreferences dictionary.
* @return an int that contains the Viewer Preferences.
*/
public virtual int SimpleViewerPreferences {
get {
return PdfViewerPreferencesImp.GetViewerPreferences(catalog).PageLayoutAndMode;
}
}
public bool Appendable {
set {
appendable = value;
if (appendable)
GetPdfObject(trailer.Get(PdfName.ROOT));
}
get {
return appendable;
}
}
/**
* Getter for property newXrefType.
* @return Value of property newXrefType.
*/
public bool IsNewXrefType() {
return newXrefType;
}
/**
* Getter for property fileLength.
* @return Value of property fileLength.
*/
public int FileLength {
get {
return fileLength;
}
}
/**
* Getter for property hybridXref.
* @return Value of property hybridXref.
*/
public bool IsHybridXref() {
return hybridXref;
}
public class PageRefs {
private PdfReader reader;
private IntHashtable refsp;
private ArrayList refsn;
private ArrayList pageInh;
private int lastPageRead = -1;
private int sizep;
private bool keepPages;
internal PageRefs(PdfReader reader) {
this.reader = reader;
if (reader.partial) {
refsp = new IntHashtable();
PdfNumber npages = (PdfNumber)PdfReader.GetPdfObjectRelease(reader.rootPages.Get(PdfName.COUNT));
sizep = npages.IntValue;
}
else {
ReadPages();
}
}
internal 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.Count; ++k) {
refsn[k] = DuplicatePdfObject((PdfObject)refsn[k], reader);
}
}
else
this.refsp = (IntHashtable)other.refsp.Clone();
}
internal int Size {
get {
if (refsn != null)
return refsn.Count;
else
return sizep;
}
}
internal void ReadPages() {
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.Count));
}
internal void ReReadPages() {
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 refi = GetPageOrigRef(pageNum);
return (PdfDictionary)PdfReader.GetPdfObject(refi);
}
/**
* @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 refi = GetPageOrigRef(pageNum);
ReleasePage(pageNum);
return refi;
}
/** 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) {
--pageNum;
if (pageNum < 0 || pageNum >= Size)
return null;
if (refsn != null)
return (PRIndirectReference)refsn[pageNum];
else {
int n = refsp[pageNum];
if (n == 0) {
PRIndirectReference refi = GetSinglePage(pageNum);
if (reader.lastXrefPartial == -1)
lastPageRead = -1;
else
lastPageRead = pageNum;
reader.lastXrefPartial = -1;
refsp[pageNum] = refi.Number;
if (keepPages)
lastPageRead = -1;
return refi;
}
else {
if (lastPageRead != pageNum)
lastPageRead = -1;
if (keepPages)
lastPageRead = -1;
return new PRIndirectReference(reader, n);
}
}
}
internal void KeepPages() {
if (refsp == null || keepPages)
return;
keepPages = true;
refsp.Clear();
}
/**
* @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[pageNum];
reader.ReleaseLastXrefPartial();
refsp.Remove(pageNum);
}
/**
*
*/
public void ResetReleasePage() {
if (refsp == null)
return;
lastPageRead = -1;
}
internal void InsertPage(int pageNum, PRIndirectReference refi) {
--pageNum;
if (refsn != null) {
if (pageNum >= refsn.Count)
refsn.Add(refi);
else
refsn.Insert(pageNum, refi);
}
else {
++sizep;
lastPageRead = -1;
if (pageNum >= Size) {
refsp[Size] = refi.Number;
}
else {
IntHashtable refs2 = new IntHashtable((refsp.Size + 1) * 2);
for (IntHashtable.IntHashtableIterator it = refsp.GetEntryIterator(); it.HasNext();) {
IntHashtable.IntHashtableEntry entry = (IntHashtable.IntHashtableEntry)it.Next();
int p = entry.Key;
refs2[p >= pageNum ? p + 1 : p] = entry.Value;
}
refs2[pageNum] = refi.Number;
refsp = refs2;
}
}
}
private void PushPageAttributes(PdfDictionary nodePages) {
PdfDictionary dic = new PdfDictionary();
if (pageInh.Count != 0) {
dic.Merge((PdfDictionary)pageInh[pageInh.Count - 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.RemoveAt(pageInh.Count - 1);
}
private void IteratePages(PRIndirectReference rpage) {
PdfDictionary page = (PdfDictionary)GetPdfObject(rpage);
PdfArray kidsPR = page.GetAsArray(PdfName.KIDS);
if (kidsPR == null) {
page.Put(PdfName.TYPE, PdfName.PAGE);
PdfDictionary dic = (PdfDictionary)pageInh[pageInh.Count - 1];
foreach (PdfName key in dic.Keys) {
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);
for (int k = 0; k < kidsPR.Size; ++k){
PdfObject obj = kidsPR[k];
if (!obj.IsIndirect()) {
while (k < kidsPR.Size)
kidsPR.Remove(k);
break;
}
IteratePages((PRIndirectReference)obj);
}
PopPageAttributes();
}
}
protected internal PRIndirectReference GetSinglePage(int n) {
PdfDictionary acc = new PdfDictionary();
PdfDictionary top = reader.rootPages;
int baseb = 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 (ListIterator it = new ListIterator(kids.ArrayList); it.HasNext();) {
PRIndirectReference refi = (PRIndirectReference)it.Next();
PdfDictionary dic = (PdfDictionary)GetPdfObject(refi);
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 < baseb + acn) {
if (count == null) {
dic.MergeDifferent(acc);
return refi;
}
reader.ReleaseLastXrefPartial();
top = dic;
break;
}
reader.ReleaseLastXrefPartial();
baseb += acn;
}
}
}
internal void SelectPages(ArrayList pagesToKeep) {
IntHashtable pg = new IntHashtable();
ArrayList finalPages = new ArrayList();
int psize = Size;
foreach (int p in pagesToKeep) {
if (p >= 1 && p <= psize && !pg.ContainsKey(p)) {
pg[p] = 1;
finalPages.Add(p);
}
}
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.Count);
PdfArray kids = new PdfArray();
foreach (int p in finalPages) {
PRIndirectReference pref = GetPageOrigRef(p);
ResetReleasePage();
kids.Add(pref);
newPageRefs.Add(pref);
GetPageN(p).Put(PdfName.PARENT, parent);
}
AcroFields af = reader.AcroFields;
bool removeFields = (af.Fields.Count > 0);
for (int k = 1; k <= psize; ++k) {
if (!pg.ContainsKey(k)) {
if (removeFields)
af.RemoveFieldsFromPage(k);
PRIndirectReference pref = GetPageOrigRef(k);
int nref = pref.Number;
reader.xrefObj[nref] = null;
if (reader.partial) {
reader.xref[nref * 2] = -1;
reader.xref[nref * 2 + 1] = 0;
}
}
}
topPages.Put(PdfName.COUNT, new PdfNumber(finalPages.Count));
topPages.Put(PdfName.KIDS, kids);
refsp = null;
refsn = newPageRefs;
}
}
internal PdfIndirectReference GetCryptoRef() {
if (cryptoRef == null)
return null;
return new PdfIndirectReference(0, cryptoRef.Number, cryptoRef.Generation);
}
/**
* Removes any usage rights that this PDF may have. Only Adobe can grant usage rights
* and any PDF modification with iText will invalidate them. Invalidated usage rights may
* confuse Acrobat and it's advisabe to remove them altogether.
*/
public void RemoveUsageRights() {
PdfDictionary perms = catalog.GetAsDict(PdfName.PERMS);
if (perms == null)
return;
perms.Remove(PdfName.UR);
perms.Remove(PdfName.UR3);
if (perms.Size == 0)
catalog.Remove(PdfName.PERMS);
}
/**
* Gets the certification level for this document. The return values can be <code>PdfSignatureAppearance.NOT_CERTIFIED</code>,
* <code>PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED</code>,
* <code>PdfSignatureAppearance.CERTIFIED_FORM_FILLING</code> and
* <code>PdfSignatureAppearance.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS</code>.
* <p>
* No signature validation is made, use the methods availabe for that in <CODE>AcroFields</CODE>.
* </p>
* @return gets the certification level for this document
*/
public int GetCertificationLevel() {
PdfDictionary dic = catalog.GetAsDict(PdfName.PERMS);
if (dic == null)
return PdfSignatureAppearance.NOT_CERTIFIED;
dic = dic.GetAsDict(PdfName.DOCMDP);
if (dic == null)
return PdfSignatureAppearance.NOT_CERTIFIED;
PdfArray arr = dic.GetAsArray(PdfName.REFERENCE);
if (arr == null || arr.Size == 0)
return PdfSignatureAppearance.NOT_CERTIFIED;
dic = arr.GetAsDict(0);
if (dic == null)
return PdfSignatureAppearance.NOT_CERTIFIED;
dic = dic.GetAsDict(PdfName.TRANSFORMPARAMS);
if (dic == null)
return PdfSignatureAppearance.NOT_CERTIFIED;
PdfNumber p = dic.GetAsNumber(PdfName.P);
if (p == null)
return PdfSignatureAppearance.NOT_CERTIFIED;
return p.IntValue;
}
/**
* Checks if the document was opened with the owner password so that the end application
* can decide what level of access restrictions to apply. If the document is not encrypted
* it will return <CODE>true</CODE>.
* @return <CODE>true</CODE> if the document was opened with the owner password or if it's not encrypted,
* <CODE>false</CODE> if the document was opened with the user password
*/
public bool IsOpenedWithFullPermissions {
get {
return !encrypted || ownerPasswordUsed;
}
}
public int GetCryptoMode() {
if (decrypt == null)
return -1;
else
return decrypt.GetCryptoMode();
}
public bool IsMetadataEncrypted() {
if (decrypt == null)
return false;
else
return decrypt.IsMetadataEncrypted();
}
public byte[] ComputeUserPassword() {
if (!encrypted || !ownerPasswordUsed) return null;
return decrypt.ComputeUserPassword(password);
}
}
}