001: /**
002: * Copyright (c) 2003-2006, www.pdfbox.org
003: * All rights reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions are met:
007: *
008: * 1. Redistributions of source code must retain the above copyright notice,
009: * this list of conditions and the following disclaimer.
010: * 2. Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: * 3. Neither the name of pdfbox; nor the names of its
014: * contributors may be used to endorse or promote products derived from this
015: * software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
021: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: *
028: * http://www.pdfbox.org
029: *
030: */package org.pdfbox.pdmodel;
031:
032: import java.awt.print.PageFormat;
033: import java.awt.print.Pageable;
034: import java.awt.print.Paper;
035: import java.awt.print.Printable;
036: import java.awt.print.PrinterException;
037: import java.awt.print.PrinterJob;
038: import java.io.BufferedInputStream;
039: import java.io.File;
040: import java.io.FileInputStream;
041: import java.io.FileOutputStream;
042: import java.io.IOException;
043: import java.io.InputStream;
044: import java.io.OutputStream;
045: import java.net.URL;
046: import java.util.List;
047:
048: import org.pdfbox.cos.COSArray;
049: import org.pdfbox.cos.COSDictionary;
050: import org.pdfbox.cos.COSDocument;
051: import org.pdfbox.cos.COSInteger;
052: import org.pdfbox.cos.COSName;
053: import org.pdfbox.cos.COSStream;
054: import org.pdfbox.exceptions.COSVisitorException;
055: import org.pdfbox.exceptions.CryptographyException;
056: import org.pdfbox.exceptions.InvalidPasswordException;
057: import org.pdfbox.io.RandomAccess;
058: import org.pdfbox.pdfparser.PDFParser;
059: import org.pdfbox.pdfwriter.COSWriter;
060: import org.pdfbox.pdmodel.common.PDRectangle;
061: import org.pdfbox.pdmodel.common.PDStream;
062: import org.pdfbox.pdmodel.encryption.AccessPermission;
063: import org.pdfbox.pdmodel.encryption.BadSecurityHandlerException;
064: import org.pdfbox.pdmodel.encryption.DecryptionMaterial;
065: import org.pdfbox.pdmodel.encryption.PDEncryptionDictionary;
066: import org.pdfbox.pdmodel.encryption.ProtectionPolicy;
067: import org.pdfbox.pdmodel.encryption.SecurityHandler;
068: import org.pdfbox.pdmodel.encryption.SecurityHandlersManager;
069: import org.pdfbox.pdmodel.encryption.StandardDecryptionMaterial;
070: import org.pdfbox.pdmodel.encryption.StandardProtectionPolicy;
071:
072: /**
073: * This is the in-memory representation of the PDF document. You need to call
074: * close() on this object when you are done using it!!
075: *
076: * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a>
077: * @version $Revision: 1.43 $
078: */
079: public class PDDocument implements Pageable {
080: private COSDocument document;
081:
082: // NOTE BGUILLON: this property must be removed because it is
083: // not the responsability of this class to know
084: //private boolean encryptOnSave = false;
085:
086: // NOTE BGUILLON: these properties are not used anymore. See getCurrentAccessPermission() instead
087: //private String encryptUserPassword = null;
088: //private String encryptOwnerPassword = null;
089:
090: //cached values
091: private PDDocumentInformation documentInformation;
092: private PDDocumentCatalog documentCatalog;
093:
094: //The encParameters will be cached here. When the document is decrypted then
095: //the COSDocument will not have an "Encrypt" dictionary anymore and this object
096: //must be used.
097: private PDEncryptionDictionary encParameters = null;
098:
099: /**
100: * This will tell if the document was decrypted with the master password.
101: * NOTE BGUILLON: this property is not used anymore. See getCurrentAccessPermission() instead
102: */
103: //private boolean decryptedWithOwnerPassword = false;
104:
105: /**
106: * The security handler used to decrypt / encrypt the document.
107: */
108: private SecurityHandler securityHandler = null;
109:
110: /**
111: * Constructor, creates a new PDF Document with no pages. You need to add
112: * at least one page for the document to be valid.
113: *
114: * @throws IOException If there is an error creating this document.
115: */
116: public PDDocument() throws IOException {
117: document = new COSDocument();
118:
119: //First we need a trailer
120: COSDictionary trailer = new COSDictionary();
121: document.setTrailer(trailer);
122:
123: //Next we need the root dictionary.
124: COSDictionary rootDictionary = new COSDictionary();
125: trailer.setItem(COSName.ROOT, rootDictionary);
126: rootDictionary.setItem(COSName.TYPE, COSName.CATALOG);
127: rootDictionary.setItem(COSName.VERSION, COSName
128: .getPDFName("1.4"));
129:
130: //next we need the pages tree structure
131: COSDictionary pages = new COSDictionary();
132: rootDictionary.setItem(COSName.PAGES, pages);
133: pages.setItem(COSName.TYPE, COSName.PAGES);
134: COSArray kidsArray = new COSArray();
135: pages.setItem(COSName.KIDS, kidsArray);
136: pages.setItem(COSName.COUNT, new COSInteger(0));
137: }
138:
139: /**
140: * This will add a page to the document. This is a convenience method, that
141: * will add the page to the root of the hierarchy and set the parent of the
142: * page to the root.
143: *
144: * @param page The page to add to the document.
145: */
146: public void addPage(PDPage page) {
147: PDPageNode rootPages = getDocumentCatalog().getPages();
148: rootPages.getKids().add(page);
149: page.setParent(rootPages);
150: rootPages.updateCount();
151: }
152:
153: /**
154: * Remove the page from the document.
155: *
156: * @param page The page to remove from the document.
157: *
158: * @return true if the page was found false otherwise.
159: */
160: public boolean removePage(PDPage page) {
161: PDPageNode parent = page.getParent();
162: boolean retval = parent.getKids().remove(page);
163: if (retval) {
164: //do a recursive updateCount starting at the root
165: //of the document
166: getDocumentCatalog().getPages().updateCount();
167: }
168: return retval;
169: }
170:
171: /**
172: * Remove the page from the document.
173: *
174: * @param pageNumber 0 based index to page number.
175: * @return true if the page was found false otherwise.
176: */
177: public boolean removePage(int pageNumber) {
178: boolean removed = false;
179: List allPages = getDocumentCatalog().getAllPages();
180: if (allPages.size() > pageNumber) {
181: PDPage page = (PDPage) allPages.get(pageNumber);
182: removed = removePage(page);
183: }
184: return removed;
185: }
186:
187: /**
188: * This will import and copy the contents from another location. Currently
189: * the content stream is stored in a scratch file. The scratch file is
190: * associated with the document. If you are adding a page to this document
191: * from another document and want to copy the contents to this document's
192: * scratch file then use this method otherwise just use the addPage method.
193: *
194: * @param page The page to import.
195: * @return The page that was imported.
196: *
197: * @throws IOException If there is an error copying the page.
198: */
199: public PDPage importPage(PDPage page) throws IOException {
200: PDPage importedPage = new PDPage(new COSDictionary(page
201: .getCOSDictionary()));
202: InputStream is = null;
203: OutputStream os = null;
204: try {
205: PDStream src = page.getContents();
206: PDStream dest = new PDStream(new COSStream(src.getStream(),
207: document.getScratchFile()));
208: importedPage.setContents(dest);
209: os = dest.createOutputStream();
210:
211: byte[] buf = new byte[10240];
212: int amountRead = 0;
213: is = src.createInputStream();
214: while ((amountRead = is.read(buf, 0, 10240)) > -1) {
215: os.write(buf, 0, amountRead);
216: }
217: addPage(importedPage);
218: } finally {
219: if (is != null) {
220: is.close();
221: }
222: if (os != null) {
223: os.close();
224: }
225: }
226: return importedPage;
227:
228: }
229:
230: /**
231: * Constructor that uses an existing document. The COSDocument that
232: * is passed in must be valid.
233: *
234: * @param doc The COSDocument that this document wraps.
235: */
236: public PDDocument(COSDocument doc) {
237: document = doc;
238: }
239:
240: /**
241: * This will get the low level document.
242: *
243: * @return The document that this layer sits on top of.
244: */
245: public COSDocument getDocument() {
246: return document;
247: }
248:
249: /**
250: * This will get the document info dictionary. This is guaranteed to not return null.
251: *
252: * @return The documents /Info dictionary
253: */
254: public PDDocumentInformation getDocumentInformation() {
255: if (documentInformation == null) {
256: COSDictionary trailer = document.getTrailer();
257: COSDictionary infoDic = (COSDictionary) trailer
258: .getDictionaryObject(COSName.INFO);
259: if (infoDic == null) {
260: infoDic = new COSDictionary();
261: trailer.setItem(COSName.INFO, infoDic);
262: }
263: documentInformation = new PDDocumentInformation(infoDic);
264: }
265: return documentInformation;
266: }
267:
268: /**
269: * This will set the document information for this document.
270: *
271: * @param info The updated document information.
272: */
273: public void setDocumentInformation(PDDocumentInformation info) {
274: documentInformation = info;
275: document.getTrailer().setItem(COSName.INFO,
276: info.getDictionary());
277: }
278:
279: /**
280: * This will get the document CATALOG. This is guaranteed to not return null.
281: *
282: * @return The documents /Root dictionary
283: */
284: public PDDocumentCatalog getDocumentCatalog() {
285: if (documentCatalog == null) {
286: COSDictionary trailer = document.getTrailer();
287: COSDictionary infoDic = (COSDictionary) trailer
288: .getDictionaryObject(COSName.ROOT);
289: if (infoDic == null) {
290: documentCatalog = new PDDocumentCatalog(this );
291: } else {
292: documentCatalog = new PDDocumentCatalog(this , infoDic);
293: }
294:
295: }
296: return documentCatalog;
297: }
298:
299: /**
300: * This will tell if this document is encrypted or not.
301: *
302: * @return true If this document is encrypted.
303: */
304: public boolean isEncrypted() {
305: return document.isEncrypted();
306: }
307:
308: /**
309: * This will get the encryption dictionary for this document. This will still
310: * return the parameters if the document was decrypted. If the document was
311: * never encrypted then this will return null. As the encryption architecture
312: * in PDF documents is plugable this returns an abstract class, but the only
313: * supported subclass at this time is a PDStandardEncryption object.
314: *
315: * @return The encryption dictionary(most likely a PDStandardEncryption object)
316: *
317: * @throws IOException If there is an error determining which security handler to use.
318: */
319: public PDEncryptionDictionary getEncryptionDictionary()
320: throws IOException {
321: if (encParameters == null) {
322: if (isEncrypted()) {
323: encParameters = new PDEncryptionDictionary(document
324: .getEncryptionDictionary());
325: } else {
326: encParameters = new PDEncryptionDictionary();
327: }
328: }
329: return encParameters;
330: }
331:
332: /**
333: * This will set the encryption dictionary for this document.
334: *
335: * @param encDictionary The encryption dictionary(most likely a PDStandardEncryption object)
336: *
337: * @throws IOException If there is an error determining which security handler to use.
338: */
339: public void setEncryptionDictionary(
340: PDEncryptionDictionary encDictionary) throws IOException {
341: encParameters = encDictionary;
342: }
343:
344: /**
345: * This will determine if this is the user password. This only applies when
346: * the document is encrypted and uses standard encryption.
347: *
348: * @param password The plain text user password.
349: *
350: * @return true If the password passed in matches the user password used to encrypt the document.
351: *
352: * @throws IOException If there is an error determining if it is the user password.
353: * @throws CryptographyException If there is an error in the encryption algorithms.
354: *
355: * @deprecated
356: */
357: public boolean isUserPassword(String password) throws IOException,
358: CryptographyException {
359: return false;
360: /*boolean retval = false;
361: if( password == null )
362: {
363: password = "";
364: }
365: PDFEncryption encryptor = new PDFEncryption();
366: PDEncryptionDictionary encryptionDictionary = getEncryptionDictionary();
367: if( encryptionDictionary == null )
368: {
369: throw new IOException( "Error: Document is not encrypted" );
370: }
371: else
372: {
373: if( encryptionDictionary instanceof PDStandardEncryption )
374: {
375: COSString documentID = (COSString)document.getDocumentID().get(0);
376: PDStandardEncryption standard = (PDStandardEncryption)encryptionDictionary;
377: retval = encryptor.isUserPassword(
378: password.getBytes(),
379: standard.getUserKey(),
380: standard.getOwnerKey(),
381: standard.getPermissions(),
382: documentID.getBytes(),
383: standard.getRevision(),
384: standard.getLength()/8 );
385: }
386: else
387: {
388: throw new IOException( "Error: Encyption dictionary is not 'Standard'" +
389: encryptionDictionary.getClass().getName() );
390: }
391: }
392: return retval;*/
393: }
394:
395: /**
396: * This will determine if this is the owner password. This only applies when
397: * the document is encrypted and uses standard encryption.
398: *
399: * @param password The plain text owner password.
400: *
401: * @return true If the password passed in matches the owner password used to encrypt the document.
402: *
403: * @throws IOException If there is an error determining if it is the user password.
404: * @throws CryptographyException If there is an error in the encryption algorithms.
405: *
406: * @deprecated
407: */
408: public boolean isOwnerPassword(String password) throws IOException,
409: CryptographyException {
410: return false;
411: /*boolean retval = false;
412: if( password == null )
413: {
414: password = "";
415: }
416: PDFEncryption encryptor = new PDFEncryption();
417: PDEncryptionDictionary encryptionDictionary = getEncryptionDictionary();
418: if( encryptionDictionary == null )
419: {
420: throw new IOException( "Error: Document is not encrypted" );
421: }
422: else
423: {
424: if( encryptionDictionary instanceof PDStandardEncryption )
425: {
426: COSString documentID = (COSString)document.getDocumentID().get( 0 );
427: PDStandardEncryption standard = (PDStandardEncryption)encryptionDictionary;
428: retval = encryptor.isOwnerPassword(
429: password.getBytes(),
430: standard.getUserKey(),
431: standard.getOwnerKey(),
432: standard.getPermissions(),
433: documentID.getBytes(),
434: standard.getRevision(),
435: standard.getLength()/8 );
436: }
437: else
438: {
439: throw new IOException( "Error: Encyption dictionary is not 'Standard'" +
440: encryptionDictionary.getClass().getName() );
441: }
442: }
443: return retval;*/
444: }
445:
446: /**
447: * This will decrypt a document. This method is provided for compatibility reasons only. User should use
448: * the new security layer instead and the openProtection method especially.
449: *
450: * @param password Either the user or owner password.
451: *
452: * @throws CryptographyException If there is an error decrypting the document.
453: * @throws IOException If there is an error getting the stream data.
454: * @throws InvalidPasswordException If the password is not a user or owner password.
455: *
456: */
457: public void decrypt(String password) throws CryptographyException,
458: IOException, InvalidPasswordException {
459: try {
460: StandardDecryptionMaterial m = new StandardDecryptionMaterial(
461: password);
462: this .openProtection(m);
463: document.dereferenceObjectStreams();
464: } catch (BadSecurityHandlerException e) {
465: throw new CryptographyException(e);
466: }
467: }
468:
469: /**
470: * This will tell if the document was decrypted with the master password. This
471: * entry is invalid if the PDF was not decrypted.
472: *
473: * @return true if the pdf was decrypted with the master password.
474: *
475: * @deprecated use <code>getCurrentAccessPermission</code> instead
476: */
477: public boolean wasDecryptedWithOwnerPassword() {
478: return false;
479: }
480:
481: /**
482: * This will <b>mark</b> a document to be encrypted. The actual encryption
483: * will occur when the document is saved.
484: * This method is provided for compatibility reasons only. User should use
485: * the new security layer instead and the openProtection method especially.
486: *
487: * @param ownerPassword The owner password to encrypt the document.
488: * @param userPassword The user password to encrypt the document.
489: *
490: * @throws CryptographyException If an error occurs during encryption.
491: * @throws IOException If there is an error accessing the data.
492: *
493: */
494: public void encrypt(String ownerPassword, String userPassword)
495: throws CryptographyException, IOException {
496: try {
497: StandardProtectionPolicy policy = new StandardProtectionPolicy(
498: ownerPassword, userPassword, new AccessPermission());
499: this .protect(policy);
500: } catch (BadSecurityHandlerException e) {
501: throw new CryptographyException(e);
502: }
503: }
504:
505: /**
506: * The owner password that was passed into the encrypt method. You should
507: * never use this method. This will not longer be valid once encryption
508: * has occured.
509: *
510: * @return The owner password passed to the encrypt method.
511: *
512: * @deprecated Do not rely on this method anymore.
513: */
514: public String getOwnerPasswordForEncryption() {
515: return null;
516: }
517:
518: /**
519: * The user password that was passed into the encrypt method. You should
520: * never use this method. This will not longer be valid once encryption
521: * has occured.
522: *
523: * @return The user password passed to the encrypt method.
524: *
525: * @deprecated Do not rely on this method anymore.
526: */
527: public String getUserPasswordForEncryption() {
528: return null;
529: }
530:
531: /**
532: * Internal method do determine if the document will be encrypted when it is saved.
533: *
534: * @return True if encrypt has been called and the document
535: * has not been saved yet.
536: *
537: * @deprecated Do not rely on this method anymore. It is the responsibility of
538: * COSWriter to hold this state
539: */
540: public boolean willEncryptWhenSaving() {
541: return false;
542: }
543:
544: /**
545: * This shoule only be called by the COSWriter after encryption has completed.
546: *
547: * @deprecated Do not rely on this method anymore. It is the responsability of
548: * COSWriter to hold this state.
549: */
550: public void clearWillEncryptWhenSaving() {
551: //method is deprecated.
552: }
553:
554: /**
555: * This will load a document from a url.
556: *
557: * @param url The url to load the PDF from.
558: *
559: * @return The document that was loaded.
560: *
561: * @throws IOException If there is an error reading from the stream.
562: */
563: public static PDDocument load(URL url) throws IOException {
564: return load(url.openStream());
565: }
566:
567: /**
568: * This will load a document from a url.
569: *
570: * @param url The url to load the PDF from.
571: * @param scratchFile A location to store temp PDFBox data for this document.
572: *
573: * @return The document that was loaded.
574: *
575: * @throws IOException If there is an error reading from the stream.
576: */
577: public static PDDocument load(URL url, RandomAccess scratchFile)
578: throws IOException {
579: return load(url.openStream(), scratchFile);
580: }
581:
582: /**
583: * This will load a document from a file.
584: *
585: * @param filename The name of the file to load.
586: *
587: * @return The document that was loaded.
588: *
589: * @throws IOException If there is an error reading from the stream.
590: */
591: public static PDDocument load(String filename) throws IOException {
592: return load(new FileInputStream(filename));
593: }
594:
595: /**
596: * This will load a document from a file.
597: *
598: * @param filename The name of the file to load.
599: * @param scratchFile A location to store temp PDFBox data for this document.
600: *
601: * @return The document that was loaded.
602: *
603: * @throws IOException If there is an error reading from the stream.
604: */
605: public static PDDocument load(String filename,
606: RandomAccess scratchFile) throws IOException {
607: return load(new FileInputStream(filename), scratchFile);
608: }
609:
610: /**
611: * This will load a document from a file.
612: *
613: * @param file The name of the file to load.
614: *
615: * @return The document that was loaded.
616: *
617: * @throws IOException If there is an error reading from the stream.
618: */
619: public static PDDocument load(File file) throws IOException {
620: return load(new FileInputStream(file));
621: }
622:
623: /**
624: * This will load a document from a file.
625: *
626: * @param file The name of the file to load.
627: * @param scratchFile A location to store temp PDFBox data for this document.
628: *
629: * @return The document that was loaded.
630: *
631: * @throws IOException If there is an error reading from the stream.
632: */
633: public static PDDocument load(File file, RandomAccess scratchFile)
634: throws IOException {
635: return load(new FileInputStream(file));
636: }
637:
638: /**
639: * This will load a document from an input stream.
640: *
641: * @param input The stream that contains the document.
642: *
643: * @return The document that was loaded.
644: *
645: * @throws IOException If there is an error reading from the stream.
646: */
647: public static PDDocument load(InputStream input) throws IOException {
648: return load(input, null);
649: }
650:
651: /**
652: * This will load a document from an input stream.
653: *
654: * @param input The stream that contains the document.
655: * @param scratchFile A location to store temp PDFBox data for this document.
656: *
657: * @return The document that was loaded.
658: *
659: * @throws IOException If there is an error reading from the stream.
660: */
661: public static PDDocument load(InputStream input,
662: RandomAccess scratchFile) throws IOException {
663: PDFParser parser = new PDFParser(
664: new BufferedInputStream(input), scratchFile);
665: parser.parse();
666: return parser.getPDDocument();
667: }
668:
669: /**
670: * This will save this document to the filesystem.
671: *
672: * @param fileName The file to save as.
673: *
674: * @throws IOException If there is an error saving the document.
675: * @throws COSVisitorException If an error occurs while generating the data.
676: */
677: public void save(String fileName) throws IOException,
678: COSVisitorException {
679: save(new FileOutputStream(fileName));
680: }
681:
682: /**
683: * This will save the document to an output stream.
684: *
685: * @param output The stream to write to.
686: *
687: * @throws IOException If there is an error writing the document.
688: * @throws COSVisitorException If an error occurs while generating the data.
689: */
690: public void save(OutputStream output) throws IOException,
691: COSVisitorException {
692: //update the count in case any pages have been added behind the scenes.
693: getDocumentCatalog().getPages().updateCount();
694: COSWriter writer = null;
695: try {
696: writer = new COSWriter(output);
697: writer.write(this );
698: writer.close();
699: } finally {
700: if (writer != null) {
701: writer.close();
702: }
703: }
704: }
705:
706: /**
707: * This will return the total page count of the PDF document. Note: This method
708: * is deprecated in favor of the getNumberOfPages method. The getNumberOfPages is
709: * a required interface method of the Pageable interface. This method will
710: * be removed in a future version of PDFBox!!
711: *
712: * @return The total number of pages in the PDF document.
713: * @deprecated Use the getNumberOfPages method instead!
714: */
715: public int getPageCount() {
716: return getNumberOfPages();
717: }
718:
719: /**
720: * {@inheritDoc}
721: */
722: public int getNumberOfPages() {
723: PDDocumentCatalog cat = getDocumentCatalog();
724: return (int) cat.getPages().getCount();
725: }
726:
727: /**
728: * {@inheritDoc}
729: */
730: public PageFormat getPageFormat(int pageIndex) {
731: PDPage page = (PDPage) getDocumentCatalog().getAllPages().get(
732: pageIndex);
733: PDRectangle mediaBox = page.findMediaBox();
734: PageFormat format = new PageFormat();
735: Paper paper = new Paper();
736: //hmm the imageable area might need to be the CropBox instead
737: //of the media box???
738: double width = mediaBox.getWidth();
739: double height = mediaBox.getHeight();
740: if (width > height) {
741: format.setOrientation(PageFormat.LANDSCAPE);
742: width = mediaBox.getHeight();
743: height = mediaBox.getWidth();
744: }
745: paper.setImageableArea(0, 0, width, height);
746: paper.setSize(width, height);
747: format.setPaper(paper);
748: return format;
749: }
750:
751: /**
752: * {@inheritDoc}
753: */
754: public Printable getPrintable(int pageIndex) {
755: return (Printable) getDocumentCatalog().getAllPages().get(
756: pageIndex);
757: }
758:
759: /**
760: * This will send the PDF document to a printer. The printing functionality
761: * depends on the org.pdfbox.pdfviewer.PageDrawer functionality. The PageDrawer
762: * is a work in progress and some PDFs will print correctly and some will
763: * not. This is a convenience method to create the java.awt.print.PrinterJob.
764: * The PDDocument implements the java.awt.print.Pageable interface and
765: * PDPage implementes the java.awt.print.Printable interface, so advanced printing
766: * capabilities can be done by using those interfaces instead of this method.
767: *
768: * @throws PrinterException If there is an error while sending the PDF to
769: * the printer, or you do not have permissions to print this document.
770: */
771: public void print() throws PrinterException {
772:
773: AccessPermission currentPermissions = this
774: .getCurrentAccessPermission();
775:
776: if (!currentPermissions.canPrint()) {
777: throw new PrinterException(
778: "You do not have permission to print this document.");
779: }
780: PrinterJob printJob = PrinterJob.getPrinterJob();
781: printJob.setPageable(this );
782: if (printJob.printDialog()) {
783: printJob.print();
784: }
785: }
786:
787: /**
788: * This will send the PDF to the default printer without prompting the user
789: * for any printer settings.
790: *
791: * @see PDDocument#print()
792: *
793: * @throws PrinterException If there is an error while printing.
794: */
795: public void silentPrint() throws PrinterException {
796:
797: AccessPermission currentPermissions = this
798: .getCurrentAccessPermission();
799:
800: if (!currentPermissions.canPrint()) {
801: throw new PrinterException(
802: "You do not have permission to print this document.");
803: }
804: PrinterJob printJob = PrinterJob.getPrinterJob();
805: printJob.setPageable(this );
806: printJob.print();
807: }
808:
809: /**
810: * This will close the underlying COSDocument object.
811: *
812: * @throws IOException If there is an error releasing resources.
813: */
814: public void close() throws IOException {
815: document.close();
816: }
817:
818: /**
819: * Protects the document with the protection policy pp. The document content will be really encrypted
820: * when it will be saved. This method only marks the document for encryption.
821: *
822: * @see org.pdfbox.pdmodel.encryption.StandardProtectionPolicy
823: * @see org.pdfbox.pdmodel.encryption.PublicKeyProtectionPolicy
824: *
825: * @param pp The protection policy.
826: *
827: * @throws BadSecurityHandlerException If there is an error during protection.
828: */
829: public void protect(ProtectionPolicy pp)
830: throws BadSecurityHandlerException {
831: SecurityHandler handler = SecurityHandlersManager.getInstance()
832: .getSecurityHandler(pp);
833: securityHandler = handler;
834: }
835:
836: /**
837: * Tries to decrypt the document in memory using the provided decryption material.
838: *
839: * @see org.pdfbox.pdmodel.encryption.StandardDecryptionMaterial
840: * @see org.pdfbox.pdmodel.encryption.PublicKeyDecryptionMaterial
841: *
842: * @param pm The decryption material (password or certificate).
843: *
844: * @throws BadSecurityHandlerException If there is an error during decryption.
845: * @throws IOException If there is an error reading cryptographic information.
846: * @throws CryptographyException If there is an error during decryption.
847: */
848: public void openProtection(DecryptionMaterial pm)
849: throws BadSecurityHandlerException, IOException,
850: CryptographyException {
851: PDEncryptionDictionary dict = this .getEncryptionDictionary();
852: if (dict.getFilter() != null) {
853: SecurityHandler handler = SecurityHandlersManager
854: .getInstance().getSecurityHandler(dict.getFilter());
855: securityHandler = handler;
856: handler.decryptDocument(this , pm);
857: document.dereferenceObjectStreams();
858: } else {
859: throw new RuntimeException(
860: "This document does not need to be decrypted");
861: }
862: }
863:
864: /**
865: * Returns the access permissions granted when the document was decrypted.
866: * If the document was not decrypted this method returns the access permission
867: * for a document owner (ie can do everything).
868: * The returned object is in read only mode so that permissions cannot be changed.
869: * Methods providing access to content should rely on this object to verify if the current
870: * user is allowed to proceed.
871: *
872: * @return the access permissions for the current user on the document.
873: */
874:
875: public AccessPermission getCurrentAccessPermission() {
876: if (this .securityHandler == null) {
877: return AccessPermission.getOwnerAccessPermission();
878: }
879: return securityHandler.getCurrentAccessPermission();
880: }
881:
882: /**
883: * Get the security handler that is used for document encryption.
884: *
885: * @return The handler used to encrypt/decrypt the document.
886: */
887: public SecurityHandler getSecurityHandler() {
888: return securityHandler;
889: }
890: }
|