Source Code Cross Referenced for PdfReader.java in  » PDF » pdf-itext » com » lowagie » text » pdf » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » PDF » pdf itext » com.lowagie.text.pdf 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * $Id: PdfReader.java 2911 2007-09-06 06:39:00Z xlv $
0003:         * $Name$
0004:         *
0005:         * Copyright 2001, 2002 Paulo Soares
0006:         *
0007:         * The contents of this file are subject to the Mozilla Public License Version 1.1
0008:         * (the "License"); you may not use this file except in compliance with the License.
0009:         * You may obtain a copy of the License at http://www.mozilla.org/MPL/
0010:         *
0011:         * Software distributed under the License is distributed on an "AS IS" basis,
0012:         * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0013:         * for the specific language governing rights and limitations under the License.
0014:         *
0015:         * The Original Code is 'iText, a free JAVA-PDF library'.
0016:         *
0017:         * The Initial Developer of the Original Code is Bruno Lowagie. Portions created by
0018:         * the Initial Developer are Copyright (C) 1999, 2000, 2001, 2002 by Bruno Lowagie.
0019:         * All Rights Reserved.
0020:         * Co-Developer of the code is Paulo Soares. Portions created by the Co-Developer
0021:         * are Copyright (C) 2000, 2001, 2002 by Paulo Soares. All Rights Reserved.
0022:         *
0023:         * Contributor(s): all the names of the contributors are added in the source code
0024:         * where applicable.
0025:         *
0026:         * Alternatively, the contents of this file may be used under the terms of the
0027:         * LGPL license (the "GNU LIBRARY GENERAL PUBLIC LICENSE"), in which case the
0028:         * provisions of LGPL are applicable instead of those above.  If you wish to
0029:         * allow use of your version of this file only under the terms of the LGPL
0030:         * License and not to allow others to use your version of this file under
0031:         * the MPL, indicate your decision by deleting the provisions above and
0032:         * replace them with the notice and other provisions required by the LGPL.
0033:         * If you do not delete the provisions above, a recipient may use your version
0034:         * of this file under either the MPL or the GNU LIBRARY GENERAL PUBLIC LICENSE.
0035:         *
0036:         * This library is free software; you can redistribute it and/or modify it
0037:         * under the terms of the MPL as stated above or under the terms of the GNU
0038:         * Library General Public License as published by the Free Software Foundation;
0039:         * either version 2 of the License, or any later version.
0040:         *
0041:         * This library is distributed in the hope that it will be useful, but WITHOUT
0042:         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
0043:         * FOR A PARTICULAR PURPOSE. See the GNU Library general Public License for more
0044:         * details.
0045:         *
0046:         * If you didn't download this code from the following link, you should check if
0047:         * you aren't using an obsolete version:
0048:         * http://www.lowagie.com/iText/
0049:         */
0050:
0051:        package com.lowagie.text.pdf;
0052:
0053:        import java.io.ByteArrayInputStream;
0054:        import java.io.ByteArrayOutputStream;
0055:        import java.io.DataInputStream;
0056:        import java.io.IOException;
0057:        import java.io.InputStream;
0058:        import java.net.URL;
0059:        import java.util.ArrayList;
0060:        import java.util.Arrays;
0061:        import java.util.Collections;
0062:        import java.util.HashMap;
0063:        import java.util.Iterator;
0064:        import java.util.List;
0065:        import java.util.Map;
0066:        import java.util.Set;
0067:        import java.util.zip.InflaterInputStream;
0068:        import java.util.Stack;
0069:        import java.security.Key;
0070:        import java.security.MessageDigest;
0071:        import java.security.cert.Certificate;
0072:
0073:        import com.lowagie.text.ExceptionConverter;
0074:        import com.lowagie.text.PageSize;
0075:        import com.lowagie.text.Rectangle;
0076:        import com.lowagie.text.pdf.interfaces.PdfViewerPreferences;
0077:        import com.lowagie.text.pdf.internal.PdfViewerPreferencesImp;
0078:
0079:        import org.bouncycastle.cms.CMSEnvelopedData;
0080:        import org.bouncycastle.cms.RecipientInformation;
0081:
0082:        /** Reads a PDF document.
0083:         * @author Paulo Soares (psoares@consiste.pt)
0084:         * @author Kazuya Ujihara
0085:         */
0086:        public class PdfReader implements  PdfViewerPreferences {
0087:
0088:            static final PdfName pageInhCandidates[] = { PdfName.MEDIABOX,
0089:                    PdfName.ROTATE, PdfName.RESOURCES, PdfName.CROPBOX };
0090:
0091:            static final byte endstream[] = PdfEncodings.convertToBytes(
0092:                    "endstream", null);
0093:            static final byte endobj[] = PdfEncodings.convertToBytes("endobj",
0094:                    null);
0095:            protected PRTokeniser tokens;
0096:            // Each xref pair is a position
0097:            // type 0 -> -1, 0
0098:            // type 1 -> offset, 0
0099:            // type 2 -> index, obj num
0100:            protected int xref[];
0101:            protected HashMap objStmMark;
0102:            protected IntHashtable objStmToOffset;
0103:            protected boolean newXrefType;
0104:            private ArrayList xrefObj;
0105:            PdfDictionary rootPages;
0106:            protected PdfDictionary trailer;
0107:            protected PdfDictionary catalog;
0108:            protected PageRefs pageRefs;
0109:            protected PRAcroForm acroForm = null;
0110:            protected boolean acroFormParsed = false;
0111:            protected boolean encrypted = false;
0112:            protected boolean rebuilt = false;
0113:            protected int freeXref;
0114:            protected boolean tampered = false;
0115:            protected int lastXref;
0116:            protected int eofPos;
0117:            protected char pdfVersion;
0118:            protected PdfEncryption decrypt;
0119:            protected byte password[] = null; //added by ujihara for decryption
0120:            protected Key certificateKey = null; //added by Aiken Sam for certificate decryption
0121:            protected Certificate certificate = null; //added by Aiken Sam for certificate decryption
0122:            protected String certificateKeyProvider = null; //added by Aiken Sam for certificate decryption
0123:            private boolean ownerPasswordUsed;
0124:            protected ArrayList strings = new ArrayList();
0125:            protected boolean sharedStreams = true;
0126:            protected boolean consolidateNamedDestinations = false;
0127:            protected int rValue;
0128:            protected int pValue;
0129:            private int objNum;
0130:            private int objGen;
0131:            private int fileLength;
0132:            private boolean hybridXref;
0133:            private int lastXrefPartial = -1;
0134:            private boolean partial;
0135:            private PRIndirectReference cryptoRef;
0136:            private PdfViewerPreferencesImp viewerPreferences = new PdfViewerPreferencesImp();
0137:
0138:            /**
0139:             * Holds value of property appendable.
0140:             */
0141:            private boolean appendable;
0142:
0143:            protected PdfReader() {
0144:            }
0145:
0146:            /** Reads and parses a PDF document.
0147:             * @param filename the file name of the document
0148:             * @throws IOException on error
0149:             */
0150:            public PdfReader(String filename) throws IOException {
0151:                this (filename, null);
0152:            }
0153:
0154:            /** Reads and parses a PDF document.
0155:             * @param filename the file name of the document
0156:             * @param ownerPassword the password to read the document
0157:             * @throws IOException on error
0158:             */
0159:            public PdfReader(String filename, byte ownerPassword[])
0160:                    throws IOException {
0161:                password = ownerPassword;
0162:                tokens = new PRTokeniser(filename);
0163:                readPdf();
0164:            }
0165:
0166:            /** Reads and parses a PDF document.
0167:             * @param pdfIn the byte array with the document
0168:             * @throws IOException on error
0169:             */
0170:            public PdfReader(byte pdfIn[]) throws IOException {
0171:                this (pdfIn, null);
0172:            }
0173:
0174:            /** Reads and parses a PDF document.
0175:             * @param pdfIn the byte array with the document
0176:             * @param ownerPassword the password to read the document
0177:             * @throws IOException on error
0178:             */
0179:            public PdfReader(byte pdfIn[], byte ownerPassword[])
0180:                    throws IOException {
0181:                password = ownerPassword;
0182:                tokens = new PRTokeniser(pdfIn);
0183:                readPdf();
0184:            }
0185:
0186:            /** Reads and parses a PDF document.
0187:             * @param filename the file name of the document
0188:             * @param certificate the certificate to read the document
0189:             * @param certificateKey the private key of the certificate
0190:             * @param certificateKeyProvider the security provider for certificateKey
0191:             * @throws IOException on error
0192:             */
0193:            public PdfReader(String filename, Certificate certificate,
0194:                    Key certificateKey, String certificateKeyProvider)
0195:                    throws IOException {
0196:                this .certificate = certificate;
0197:                this .certificateKey = certificateKey;
0198:                this .certificateKeyProvider = certificateKeyProvider;
0199:                tokens = new PRTokeniser(filename);
0200:                readPdf();
0201:            }
0202:
0203:            /** Reads and parses a PDF document.
0204:             * @param url the URL of the document
0205:             * @throws IOException on error
0206:             */
0207:            public PdfReader(URL url) throws IOException {
0208:                this (url, null);
0209:            }
0210:
0211:            /** Reads and parses a PDF document.
0212:             * @param url the URL of the document
0213:             * @param ownerPassword the password to read the document
0214:             * @throws IOException on error
0215:             */
0216:            public PdfReader(URL url, byte ownerPassword[]) throws IOException {
0217:                password = ownerPassword;
0218:                tokens = new PRTokeniser(new RandomAccessFileOrArray(url));
0219:                readPdf();
0220:            }
0221:
0222:            /**
0223:             * Reads and parses a PDF document.
0224:             * @param is the <CODE>InputStream</CODE> containing the document. The stream is read to the
0225:             * end but is not closed
0226:             * @param ownerPassword the password to read the document
0227:             * @throws IOException on error
0228:             */
0229:            public PdfReader(InputStream is, byte ownerPassword[])
0230:                    throws IOException {
0231:                password = ownerPassword;
0232:                tokens = new PRTokeniser(new RandomAccessFileOrArray(is));
0233:                readPdf();
0234:            }
0235:
0236:            /**
0237:             * Reads and parses a PDF document.
0238:             * @param is the <CODE>InputStream</CODE> containing the document. The stream is read to the
0239:             * end but is not closed
0240:             * @throws IOException on error
0241:             */
0242:            public PdfReader(InputStream is) throws IOException {
0243:                this (is, null);
0244:            }
0245:
0246:            /**
0247:             * Reads and parses a pdf document. Contrary to the other constructors only the xref is read
0248:             * into memory. The reader is said to be working in "partial" mode as only parts of the pdf
0249:             * are read as needed. The pdf is left open but may be closed at any time with
0250:             * <CODE>PdfReader.close()</CODE>, reopen is automatic.
0251:             * @param raf the document location
0252:             * @param ownerPassword the password or <CODE>null</CODE> for no password
0253:             * @throws IOException on error
0254:             */
0255:            public PdfReader(RandomAccessFileOrArray raf, byte ownerPassword[])
0256:                    throws IOException {
0257:                password = ownerPassword;
0258:                partial = true;
0259:                tokens = new PRTokeniser(raf);
0260:                readPdfPartial();
0261:            }
0262:
0263:            /** Creates an independent duplicate.
0264:             * @param reader the <CODE>PdfReader</CODE> to duplicate
0265:             */
0266:            public PdfReader(PdfReader reader) {
0267:                this .appendable = reader.appendable;
0268:                this .consolidateNamedDestinations = reader.consolidateNamedDestinations;
0269:                this .encrypted = reader.encrypted;
0270:                this .rebuilt = reader.rebuilt;
0271:                this .sharedStreams = reader.sharedStreams;
0272:                this .tampered = reader.tampered;
0273:                this .password = reader.password;
0274:                this .pdfVersion = reader.pdfVersion;
0275:                this .eofPos = reader.eofPos;
0276:                this .freeXref = reader.freeXref;
0277:                this .lastXref = reader.lastXref;
0278:                this .tokens = new PRTokeniser(reader.tokens.getSafeFile());
0279:                if (reader.decrypt != null)
0280:                    this .decrypt = new PdfEncryption(reader.decrypt);
0281:                this .pValue = reader.pValue;
0282:                this .rValue = reader.rValue;
0283:                this .xrefObj = new ArrayList(reader.xrefObj);
0284:                for (int k = 0; k < reader.xrefObj.size(); ++k) {
0285:                    this .xrefObj.set(k, duplicatePdfObject(
0286:                            (PdfObject) reader.xrefObj.get(k), this ));
0287:                }
0288:                this .pageRefs = new PageRefs(reader.pageRefs, this );
0289:                this .trailer = (PdfDictionary) duplicatePdfObject(
0290:                        reader.trailer, this );
0291:                this .catalog = (PdfDictionary) getPdfObject(trailer
0292:                        .get(PdfName.ROOT));
0293:                this .rootPages = (PdfDictionary) getPdfObject(catalog
0294:                        .get(PdfName.PAGES));
0295:                this .fileLength = reader.fileLength;
0296:                this .partial = reader.partial;
0297:                this .hybridXref = reader.hybridXref;
0298:                this .objStmToOffset = reader.objStmToOffset;
0299:                this .xref = reader.xref;
0300:                this .cryptoRef = (PRIndirectReference) duplicatePdfObject(
0301:                        reader.cryptoRef, this );
0302:                this .ownerPasswordUsed = reader.ownerPasswordUsed;
0303:            }
0304:
0305:            /** Gets a new file instance of the original PDF
0306:             * document.
0307:             * @return a new file instance of the original PDF document
0308:             */
0309:            public RandomAccessFileOrArray getSafeFile() {
0310:                return tokens.getSafeFile();
0311:            }
0312:
0313:            protected PdfReaderInstance getPdfReaderInstance(PdfWriter writer) {
0314:                return new PdfReaderInstance(this , writer);
0315:            }
0316:
0317:            /** Gets the number of pages in the document.
0318:             * @return the number of pages in the document
0319:             */
0320:            public int getNumberOfPages() {
0321:                return pageRefs.size();
0322:            }
0323:
0324:            /** Returns the document's catalog. This dictionary is not a copy,
0325:             * any changes will be reflected in the catalog.
0326:             * @return the document's catalog
0327:             */
0328:            public PdfDictionary getCatalog() {
0329:                return catalog;
0330:            }
0331:
0332:            /** Returns the document's acroform, if it has one.
0333:             * @return the document's acroform
0334:             */
0335:            public PRAcroForm getAcroForm() {
0336:                if (!acroFormParsed) {
0337:                    acroFormParsed = true;
0338:                    PdfObject form = catalog.get(PdfName.ACROFORM);
0339:                    if (form != null) {
0340:                        try {
0341:                            acroForm = new PRAcroForm(this );
0342:                            acroForm
0343:                                    .readAcroForm((PdfDictionary) getPdfObject(form));
0344:                        } catch (Exception e) {
0345:                            acroForm = null;
0346:                        }
0347:                    }
0348:                }
0349:                return acroForm;
0350:            }
0351:
0352:            /**
0353:             * Gets the page rotation. This value can be 0, 90, 180 or 270.
0354:             * @param index the page number. The first page is 1
0355:             * @return the page rotation
0356:             */
0357:            public int getPageRotation(int index) {
0358:                return getPageRotation(pageRefs.getPageNRelease(index));
0359:            }
0360:
0361:            int getPageRotation(PdfDictionary page) {
0362:                PdfNumber rotate = (PdfNumber) getPdfObject(page
0363:                        .get(PdfName.ROTATE));
0364:                if (rotate == null)
0365:                    return 0;
0366:                else {
0367:                    int n = rotate.intValue();
0368:                    n %= 360;
0369:                    return n < 0 ? n + 360 : n;
0370:                }
0371:            }
0372:
0373:            /** Gets the page size, taking rotation into account. This
0374:             * is a <CODE>Rectangle</CODE> with the value of the /MediaBox and the /Rotate key.
0375:             * @param index the page number. The first page is 1
0376:             * @return a <CODE>Rectangle</CODE>
0377:             */
0378:            public Rectangle getPageSizeWithRotation(int index) {
0379:                return getPageSizeWithRotation(pageRefs.getPageNRelease(index));
0380:            }
0381:
0382:            /**
0383:             * Gets the rotated page from a page dictionary.
0384:             * @param page the page dictionary
0385:             * @return the rotated page
0386:             */
0387:            public Rectangle getPageSizeWithRotation(PdfDictionary page) {
0388:                Rectangle rect = getPageSize(page);
0389:                int rotation = getPageRotation(page);
0390:                while (rotation > 0) {
0391:                    rect = rect.rotate();
0392:                    rotation -= 90;
0393:                }
0394:                return rect;
0395:            }
0396:
0397:            /** Gets the page size without taking rotation into account. This
0398:             * is the value of the /MediaBox key.
0399:             * @param index the page number. The first page is 1
0400:             * @return the page size
0401:             */
0402:            public Rectangle getPageSize(int index) {
0403:                return getPageSize(pageRefs.getPageNRelease(index));
0404:            }
0405:
0406:            /**
0407:             * Gets the page from a page dictionary
0408:             * @param page the page dictionary
0409:             * @return the page
0410:             */
0411:            public Rectangle getPageSize(PdfDictionary page) {
0412:                PdfArray mediaBox = (PdfArray) getPdfObject(page
0413:                        .get(PdfName.MEDIABOX));
0414:                return getNormalizedRectangle(mediaBox);
0415:            }
0416:
0417:            /** Gets the crop box without taking rotation into account. This
0418:             * is the value of the /CropBox key. The crop box is the part
0419:             * of the document to be displayed or printed. It usually is the same
0420:             * as the media box but may be smaller. If the page doesn't have a crop
0421:             * box the page size will be returned.
0422:             * @param index the page number. The first page is 1
0423:             * @return the crop box
0424:             */
0425:            public Rectangle getCropBox(int index) {
0426:                PdfDictionary page = pageRefs.getPageNRelease(index);
0427:                PdfArray cropBox = (PdfArray) getPdfObjectRelease(page
0428:                        .get(PdfName.CROPBOX));
0429:                if (cropBox == null)
0430:                    return getPageSize(page);
0431:                return getNormalizedRectangle(cropBox);
0432:            }
0433:
0434:            /** Gets the box size. Allowed names are: "crop", "trim", "art", "bleed" and "media".
0435:             * @param index the page number. The first page is 1
0436:             * @param boxName the box name
0437:             * @return the box rectangle or null
0438:             */
0439:            public Rectangle getBoxSize(int index, String boxName) {
0440:                PdfDictionary page = pageRefs.getPageNRelease(index);
0441:                PdfArray box = null;
0442:                if (boxName.equals("trim"))
0443:                    box = (PdfArray) getPdfObjectRelease(page
0444:                            .get(PdfName.TRIMBOX));
0445:                else if (boxName.equals("art"))
0446:                    box = (PdfArray) getPdfObjectRelease(page
0447:                            .get(PdfName.ARTBOX));
0448:                else if (boxName.equals("bleed"))
0449:                    box = (PdfArray) getPdfObjectRelease(page
0450:                            .get(PdfName.BLEEDBOX));
0451:                else if (boxName.equals("crop"))
0452:                    box = (PdfArray) getPdfObjectRelease(page
0453:                            .get(PdfName.CROPBOX));
0454:                else if (boxName.equals("media"))
0455:                    box = (PdfArray) getPdfObjectRelease(page
0456:                            .get(PdfName.MEDIABOX));
0457:                if (box == null)
0458:                    return null;
0459:                return getNormalizedRectangle(box);
0460:            }
0461:
0462:            /** Returns the content of the document information dictionary as a <CODE>HashMap</CODE>
0463:             * of <CODE>String</CODE>.
0464:             * @return content of the document information dictionary
0465:             */
0466:            public HashMap getInfo() {
0467:                HashMap map = new HashMap();
0468:                PdfDictionary info = (PdfDictionary) getPdfObject(trailer
0469:                        .get(PdfName.INFO));
0470:                if (info == null)
0471:                    return map;
0472:                for (Iterator it = info.getKeys().iterator(); it.hasNext();) {
0473:                    PdfName key = (PdfName) it.next();
0474:                    PdfObject obj = getPdfObject(info.get(key));
0475:                    if (obj == null)
0476:                        continue;
0477:                    String value = obj.toString();
0478:                    switch (obj.type()) {
0479:                    case PdfObject.STRING: {
0480:                        value = ((PdfString) obj).toUnicodeString();
0481:                        break;
0482:                    }
0483:                    case PdfObject.NAME: {
0484:                        value = PdfName.decodeName(value);
0485:                        break;
0486:                    }
0487:                    }
0488:                    map.put(PdfName.decodeName(key.toString()), value);
0489:                }
0490:                return map;
0491:            }
0492:
0493:            /** Normalizes a <CODE>Rectangle</CODE> so that llx and lly are smaller than urx and ury.
0494:             * @param box the original rectangle
0495:             * @return a normalized <CODE>Rectangle</CODE>
0496:             */
0497:            public static Rectangle getNormalizedRectangle(PdfArray box) {
0498:                ArrayList rect = box.getArrayList();
0499:                float llx = ((PdfNumber) getPdfObjectRelease((PdfObject) rect
0500:                        .get(0))).floatValue();
0501:                float lly = ((PdfNumber) getPdfObjectRelease((PdfObject) rect
0502:                        .get(1))).floatValue();
0503:                float urx = ((PdfNumber) getPdfObjectRelease((PdfObject) rect
0504:                        .get(2))).floatValue();
0505:                float ury = ((PdfNumber) getPdfObjectRelease((PdfObject) rect
0506:                        .get(3))).floatValue();
0507:                return new Rectangle(Math.min(llx, urx), Math.min(lly, ury),
0508:                        Math.max(llx, urx), Math.max(lly, ury));
0509:            }
0510:
0511:            protected void readPdf() throws IOException {
0512:                try {
0513:                    fileLength = tokens.getFile().length();
0514:                    pdfVersion = tokens.checkPdfHeader();
0515:                    try {
0516:                        readXref();
0517:                    } catch (Exception e) {
0518:                        try {
0519:                            rebuilt = true;
0520:                            rebuildXref();
0521:                            lastXref = -1;
0522:                        } catch (Exception ne) {
0523:                            throw new IOException("Rebuild failed: "
0524:                                    + ne.getMessage() + "; Original message: "
0525:                                    + e.getMessage());
0526:                        }
0527:                    }
0528:                    try {
0529:                        readDocObj();
0530:                    } catch (Exception ne) {
0531:                        if (rebuilt)
0532:                            throw new IOException(ne.getMessage());
0533:                        rebuilt = true;
0534:                        encrypted = false;
0535:                        rebuildXref();
0536:                        lastXref = -1;
0537:                        readDocObj();
0538:                    }
0539:
0540:                    strings.clear();
0541:                    readPages();
0542:                    eliminateSharedStreams();
0543:                    removeUnusedObjects();
0544:                } finally {
0545:                    try {
0546:                        tokens.close();
0547:                    } catch (Exception e) {
0548:                        // empty on purpose
0549:                    }
0550:                }
0551:            }
0552:
0553:            protected void readPdfPartial() throws IOException {
0554:                try {
0555:                    fileLength = tokens.getFile().length();
0556:                    pdfVersion = tokens.checkPdfHeader();
0557:                    try {
0558:                        readXref();
0559:                    } catch (Exception e) {
0560:                        try {
0561:                            rebuilt = true;
0562:                            rebuildXref();
0563:                            lastXref = -1;
0564:                        } catch (Exception ne) {
0565:                            throw new IOException("Rebuild failed: "
0566:                                    + ne.getMessage() + "; Original message: "
0567:                                    + e.getMessage());
0568:                        }
0569:                    }
0570:                    readDocObjPartial();
0571:                    readPages();
0572:                } catch (IOException e) {
0573:                    try {
0574:                        tokens.close();
0575:                    } catch (Exception ee) {
0576:                    }
0577:                    throw e;
0578:                }
0579:            }
0580:
0581:            private boolean equalsArray(byte ar1[], byte ar2[], int size) {
0582:                for (int k = 0; k < size; ++k) {
0583:                    if (ar1[k] != ar2[k])
0584:                        return false;
0585:                }
0586:                return true;
0587:            }
0588:
0589:            /**
0590:             * @throws IOException
0591:             */
0592:            private void readDecryptedDocObj() throws IOException {
0593:                if (encrypted)
0594:                    return;
0595:                PdfObject encDic = trailer.get(PdfName.ENCRYPT);
0596:                if (encDic == null || encDic.toString().equals("null"))
0597:                    return;
0598:                byte[] encryptionKey = null;
0599:                encrypted = true;
0600:                PdfDictionary enc = (PdfDictionary) getPdfObject(encDic);
0601:
0602:                String s;
0603:                PdfObject o;
0604:
0605:                PdfArray documentIDs = (PdfArray) getPdfObject(trailer
0606:                        .get(PdfName.ID));
0607:                byte documentID[] = null;
0608:                if (documentIDs != null) {
0609:                    o = (PdfObject) documentIDs.getArrayList().get(0);
0610:                    strings.remove(o);
0611:                    s = o.toString();
0612:                    documentID = com.lowagie.text.DocWriter.getISOBytes(s);
0613:                    if (documentIDs.size() > 1)
0614:                        strings.remove(documentIDs.getArrayList().get(1));
0615:                }
0616:
0617:                byte uValue[] = null;
0618:                byte oValue[] = null;
0619:                int cryptoMode = PdfWriter.STANDARD_ENCRYPTION_40;
0620:                int lengthValue = 0;
0621:
0622:                PdfObject filter = getPdfObjectRelease(enc.get(PdfName.FILTER));
0623:
0624:                if (filter.equals(PdfName.STANDARD)) {
0625:                    s = enc.get(PdfName.U).toString();
0626:                    strings.remove(enc.get(PdfName.U));
0627:                    uValue = com.lowagie.text.DocWriter.getISOBytes(s);
0628:                    s = enc.get(PdfName.O).toString();
0629:                    strings.remove(enc.get(PdfName.O));
0630:                    oValue = com.lowagie.text.DocWriter.getISOBytes(s);
0631:
0632:                    o = enc.get(PdfName.R);
0633:                    if (!o.isNumber())
0634:                        throw new IOException("Illegal R value.");
0635:                    rValue = ((PdfNumber) o).intValue();
0636:                    if (rValue != 2 && rValue != 3 && rValue != 4)
0637:                        throw new IOException("Unknown encryption type ("
0638:                                + rValue + ")");
0639:
0640:                    o = enc.get(PdfName.P);
0641:                    if (!o.isNumber())
0642:                        throw new IOException("Illegal P value.");
0643:                    pValue = ((PdfNumber) o).intValue();
0644:
0645:                    if (rValue == 3) {
0646:                        o = enc.get(PdfName.LENGTH);
0647:                        if (!o.isNumber())
0648:                            throw new IOException("Illegal Length value.");
0649:                        lengthValue = ((PdfNumber) o).intValue();
0650:                        if (lengthValue > 128 || lengthValue < 40
0651:                                || lengthValue % 8 != 0)
0652:                            throw new IOException("Illegal Length value.");
0653:                        cryptoMode = PdfWriter.STANDARD_ENCRYPTION_128;
0654:                    } else if (rValue == 4) {
0655:                        PdfDictionary dic = (PdfDictionary) enc.get(PdfName.CF);
0656:                        if (dic == null)
0657:                            throw new IOException("/CF not found (encryption)");
0658:                        dic = (PdfDictionary) dic.get(PdfName.STDCF);
0659:                        if (dic == null)
0660:                            throw new IOException(
0661:                                    "/StdCF not found (encryption)");
0662:                        if (PdfName.V2.equals(dic.get(PdfName.CFM)))
0663:                            cryptoMode = PdfWriter.STANDARD_ENCRYPTION_128;
0664:                        else if (PdfName.AESV2.equals(dic.get(PdfName.CFM)))
0665:                            cryptoMode = PdfWriter.ENCRYPTION_AES_128;
0666:                        else
0667:                            throw new IOException(
0668:                                    "No compatible encryption found");
0669:                        PdfObject em = enc.get(PdfName.ENCRYPTMETADATA);
0670:                        if (em != null && em.toString().equals("false"))
0671:                            cryptoMode |= PdfWriter.DO_NOT_ENCRYPT_METADATA;
0672:                    } else {
0673:                        cryptoMode = PdfWriter.STANDARD_ENCRYPTION_40;
0674:                    }
0675:                } else if (filter.equals(PdfName.PUBSEC)) {
0676:                    boolean foundRecipient = false;
0677:                    byte[] envelopedData = null;
0678:                    PdfArray recipients = null;
0679:
0680:                    o = enc.get(PdfName.V);
0681:                    if (!o.isNumber())
0682:                        throw new IOException("Illegal V value.");
0683:                    int vValue = ((PdfNumber) o).intValue();
0684:                    if (vValue != 1 && vValue != 2 && vValue != 4)
0685:                        throw new IOException("Unknown encryption type V = "
0686:                                + rValue);
0687:
0688:                    if (vValue == 2) {
0689:                        o = enc.get(PdfName.LENGTH);
0690:                        if (!o.isNumber())
0691:                            throw new IOException("Illegal Length value.");
0692:                        lengthValue = ((PdfNumber) o).intValue();
0693:                        if (lengthValue > 128 || lengthValue < 40
0694:                                || lengthValue % 8 != 0)
0695:                            throw new IOException("Illegal Length value.");
0696:                        cryptoMode = PdfWriter.STANDARD_ENCRYPTION_128;
0697:                        recipients = (PdfArray) enc.get(PdfName.RECIPIENTS);
0698:                    } else if (vValue == 4) {
0699:                        PdfDictionary dic = (PdfDictionary) enc.get(PdfName.CF);
0700:                        if (dic == null)
0701:                            throw new IOException("/CF not found (encryption)");
0702:                        dic = (PdfDictionary) dic
0703:                                .get(PdfName.DEFAULTCRYPTFILER);
0704:                        if (dic == null)
0705:                            throw new IOException(
0706:                                    "/DefaultCryptFilter not found (encryption)");
0707:                        if (PdfName.V2.equals(dic.get(PdfName.CFM))) {
0708:                            cryptoMode = PdfWriter.STANDARD_ENCRYPTION_128;
0709:                            lengthValue = 128;
0710:                        } else if (PdfName.AESV2.equals(dic.get(PdfName.CFM))) {
0711:                            cryptoMode = PdfWriter.ENCRYPTION_AES_128;
0712:                            lengthValue = 128;
0713:                        } else
0714:                            throw new IOException(
0715:                                    "No compatible encryption found");
0716:                        PdfObject em = dic.get(PdfName.ENCRYPTMETADATA);
0717:                        if (em != null && em.toString().equals("false"))
0718:                            cryptoMode |= PdfWriter.DO_NOT_ENCRYPT_METADATA;
0719:
0720:                        recipients = (PdfArray) dic.get(PdfName.RECIPIENTS);
0721:                    } else {
0722:                        cryptoMode = PdfWriter.STANDARD_ENCRYPTION_40;
0723:                        lengthValue = 40;
0724:                        recipients = (PdfArray) enc.get(PdfName.RECIPIENTS);
0725:                    }
0726:
0727:                    for (int i = 0; i < recipients.size(); i++) {
0728:                        PdfObject recipient = (PdfObject) recipients
0729:                                .getArrayList().get(i);
0730:                        strings.remove(recipient);
0731:
0732:                        CMSEnvelopedData data = null;
0733:                        try {
0734:                            data = new CMSEnvelopedData(recipient.getBytes());
0735:
0736:                            Iterator recipientCertificatesIt = data
0737:                                    .getRecipientInfos().getRecipients()
0738:                                    .iterator();
0739:
0740:                            while (recipientCertificatesIt.hasNext()) {
0741:                                RecipientInformation recipientInfo = (RecipientInformation) recipientCertificatesIt
0742:                                        .next();
0743:
0744:                                if (recipientInfo.getRID().match(certificate)
0745:                                        && !foundRecipient) {
0746:
0747:                                    envelopedData = recipientInfo.getContent(
0748:                                            certificateKey,
0749:                                            certificateKeyProvider);
0750:                                    foundRecipient = true;
0751:                                }
0752:                            }
0753:                        } catch (Exception f) {
0754:                            throw new ExceptionConverter(f);
0755:                        }
0756:                    }
0757:
0758:                    if (!foundRecipient || envelopedData == null) {
0759:                        throw new IOException("Bad certificate and key.");
0760:                    }
0761:
0762:                    MessageDigest md = null;
0763:
0764:                    try {
0765:                        md = MessageDigest.getInstance("SHA-1");
0766:                        md.update(envelopedData, 0, 20);
0767:                        for (int i = 0; i < recipients.size(); i++) {
0768:                            byte[] encodedRecipient = ((PdfObject) recipients
0769:                                    .getArrayList().get(i)).getBytes();
0770:                            md.update(encodedRecipient);
0771:                        }
0772:                        if ((cryptoMode & PdfWriter.DO_NOT_ENCRYPT_METADATA) != 0)
0773:                            md.update(new byte[] { (byte) 255, (byte) 255,
0774:                                    (byte) 255, (byte) 255 });
0775:                        encryptionKey = md.digest();
0776:
0777:                    } catch (Exception f) {
0778:                        throw new ExceptionConverter(f);
0779:                    }
0780:                }
0781:
0782:                decrypt = new PdfEncryption();
0783:                decrypt.setCryptoMode(cryptoMode, lengthValue);
0784:
0785:                if (filter.equals(PdfName.STANDARD)) {
0786:                    //check by owner password
0787:                    decrypt.setupByOwnerPassword(documentID, password, uValue,
0788:                            oValue, pValue);
0789:                    if (!equalsArray(uValue, decrypt.userKey,
0790:                            (rValue == 3 || rValue == 4) ? 16 : 32)) {
0791:                        //check by user password
0792:                        decrypt.setupByUserPassword(documentID, password,
0793:                                oValue, pValue);
0794:                        if (!equalsArray(uValue, decrypt.userKey,
0795:                                (rValue == 3 || rValue == 4) ? 16 : 32)) {
0796:                            throw new IOException("Bad user password");
0797:                        }
0798:                    } else
0799:                        ownerPasswordUsed = true;
0800:                } else if (filter.equals(PdfName.PUBSEC)) {
0801:                    decrypt.setupByEncryptionKey(encryptionKey, lengthValue);
0802:                    ownerPasswordUsed = true;
0803:                }
0804:
0805:                for (int k = 0; k < strings.size(); ++k) {
0806:                    PdfString str = (PdfString) strings.get(k);
0807:                    str.decrypt(this );
0808:                }
0809:
0810:                if (encDic.isIndirect()) {
0811:                    cryptoRef = (PRIndirectReference) encDic;
0812:                    xrefObj.set(cryptoRef.getNumber(), null);
0813:                }
0814:            }
0815:
0816:            /**
0817:             * @param obj
0818:             * @return a PdfObject
0819:             */
0820:            public static PdfObject getPdfObjectRelease(PdfObject obj) {
0821:                PdfObject obj2 = getPdfObject(obj);
0822:                releaseLastXrefPartial(obj);
0823:                return obj2;
0824:            }
0825:
0826:            /**
0827:             * Reads a <CODE>PdfObject</CODE> resolving an indirect reference
0828:             * if needed.
0829:             * @param obj the <CODE>PdfObject</CODE> to read
0830:             * @return the resolved <CODE>PdfObject</CODE>
0831:             */
0832:            public static PdfObject getPdfObject(PdfObject obj) {
0833:                if (obj == null)
0834:                    return null;
0835:                if (!obj.isIndirect())
0836:                    return obj;
0837:                try {
0838:                    PRIndirectReference ref = (PRIndirectReference) obj;
0839:                    int idx = ref.getNumber();
0840:                    boolean appendable = ref.getReader().appendable;
0841:                    obj = ref.getReader().getPdfObject(idx);
0842:                    if (obj == null) {
0843:                        return null;
0844:                    } else {
0845:                        if (appendable) {
0846:                            switch (obj.type()) {
0847:                            case PdfObject.NULL:
0848:                                obj = new PdfNull();
0849:                                break;
0850:                            case PdfObject.BOOLEAN:
0851:                                obj = new PdfBoolean(((PdfBoolean) obj)
0852:                                        .booleanValue());
0853:                                break;
0854:                            case PdfObject.NAME:
0855:                                obj = new PdfName(obj.getBytes());
0856:                                break;
0857:                            }
0858:                            obj.setIndRef(ref);
0859:                        }
0860:                        return obj;
0861:                    }
0862:                } catch (Exception e) {
0863:                    throw new ExceptionConverter(e);
0864:                }
0865:            }
0866:
0867:            /**
0868:             * Reads a <CODE>PdfObject</CODE> resolving an indirect reference
0869:             * if needed. If the reader was opened in partial mode the object will be released
0870:             * to save memory.
0871:             * @param obj the <CODE>PdfObject</CODE> to read
0872:             * @param parent
0873:             * @return a PdfObject
0874:             */
0875:            public static PdfObject getPdfObjectRelease(PdfObject obj,
0876:                    PdfObject parent) {
0877:                PdfObject obj2 = getPdfObject(obj, parent);
0878:                releaseLastXrefPartial(obj);
0879:                return obj2;
0880:            }
0881:
0882:            /**
0883:             * @param obj
0884:             * @param parent
0885:             * @return a PdfObject
0886:             */
0887:            public static PdfObject getPdfObject(PdfObject obj, PdfObject parent) {
0888:                if (obj == null)
0889:                    return null;
0890:                if (!obj.isIndirect()) {
0891:                    PRIndirectReference ref = null;
0892:                    if (parent != null && (ref = parent.getIndRef()) != null
0893:                            && ref.getReader().isAppendable()) {
0894:                        switch (obj.type()) {
0895:                        case PdfObject.NULL:
0896:                            obj = new PdfNull();
0897:                            break;
0898:                        case PdfObject.BOOLEAN:
0899:                            obj = new PdfBoolean(((PdfBoolean) obj)
0900:                                    .booleanValue());
0901:                            break;
0902:                        case PdfObject.NAME:
0903:                            obj = new PdfName(obj.getBytes());
0904:                            break;
0905:                        }
0906:                        obj.setIndRef(ref);
0907:                    }
0908:                    return obj;
0909:                }
0910:                return getPdfObject(obj);
0911:            }
0912:
0913:            /**
0914:             * @param idx
0915:             * @return a PdfObject
0916:             */
0917:            public PdfObject getPdfObjectRelease(int idx) {
0918:                PdfObject obj = getPdfObject(idx);
0919:                releaseLastXrefPartial();
0920:                return obj;
0921:            }
0922:
0923:            /**
0924:             * @param idx
0925:             * @return aPdfObject
0926:             */
0927:            public PdfObject getPdfObject(int idx) {
0928:                try {
0929:                    lastXrefPartial = -1;
0930:                    if (idx < 0 || idx >= xrefObj.size())
0931:                        return null;
0932:                    PdfObject obj = (PdfObject) xrefObj.get(idx);
0933:                    if (!partial || obj != null)
0934:                        return obj;
0935:                    if (idx * 2 >= xref.length)
0936:                        return null;
0937:                    obj = readSingleObject(idx);
0938:                    lastXrefPartial = -1;
0939:                    if (obj != null)
0940:                        lastXrefPartial = idx;
0941:                    return obj;
0942:                } catch (Exception e) {
0943:                    throw new ExceptionConverter(e);
0944:                }
0945:            }
0946:
0947:            /**
0948:             *
0949:             */
0950:            public void resetLastXrefPartial() {
0951:                lastXrefPartial = -1;
0952:            }
0953:
0954:            /**
0955:             *
0956:             */
0957:            public void releaseLastXrefPartial() {
0958:                if (partial && lastXrefPartial != -1) {
0959:                    xrefObj.set(lastXrefPartial, null);
0960:                    lastXrefPartial = -1;
0961:                }
0962:            }
0963:
0964:            /**
0965:             * @param obj
0966:             */
0967:            public static void releaseLastXrefPartial(PdfObject obj) {
0968:                if (obj == null)
0969:                    return;
0970:                if (!obj.isIndirect())
0971:                    return;
0972:                PRIndirectReference ref = (PRIndirectReference) obj;
0973:                PdfReader reader = ref.getReader();
0974:                if (reader.partial && reader.lastXrefPartial != -1
0975:                        && reader.lastXrefPartial == ref.getNumber()) {
0976:                    reader.xrefObj.set(reader.lastXrefPartial, null);
0977:                }
0978:                reader.lastXrefPartial = -1;
0979:            }
0980:
0981:            private void setXrefPartialObject(int idx, PdfObject obj) {
0982:                if (!partial || idx < 0)
0983:                    return;
0984:                xrefObj.set(idx, obj);
0985:            }
0986:
0987:            /**
0988:             * @param obj
0989:             * @return an indirect reference
0990:             */
0991:            public PRIndirectReference addPdfObject(PdfObject obj) {
0992:                xrefObj.add(obj);
0993:                return new PRIndirectReference(this , xrefObj.size() - 1);
0994:            }
0995:
0996:            protected void readPages() throws IOException {
0997:                catalog = (PdfDictionary) getPdfObject(trailer
0998:                        .get(PdfName.ROOT));
0999:                rootPages = (PdfDictionary) getPdfObject(catalog
1000:                        .get(PdfName.PAGES));
1001:                pageRefs = new PageRefs(this );
1002:            }
1003:
1004:            protected void readDocObjPartial() throws IOException {
1005:                xrefObj = new ArrayList(xref.length / 2);
1006:                xrefObj.addAll(Collections.nCopies(xref.length / 2, null));
1007:                readDecryptedDocObj();
1008:                if (objStmToOffset != null) {
1009:                    int keys[] = objStmToOffset.getKeys();
1010:                    for (int k = 0; k < keys.length; ++k) {
1011:                        int n = keys[k];
1012:                        objStmToOffset.put(n, xref[n * 2]);
1013:                        xref[n * 2] = -1;
1014:                    }
1015:                }
1016:            }
1017:
1018:            protected PdfObject readSingleObject(int k) throws IOException {
1019:                strings.clear();
1020:                int k2 = k * 2;
1021:                int pos = xref[k2];
1022:                if (pos < 0)
1023:                    return null;
1024:                if (xref[k2 + 1] > 0)
1025:                    pos = objStmToOffset.get(xref[k2 + 1]);
1026:                if (pos == 0)
1027:                    return null;
1028:                tokens.seek(pos);
1029:                tokens.nextValidToken();
1030:                if (tokens.getTokenType() != PRTokeniser.TK_NUMBER)
1031:                    tokens.throwError("Invalid object number.");
1032:                objNum = tokens.intValue();
1033:                tokens.nextValidToken();
1034:                if (tokens.getTokenType() != PRTokeniser.TK_NUMBER)
1035:                    tokens.throwError("Invalid generation number.");
1036:                objGen = tokens.intValue();
1037:                tokens.nextValidToken();
1038:                if (!tokens.getStringValue().equals("obj"))
1039:                    tokens.throwError("Token 'obj' expected.");
1040:                PdfObject obj;
1041:                try {
1042:                    obj = readPRObject();
1043:                    for (int j = 0; j < strings.size(); ++j) {
1044:                        PdfString str = (PdfString) strings.get(j);
1045:                        str.decrypt(this );
1046:                    }
1047:                    if (obj.isStream()) {
1048:                        checkPRStreamLength((PRStream) obj);
1049:                    }
1050:                } catch (Exception e) {
1051:                    obj = null;
1052:                }
1053:                if (xref[k2 + 1] > 0) {
1054:                    obj = readOneObjStm((PRStream) obj, xref[k2]);
1055:                }
1056:                xrefObj.set(k, obj);
1057:                return obj;
1058:            }
1059:
1060:            protected PdfObject readOneObjStm(PRStream stream, int idx)
1061:                    throws IOException {
1062:                int first = ((PdfNumber) getPdfObject(stream.get(PdfName.FIRST)))
1063:                        .intValue();
1064:                byte b[] = getStreamBytes(stream, tokens.getFile());
1065:                PRTokeniser saveTokens = tokens;
1066:                tokens = new PRTokeniser(b);
1067:                try {
1068:                    int address = 0;
1069:                    boolean ok = true;
1070:                    ++idx;
1071:                    for (int k = 0; k < idx; ++k) {
1072:                        ok = tokens.nextToken();
1073:                        if (!ok)
1074:                            break;
1075:                        if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) {
1076:                            ok = false;
1077:                            break;
1078:                        }
1079:                        ok = tokens.nextToken();
1080:                        if (!ok)
1081:                            break;
1082:                        if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) {
1083:                            ok = false;
1084:                            break;
1085:                        }
1086:                        address = tokens.intValue() + first;
1087:                    }
1088:                    if (!ok)
1089:                        throw new IOException("Error reading ObjStm");
1090:                    tokens.seek(address);
1091:                    return readPRObject();
1092:                } finally {
1093:                    tokens = saveTokens;
1094:                }
1095:            }
1096:
1097:            /**
1098:             * @return the percentage of the cross reference table that has been read
1099:             */
1100:            public double dumpPerc() {
1101:                int total = 0;
1102:                for (int k = 0; k < xrefObj.size(); ++k) {
1103:                    if (xrefObj.get(k) != null)
1104:                        ++total;
1105:                }
1106:                return (total * 100.0 / xrefObj.size());
1107:            }
1108:
1109:            protected void readDocObj() throws IOException {
1110:                ArrayList streams = new ArrayList();
1111:                xrefObj = new ArrayList(xref.length / 2);
1112:                xrefObj.addAll(Collections.nCopies(xref.length / 2, null));
1113:                for (int k = 2; k < xref.length; k += 2) {
1114:                    int pos = xref[k];
1115:                    if (pos <= 0 || xref[k + 1] > 0)
1116:                        continue;
1117:                    tokens.seek(pos);
1118:                    tokens.nextValidToken();
1119:                    if (tokens.getTokenType() != PRTokeniser.TK_NUMBER)
1120:                        tokens.throwError("Invalid object number.");
1121:                    objNum = tokens.intValue();
1122:                    tokens.nextValidToken();
1123:                    if (tokens.getTokenType() != PRTokeniser.TK_NUMBER)
1124:                        tokens.throwError("Invalid generation number.");
1125:                    objGen = tokens.intValue();
1126:                    tokens.nextValidToken();
1127:                    if (!tokens.getStringValue().equals("obj"))
1128:                        tokens.throwError("Token 'obj' expected.");
1129:                    PdfObject obj;
1130:                    try {
1131:                        obj = readPRObject();
1132:                        if (obj.isStream()) {
1133:                            streams.add(obj);
1134:                        }
1135:                    } catch (Exception e) {
1136:                        obj = null;
1137:                    }
1138:                    xrefObj.set(k / 2, obj);
1139:                }
1140:                for (int k = 0; k < streams.size(); ++k) {
1141:                    checkPRStreamLength((PRStream) streams.get(k));
1142:                }
1143:                readDecryptedDocObj();
1144:                if (objStmMark != null) {
1145:                    for (Iterator i = objStmMark.entrySet().iterator(); i
1146:                            .hasNext();) {
1147:                        Map.Entry entry = (Map.Entry) i.next();
1148:                        int n = ((Integer) entry.getKey()).intValue();
1149:                        IntHashtable h = (IntHashtable) entry.getValue();
1150:                        readObjStm((PRStream) xrefObj.get(n), h);
1151:                        xrefObj.set(n, null);
1152:                    }
1153:                    objStmMark = null;
1154:                }
1155:                xref = null;
1156:            }
1157:
1158:            private void checkPRStreamLength(PRStream stream)
1159:                    throws IOException {
1160:                int fileLength = tokens.length();
1161:                int start = stream.getOffset();
1162:                boolean calc = false;
1163:                int streamLength = 0;
1164:                PdfObject obj = getPdfObjectRelease(stream.get(PdfName.LENGTH));
1165:                if (obj != null && obj.type() == PdfObject.NUMBER) {
1166:                    streamLength = ((PdfNumber) obj).intValue();
1167:                    if (streamLength + start > fileLength - 20)
1168:                        calc = true;
1169:                    else {
1170:                        tokens.seek(start + streamLength);
1171:                        String line = tokens.readString(20);
1172:                        if (!line.startsWith("\nendstream")
1173:                                && !line.startsWith("\r\nendstream")
1174:                                && !line.startsWith("\rendstream")
1175:                                && !line.startsWith("endstream"))
1176:                            calc = true;
1177:                    }
1178:                } else
1179:                    calc = true;
1180:                if (calc) {
1181:                    byte tline[] = new byte[16];
1182:                    tokens.seek(start);
1183:                    while (true) {
1184:                        int pos = tokens.getFilePointer();
1185:                        if (!tokens.readLineSegment(tline))
1186:                            break;
1187:                        if (equalsn(tline, endstream)) {
1188:                            streamLength = pos - start;
1189:                            break;
1190:                        }
1191:                        if (equalsn(tline, endobj)) {
1192:                            tokens.seek(pos - 16);
1193:                            String s = tokens.readString(16);
1194:                            int index = s.indexOf("endstream");
1195:                            if (index >= 0)
1196:                                pos = pos - 16 + index;
1197:                            streamLength = pos - start;
1198:                            break;
1199:                        }
1200:                    }
1201:                }
1202:                stream.setLength(streamLength);
1203:            }
1204:
1205:            protected void readObjStm(PRStream stream, IntHashtable map)
1206:                    throws IOException {
1207:                int first = ((PdfNumber) getPdfObject(stream.get(PdfName.FIRST)))
1208:                        .intValue();
1209:                int n = ((PdfNumber) getPdfObject(stream.get(PdfName.N)))
1210:                        .intValue();
1211:                byte b[] = getStreamBytes(stream, tokens.getFile());
1212:                PRTokeniser saveTokens = tokens;
1213:                tokens = new PRTokeniser(b);
1214:                try {
1215:                    int address[] = new int[n];
1216:                    int objNumber[] = new int[n];
1217:                    boolean ok = true;
1218:                    for (int k = 0; k < n; ++k) {
1219:                        ok = tokens.nextToken();
1220:                        if (!ok)
1221:                            break;
1222:                        if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) {
1223:                            ok = false;
1224:                            break;
1225:                        }
1226:                        objNumber[k] = tokens.intValue();
1227:                        ok = tokens.nextToken();
1228:                        if (!ok)
1229:                            break;
1230:                        if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) {
1231:                            ok = false;
1232:                            break;
1233:                        }
1234:                        address[k] = tokens.intValue() + first;
1235:                    }
1236:                    if (!ok)
1237:                        throw new IOException("Error reading ObjStm");
1238:                    for (int k = 0; k < n; ++k) {
1239:                        if (map.containsKey(k)) {
1240:                            tokens.seek(address[k]);
1241:                            PdfObject obj = readPRObject();
1242:                            xrefObj.set(objNumber[k], obj);
1243:                        }
1244:                    }
1245:                } finally {
1246:                    tokens = saveTokens;
1247:                }
1248:            }
1249:
1250:            /**
1251:             * Eliminates the reference to the object freeing the memory used by it and clearing
1252:             * the xref entry.
1253:             * @param obj the object. If it's an indirect reference it will be eliminated
1254:             * @return the object or the already erased dereferenced object
1255:             */
1256:            public static PdfObject killIndirect(PdfObject obj) {
1257:                if (obj == null || obj.isNull())
1258:                    return null;
1259:                PdfObject ret = getPdfObjectRelease(obj);
1260:                if (obj.isIndirect()) {
1261:                    PRIndirectReference ref = (PRIndirectReference) obj;
1262:                    PdfReader reader = ref.getReader();
1263:                    int n = ref.getNumber();
1264:                    reader.xrefObj.set(n, null);
1265:                    if (reader.partial)
1266:                        reader.xref[n * 2] = -1;
1267:                }
1268:                return ret;
1269:            }
1270:
1271:            private void ensureXrefSize(int size) {
1272:                if (size == 0)
1273:                    return;
1274:                if (xref == null)
1275:                    xref = new int[size];
1276:                else {
1277:                    if (xref.length < size) {
1278:                        int xref2[] = new int[size];
1279:                        System.arraycopy(xref, 0, xref2, 0, xref.length);
1280:                        xref = xref2;
1281:                    }
1282:                }
1283:            }
1284:
1285:            protected void readXref() throws IOException {
1286:                hybridXref = false;
1287:                newXrefType = false;
1288:                tokens.seek(tokens.getStartxref());
1289:                tokens.nextToken();
1290:                if (!tokens.getStringValue().equals("startxref"))
1291:                    throw new IOException("startxref not found.");
1292:                tokens.nextToken();
1293:                if (tokens.getTokenType() != PRTokeniser.TK_NUMBER)
1294:                    throw new IOException(
1295:                            "startxref is not followed by a number.");
1296:                int startxref = tokens.intValue();
1297:                lastXref = startxref;
1298:                eofPos = tokens.getFilePointer();
1299:                try {
1300:                    if (readXRefStream(startxref)) {
1301:                        newXrefType = true;
1302:                        return;
1303:                    }
1304:                } catch (Exception e) {
1305:                }
1306:                xref = null;
1307:                tokens.seek(startxref);
1308:                trailer = readXrefSection();
1309:                PdfDictionary trailer2 = trailer;
1310:                while (true) {
1311:                    PdfNumber prev = (PdfNumber) trailer2.get(PdfName.PREV);
1312:                    if (prev == null)
1313:                        break;
1314:                    tokens.seek(prev.intValue());
1315:                    trailer2 = readXrefSection();
1316:                }
1317:            }
1318:
1319:            protected PdfDictionary readXrefSection() throws IOException {
1320:                tokens.nextValidToken();
1321:                if (!tokens.getStringValue().equals("xref"))
1322:                    tokens.throwError("xref subsection not found");
1323:                int start = 0;
1324:                int end = 0;
1325:                int pos = 0;
1326:                int gen = 0;
1327:                while (true) {
1328:                    tokens.nextValidToken();
1329:                    if (tokens.getStringValue().equals("trailer"))
1330:                        break;
1331:                    if (tokens.getTokenType() != PRTokeniser.TK_NUMBER)
1332:                        tokens
1333:                                .throwError("Object number of the first object in this xref subsection not found");
1334:                    start = tokens.intValue();
1335:                    tokens.nextValidToken();
1336:                    if (tokens.getTokenType() != PRTokeniser.TK_NUMBER)
1337:                        tokens
1338:                                .throwError("Number of entries in this xref subsection not found");
1339:                    end = tokens.intValue() + start;
1340:                    if (start == 1) { // fix incorrect start number
1341:                        int back = tokens.getFilePointer();
1342:                        tokens.nextValidToken();
1343:                        pos = tokens.intValue();
1344:                        tokens.nextValidToken();
1345:                        gen = tokens.intValue();
1346:                        if (pos == 0 && gen == 65535) {
1347:                            --start;
1348:                            --end;
1349:                        }
1350:                        tokens.seek(back);
1351:                    }
1352:                    ensureXrefSize(end * 2);
1353:                    for (int k = start; k < end; ++k) {
1354:                        tokens.nextValidToken();
1355:                        pos = tokens.intValue();
1356:                        tokens.nextValidToken();
1357:                        gen = tokens.intValue();
1358:                        tokens.nextValidToken();
1359:                        int p = k * 2;
1360:                        if (tokens.getStringValue().equals("n")) {
1361:                            if (xref[p] == 0 && xref[p + 1] == 0) {
1362:                                //                        if (pos == 0)
1363:                                //                            tokens.throwError("File position 0 cross-reference entry in this xref subsection");
1364:                                xref[p] = pos;
1365:                            }
1366:                        } else if (tokens.getStringValue().equals("f")) {
1367:                            if (xref[p] == 0 && xref[p + 1] == 0)
1368:                                xref[p] = -1;
1369:                        } else
1370:                            tokens
1371:                                    .throwError("Invalid cross-reference entry in this xref subsection");
1372:                    }
1373:                }
1374:                PdfDictionary trailer = (PdfDictionary) readPRObject();
1375:                PdfNumber xrefSize = (PdfNumber) trailer.get(PdfName.SIZE);
1376:                ensureXrefSize(xrefSize.intValue() * 2);
1377:                PdfObject xrs = trailer.get(PdfName.XREFSTM);
1378:                if (xrs != null && xrs.isNumber()) {
1379:                    int loc = ((PdfNumber) xrs).intValue();
1380:                    try {
1381:                        readXRefStream(loc);
1382:                        newXrefType = true;
1383:                        hybridXref = true;
1384:                    } catch (IOException e) {
1385:                        xref = null;
1386:                        throw e;
1387:                    }
1388:                }
1389:                return trailer;
1390:            }
1391:
1392:            protected boolean readXRefStream(int ptr) throws IOException {
1393:                tokens.seek(ptr);
1394:                int this Stream = 0;
1395:                if (!tokens.nextToken())
1396:                    return false;
1397:                if (tokens.getTokenType() != PRTokeniser.TK_NUMBER)
1398:                    return false;
1399:                this Stream = tokens.intValue();
1400:                if (!tokens.nextToken()
1401:                        || tokens.getTokenType() != PRTokeniser.TK_NUMBER)
1402:                    return false;
1403:                if (!tokens.nextToken()
1404:                        || !tokens.getStringValue().equals("obj"))
1405:                    return false;
1406:                PdfObject object = readPRObject();
1407:                PRStream stm = null;
1408:                if (object.isStream()) {
1409:                    stm = (PRStream) object;
1410:                    if (!PdfName.XREF.equals(stm.get(PdfName.TYPE)))
1411:                        return false;
1412:                } else
1413:                    return false;
1414:                if (trailer == null) {
1415:                    trailer = new PdfDictionary();
1416:                    trailer.putAll(stm);
1417:                }
1418:                stm.setLength(((PdfNumber) stm.get(PdfName.LENGTH)).intValue());
1419:                int size = ((PdfNumber) stm.get(PdfName.SIZE)).intValue();
1420:                PdfArray index;
1421:                PdfObject obj = stm.get(PdfName.INDEX);
1422:                if (obj == null) {
1423:                    index = new PdfArray();
1424:                    index.add(new int[] { 0, size });
1425:                } else
1426:                    index = (PdfArray) obj;
1427:                PdfArray w = (PdfArray) stm.get(PdfName.W);
1428:                int prev = -1;
1429:                obj = stm.get(PdfName.PREV);
1430:                if (obj != null)
1431:                    prev = ((PdfNumber) obj).intValue();
1432:                // Each xref pair is a position
1433:                // type 0 -> -1, 0
1434:                // type 1 -> offset, 0
1435:                // type 2 -> index, obj num
1436:                ensureXrefSize(size * 2);
1437:                if (objStmMark == null && !partial)
1438:                    objStmMark = new HashMap();
1439:                if (objStmToOffset == null && partial)
1440:                    objStmToOffset = new IntHashtable();
1441:                byte b[] = getStreamBytes(stm, tokens.getFile());
1442:                int bptr = 0;
1443:                ArrayList wa = w.getArrayList();
1444:                int wc[] = new int[3];
1445:                for (int k = 0; k < 3; ++k)
1446:                    wc[k] = ((PdfNumber) wa.get(k)).intValue();
1447:                ArrayList sections = index.getArrayList();
1448:                for (int idx = 0; idx < sections.size(); idx += 2) {
1449:                    int start = ((PdfNumber) sections.get(idx)).intValue();
1450:                    int length = ((PdfNumber) sections.get(idx + 1)).intValue();
1451:                    ensureXrefSize((start + length) * 2);
1452:                    while (length-- > 0) {
1453:                        int type = 1;
1454:                        if (wc[0] > 0) {
1455:                            type = 0;
1456:                            for (int k = 0; k < wc[0]; ++k)
1457:                                type = (type << 8) + (b[bptr++] & 0xff);
1458:                        }
1459:                        int field2 = 0;
1460:                        for (int k = 0; k < wc[1]; ++k)
1461:                            field2 = (field2 << 8) + (b[bptr++] & 0xff);
1462:                        int field3 = 0;
1463:                        for (int k = 0; k < wc[2]; ++k)
1464:                            field3 = (field3 << 8) + (b[bptr++] & 0xff);
1465:                        int base = start * 2;
1466:                        if (xref[base] == 0 && xref[base + 1] == 0) {
1467:                            switch (type) {
1468:                            case 0:
1469:                                xref[base] = -1;
1470:                                break;
1471:                            case 1:
1472:                                xref[base] = field2;
1473:                                break;
1474:                            case 2:
1475:                                xref[base] = field3;
1476:                                xref[base + 1] = field2;
1477:                                if (partial) {
1478:                                    objStmToOffset.put(field2, 0);
1479:                                } else {
1480:                                    Integer on = new Integer(field2);
1481:                                    IntHashtable seq = (IntHashtable) objStmMark
1482:                                            .get(on);
1483:                                    if (seq == null) {
1484:                                        seq = new IntHashtable();
1485:                                        seq.put(field3, 1);
1486:                                        objStmMark.put(on, seq);
1487:                                    } else
1488:                                        seq.put(field3, 1);
1489:                                }
1490:                                break;
1491:                            }
1492:                        }
1493:                        ++start;
1494:                    }
1495:                }
1496:                this Stream *= 2;
1497:                if (this Stream < xref.length)
1498:                    xref[this Stream] = -1;
1499:
1500:                if (prev == -1)
1501:                    return true;
1502:                return readXRefStream(prev);
1503:            }
1504:
1505:            protected void rebuildXref() throws IOException {
1506:                hybridXref = false;
1507:                newXrefType = false;
1508:                tokens.seek(0);
1509:                int xr[][] = new int[1024][];
1510:                int top = 0;
1511:                trailer = null;
1512:                byte line[] = new byte[64];
1513:                for (;;) {
1514:                    int pos = tokens.getFilePointer();
1515:                    if (!tokens.readLineSegment(line))
1516:                        break;
1517:                    if (line[0] == 't') {
1518:                        if (!PdfEncodings.convertToString(line, null)
1519:                                .startsWith("trailer"))
1520:                            continue;
1521:                        tokens.seek(pos);
1522:                        tokens.nextToken();
1523:                        pos = tokens.getFilePointer();
1524:                        try {
1525:                            PdfDictionary dic = (PdfDictionary) readPRObject();
1526:                            if (dic.get(PdfName.ROOT) != null)
1527:                                trailer = dic;
1528:                            else
1529:                                tokens.seek(pos);
1530:                        } catch (Exception e) {
1531:                            tokens.seek(pos);
1532:                        }
1533:                    } else if (line[0] >= '0' && line[0] <= '9') {
1534:                        int obj[] = PRTokeniser.checkObjectStart(line);
1535:                        if (obj == null)
1536:                            continue;
1537:                        int num = obj[0];
1538:                        int gen = obj[1];
1539:                        if (num >= xr.length) {
1540:                            int newLength = num * 2;
1541:                            int xr2[][] = new int[newLength][];
1542:                            System.arraycopy(xr, 0, xr2, 0, top);
1543:                            xr = xr2;
1544:                        }
1545:                        if (num >= top)
1546:                            top = num + 1;
1547:                        if (xr[num] == null || gen >= xr[num][1]) {
1548:                            obj[0] = pos;
1549:                            xr[num] = obj;
1550:                        }
1551:                    }
1552:                }
1553:                if (trailer == null)
1554:                    throw new IOException("trailer not found.");
1555:                xref = new int[top * 2];
1556:                for (int k = 0; k < top; ++k) {
1557:                    int obj[] = xr[k];
1558:                    if (obj != null)
1559:                        xref[k * 2] = obj[0];
1560:                }
1561:            }
1562:
1563:            protected PdfDictionary readDictionary() throws IOException {
1564:                PdfDictionary dic = new PdfDictionary();
1565:                while (true) {
1566:                    tokens.nextValidToken();
1567:                    if (tokens.getTokenType() == PRTokeniser.TK_END_DIC)
1568:                        break;
1569:                    if (tokens.getTokenType() != PRTokeniser.TK_NAME)
1570:                        tokens.throwError("Dictionary key is not a name.");
1571:                    PdfName name = new PdfName(tokens.getStringValue(), false);
1572:                    PdfObject obj = readPRObject();
1573:                    int type = obj.type();
1574:                    if (-type == PRTokeniser.TK_END_DIC)
1575:                        tokens.throwError("Unexpected '>>'");
1576:                    if (-type == PRTokeniser.TK_END_ARRAY)
1577:                        tokens.throwError("Unexpected ']'");
1578:                    dic.put(name, obj);
1579:                }
1580:                return dic;
1581:            }
1582:
1583:            protected PdfArray readArray() throws IOException {
1584:                PdfArray array = new PdfArray();
1585:                while (true) {
1586:                    PdfObject obj = readPRObject();
1587:                    int type = obj.type();
1588:                    if (-type == PRTokeniser.TK_END_ARRAY)
1589:                        break;
1590:                    if (-type == PRTokeniser.TK_END_DIC)
1591:                        tokens.throwError("Unexpected '>>'");
1592:                    array.add(obj);
1593:                }
1594:                return array;
1595:            }
1596:
1597:            protected PdfObject readPRObject() throws IOException {
1598:                tokens.nextValidToken();
1599:                int type = tokens.getTokenType();
1600:                switch (type) {
1601:                case PRTokeniser.TK_START_DIC: {
1602:                    PdfDictionary dic = readDictionary();
1603:                    int pos = tokens.getFilePointer();
1604:                    // be careful in the trailer. May not be a "next" token.
1605:                    if (tokens.nextToken()
1606:                            && tokens.getStringValue().equals("stream")) {
1607:                        int ch = tokens.read();
1608:                        if (ch != '\n')
1609:                            ch = tokens.read();
1610:                        if (ch != '\n')
1611:                            tokens.backOnePosition(ch);
1612:                        PRStream stream = new PRStream(this , tokens
1613:                                .getFilePointer());
1614:                        stream.putAll(dic);
1615:                        stream.setObjNum(objNum, objGen);
1616:                        return stream;
1617:                    } else {
1618:                        tokens.seek(pos);
1619:                        return dic;
1620:                    }
1621:                }
1622:                case PRTokeniser.TK_START_ARRAY:
1623:                    return readArray();
1624:                case PRTokeniser.TK_NUMBER:
1625:                    return new PdfNumber(tokens.getStringValue());
1626:                case PRTokeniser.TK_STRING:
1627:                    PdfString str = new PdfString(tokens.getStringValue(), null)
1628:                            .setHexWriting(tokens.isHexString());
1629:                    str.setObjNum(objNum, objGen);
1630:                    if (strings != null)
1631:                        strings.add(str);
1632:                    return str;
1633:                case PRTokeniser.TK_NAME:
1634:                    return new PdfName(tokens.getStringValue(), false);
1635:                case PRTokeniser.TK_REF:
1636:                    int num = tokens.getReference();
1637:                    PRIndirectReference ref = new PRIndirectReference(this ,
1638:                            num, tokens.getGeneration());
1639:                    return ref;
1640:                default:
1641:                    String sv = tokens.getStringValue();
1642:                    if ("null".equals(sv))
1643:                        return PdfNull.PDFNULL;
1644:                    else if ("true".equals(sv))
1645:                        return PdfBoolean.PDFTRUE;
1646:                    else if ("false".equals(sv))
1647:                        return PdfBoolean.PDFFALSE;
1648:                    return new PdfLiteral(-type, tokens.getStringValue());
1649:                }
1650:            }
1651:
1652:            /** Decodes a stream that has the FlateDecode filter.
1653:             * @param in the input data
1654:             * @return the decoded data
1655:             */
1656:            public static byte[] FlateDecode(byte in[]) {
1657:                byte b[] = FlateDecode(in, true);
1658:                if (b == null)
1659:                    return FlateDecode(in, false);
1660:                return b;
1661:            }
1662:
1663:            /**
1664:             * @param in
1665:             * @param dicPar
1666:             * @return a byte array
1667:             */
1668:            public static byte[] decodePredictor(byte in[], PdfObject dicPar) {
1669:                if (dicPar == null || !dicPar.isDictionary())
1670:                    return in;
1671:                PdfDictionary dic = (PdfDictionary) dicPar;
1672:                PdfObject obj = getPdfObject(dic.get(PdfName.PREDICTOR));
1673:                if (obj == null || !obj.isNumber())
1674:                    return in;
1675:                int predictor = ((PdfNumber) obj).intValue();
1676:                if (predictor < 10)
1677:                    return in;
1678:                int width = 1;
1679:                obj = getPdfObject(dic.get(PdfName.COLUMNS));
1680:                if (obj != null && obj.isNumber())
1681:                    width = ((PdfNumber) obj).intValue();
1682:                int colors = 1;
1683:                obj = getPdfObject(dic.get(PdfName.COLORS));
1684:                if (obj != null && obj.isNumber())
1685:                    colors = ((PdfNumber) obj).intValue();
1686:                int bpc = 8;
1687:                obj = getPdfObject(dic.get(PdfName.BITSPERCOMPONENT));
1688:                if (obj != null && obj.isNumber())
1689:                    bpc = ((PdfNumber) obj).intValue();
1690:                DataInputStream dataStream = new DataInputStream(
1691:                        new ByteArrayInputStream(in));
1692:                ByteArrayOutputStream fout = new ByteArrayOutputStream(
1693:                        in.length);
1694:                int bytesPerPixel = colors * bpc / 8;
1695:                int bytesPerRow = (colors * width * bpc + 7) / 8;
1696:                byte[] curr = new byte[bytesPerRow];
1697:                byte[] prior = new byte[bytesPerRow];
1698:
1699:                // Decode the (sub)image row-by-row
1700:                while (true) {
1701:                    // Read the filter type byte and a row of data
1702:                    int filter = 0;
1703:                    try {
1704:                        filter = dataStream.read();
1705:                        if (filter < 0) {
1706:                            return fout.toByteArray();
1707:                        }
1708:                        dataStream.readFully(curr, 0, bytesPerRow);
1709:                    } catch (Exception e) {
1710:                        return fout.toByteArray();
1711:                    }
1712:
1713:                    switch (filter) {
1714:                    case 0: //PNG_FILTER_NONE
1715:                        break;
1716:                    case 1: //PNG_FILTER_SUB
1717:                        for (int i = bytesPerPixel; i < bytesPerRow; i++) {
1718:                            curr[i] += curr[i - bytesPerPixel];
1719:                        }
1720:                        break;
1721:                    case 2: //PNG_FILTER_UP
1722:                        for (int i = 0; i < bytesPerRow; i++) {
1723:                            curr[i] += prior[i];
1724:                        }
1725:                        break;
1726:                    case 3: //PNG_FILTER_AVERAGE
1727:                        for (int i = 0; i < bytesPerPixel; i++) {
1728:                            curr[i] += prior[i] / 2;
1729:                        }
1730:                        for (int i = bytesPerPixel; i < bytesPerRow; i++) {
1731:                            curr[i] += ((curr[i - bytesPerPixel] & 0xff) + (prior[i] & 0xff)) / 2;
1732:                        }
1733:                        break;
1734:                    case 4: //PNG_FILTER_PAETH
1735:                        for (int i = 0; i < bytesPerPixel; i++) {
1736:                            curr[i] += prior[i];
1737:                        }
1738:
1739:                        for (int i = bytesPerPixel; i < bytesPerRow; i++) {
1740:                            int a = curr[i - bytesPerPixel] & 0xff;
1741:                            int b = prior[i] & 0xff;
1742:                            int c = prior[i - bytesPerPixel] & 0xff;
1743:
1744:                            int p = a + b - c;
1745:                            int pa = Math.abs(p - a);
1746:                            int pb = Math.abs(p - b);
1747:                            int pc = Math.abs(p - c);
1748:
1749:                            int ret;
1750:
1751:                            if ((pa <= pb) && (pa <= pc)) {
1752:                                ret = a;
1753:                            } else if (pb <= pc) {
1754:                                ret = b;
1755:                            } else {
1756:                                ret = c;
1757:                            }
1758:                            curr[i] += (byte) (ret);
1759:                        }
1760:                        break;
1761:                    default:
1762:                        // Error -- uknown filter type
1763:                        throw new RuntimeException("PNG filter unknown.");
1764:                    }
1765:                    try {
1766:                        fout.write(curr);
1767:                    } catch (IOException ioe) {
1768:                        // Never happens
1769:                    }
1770:
1771:                    // Swap curr and prior
1772:                    byte[] tmp = prior;
1773:                    prior = curr;
1774:                    curr = tmp;
1775:                }
1776:            }
1777:
1778:            /** A helper to FlateDecode.
1779:             * @param in the input data
1780:             * @param strict <CODE>true</CODE> to read a correct stream. <CODE>false</CODE>
1781:             * to try to read a corrupted stream
1782:             * @return the decoded data
1783:             */
1784:            public static byte[] FlateDecode(byte in[], boolean strict) {
1785:                ByteArrayInputStream stream = new ByteArrayInputStream(in);
1786:                InflaterInputStream zip = new InflaterInputStream(stream);
1787:                ByteArrayOutputStream out = new ByteArrayOutputStream();
1788:                byte b[] = new byte[strict ? 4092 : 1];
1789:                try {
1790:                    int n;
1791:                    while ((n = zip.read(b)) >= 0) {
1792:                        out.write(b, 0, n);
1793:                    }
1794:                    zip.close();
1795:                    out.close();
1796:                    return out.toByteArray();
1797:                } catch (Exception e) {
1798:                    if (strict)
1799:                        return null;
1800:                    return out.toByteArray();
1801:                }
1802:            }
1803:
1804:            /** Decodes a stream that has the ASCIIHexDecode filter.
1805:             * @param in the input data
1806:             * @return the decoded data
1807:             */
1808:            public static byte[] ASCIIHexDecode(byte in[]) {
1809:                ByteArrayOutputStream out = new ByteArrayOutputStream();
1810:                boolean first = true;
1811:                int n1 = 0;
1812:                for (int k = 0; k < in.length; ++k) {
1813:                    int ch = in[k] & 0xff;
1814:                    if (ch == '>')
1815:                        break;
1816:                    if (PRTokeniser.isWhitespace(ch))
1817:                        continue;
1818:                    int n = PRTokeniser.getHex(ch);
1819:                    if (n == -1)
1820:                        throw new RuntimeException(
1821:                                "Illegal character in ASCIIHexDecode.");
1822:                    if (first)
1823:                        n1 = n;
1824:                    else
1825:                        out.write((byte) ((n1 << 4) + n));
1826:                    first = !first;
1827:                }
1828:                if (!first)
1829:                    out.write((byte) (n1 << 4));
1830:                return out.toByteArray();
1831:            }
1832:
1833:            /** Decodes a stream that has the ASCII85Decode filter.
1834:             * @param in the input data
1835:             * @return the decoded data
1836:             */
1837:            public static byte[] ASCII85Decode(byte in[]) {
1838:                ByteArrayOutputStream out = new ByteArrayOutputStream();
1839:                int state = 0;
1840:                int chn[] = new int[5];
1841:                for (int k = 0; k < in.length; ++k) {
1842:                    int ch = in[k] & 0xff;
1843:                    if (ch == '~')
1844:                        break;
1845:                    if (PRTokeniser.isWhitespace(ch))
1846:                        continue;
1847:                    if (ch == 'z' && state == 0) {
1848:                        out.write(0);
1849:                        out.write(0);
1850:                        out.write(0);
1851:                        out.write(0);
1852:                        continue;
1853:                    }
1854:                    if (ch < '!' || ch > 'u')
1855:                        throw new RuntimeException(
1856:                                "Illegal character in ASCII85Decode.");
1857:                    chn[state] = ch - '!';
1858:                    ++state;
1859:                    if (state == 5) {
1860:                        state = 0;
1861:                        int r = 0;
1862:                        for (int j = 0; j < 5; ++j)
1863:                            r = r * 85 + chn[j];
1864:                        out.write((byte) (r >> 24));
1865:                        out.write((byte) (r >> 16));
1866:                        out.write((byte) (r >> 8));
1867:                        out.write((byte) r);
1868:                    }
1869:                }
1870:                int r = 0;
1871:                if (state == 1)
1872:                    throw new RuntimeException(
1873:                            "Illegal length in ASCII85Decode.");
1874:                if (state == 2) {
1875:                    r = chn[0] * 85 * 85 * 85 * 85 + chn[1] * 85 * 85 * 85 + 85
1876:                            * 85 * 85 + 85 * 85 + 85;
1877:                    out.write((byte) (r >> 24));
1878:                } else if (state == 3) {
1879:                    r = chn[0] * 85 * 85 * 85 * 85 + chn[1] * 85 * 85 * 85
1880:                            + chn[2] * 85 * 85 + 85 * 85 + 85;
1881:                    out.write((byte) (r >> 24));
1882:                    out.write((byte) (r >> 16));
1883:                } else if (state == 4) {
1884:                    r = chn[0] * 85 * 85 * 85 * 85 + chn[1] * 85 * 85 * 85
1885:                            + chn[2] * 85 * 85 + chn[3] * 85 + 85;
1886:                    out.write((byte) (r >> 24));
1887:                    out.write((byte) (r >> 16));
1888:                    out.write((byte) (r >> 8));
1889:                }
1890:                return out.toByteArray();
1891:            }
1892:
1893:            /** Decodes a stream that has the LZWDecode filter.
1894:             * @param in the input data
1895:             * @return the decoded data
1896:             */
1897:            public static byte[] LZWDecode(byte in[]) {
1898:                ByteArrayOutputStream out = new ByteArrayOutputStream();
1899:                LZWDecoder lzw = new LZWDecoder();
1900:                lzw.decode(in, out);
1901:                return out.toByteArray();
1902:            }
1903:
1904:            /** Checks if the document had errors and was rebuilt.
1905:             * @return true if rebuilt.
1906:             *
1907:             */
1908:            public boolean isRebuilt() {
1909:                return this .rebuilt;
1910:            }
1911:
1912:            /** Gets the dictionary that represents a page.
1913:             * @param pageNum the page number. 1 is the first
1914:             * @return the page dictionary
1915:             */
1916:            public PdfDictionary getPageN(int pageNum) {
1917:                PdfDictionary dic = pageRefs.getPageN(pageNum);
1918:                if (dic == null)
1919:                    return null;
1920:                if (appendable)
1921:                    dic.setIndRef(pageRefs.getPageOrigRef(pageNum));
1922:                return dic;
1923:            }
1924:
1925:            /**
1926:             * @param pageNum
1927:             * @return a Dictionary object
1928:             */
1929:            public PdfDictionary getPageNRelease(int pageNum) {
1930:                PdfDictionary dic = getPageN(pageNum);
1931:                pageRefs.releasePage(pageNum);
1932:                return dic;
1933:            }
1934:
1935:            /**
1936:             * @param pageNum
1937:             */
1938:            public void releasePage(int pageNum) {
1939:                pageRefs.releasePage(pageNum);
1940:            }
1941:
1942:            /**
1943:             *
1944:             */
1945:            public void resetReleasePage() {
1946:                pageRefs.resetReleasePage();
1947:            }
1948:
1949:            /** Gets the page reference to this page.
1950:             * @param pageNum the page number. 1 is the first
1951:             * @return the page reference
1952:             */
1953:            public PRIndirectReference getPageOrigRef(int pageNum) {
1954:                return pageRefs.getPageOrigRef(pageNum);
1955:            }
1956:
1957:            /** Gets the contents of the page.
1958:             * @param pageNum the page number. 1 is the first
1959:             * @param file the location of the PDF document
1960:             * @throws IOException on error
1961:             * @return the content
1962:             */
1963:            public byte[] getPageContent(int pageNum,
1964:                    RandomAccessFileOrArray file) throws IOException {
1965:                PdfDictionary page = getPageNRelease(pageNum);
1966:                if (page == null)
1967:                    return null;
1968:                PdfObject contents = getPdfObjectRelease(page
1969:                        .get(PdfName.CONTENTS));
1970:                if (contents == null)
1971:                    return new byte[0];
1972:                ByteArrayOutputStream bout = null;
1973:                if (contents.isStream()) {
1974:                    return getStreamBytes((PRStream) contents, file);
1975:                } else if (contents.isArray()) {
1976:                    PdfArray array = (PdfArray) contents;
1977:                    ArrayList list = array.getArrayList();
1978:                    bout = new ByteArrayOutputStream();
1979:                    for (int k = 0; k < list.size(); ++k) {
1980:                        PdfObject item = getPdfObjectRelease((PdfObject) list
1981:                                .get(k));
1982:                        if (item == null || !item.isStream())
1983:                            continue;
1984:                        byte[] b = getStreamBytes((PRStream) item, file);
1985:                        bout.write(b);
1986:                        if (k != list.size() - 1)
1987:                            bout.write('\n');
1988:                    }
1989:                    return bout.toByteArray();
1990:                } else
1991:                    return new byte[0];
1992:            }
1993:
1994:            /** Gets the contents of the page.
1995:             * @param pageNum the page number. 1 is the first
1996:             * @throws IOException on error
1997:             * @return the content
1998:             */
1999:            public byte[] getPageContent(int pageNum) throws IOException {
2000:                RandomAccessFileOrArray rf = getSafeFile();
2001:                try {
2002:                    rf.reOpen();
2003:                    return getPageContent(pageNum, rf);
2004:                } finally {
2005:                    try {
2006:                        rf.close();
2007:                    } catch (Exception e) {
2008:                    }
2009:                }
2010:            }
2011:
2012:            protected void killXref(PdfObject obj) {
2013:                if (obj == null)
2014:                    return;
2015:                if ((obj instanceof  PdfIndirectReference) && !obj.isIndirect())
2016:                    return;
2017:                switch (obj.type()) {
2018:                case PdfObject.INDIRECT: {
2019:                    int xr = ((PRIndirectReference) obj).getNumber();
2020:                    obj = (PdfObject) xrefObj.get(xr);
2021:                    xrefObj.set(xr, null);
2022:                    freeXref = xr;
2023:                    killXref(obj);
2024:                    break;
2025:                }
2026:                case PdfObject.ARRAY: {
2027:                    ArrayList t = ((PdfArray) obj).getArrayList();
2028:                    for (int i = 0; i < t.size(); ++i)
2029:                        killXref((PdfObject) t.get(i));
2030:                    break;
2031:                }
2032:                case PdfObject.STREAM:
2033:                case PdfObject.DICTIONARY: {
2034:                    PdfDictionary dic = (PdfDictionary) obj;
2035:                    for (Iterator i = dic.getKeys().iterator(); i.hasNext();) {
2036:                        killXref(dic.get((PdfName) i.next()));
2037:                    }
2038:                    break;
2039:                }
2040:                }
2041:            }
2042:
2043:            /** Sets the contents of the page.
2044:             * @param content the new page content
2045:             * @param pageNum the page number. 1 is the first
2046:             */
2047:            public void setPageContent(int pageNum, byte content[]) {
2048:                PdfDictionary page = getPageN(pageNum);
2049:                if (page == null)
2050:                    return;
2051:                PdfObject contents = page.get(PdfName.CONTENTS);
2052:                freeXref = -1;
2053:                killXref(contents);
2054:                if (freeXref == -1) {
2055:                    xrefObj.add(null);
2056:                    freeXref = xrefObj.size() - 1;
2057:                }
2058:                page.put(PdfName.CONTENTS, new PRIndirectReference(this ,
2059:                        freeXref));
2060:                xrefObj.set(freeXref, new PRStream(this , content));
2061:            }
2062:
2063:            /** Get the content from a stream applying the required filters.
2064:             * @param stream the stream
2065:             * @param file the location where the stream is
2066:             * @throws IOException on error
2067:             * @return the stream content
2068:             */
2069:            public static byte[] getStreamBytes(PRStream stream,
2070:                    RandomAccessFileOrArray file) throws IOException {
2071:                PdfObject filter = getPdfObjectRelease(stream
2072:                        .get(PdfName.FILTER));
2073:                byte[] b = getStreamBytesRaw(stream, file);
2074:                ArrayList filters = new ArrayList();
2075:                if (filter != null) {
2076:                    if (filter.isName())
2077:                        filters.add(filter);
2078:                    else if (filter.isArray())
2079:                        filters = ((PdfArray) filter).getArrayList();
2080:                }
2081:                ArrayList dp = new ArrayList();
2082:                PdfObject dpo = getPdfObjectRelease(stream
2083:                        .get(PdfName.DECODEPARMS));
2084:                if (dpo == null || (!dpo.isDictionary() && !dpo.isArray()))
2085:                    dpo = getPdfObjectRelease(stream.get(PdfName.DP));
2086:                if (dpo != null) {
2087:                    if (dpo.isDictionary())
2088:                        dp.add(dpo);
2089:                    else if (dpo.isArray())
2090:                        dp = ((PdfArray) dpo).getArrayList();
2091:                }
2092:                String name;
2093:                for (int j = 0; j < filters.size(); ++j) {
2094:                    name = ((PdfName) getPdfObjectRelease((PdfObject) filters
2095:                            .get(j))).toString();
2096:                    if (name.equals("/FlateDecode") || name.equals("/Fl")) {
2097:                        b = FlateDecode(b);
2098:                        PdfObject dicParam = null;
2099:                        if (j < dp.size()) {
2100:                            dicParam = (PdfObject) dp.get(j);
2101:                            b = decodePredictor(b, dicParam);
2102:                        }
2103:                    } else if (name.equals("/ASCIIHexDecode")
2104:                            || name.equals("/AHx"))
2105:                        b = ASCIIHexDecode(b);
2106:                    else if (name.equals("/ASCII85Decode")
2107:                            || name.equals("/A85"))
2108:                        b = ASCII85Decode(b);
2109:                    else if (name.equals("/LZWDecode")) {
2110:                        b = LZWDecode(b);
2111:                        PdfObject dicParam = null;
2112:                        if (j < dp.size()) {
2113:                            dicParam = (PdfObject) dp.get(j);
2114:                            b = decodePredictor(b, dicParam);
2115:                        }
2116:                    } else if (name.equals("/Crypt")) {
2117:                    } else
2118:                        throw new IOException("The filter " + name
2119:                                + " is not supported.");
2120:                }
2121:                return b;
2122:            }
2123:
2124:            /** Get the content from a stream applying the required filters.
2125:             * @param stream the stream
2126:             * @throws IOException on error
2127:             * @return the stream content
2128:             */
2129:            public static byte[] getStreamBytes(PRStream stream)
2130:                    throws IOException {
2131:                RandomAccessFileOrArray rf = stream.getReader().getSafeFile();
2132:                try {
2133:                    rf.reOpen();
2134:                    return getStreamBytes(stream, rf);
2135:                } finally {
2136:                    try {
2137:                        rf.close();
2138:                    } catch (Exception e) {
2139:                    }
2140:                }
2141:            }
2142:
2143:            /** Get the content from a stream as it is without applying any filter.
2144:             * @param stream the stream
2145:             * @param file the location where the stream is
2146:             * @throws IOException on error
2147:             * @return the stream content
2148:             */
2149:            public static byte[] getStreamBytesRaw(PRStream stream,
2150:                    RandomAccessFileOrArray file) throws IOException {
2151:                PdfReader reader = stream.getReader();
2152:                byte b[];
2153:                if (stream.getOffset() < 0)
2154:                    b = stream.getBytes();
2155:                else {
2156:                    b = new byte[stream.getLength()];
2157:                    file.seek(stream.getOffset());
2158:                    file.readFully(b);
2159:                    PdfEncryption decrypt = reader.getDecrypt();
2160:                    if (decrypt != null) {
2161:                        PdfObject filter = getPdfObjectRelease(stream
2162:                                .get(PdfName.FILTER));
2163:                        ArrayList filters = new ArrayList();
2164:                        if (filter != null) {
2165:                            if (filter.isName())
2166:                                filters.add(filter);
2167:                            else if (filter.isArray())
2168:                                filters = ((PdfArray) filter).getArrayList();
2169:                        }
2170:                        boolean skip = false;
2171:                        for (int k = 0; k < filters.size(); ++k) {
2172:                            PdfObject obj = getPdfObjectRelease((PdfObject) filters
2173:                                    .get(k));
2174:                            if (obj != null && obj.toString().equals("/Crypt")) {
2175:                                skip = true;
2176:                                break;
2177:                            }
2178:                        }
2179:                        if (!skip) {
2180:                            decrypt.setHashKey(stream.getObjNum(), stream
2181:                                    .getObjGen());
2182:                            b = decrypt.decryptByteArray(b);
2183:                        }
2184:                    }
2185:                }
2186:                return b;
2187:            }
2188:
2189:            /** Get the content from a stream as it is without applying any filter.
2190:             * @param stream the stream
2191:             * @throws IOException on error
2192:             * @return the stream content
2193:             */
2194:            public static byte[] getStreamBytesRaw(PRStream stream)
2195:                    throws IOException {
2196:                RandomAccessFileOrArray rf = stream.getReader().getSafeFile();
2197:                try {
2198:                    rf.reOpen();
2199:                    return getStreamBytesRaw(stream, rf);
2200:                } finally {
2201:                    try {
2202:                        rf.close();
2203:                    } catch (Exception e) {
2204:                    }
2205:                }
2206:            }
2207:
2208:            /** Eliminates shared streams if they exist. */
2209:            public void eliminateSharedStreams() {
2210:                if (!sharedStreams)
2211:                    return;
2212:                sharedStreams = false;
2213:                if (pageRefs.size() == 1)
2214:                    return;
2215:                ArrayList newRefs = new ArrayList();
2216:                ArrayList newStreams = new ArrayList();
2217:                IntHashtable visited = new IntHashtable();
2218:                for (int k = 1; k <= pageRefs.size(); ++k) {
2219:                    PdfDictionary page = pageRefs.getPageN(k);
2220:                    if (page == null)
2221:                        continue;
2222:                    PdfObject contents = getPdfObject(page
2223:                            .get(PdfName.CONTENTS));
2224:                    if (contents == null)
2225:                        continue;
2226:                    if (contents.isStream()) {
2227:                        PRIndirectReference ref = (PRIndirectReference) page
2228:                                .get(PdfName.CONTENTS);
2229:                        if (visited.containsKey(ref.getNumber())) {
2230:                            // need to duplicate
2231:                            newRefs.add(ref);
2232:                            newStreams.add(new PRStream((PRStream) contents,
2233:                                    null));
2234:                        } else
2235:                            visited.put(ref.getNumber(), 1);
2236:                    } else if (contents.isArray()) {
2237:                        PdfArray array = (PdfArray) contents;
2238:                        ArrayList list = array.getArrayList();
2239:                        for (int j = 0; j < list.size(); ++j) {
2240:                            PRIndirectReference ref = (PRIndirectReference) list
2241:                                    .get(j);
2242:                            if (visited.containsKey(ref.getNumber())) {
2243:                                // need to duplicate
2244:                                newRefs.add(ref);
2245:                                newStreams.add(new PRStream(
2246:                                        (PRStream) getPdfObject(ref), null));
2247:                            } else
2248:                                visited.put(ref.getNumber(), 1);
2249:                        }
2250:                    }
2251:                }
2252:                if (newStreams.isEmpty())
2253:                    return;
2254:                for (int k = 0; k < newStreams.size(); ++k) {
2255:                    xrefObj.add(newStreams.get(k));
2256:                    PRIndirectReference ref = (PRIndirectReference) newRefs
2257:                            .get(k);
2258:                    ref.setNumber(xrefObj.size() - 1, 0);
2259:                }
2260:            }
2261:
2262:            /** Checks if the document was changed.
2263:             * @return <CODE>true</CODE> if the document was changed,
2264:             * <CODE>false</CODE> otherwise
2265:             */
2266:            public boolean isTampered() {
2267:                return tampered;
2268:            }
2269:
2270:            /**
2271:             * Sets the tampered state. A tampered PdfReader cannot be reused in PdfStamper.
2272:             * @param tampered the tampered state
2273:             */
2274:            public void setTampered(boolean tampered) {
2275:                this .tampered = tampered;
2276:            }
2277:
2278:            /** Gets the XML metadata.
2279:             * @throws IOException on error
2280:             * @return the XML metadata
2281:             */
2282:            public byte[] getMetadata() throws IOException {
2283:                PdfObject obj = getPdfObject(catalog.get(PdfName.METADATA));
2284:                if (!(obj instanceof  PRStream))
2285:                    return null;
2286:                RandomAccessFileOrArray rf = getSafeFile();
2287:                byte b[] = null;
2288:                try {
2289:                    rf.reOpen();
2290:                    b = getStreamBytes((PRStream) obj, rf);
2291:                } finally {
2292:                    try {
2293:                        rf.close();
2294:                    } catch (Exception e) {
2295:                        // empty on purpose
2296:                    }
2297:                }
2298:                return b;
2299:            }
2300:
2301:            /**
2302:             * Gets the byte address of the last xref table.
2303:             * @return the byte address of the last xref table
2304:             */
2305:            public int getLastXref() {
2306:                return lastXref;
2307:            }
2308:
2309:            /**
2310:             * Gets the number of xref objects.
2311:             * @return the number of xref objects
2312:             */
2313:            public int getXrefSize() {
2314:                return xrefObj.size();
2315:            }
2316:
2317:            /**
2318:             * Gets the byte address of the %%EOF marker.
2319:             * @return the byte address of the %%EOF marker
2320:             */
2321:            public int getEofPos() {
2322:                return eofPos;
2323:            }
2324:
2325:            /**
2326:             * Gets the PDF version. Only the last version char is returned. For example
2327:             * version 1.4 is returned as '4'.
2328:             * @return the PDF version
2329:             */
2330:            public char getPdfVersion() {
2331:                return pdfVersion;
2332:            }
2333:
2334:            /**
2335:             * Returns <CODE>true</CODE> if the PDF is encrypted.
2336:             * @return <CODE>true</CODE> if the PDF is encrypted
2337:             */
2338:            public boolean isEncrypted() {
2339:                return encrypted;
2340:            }
2341:
2342:            /**
2343:             * Gets the encryption permissions. It can be used directly in
2344:             * <CODE>PdfWriter.setEncryption()</CODE>.
2345:             * @return the encryption permissions
2346:             */
2347:            public int getPermissions() {
2348:                return pValue;
2349:            }
2350:
2351:            /**
2352:             * Returns <CODE>true</CODE> if the PDF has a 128 bit key encryption.
2353:             * @return <CODE>true</CODE> if the PDF has a 128 bit key encryption
2354:             */
2355:            public boolean is128Key() {
2356:                return rValue == 3;
2357:            }
2358:
2359:            /**
2360:             * Gets the trailer dictionary
2361:             * @return the trailer dictionary
2362:             */
2363:            public PdfDictionary getTrailer() {
2364:                return trailer;
2365:            }
2366:
2367:            PdfEncryption getDecrypt() {
2368:                return decrypt;
2369:            }
2370:
2371:            static boolean equalsn(byte a1[], byte a2[]) {
2372:                int length = a2.length;
2373:                for (int k = 0; k < length; ++k) {
2374:                    if (a1[k] != a2[k])
2375:                        return false;
2376:                }
2377:                return true;
2378:            }
2379:
2380:            static boolean existsName(PdfDictionary dic, PdfName key,
2381:                    PdfName value) {
2382:                PdfObject type = getPdfObjectRelease(dic.get(key));
2383:                if (type == null || !type.isName())
2384:                    return false;
2385:                PdfName name = (PdfName) type;
2386:                return name.equals(value);
2387:            }
2388:
2389:            static String getFontName(PdfDictionary dic) {
2390:                if (dic == null)
2391:                    return null;
2392:                PdfObject type = getPdfObjectRelease(dic.get(PdfName.BASEFONT));
2393:                if (type == null || !type.isName())
2394:                    return null;
2395:                return PdfName.decodeName(type.toString());
2396:            }
2397:
2398:            static String getSubsetPrefix(PdfDictionary dic) {
2399:                if (dic == null)
2400:                    return null;
2401:                String s = getFontName(dic);
2402:                if (s == null)
2403:                    return null;
2404:                if (s.length() < 8 || s.charAt(6) != '+')
2405:                    return null;
2406:                for (int k = 0; k < 6; ++k) {
2407:                    char c = s.charAt(k);
2408:                    if (c < 'A' || c > 'Z')
2409:                        return null;
2410:                }
2411:                return s;
2412:            }
2413:
2414:            /** Finds all the font subsets and changes the prefixes to some
2415:             * random values.
2416:             * @return the number of font subsets altered
2417:             */
2418:            public int shuffleSubsetNames() {
2419:                int total = 0;
2420:                for (int k = 1; k < xrefObj.size(); ++k) {
2421:                    PdfObject obj = getPdfObjectRelease(k);
2422:                    if (obj == null || !obj.isDictionary())
2423:                        continue;
2424:                    PdfDictionary dic = (PdfDictionary) obj;
2425:                    if (!existsName(dic, PdfName.TYPE, PdfName.FONT))
2426:                        continue;
2427:                    if (existsName(dic, PdfName.SUBTYPE, PdfName.TYPE1)
2428:                            || existsName(dic, PdfName.SUBTYPE, PdfName.MMTYPE1)
2429:                            || existsName(dic, PdfName.SUBTYPE,
2430:                                    PdfName.TRUETYPE)) {
2431:                        String s = getSubsetPrefix(dic);
2432:                        if (s == null)
2433:                            continue;
2434:                        String ns = BaseFont.createSubsetPrefix()
2435:                                + s.substring(7);
2436:                        PdfName newName = new PdfName(ns);
2437:                        dic.put(PdfName.BASEFONT, newName);
2438:                        setXrefPartialObject(k, dic);
2439:                        ++total;
2440:                        PdfDictionary fd = (PdfDictionary) getPdfObject(dic
2441:                                .get(PdfName.FONTDESCRIPTOR));
2442:                        if (fd == null)
2443:                            continue;
2444:                        fd.put(PdfName.FONTNAME, newName);
2445:                    } else if (existsName(dic, PdfName.SUBTYPE, PdfName.TYPE0)) {
2446:                        String s = getSubsetPrefix(dic);
2447:                        PdfArray arr = (PdfArray) getPdfObject(dic
2448:                                .get(PdfName.DESCENDANTFONTS));
2449:                        if (arr == null)
2450:                            continue;
2451:                        ArrayList list = arr.getArrayList();
2452:                        if (list.isEmpty())
2453:                            continue;
2454:                        PdfDictionary desc = (PdfDictionary) getPdfObject((PdfObject) list
2455:                                .get(0));
2456:                        String sde = getSubsetPrefix(desc);
2457:                        if (sde == null)
2458:                            continue;
2459:                        String ns = BaseFont.createSubsetPrefix();
2460:                        if (s != null)
2461:                            dic.put(PdfName.BASEFONT, new PdfName(ns
2462:                                    + s.substring(7)));
2463:                        setXrefPartialObject(k, dic);
2464:                        PdfName newName = new PdfName(ns + sde.substring(7));
2465:                        desc.put(PdfName.BASEFONT, newName);
2466:                        ++total;
2467:                        PdfDictionary fd = (PdfDictionary) getPdfObject(desc
2468:                                .get(PdfName.FONTDESCRIPTOR));
2469:                        if (fd == null)
2470:                            continue;
2471:                        fd.put(PdfName.FONTNAME, newName);
2472:                    }
2473:                }
2474:                return total;
2475:            }
2476:
2477:            /** Finds all the fonts not subset but embedded and marks them as subset.
2478:             * @return the number of fonts altered
2479:             */
2480:            public int createFakeFontSubsets() {
2481:                int total = 0;
2482:                for (int k = 1; k < xrefObj.size(); ++k) {
2483:                    PdfObject obj = getPdfObjectRelease(k);
2484:                    if (obj == null || !obj.isDictionary())
2485:                        continue;
2486:                    PdfDictionary dic = (PdfDictionary) obj;
2487:                    if (!existsName(dic, PdfName.TYPE, PdfName.FONT))
2488:                        continue;
2489:                    if (existsName(dic, PdfName.SUBTYPE, PdfName.TYPE1)
2490:                            || existsName(dic, PdfName.SUBTYPE, PdfName.MMTYPE1)
2491:                            || existsName(dic, PdfName.SUBTYPE,
2492:                                    PdfName.TRUETYPE)) {
2493:                        String s = getSubsetPrefix(dic);
2494:                        if (s != null)
2495:                            continue;
2496:                        s = getFontName(dic);
2497:                        if (s == null)
2498:                            continue;
2499:                        String ns = BaseFont.createSubsetPrefix() + s;
2500:                        PdfDictionary fd = (PdfDictionary) getPdfObjectRelease(dic
2501:                                .get(PdfName.FONTDESCRIPTOR));
2502:                        if (fd == null)
2503:                            continue;
2504:                        if (fd.get(PdfName.FONTFILE) == null
2505:                                && fd.get(PdfName.FONTFILE2) == null
2506:                                && fd.get(PdfName.FONTFILE3) == null)
2507:                            continue;
2508:                        fd = (PdfDictionary) getPdfObject(dic
2509:                                .get(PdfName.FONTDESCRIPTOR));
2510:                        PdfName newName = new PdfName(ns);
2511:                        dic.put(PdfName.BASEFONT, newName);
2512:                        fd.put(PdfName.FONTNAME, newName);
2513:                        setXrefPartialObject(k, dic);
2514:                        ++total;
2515:                    }
2516:                }
2517:                return total;
2518:            }
2519:
2520:            private static PdfArray getNameArray(PdfObject obj) {
2521:                if (obj == null)
2522:                    return null;
2523:                obj = getPdfObjectRelease(obj);
2524:                if (obj == null)
2525:                    return null;
2526:                if (obj.isArray())
2527:                    return (PdfArray) obj;
2528:                else if (obj.isDictionary()) {
2529:                    PdfObject arr2 = getPdfObjectRelease(((PdfDictionary) obj)
2530:                            .get(PdfName.D));
2531:                    if (arr2 != null && arr2.isArray())
2532:                        return (PdfArray) arr2;
2533:                }
2534:                return null;
2535:            }
2536:
2537:            /**
2538:             * Gets all the named destinations as an <CODE>HashMap</CODE>. The key is the name
2539:             * and the value is the destinations array.
2540:             * @return gets all the named destinations
2541:             */
2542:            public HashMap getNamedDestination() {
2543:                HashMap names = getNamedDestinationFromNames();
2544:                names.putAll(getNamedDestinationFromStrings());
2545:                return names;
2546:            }
2547:
2548:            /**
2549:             * Gets the named destinations from the /Dests key in the catalog as an <CODE>HashMap</CODE>. The key is the name
2550:             * and the value is the destinations array.
2551:             * @return gets the named destinations
2552:             */
2553:            public HashMap getNamedDestinationFromNames() {
2554:                HashMap names = new HashMap();
2555:                if (catalog.get(PdfName.DESTS) != null) {
2556:                    PdfDictionary dic = (PdfDictionary) getPdfObjectRelease(catalog
2557:                            .get(PdfName.DESTS));
2558:                    if (dic == null)
2559:                        return names;
2560:                    Set keys = dic.getKeys();
2561:                    for (Iterator it = keys.iterator(); it.hasNext();) {
2562:                        PdfName key = (PdfName) it.next();
2563:                        String name = PdfName.decodeName(key.toString());
2564:                        PdfArray arr = getNameArray(dic.get(key));
2565:                        if (arr != null)
2566:                            names.put(name, arr);
2567:                    }
2568:                }
2569:                return names;
2570:            }
2571:
2572:            /**
2573:             * Gets the named destinations from the /Names key in the catalog as an <CODE>HashMap</CODE>. The key is the name
2574:             * and the value is the destinations array.
2575:             * @return gets the named destinations
2576:             */
2577:            public HashMap getNamedDestinationFromStrings() {
2578:                if (catalog.get(PdfName.NAMES) != null) {
2579:                    PdfDictionary dic = (PdfDictionary) getPdfObjectRelease(catalog
2580:                            .get(PdfName.NAMES));
2581:                    if (dic != null) {
2582:                        dic = (PdfDictionary) getPdfObjectRelease(dic
2583:                                .get(PdfName.DESTS));
2584:                        if (dic != null) {
2585:                            HashMap names = PdfNameTree.readTree(dic);
2586:                            for (Iterator it = names.entrySet().iterator(); it
2587:                                    .hasNext();) {
2588:                                Map.Entry entry = (Map.Entry) it.next();
2589:                                PdfArray arr = getNameArray((PdfObject) entry
2590:                                        .getValue());
2591:                                if (arr != null)
2592:                                    entry.setValue(arr);
2593:                                else
2594:                                    it.remove();
2595:                            }
2596:                            return names;
2597:                        }
2598:                    }
2599:                }
2600:                return new HashMap();
2601:            }
2602:
2603:            private boolean replaceNamedDestination(PdfObject obj, HashMap names) {
2604:                obj = getPdfObject(obj);
2605:                int objIdx = lastXrefPartial;
2606:                releaseLastXrefPartial();
2607:                if (obj != null && obj.isDictionary()) {
2608:                    PdfObject ob2 = getPdfObjectRelease(((PdfDictionary) obj)
2609:                            .get(PdfName.DEST));
2610:                    String name = null;
2611:                    if (ob2 != null) {
2612:                        if (ob2.isName())
2613:                            name = PdfName.decodeName(ob2.toString());
2614:                        else if (ob2.isString())
2615:                            name = ob2.toString();
2616:                        PdfArray dest = (PdfArray) names.get(name);
2617:                        if (dest != null) {
2618:                            ((PdfDictionary) obj).put(PdfName.DEST, dest);
2619:                            setXrefPartialObject(objIdx, obj);
2620:                            return true;
2621:                        }
2622:                    } else if ((ob2 = getPdfObject(((PdfDictionary) obj)
2623:                            .get(PdfName.A))) != null) {
2624:                        int obj2Idx = lastXrefPartial;
2625:                        releaseLastXrefPartial();
2626:                        PdfDictionary dic = (PdfDictionary) ob2;
2627:                        PdfName type = (PdfName) getPdfObjectRelease(dic
2628:                                .get(PdfName.S));
2629:                        if (PdfName.GOTO.equals(type)) {
2630:                            PdfObject ob3 = getPdfObjectRelease(dic
2631:                                    .get(PdfName.D));
2632:                            if (ob3 != null) {
2633:                                if (ob3.isName())
2634:                                    name = PdfName.decodeName(ob3.toString());
2635:                                else if (ob3.isString())
2636:                                    name = ob3.toString();
2637:                            }
2638:                            PdfArray dest = (PdfArray) names.get(name);
2639:                            if (dest != null) {
2640:                                dic.put(PdfName.D, dest);
2641:                                setXrefPartialObject(obj2Idx, ob2);
2642:                                setXrefPartialObject(objIdx, obj);
2643:                                return true;
2644:                            }
2645:                        }
2646:                    }
2647:                }
2648:                return false;
2649:            }
2650:
2651:            /**
2652:             * Removes all the fields from the document.
2653:             */
2654:            public void removeFields() {
2655:                pageRefs.resetReleasePage();
2656:                for (int k = 1; k <= pageRefs.size(); ++k) {
2657:                    PdfDictionary page = pageRefs.getPageN(k);
2658:                    PdfArray annots = (PdfArray) getPdfObject(page
2659:                            .get(PdfName.ANNOTS));
2660:                    if (annots == null) {
2661:                        pageRefs.releasePage(k);
2662:                        continue;
2663:                    }
2664:                    ArrayList arr = annots.getArrayList();
2665:                    for (int j = 0; j < arr.size(); ++j) {
2666:                        PdfDictionary annot = (PdfDictionary) getPdfObjectRelease((PdfObject) arr
2667:                                .get(j));
2668:                        if (PdfName.WIDGET.equals(annot.get(PdfName.SUBTYPE)))
2669:                            arr.remove(j--);
2670:                    }
2671:                    if (arr.isEmpty())
2672:                        page.remove(PdfName.ANNOTS);
2673:                    else
2674:                        pageRefs.releasePage(k);
2675:                }
2676:                catalog.remove(PdfName.ACROFORM);
2677:                pageRefs.resetReleasePage();
2678:            }
2679:
2680:            /**
2681:             * Removes all the annotations and fields from the document.
2682:             */
2683:            public void removeAnnotations() {
2684:                pageRefs.resetReleasePage();
2685:                for (int k = 1; k <= pageRefs.size(); ++k) {
2686:                    PdfDictionary page = pageRefs.getPageN(k);
2687:                    if (page.get(PdfName.ANNOTS) == null)
2688:                        pageRefs.releasePage(k);
2689:                    else
2690:                        page.remove(PdfName.ANNOTS);
2691:                }
2692:                catalog.remove(PdfName.ACROFORM);
2693:                pageRefs.resetReleasePage();
2694:            }
2695:
2696:            public ArrayList getLinks(int page) {
2697:                pageRefs.resetReleasePage();
2698:                ArrayList result = new ArrayList();
2699:                PdfDictionary pageDic = pageRefs.getPageN(page);
2700:                if (pageDic.get(PdfName.ANNOTS) != null) {
2701:                    PdfArray annots = (PdfArray) getPdfObject(pageDic
2702:                            .get(PdfName.ANNOTS));
2703:                    ArrayList arr = annots.getArrayList();
2704:                    for (int j = 0; j < arr.size(); ++j) {
2705:                        PdfDictionary annot = (PdfDictionary) getPdfObjectRelease((PdfObject) arr
2706:                                .get(j));
2707:
2708:                        if (PdfName.LINK.equals(annot.get(PdfName.SUBTYPE))) {
2709:                            result
2710:                                    .add(new PdfAnnotation.PdfImportedLink(
2711:                                            annot));
2712:                        }
2713:                    }
2714:                }
2715:                pageRefs.releasePage(page);
2716:                pageRefs.resetReleasePage();
2717:                return result;
2718:            }
2719:
2720:            private void iterateBookmarks(PdfObject outlineRef, HashMap names) {
2721:                while (outlineRef != null) {
2722:                    replaceNamedDestination(outlineRef, names);
2723:                    PdfDictionary outline = (PdfDictionary) getPdfObjectRelease(outlineRef);
2724:                    PdfObject first = outline.get(PdfName.FIRST);
2725:                    if (first != null) {
2726:                        iterateBookmarks(first, names);
2727:                    }
2728:                    outlineRef = outline.get(PdfName.NEXT);
2729:                }
2730:            }
2731:
2732:            /** Replaces all the local named links with the actual destinations. */
2733:            public void consolidateNamedDestinations() {
2734:                if (consolidateNamedDestinations)
2735:                    return;
2736:                consolidateNamedDestinations = true;
2737:                HashMap names = getNamedDestination();
2738:                if (names.isEmpty())
2739:                    return;
2740:                for (int k = 1; k <= pageRefs.size(); ++k) {
2741:                    PdfDictionary page = pageRefs.getPageN(k);
2742:                    PdfObject annotsRef;
2743:                    PdfArray annots = (PdfArray) getPdfObject(annotsRef = page
2744:                            .get(PdfName.ANNOTS));
2745:                    int annotIdx = lastXrefPartial;
2746:                    releaseLastXrefPartial();
2747:                    if (annots == null) {
2748:                        pageRefs.releasePage(k);
2749:                        continue;
2750:                    }
2751:                    ArrayList list = annots.getArrayList();
2752:                    boolean commitAnnots = false;
2753:                    for (int an = 0; an < list.size(); ++an) {
2754:                        PdfObject objRef = (PdfObject) list.get(an);
2755:                        if (replaceNamedDestination(objRef, names)
2756:                                && !objRef.isIndirect())
2757:                            commitAnnots = true;
2758:                    }
2759:                    if (commitAnnots)
2760:                        setXrefPartialObject(annotIdx, annots);
2761:                    if (!commitAnnots || annotsRef.isIndirect())
2762:                        pageRefs.releasePage(k);
2763:                }
2764:                PdfDictionary outlines = (PdfDictionary) getPdfObjectRelease(catalog
2765:                        .get(PdfName.OUTLINES));
2766:                if (outlines == null)
2767:                    return;
2768:                iterateBookmarks(outlines.get(PdfName.FIRST), names);
2769:            }
2770:
2771:            protected static PdfDictionary duplicatePdfDictionary(
2772:                    PdfDictionary original, PdfDictionary copy,
2773:                    PdfReader newReader) {
2774:                if (copy == null)
2775:                    copy = new PdfDictionary();
2776:                for (Iterator it = original.getKeys().iterator(); it.hasNext();) {
2777:                    PdfName key = (PdfName) it.next();
2778:                    copy.put(key, duplicatePdfObject(original.get(key),
2779:                            newReader));
2780:                }
2781:                return copy;
2782:            }
2783:
2784:            protected static PdfObject duplicatePdfObject(PdfObject original,
2785:                    PdfReader newReader) {
2786:                if (original == null)
2787:                    return null;
2788:                switch (original.type()) {
2789:                case PdfObject.DICTIONARY: {
2790:                    return duplicatePdfDictionary((PdfDictionary) original,
2791:                            null, newReader);
2792:                }
2793:                case PdfObject.STREAM: {
2794:                    PRStream org = (PRStream) original;
2795:                    PRStream stream = new PRStream(org, null, newReader);
2796:                    duplicatePdfDictionary(org, stream, newReader);
2797:                    return stream;
2798:                }
2799:                case PdfObject.ARRAY: {
2800:                    ArrayList list = ((PdfArray) original).getArrayList();
2801:                    PdfArray arr = new PdfArray();
2802:                    for (Iterator it = list.iterator(); it.hasNext();) {
2803:                        arr.add(duplicatePdfObject((PdfObject) it.next(),
2804:                                newReader));
2805:                    }
2806:                    return arr;
2807:                }
2808:                case PdfObject.INDIRECT: {
2809:                    PRIndirectReference org = (PRIndirectReference) original;
2810:                    return new PRIndirectReference(newReader, org.getNumber(),
2811:                            org.getGeneration());
2812:                }
2813:                default:
2814:                    return original;
2815:                }
2816:            }
2817:
2818:            /**
2819:             * Closes the reader
2820:             */
2821:            public void close() {
2822:                if (!partial)
2823:                    return;
2824:                try {
2825:                    tokens.close();
2826:                } catch (IOException e) {
2827:                    throw new ExceptionConverter(e);
2828:                }
2829:            }
2830:
2831:            protected void removeUnusedNode(PdfObject obj, boolean hits[]) {
2832:                Stack state = new Stack();
2833:                state.push(obj);
2834:                while (!state.empty()) {
2835:                    Object current = state.pop();
2836:                    if (current == null)
2837:                        continue;
2838:                    ArrayList ar = null;
2839:                    PdfDictionary dic = null;
2840:                    PdfName[] keys = null;
2841:                    Object[] objs = null;
2842:                    int idx = 0;
2843:                    if (current instanceof  PdfObject) {
2844:                        obj = (PdfObject) current;
2845:                        switch (obj.type()) {
2846:                        case PdfObject.DICTIONARY:
2847:                        case PdfObject.STREAM:
2848:                            dic = (PdfDictionary) obj;
2849:                            keys = new PdfName[dic.size()];
2850:                            dic.getKeys().toArray(keys);
2851:                            break;
2852:                        case PdfObject.ARRAY:
2853:                            ar = ((PdfArray) obj).getArrayList();
2854:                            break;
2855:                        case PdfObject.INDIRECT:
2856:                            PRIndirectReference ref = (PRIndirectReference) obj;
2857:                            int num = ref.getNumber();
2858:                            if (!hits[num]) {
2859:                                hits[num] = true;
2860:                                state.push(getPdfObjectRelease(ref));
2861:                            }
2862:                            continue;
2863:                        default:
2864:                            continue;
2865:                        }
2866:                    } else {
2867:                        objs = (Object[]) current;
2868:                        if (objs[0] instanceof  ArrayList) {
2869:                            ar = (ArrayList) objs[0];
2870:                            idx = ((Integer) objs[1]).intValue();
2871:                        } else {
2872:                            keys = (PdfName[]) objs[0];
2873:                            dic = (PdfDictionary) objs[1];
2874:                            idx = ((Integer) objs[2]).intValue();
2875:                        }
2876:                    }
2877:                    if (ar != null) {
2878:                        for (int k = idx; k < ar.size(); ++k) {
2879:                            PdfObject v = (PdfObject) ar.get(k);
2880:                            if (v.isIndirect()) {
2881:                                int num = ((PRIndirectReference) v).getNumber();
2882:                                if (num >= xrefObj.size()
2883:                                        || (!partial && xrefObj.get(num) == null)) {
2884:                                    ar.set(k, PdfNull.PDFNULL);
2885:                                    continue;
2886:                                }
2887:                            }
2888:                            if (objs == null)
2889:                                state.push(new Object[] { ar,
2890:                                        new Integer(k + 1) });
2891:                            else {
2892:                                objs[1] = new Integer(k + 1);
2893:                                state.push(objs);
2894:                            }
2895:                            state.push(v);
2896:                            break;
2897:                        }
2898:                    } else {
2899:                        for (int k = idx; k < keys.length; ++k) {
2900:                            PdfName key = keys[k];
2901:                            PdfObject v = dic.get(key);
2902:                            if (v.isIndirect()) {
2903:                                int num = ((PRIndirectReference) v).getNumber();
2904:                                if (num >= xrefObj.size()
2905:                                        || (!partial && xrefObj.get(num) == null)) {
2906:                                    dic.put(key, PdfNull.PDFNULL);
2907:                                    continue;
2908:                                }
2909:                            }
2910:                            if (objs == null)
2911:                                state.push(new Object[] { keys, dic,
2912:                                        new Integer(k + 1) });
2913:                            else {
2914:                                objs[2] = new Integer(k + 1);
2915:                                state.push(objs);
2916:                            }
2917:                            state.push(v);
2918:                            break;
2919:                        }
2920:                    }
2921:                }
2922:            }
2923:
2924:            /** Removes all the unreachable objects.
2925:             * @return the number of indirect objects removed
2926:             */
2927:            public int removeUnusedObjects() {
2928:                boolean hits[] = new boolean[xrefObj.size()];
2929:                removeUnusedNode(trailer, hits);
2930:                int total = 0;
2931:                if (partial) {
2932:                    for (int k = 1; k < hits.length; ++k) {
2933:                        if (!hits[k]) {
2934:                            xref[k * 2] = -1;
2935:                            xref[k * 2 + 1] = 0;
2936:                            xrefObj.set(k, null);
2937:                            ++total;
2938:                        }
2939:                    }
2940:                } else {
2941:                    for (int k = 1; k < hits.length; ++k) {
2942:                        if (!hits[k]) {
2943:                            xrefObj.set(k, null);
2944:                            ++total;
2945:                        }
2946:                    }
2947:                }
2948:                return total;
2949:            }
2950:
2951:            /** Gets a read-only version of <CODE>AcroFields</CODE>.
2952:             * @return a read-only version of <CODE>AcroFields</CODE>
2953:             */
2954:            public AcroFields getAcroFields() {
2955:                return new AcroFields(this , null);
2956:            }
2957:
2958:            /**
2959:             * Gets the global document JavaScript.
2960:             * @param file the document file
2961:             * @throws IOException on error
2962:             * @return the global document JavaScript
2963:             */
2964:            public String getJavaScript(RandomAccessFileOrArray file)
2965:                    throws IOException {
2966:                PdfDictionary names = (PdfDictionary) getPdfObjectRelease(catalog
2967:                        .get(PdfName.NAMES));
2968:                if (names == null)
2969:                    return null;
2970:                PdfDictionary js = (PdfDictionary) getPdfObjectRelease(names
2971:                        .get(PdfName.JAVASCRIPT));
2972:                if (js == null)
2973:                    return null;
2974:                HashMap jscript = PdfNameTree.readTree(js);
2975:                String sortedNames[] = new String[jscript.size()];
2976:                sortedNames = (String[]) jscript.keySet().toArray(sortedNames);
2977:                Arrays.sort(sortedNames);
2978:                StringBuffer buf = new StringBuffer();
2979:                for (int k = 0; k < sortedNames.length; ++k) {
2980:                    PdfDictionary j = (PdfDictionary) getPdfObjectRelease((PdfIndirectReference) jscript
2981:                            .get(sortedNames[k]));
2982:                    if (j == null)
2983:                        continue;
2984:                    PdfObject obj = getPdfObjectRelease(j.get(PdfName.JS));
2985:                    if (obj != null) {
2986:                        if (obj.isString())
2987:                            buf.append(((PdfString) obj).toUnicodeString())
2988:                                    .append('\n');
2989:                        else if (obj.isStream()) {
2990:                            byte bytes[] = getStreamBytes((PRStream) obj, file);
2991:                            if (bytes.length >= 2 && bytes[0] == (byte) 254
2992:                                    && bytes[1] == (byte) 255)
2993:                                buf.append(PdfEncodings.convertToString(bytes,
2994:                                        PdfObject.TEXT_UNICODE));
2995:                            else
2996:                                buf.append(PdfEncodings.convertToString(bytes,
2997:                                        PdfObject.TEXT_PDFDOCENCODING));
2998:                            buf.append('\n');
2999:                        }
3000:                    }
3001:                }
3002:                return buf.toString();
3003:            }
3004:
3005:            /**
3006:             * Gets the global document JavaScript.
3007:             * @throws IOException on error
3008:             * @return the global document JavaScript
3009:             */
3010:            public String getJavaScript() throws IOException {
3011:                RandomAccessFileOrArray rf = getSafeFile();
3012:                try {
3013:                    rf.reOpen();
3014:                    return getJavaScript(rf);
3015:                } finally {
3016:                    try {
3017:                        rf.close();
3018:                    } catch (Exception e) {
3019:                    }
3020:                }
3021:            }
3022:
3023:            /**
3024:             * Selects the pages to keep in the document. The pages are described as
3025:             * ranges. The page ordering can be changed but
3026:             * no page repetitions are allowed. Note that it may be very slow in partial mode.
3027:             * @param ranges the comma separated ranges as described in {@link SequenceList}
3028:             */
3029:            public void selectPages(String ranges) {
3030:                selectPages(SequenceList.expand(ranges, getNumberOfPages()));
3031:            }
3032:
3033:            /**
3034:             * Selects the pages to keep in the document. The pages are described as a
3035:             * <CODE>List</CODE> of <CODE>Integer</CODE>. The page ordering can be changed but
3036:             * no page repetitions are allowed. Note that it may be very slow in partial mode.
3037:             * @param pagesToKeep the pages to keep in the document
3038:             */
3039:            public void selectPages(List pagesToKeep) {
3040:                pageRefs.selectPages(pagesToKeep);
3041:                removeUnusedObjects();
3042:            }
3043:
3044:            /** Sets the viewer preferences as the sum of several constants.
3045:             * @param preferences the viewer preferences
3046:             * @see PdfViewerPreferences#setViewerPreferences
3047:             */
3048:            public void setViewerPreferences(int preferences) {
3049:                this .viewerPreferences.setViewerPreferences(preferences);
3050:                setViewerPreferences(this .viewerPreferences);
3051:            }
3052:
3053:            /** Adds a viewer preference
3054:             * @param key a key for a viewer preference
3055:             * @param value	a value for the viewer preference
3056:             * @see PdfViewerPreferences#addViewerPreference
3057:             */
3058:
3059:            public void addViewerPreference(PdfName key, PdfObject value) {
3060:                this .viewerPreferences.addViewerPreference(key, value);
3061:                setViewerPreferences(this .viewerPreferences);
3062:            }
3063:
3064:            void setViewerPreferences(PdfViewerPreferencesImp vp) {
3065:                vp.addToCatalog(catalog);
3066:            }
3067:
3068:            /**
3069:             * @return an int that contains the Viewer Preferences.
3070:             */
3071:            public int getSimpleViewerPreferences() {
3072:                return PdfViewerPreferencesImp.getViewerPreferences(catalog)
3073:                        .getPageLayoutAndMode();
3074:            }
3075:
3076:            /**
3077:             * Getter for property appendable.
3078:             * @return Value of property appendable.
3079:             */
3080:            public boolean isAppendable() {
3081:                return this .appendable;
3082:            }
3083:
3084:            /**
3085:             * Setter for property appendable.
3086:             * @param appendable New value of property appendable.
3087:             */
3088:            public void setAppendable(boolean appendable) {
3089:                this .appendable = appendable;
3090:                if (appendable)
3091:                    getPdfObject(trailer.get(PdfName.ROOT));
3092:            }
3093:
3094:            /**
3095:             * Getter for property newXrefType.
3096:             * @return Value of property newXrefType.
3097:             */
3098:            public boolean isNewXrefType() {
3099:                return newXrefType;
3100:            }
3101:
3102:            /**
3103:             * Getter for property fileLength.
3104:             * @return Value of property fileLength.
3105:             */
3106:            public int getFileLength() {
3107:                return fileLength;
3108:            }
3109:
3110:            /**
3111:             * Getter for property hybridXref.
3112:             * @return Value of property hybridXref.
3113:             */
3114:            public boolean isHybridXref() {
3115:                return hybridXref;
3116:            }
3117:
3118:            static class PageRefs {
3119:                private PdfReader reader;
3120:                private IntHashtable refsp;
3121:                private ArrayList refsn;
3122:                private ArrayList pageInh;
3123:                private int lastPageRead = -1;
3124:                private int sizep;
3125:
3126:                private PageRefs(PdfReader reader) throws IOException {
3127:                    this .reader = reader;
3128:                    if (reader.partial) {
3129:                        refsp = new IntHashtable();
3130:                        PdfNumber npages = (PdfNumber) PdfReader
3131:                                .getPdfObjectRelease(reader.rootPages
3132:                                        .get(PdfName.COUNT));
3133:                        sizep = npages.intValue();
3134:                    } else {
3135:                        readPages();
3136:                    }
3137:                }
3138:
3139:                PageRefs(PageRefs other, PdfReader reader) {
3140:                    this .reader = reader;
3141:                    this .sizep = other.sizep;
3142:                    if (other.refsn != null) {
3143:                        refsn = new ArrayList(other.refsn);
3144:                        for (int k = 0; k < refsn.size(); ++k) {
3145:                            refsn.set(k, duplicatePdfObject((PdfObject) refsn
3146:                                    .get(k), reader));
3147:                        }
3148:                    } else
3149:                        this .refsp = (IntHashtable) other.refsp.clone();
3150:                }
3151:
3152:                int size() {
3153:                    if (refsn != null)
3154:                        return refsn.size();
3155:                    else
3156:                        return sizep;
3157:                }
3158:
3159:                void readPages() throws IOException {
3160:                    if (refsn != null)
3161:                        return;
3162:                    refsp = null;
3163:                    refsn = new ArrayList();
3164:                    pageInh = new ArrayList();
3165:                    iteratePages((PRIndirectReference) reader.catalog
3166:                            .get(PdfName.PAGES));
3167:                    pageInh = null;
3168:                    reader.rootPages.put(PdfName.COUNT, new PdfNumber(refsn
3169:                            .size()));
3170:                }
3171:
3172:                void reReadPages() throws IOException {
3173:                    refsn = null;
3174:                    readPages();
3175:                }
3176:
3177:                /** Gets the dictionary that represents a page.
3178:                 * @param pageNum the page number. 1 is the first
3179:                 * @return the page dictionary
3180:                 */
3181:                public PdfDictionary getPageN(int pageNum) {
3182:                    PRIndirectReference ref = getPageOrigRef(pageNum);
3183:                    return (PdfDictionary) PdfReader.getPdfObject(ref);
3184:                }
3185:
3186:                /**
3187:                 * @param pageNum
3188:                 * @return a dictionary object
3189:                 */
3190:                public PdfDictionary getPageNRelease(int pageNum) {
3191:                    PdfDictionary page = getPageN(pageNum);
3192:                    releasePage(pageNum);
3193:                    return page;
3194:                }
3195:
3196:                /**
3197:                 * @param pageNum
3198:                 * @return an indirect reference
3199:                 */
3200:                public PRIndirectReference getPageOrigRefRelease(int pageNum) {
3201:                    PRIndirectReference ref = getPageOrigRef(pageNum);
3202:                    releasePage(pageNum);
3203:                    return ref;
3204:                }
3205:
3206:                /** Gets the page reference to this page.
3207:                 * @param pageNum the page number. 1 is the first
3208:                 * @return the page reference
3209:                 */
3210:                public PRIndirectReference getPageOrigRef(int pageNum) {
3211:                    try {
3212:                        --pageNum;
3213:                        if (pageNum < 0 || pageNum >= size())
3214:                            return null;
3215:                        if (refsn != null)
3216:                            return (PRIndirectReference) refsn.get(pageNum);
3217:                        else {
3218:                            int n = refsp.get(pageNum);
3219:                            if (n == 0) {
3220:                                PRIndirectReference ref = getSinglePage(pageNum);
3221:                                if (reader.lastXrefPartial == -1)
3222:                                    lastPageRead = -1;
3223:                                else
3224:                                    lastPageRead = pageNum;
3225:                                reader.lastXrefPartial = -1;
3226:                                refsp.put(pageNum, ref.getNumber());
3227:                                return ref;
3228:                            } else {
3229:                                if (lastPageRead != pageNum)
3230:                                    lastPageRead = -1;
3231:                                return new PRIndirectReference(reader, n);
3232:                            }
3233:                        }
3234:                    } catch (Exception e) {
3235:                        throw new ExceptionConverter(e);
3236:                    }
3237:                }
3238:
3239:                /**
3240:                 * @param pageNum
3241:                 */
3242:                public void releasePage(int pageNum) {
3243:                    if (refsp == null)
3244:                        return;
3245:                    --pageNum;
3246:                    if (pageNum < 0 || pageNum >= size())
3247:                        return;
3248:                    if (pageNum != lastPageRead)
3249:                        return;
3250:                    lastPageRead = -1;
3251:                    reader.lastXrefPartial = refsp.get(pageNum);
3252:                    reader.releaseLastXrefPartial();
3253:                    refsp.remove(pageNum);
3254:                }
3255:
3256:                /**
3257:                 *
3258:                 */
3259:                public void resetReleasePage() {
3260:                    if (refsp == null)
3261:                        return;
3262:                    lastPageRead = -1;
3263:                }
3264:
3265:                void insertPage(int pageNum, PRIndirectReference ref) {
3266:                    --pageNum;
3267:                    if (refsn != null) {
3268:                        if (pageNum >= refsn.size())
3269:                            refsn.add(ref);
3270:                        else
3271:                            refsn.add(pageNum, ref);
3272:                    } else {
3273:                        ++sizep;
3274:                        lastPageRead = -1;
3275:                        if (pageNum >= size()) {
3276:                            refsp.put(size(), ref.getNumber());
3277:                        } else {
3278:                            IntHashtable refs2 = new IntHashtable(
3279:                                    (refsp.size() + 1) * 2);
3280:                            for (Iterator it = refsp.getEntryIterator(); it
3281:                                    .hasNext();) {
3282:                                IntHashtable.Entry entry = (IntHashtable.Entry) it
3283:                                        .next();
3284:                                int p = entry.getKey();
3285:                                refs2.put(p >= pageNum ? p + 1 : p, entry
3286:                                        .getValue());
3287:                            }
3288:                            refs2.put(pageNum, ref.getNumber());
3289:                            refsp = refs2;
3290:                        }
3291:                    }
3292:                }
3293:
3294:                private void pushPageAttributes(PdfDictionary nodePages) {
3295:                    PdfDictionary dic = new PdfDictionary();
3296:                    if (!pageInh.isEmpty()) {
3297:                        dic.putAll((PdfDictionary) pageInh
3298:                                .get(pageInh.size() - 1));
3299:                    }
3300:                    for (int k = 0; k < pageInhCandidates.length; ++k) {
3301:                        PdfObject obj = nodePages.get(pageInhCandidates[k]);
3302:                        if (obj != null)
3303:                            dic.put(pageInhCandidates[k], obj);
3304:                    }
3305:                    pageInh.add(dic);
3306:                }
3307:
3308:                private void popPageAttributes() {
3309:                    pageInh.remove(pageInh.size() - 1);
3310:                }
3311:
3312:                private void iteratePages(PRIndirectReference rpage)
3313:                        throws IOException {
3314:                    PdfDictionary page = (PdfDictionary) getPdfObject(rpage);
3315:                    PdfArray kidsPR = (PdfArray) getPdfObject(page
3316:                            .get(PdfName.KIDS));
3317:                    if (kidsPR == null) {
3318:                        page.put(PdfName.TYPE, PdfName.PAGE);
3319:                        PdfDictionary dic = (PdfDictionary) pageInh.get(pageInh
3320:                                .size() - 1);
3321:                        PdfName key;
3322:                        for (Iterator i = dic.getKeys().iterator(); i.hasNext();) {
3323:                            key = (PdfName) i.next();
3324:                            if (page.get(key) == null)
3325:                                page.put(key, dic.get(key));
3326:                        }
3327:                        if (page.get(PdfName.MEDIABOX) == null) {
3328:                            PdfArray arr = new PdfArray(new float[] { 0, 0,
3329:                                    PageSize.LETTER.getRight(),
3330:                                    PageSize.LETTER.getTop() });
3331:                            page.put(PdfName.MEDIABOX, arr);
3332:                        }
3333:                        refsn.add(rpage);
3334:                    } else {
3335:                        page.put(PdfName.TYPE, PdfName.PAGES);
3336:                        pushPageAttributes(page);
3337:                        ArrayList kids = kidsPR.getArrayList();
3338:                        for (int k = 0; k < kids.size(); ++k) {
3339:                            PdfObject obj = (PdfObject) kids.get(k);
3340:                            if (!obj.isIndirect()) {
3341:                                while (k < kids.size())
3342:                                    kids.remove(k);
3343:                                break;
3344:                            }
3345:                            iteratePages((PRIndirectReference) obj);
3346:                        }
3347:                        popPageAttributes();
3348:                    }
3349:                }
3350:
3351:                protected PRIndirectReference getSinglePage(int n) {
3352:                    PdfDictionary acc = new PdfDictionary();
3353:                    PdfDictionary top = reader.rootPages;
3354:                    int base = 0;
3355:                    while (true) {
3356:                        for (int k = 0; k < pageInhCandidates.length; ++k) {
3357:                            PdfObject obj = top.get(pageInhCandidates[k]);
3358:                            if (obj != null)
3359:                                acc.put(pageInhCandidates[k], obj);
3360:                        }
3361:                        PdfArray kids = (PdfArray) PdfReader
3362:                                .getPdfObjectRelease(top.get(PdfName.KIDS));
3363:                        for (Iterator it = kids.listIterator(); it.hasNext();) {
3364:                            PRIndirectReference ref = (PRIndirectReference) it
3365:                                    .next();
3366:                            PdfDictionary dic = (PdfDictionary) getPdfObject(ref);
3367:                            int last = reader.lastXrefPartial;
3368:                            PdfObject count = getPdfObjectRelease(dic
3369:                                    .get(PdfName.COUNT));
3370:                            reader.lastXrefPartial = last;
3371:                            int acn = 1;
3372:                            if (count != null
3373:                                    && count.type() == PdfObject.NUMBER)
3374:                                acn = ((PdfNumber) count).intValue();
3375:                            if (n < base + acn) {
3376:                                if (count == null) {
3377:                                    dic.mergeDifferent(acc);
3378:                                    return ref;
3379:                                }
3380:                                reader.releaseLastXrefPartial();
3381:                                top = dic;
3382:                                break;
3383:                            }
3384:                            reader.releaseLastXrefPartial();
3385:                            base += acn;
3386:                        }
3387:                    }
3388:                }
3389:
3390:                private void selectPages(List pagesToKeep) {
3391:                    IntHashtable pg = new IntHashtable();
3392:                    ArrayList finalPages = new ArrayList();
3393:                    int psize = size();
3394:                    for (Iterator it = pagesToKeep.iterator(); it.hasNext();) {
3395:                        Integer pi = (Integer) it.next();
3396:                        int p = pi.intValue();
3397:                        if (p >= 1 && p <= psize && pg.put(p, 1) == 0)
3398:                            finalPages.add(pi);
3399:                    }
3400:                    if (reader.partial) {
3401:                        for (int k = 1; k <= psize; ++k) {
3402:                            getPageOrigRef(k);
3403:                            resetReleasePage();
3404:                        }
3405:                    }
3406:                    PRIndirectReference parent = (PRIndirectReference) reader.catalog
3407:                            .get(PdfName.PAGES);
3408:                    PdfDictionary topPages = (PdfDictionary) PdfReader
3409:                            .getPdfObject(parent);
3410:                    ArrayList newPageRefs = new ArrayList(finalPages.size());
3411:                    PdfArray kids = new PdfArray();
3412:                    for (int k = 0; k < finalPages.size(); ++k) {
3413:                        int p = ((Integer) finalPages.get(k)).intValue();
3414:                        PRIndirectReference pref = getPageOrigRef(p);
3415:                        resetReleasePage();
3416:                        kids.add(pref);
3417:                        newPageRefs.add(pref);
3418:                        getPageN(p).put(PdfName.PARENT, parent);
3419:                    }
3420:                    AcroFields af = reader.getAcroFields();
3421:                    boolean removeFields = (af.getFields().size() > 0);
3422:                    for (int k = 1; k <= psize; ++k) {
3423:                        if (!pg.containsKey(k)) {
3424:                            if (removeFields)
3425:                                af.removeFieldsFromPage(k);
3426:                            PRIndirectReference pref = getPageOrigRef(k);
3427:                            int nref = pref.getNumber();
3428:                            reader.xrefObj.set(nref, null);
3429:                            if (reader.partial) {
3430:                                reader.xref[nref * 2] = -1;
3431:                                reader.xref[nref * 2 + 1] = 0;
3432:                            }
3433:                        }
3434:                    }
3435:                    topPages.put(PdfName.COUNT,
3436:                            new PdfNumber(finalPages.size()));
3437:                    topPages.put(PdfName.KIDS, kids);
3438:                    refsp = null;
3439:                    refsn = newPageRefs;
3440:                }
3441:            }
3442:
3443:            PdfIndirectReference getCryptoRef() {
3444:                if (cryptoRef == null)
3445:                    return null;
3446:                return new PdfIndirectReference(0, cryptoRef.getNumber(),
3447:                        cryptoRef.getGeneration());
3448:            }
3449:
3450:            /**
3451:             * Removes any usage rights that this PDF may have. Only Adobe can grant usage rights
3452:             * and any PDF modification with iText will invalidate them. Invalidated usage rights may
3453:             * confuse Acrobat and it's advisabe to remove them altogether.
3454:             */
3455:            public void removeUsageRights() {
3456:                PdfDictionary perms = (PdfDictionary) getPdfObject(catalog
3457:                        .get(PdfName.PERMS));
3458:                if (perms == null)
3459:                    return;
3460:                perms.remove(PdfName.UR);
3461:                perms.remove(PdfName.UR3);
3462:                if (perms.size() == 0)
3463:                    catalog.remove(PdfName.PERMS);
3464:            }
3465:
3466:            /**
3467:             * Gets the certification level for this document. The return values can be <code>PdfSignatureAppearance.NOT_CERTIFIED</code>, 
3468:             * <code>PdfSignatureAppearance.CERTIFIED_NO_CHANGES_ALLOWED</code>,
3469:             * <code>PdfSignatureAppearance.CERTIFIED_FORM_FILLING</code> and
3470:             * <code>PdfSignatureAppearance.CERTIFIED_FORM_FILLING_AND_ANNOTATIONS</code>.
3471:             * <p>
3472:             * No signature validation is made, use the methods availabe for that in <CODE>AcroFields</CODE>.
3473:             * </p>
3474:             * @return gets the certification level for this document
3475:             */
3476:            public int getCertificationLevel() {
3477:                PdfDictionary dic = (PdfDictionary) getPdfObject(catalog
3478:                        .get(PdfName.PERMS));
3479:                if (dic == null)
3480:                    return PdfSignatureAppearance.NOT_CERTIFIED;
3481:                dic = (PdfDictionary) getPdfObject(dic.get(PdfName.DOCMDP));
3482:                if (dic == null)
3483:                    return PdfSignatureAppearance.NOT_CERTIFIED;
3484:                PdfArray arr = (PdfArray) getPdfObject(dic
3485:                        .get(PdfName.REFERENCE));
3486:                if (arr == null || arr.size() == 0)
3487:                    return PdfSignatureAppearance.NOT_CERTIFIED;
3488:                dic = (PdfDictionary) getPdfObject((PdfObject) (arr
3489:                        .getArrayList().get(0)));
3490:                if (dic == null)
3491:                    return PdfSignatureAppearance.NOT_CERTIFIED;
3492:                dic = (PdfDictionary) getPdfObject(dic
3493:                        .get(PdfName.TRANSFORMPARAMS));
3494:                if (dic == null)
3495:                    return PdfSignatureAppearance.NOT_CERTIFIED;
3496:                PdfNumber p = (PdfNumber) getPdfObject(dic.get(PdfName.P));
3497:                if (p == null)
3498:                    return PdfSignatureAppearance.NOT_CERTIFIED;
3499:                return p.intValue();
3500:            }
3501:
3502:            /**
3503:             * Checks if the document was opened with the owner password so that the end application
3504:             * can decide what level of access restrictions to apply. If the document is not encrypted
3505:             * it will return <CODE>true</CODE>.
3506:             * @return <CODE>true</CODE> if the document was opened with the owner password or if it's not encrypted,
3507:             * <CODE>false</CODE> if the document was opened with the user password
3508:             */
3509:            public final boolean isOpenedWithFullPermissions() {
3510:                return !encrypted || ownerPasswordUsed;
3511:            }
3512:
3513:            public int getCryptoMode() {
3514:                if (decrypt == null)
3515:                    return -1;
3516:                else
3517:                    return decrypt.getCryptoMode();
3518:            }
3519:
3520:            public boolean isMetadataEncrypted() {
3521:                if (decrypt == null)
3522:                    return false;
3523:                else
3524:                    return decrypt.isMetadataEncrypted();
3525:            }
3526:
3527:            public byte[] computeUserPassword() {
3528:                if (!encrypted || !ownerPasswordUsed)
3529:                    return null;
3530:                return decrypt.computeUserPassword(password);
3531:            }
3532:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.