1 50 51 package com.lowagie.text.pdf; 52 53 import java.io.ByteArrayInputStream ; 54 import java.io.ByteArrayOutputStream ; 55 import java.io.DataInputStream ; 56 import java.io.IOException ; 57 import java.io.InputStream ; 58 import java.net.URL ; 59 import java.util.ArrayList ; 60 import java.util.Arrays ; 61 import java.util.Collections ; 62 import java.util.HashMap ; 63 import java.util.Iterator ; 64 import java.util.List ; 65 import java.util.Map ; 66 import java.util.Set ; 67 import java.util.zip.InflaterInputStream ; 68 import java.util.Stack ; 69 import java.security.Key ; 70 import java.security.MessageDigest ; 71 import java.security.cert.Certificate ; 72 73 import com.lowagie.text.ExceptionConverter; 74 import com.lowagie.text.PageSize; 75 import com.lowagie.text.Rectangle; 76 import com.lowagie.text.pdf.interfaces.PdfViewerPreferences; 77 import com.lowagie.text.pdf.internal.PdfViewerPreferencesImp; 78 79 import org.bouncycastle.cms.CMSEnvelopedData; 80 import org.bouncycastle.cms.RecipientInformation; 81 82 86 public class PdfReader implements PdfViewerPreferences { 87 88 static final PdfName pageInhCandidates[] = { 89 PdfName.MEDIABOX, PdfName.ROTATE, PdfName.RESOURCES, PdfName.CROPBOX 90 }; 91 92 static final byte endstream[] = PdfEncodings.convertToBytes("endstream", null); 93 static final byte endobj[] = PdfEncodings.convertToBytes("endobj", null); 94 protected PRTokeniser tokens; 95 protected int xref[]; 100 protected HashMap objStmMark; 101 protected IntHashtable objStmToOffset; 102 protected boolean newXrefType; 103 private ArrayList xrefObj; 104 PdfDictionary rootPages; 105 protected PdfDictionary trailer; 106 protected PdfDictionary catalog; 107 protected PageRefs pageRefs; 108 protected PRAcroForm acroForm = null; 109 protected boolean acroFormParsed = false; 110 protected boolean encrypted = false; 111 protected boolean rebuilt = false; 112 protected int freeXref; 113 protected boolean tampered = false; 114 protected int lastXref; 115 protected int eofPos; 116 protected char pdfVersion; 117 protected PdfEncryption decrypt; 118 protected byte password[] = null; protected Key certificateKey = null; protected Certificate certificate = null; protected String certificateKeyProvider = null; private boolean ownerPasswordUsed; 123 protected ArrayList strings = new ArrayList (); 124 protected boolean sharedStreams = true; 125 protected boolean consolidateNamedDestinations = false; 126 protected int rValue; 127 protected int pValue; 128 private int objNum; 129 private int objGen; 130 private int fileLength; 131 private boolean hybridXref; 132 private int lastXrefPartial = -1; 133 private boolean partial; 134 private PRIndirectReference cryptoRef; 135 private PdfViewerPreferencesImp viewerPreferences = new PdfViewerPreferencesImp(); 136 137 140 private boolean appendable; 141 142 protected PdfReader() { 143 } 144 145 149 public PdfReader(String filename) throws IOException { 150 this(filename, null); 151 } 152 153 158 public PdfReader(String filename, byte ownerPassword[]) throws IOException { 159 password = ownerPassword; 160 tokens = new PRTokeniser(filename); 161 readPdf(); 162 } 163 164 168 public PdfReader(byte pdfIn[]) throws IOException { 169 this(pdfIn, null); 170 } 171 172 177 public PdfReader(byte pdfIn[], byte ownerPassword[]) throws IOException { 178 password = ownerPassword; 179 tokens = new PRTokeniser(pdfIn); 180 readPdf(); 181 } 182 183 190 public PdfReader(String filename, Certificate certificate, Key certificateKey, String certificateKeyProvider) throws IOException { 191 this.certificate = certificate; 192 this.certificateKey = certificateKey; 193 this.certificateKeyProvider = certificateKeyProvider; 194 tokens = new PRTokeniser(filename); 195 readPdf(); 196 } 197 198 202 public PdfReader(URL url) throws IOException { 203 this(url, null); 204 } 205 206 211 public PdfReader(URL url, byte ownerPassword[]) throws IOException { 212 password = ownerPassword; 213 tokens = new PRTokeniser(new RandomAccessFileOrArray(url)); 214 readPdf(); 215 } 216 217 224 public PdfReader(InputStream is, byte ownerPassword[]) throws IOException { 225 password = ownerPassword; 226 tokens = new PRTokeniser(new RandomAccessFileOrArray(is)); 227 readPdf(); 228 } 229 230 236 public PdfReader(InputStream is) throws IOException { 237 this(is, null); 238 } 239 240 249 public PdfReader(RandomAccessFileOrArray raf, byte ownerPassword[]) throws IOException { 250 password = ownerPassword; 251 partial = true; 252 tokens = new PRTokeniser(raf); 253 readPdfPartial(); 254 } 255 256 259 public PdfReader(PdfReader reader) { 260 this.appendable = reader.appendable; 261 this.consolidateNamedDestinations = reader.consolidateNamedDestinations; 262 this.encrypted = reader.encrypted; 263 this.rebuilt = reader.rebuilt; 264 this.sharedStreams = reader.sharedStreams; 265 this.tampered = reader.tampered; 266 this.password = reader.password; 267 this.pdfVersion = reader.pdfVersion; 268 this.eofPos = reader.eofPos; 269 this.freeXref = reader.freeXref; 270 this.lastXref = reader.lastXref; 271 this.tokens = new PRTokeniser(reader.tokens.getSafeFile()); 272 if (reader.decrypt != null) 273 this.decrypt = new PdfEncryption(reader.decrypt); 274 this.pValue = reader.pValue; 275 this.rValue = reader.rValue; 276 this.xrefObj = new ArrayList (reader.xrefObj); 277 for (int k = 0; k < reader.xrefObj.size(); ++k) { 278 this.xrefObj.set(k, duplicatePdfObject((PdfObject)reader.xrefObj.get(k), this)); 279 } 280 this.pageRefs = new PageRefs(reader.pageRefs, this); 281 this.trailer = (PdfDictionary)duplicatePdfObject(reader.trailer, this); 282 this.catalog = (PdfDictionary)getPdfObject(trailer.get(PdfName.ROOT)); 283 this.rootPages = (PdfDictionary)getPdfObject(catalog.get(PdfName.PAGES)); 284 this.fileLength = reader.fileLength; 285 this.partial = reader.partial; 286 this.hybridXref = reader.hybridXref; 287 this.objStmToOffset = reader.objStmToOffset; 288 this.xref = reader.xref; 289 this.cryptoRef = (PRIndirectReference)duplicatePdfObject(reader.cryptoRef, this); 290 this.ownerPasswordUsed = reader.ownerPasswordUsed; 291 } 292 293 297 public RandomAccessFileOrArray getSafeFile() { 298 return tokens.getSafeFile(); 299 } 300 301 protected PdfReaderInstance getPdfReaderInstance(PdfWriter writer) { 302 return new PdfReaderInstance(this, writer); 303 } 304 305 308 public int getNumberOfPages() { 309 return pageRefs.size(); 310 } 311 312 316 public PdfDictionary getCatalog() { 317 return catalog; 318 } 319 320 323 public PRAcroForm getAcroForm() { 324 if (!acroFormParsed) { 325 acroFormParsed = true; 326 PdfObject form = catalog.get(PdfName.ACROFORM); 327 if (form != null) { 328 try { 329 acroForm = new PRAcroForm(this); 330 acroForm.readAcroForm((PdfDictionary)getPdfObject(form)); 331 } 332 catch (Exception e) { 333 acroForm = null; 334 } 335 } 336 } 337 return acroForm; 338 } 339 344 public int getPageRotation(int index) { 345 return getPageRotation(pageRefs.getPageNRelease(index)); 346 } 347 348 int getPageRotation(PdfDictionary page) { 349 PdfNumber rotate = (PdfNumber)getPdfObject(page.get(PdfName.ROTATE)); 350 if (rotate == null) 351 return 0; 352 else { 353 int n = rotate.intValue(); 354 n %= 360; 355 return n < 0 ? n + 360 : n; 356 } 357 } 358 363 public Rectangle getPageSizeWithRotation(int index) { 364 return getPageSizeWithRotation(pageRefs.getPageNRelease(index)); 365 } 366 367 372 public Rectangle getPageSizeWithRotation(PdfDictionary page) { 373 Rectangle rect = getPageSize(page); 374 int rotation = getPageRotation(page); 375 while (rotation > 0) { 376 rect = rect.rotate(); 377 rotation -= 90; 378 } 379 return rect; 380 } 381 382 387 public Rectangle getPageSize(int index) { 388 return getPageSize(pageRefs.getPageNRelease(index)); 389 } 390 391 396 public Rectangle getPageSize(PdfDictionary page) { 397 PdfArray mediaBox = (PdfArray)getPdfObject(page.get(PdfName.MEDIABOX)); 398 return getNormalizedRectangle(mediaBox); 399 } 400 401 409 public Rectangle getCropBox(int index) { 410 PdfDictionary page = pageRefs.getPageNRelease(index); 411 PdfArray cropBox = (PdfArray)getPdfObjectRelease(page.get(PdfName.CROPBOX)); 412 if (cropBox == null) 413 return getPageSize(page); 414 return getNormalizedRectangle(cropBox); 415 } 416 417 422 public Rectangle getBoxSize(int index, String boxName) { 423 PdfDictionary page = pageRefs.getPageNRelease(index); 424 PdfArray box = null; 425 if (boxName.equals("trim")) 426 box = (PdfArray)getPdfObjectRelease(page.get(PdfName.TRIMBOX)); 427 else if (boxName.equals("art")) 428 box = (PdfArray)getPdfObjectRelease(page.get(PdfName.ARTBOX)); 429 else if (boxName.equals("bleed")) 430 box = (PdfArray)getPdfObjectRelease(page.get(PdfName.BLEEDBOX)); 431 else if (boxName.equals("crop")) 432 box = (PdfArray)getPdfObjectRelease(page.get(PdfName.CROPBOX)); 433 else if (boxName.equals("media")) 434 box = (PdfArray)getPdfObjectRelease(page.get(PdfName.MEDIABOX)); 435 if (box == null) 436 return null; 437 return getNormalizedRectangle(box); 438 } 439 440 444 public HashMap getInfo() { 445 HashMap map = new HashMap (); 446 PdfDictionary info = (PdfDictionary)getPdfObject(trailer.get(PdfName.INFO)); 447 if (info == null) 448 return map; 449 for (Iterator it = info.getKeys().iterator(); it.hasNext();) { 450 PdfName key = (PdfName)it.next(); 451 PdfObject obj = getPdfObject(info.get(key)); 452 if (obj == null) 453 continue; 454 String value = obj.toString(); 455 switch (obj.type()) { 456 case PdfObject.STRING: { 457 value = ((PdfString)obj).toUnicodeString(); 458 break; 459 } 460 case PdfObject.NAME: { 461 value = PdfName.decodeName(value); 462 break; 463 } 464 } 465 map.put(PdfName.decodeName(key.toString()), value); 466 } 467 return map; 468 } 469 470 474 public static Rectangle getNormalizedRectangle(PdfArray box) { 475 ArrayList rect = box.getArrayList(); 476 float llx = ((PdfNumber)rect.get(0)).floatValue(); 477 float lly = ((PdfNumber)rect.get(1)).floatValue(); 478 float urx = ((PdfNumber)rect.get(2)).floatValue(); 479 float ury = ((PdfNumber)rect.get(3)).floatValue(); 480 return new Rectangle(Math.min(llx, urx), Math.min(lly, ury), 481 Math.max(llx, urx), Math.max(lly, ury)); 482 } 483 484 protected void readPdf() throws IOException { 485 try { 486 fileLength = tokens.getFile().length(); 487 pdfVersion = tokens.checkPdfHeader(); 488 try { 489 readXref(); 490 } 491 catch (Exception e) { 492 try { 493 rebuilt = true; 494 rebuildXref(); 495 lastXref = -1; 496 } 497 catch (Exception ne) { 498 throw new IOException ("Rebuild failed: " + ne.getMessage() + "; Original message: " + e.getMessage()); 499 } 500 } 501 try { 502 readDocObj(); 503 } 504 catch (Exception ne) { 505 if (rebuilt) 506 throw new IOException (ne.getMessage()); 507 rebuilt = true; 508 encrypted = false; 509 rebuildXref(); 510 lastXref = -1; 511 readDocObj(); 512 } 513 514 strings.clear(); 515 readPages(); 516 eliminateSharedStreams(); 517 removeUnusedObjects(); 518 } 519 finally { 520 try { 521 tokens.close(); 522 } 523 catch (Exception e) { 524 } 526 } 527 } 528 529 protected void readPdfPartial() throws IOException { 530 try { 531 fileLength = tokens.getFile().length(); 532 pdfVersion = tokens.checkPdfHeader(); 533 try { 534 readXref(); 535 } 536 catch (Exception e) { 537 try { 538 rebuilt = true; 539 rebuildXref(); 540 lastXref = -1; 541 } 542 catch (Exception ne) { 543 throw new IOException ("Rebuild failed: " + ne.getMessage() + "; Original message: " + e.getMessage()); 544 } 545 } 546 readDocObjPartial(); 547 readPages(); 548 } 549 catch (IOException e) { 550 try{tokens.close();}catch(Exception ee){} 551 throw e; 552 } 553 } 554 555 private boolean equalsArray(byte ar1[], byte ar2[], int size) { 556 for (int k = 0; k < size; ++k) { 557 if (ar1[k] != ar2[k]) 558 return false; 559 } 560 return true; 561 } 562 563 566 private void readDecryptedDocObj() throws IOException { 567 if (encrypted) 568 return; 569 PdfObject encDic = trailer.get(PdfName.ENCRYPT); 570 if (encDic == null || encDic.toString().equals("null")) 571 return; 572 byte[] encryptionKey = null; 573 encrypted = true; 574 PdfDictionary enc = (PdfDictionary)getPdfObject(encDic); 575 576 String s; 577 PdfObject o; 578 579 PdfArray documentIDs = (PdfArray)getPdfObject(trailer.get(PdfName.ID)); 580 byte documentID[] = null; 581 if (documentIDs != null) { 582 o = (PdfObject)documentIDs.getArrayList().get(0); 583 strings.remove(o); 584 s = o.toString(); 585 documentID = com.lowagie.text.DocWriter.getISOBytes(s); 586 if (documentIDs.size() > 1) 587 strings.remove(documentIDs.getArrayList().get(1)); 588 } 589 590 byte uValue[] = null; 591 byte oValue[] = null; 592 int cryptoMode = PdfWriter.STANDARD_ENCRYPTION_40; 593 int lengthValue = 0; 594 595 PdfObject filter = getPdfObjectRelease(enc.get(PdfName.FILTER)); 596 597 if (filter.equals(PdfName.STANDARD)) 598 { 599 s = enc.get(PdfName.U).toString(); 600 strings.remove(enc.get(PdfName.U)); 601 uValue = com.lowagie.text.DocWriter.getISOBytes(s); 602 s = enc.get(PdfName.O).toString(); 603 strings.remove(enc.get(PdfName.O)); 604 oValue = com.lowagie.text.DocWriter.getISOBytes(s); 605 606 o = enc.get(PdfName.R); 607 if (!o.isNumber()) throw new IOException ("Illegal R value."); 608 rValue = ((PdfNumber)o).intValue(); 609 if (rValue != 2 && rValue != 3 && rValue != 4) 610 throw new IOException ("Unknown encryption type (" + rValue + ")"); 611 612 o = enc.get(PdfName.P); 613 if (!o.isNumber()) throw new IOException ("Illegal P value."); 614 pValue = ((PdfNumber)o).intValue(); 615 616 if ( rValue == 3 ){ 617 o = enc.get(PdfName.LENGTH); 618 if (!o.isNumber()) 619 throw new IOException ("Illegal Length value."); 620 lengthValue = ( (PdfNumber) o).intValue(); 621 if (lengthValue > 128 || lengthValue < 40 || lengthValue % 8 != 0) 622 throw new IOException ("Illegal Length value."); 623 cryptoMode = PdfWriter.STANDARD_ENCRYPTION_128; 624 } else if (rValue == 4) { 625 PdfDictionary dic = (PdfDictionary)enc.get(PdfName.CF); 626 if (dic == null) 627 throw new IOException ("/CF not found (encryption)"); 628 dic = (PdfDictionary)dic.get(PdfName.STDCF); 629 if (dic == null) 630 throw new IOException ("/StdCF not found (encryption)"); 631 if (PdfName.V2.equals(dic.get(PdfName.CFM))) 632 cryptoMode = PdfWriter.STANDARD_ENCRYPTION_128; 633 else if (PdfName.AESV2.equals(dic.get(PdfName.CFM))) 634 cryptoMode = PdfWriter.ENCRYPTION_AES_128; 635 else 636 throw new IOException ("No compatible encryption found"); 637 PdfObject em = enc.get(PdfName.ENCRYPTMETADATA); 638 if (em != null && em.toString().equals("false")) 639 cryptoMode |= PdfWriter.DO_NOT_ENCRYPT_METADATA; 640 } else { 641 cryptoMode = PdfWriter.STANDARD_ENCRYPTION_40; 642 } 643 } else if (filter.equals(PdfName.PUBSEC)) { 644 boolean foundRecipient = false; 645 byte[] envelopedData = null; 646 PdfArray recipients = null; 647 648 o = enc.get(PdfName.V); 649 if (!o.isNumber()) throw new IOException ("Illegal V value."); 650 int vValue = ((PdfNumber)o).intValue(); 651 if (vValue != 1 && vValue != 2 && vValue != 4) 652 throw new IOException ("Unknown encryption type V = " + rValue); 653 654 if ( vValue == 2 ){ 655 o = enc.get(PdfName.LENGTH); 656 if (!o.isNumber()) 657 throw new IOException ("Illegal Length value."); 658 lengthValue = ( (PdfNumber) o).intValue(); 659 if (lengthValue > 128 || lengthValue < 40 || lengthValue % 8 != 0) 660 throw new IOException ("Illegal Length value."); 661 cryptoMode = PdfWriter.STANDARD_ENCRYPTION_128; 662 recipients = (PdfArray)enc.get(PdfName.RECIPIENTS); 663 } else if (vValue == 4) { 664 PdfDictionary dic = (PdfDictionary)enc.get(PdfName.CF); 665 if (dic == null) 666 throw new IOException ("/CF not found (encryption)"); 667 dic = (PdfDictionary)dic.get(PdfName.DEFAULTCRYPTFILER); 668 if (dic == null) 669 throw new IOException ("/DefaultCryptFilter not found (encryption)"); 670 if (PdfName.V2.equals(dic.get(PdfName.CFM))) 671 { 672 cryptoMode = PdfWriter.STANDARD_ENCRYPTION_128; 673 lengthValue = 128; 674 } 675 else if (PdfName.AESV2.equals(dic.get(PdfName.CFM))) 676 { 677 cryptoMode = PdfWriter.ENCRYPTION_AES_128; 678 lengthValue = 128; 679 } 680 else 681 throw new IOException ("No compatible encryption found"); 682 PdfObject em = dic.get(PdfName.ENCRYPTMETADATA); 683 if (em != null && em.toString().equals("false")) 684 cryptoMode |= PdfWriter.DO_NOT_ENCRYPT_METADATA; 685 686 recipients = (PdfArray)dic.get(PdfName.RECIPIENTS); 687 } else { 688 cryptoMode = PdfWriter.STANDARD_ENCRYPTION_40; 689 lengthValue = 40; 690 recipients = (PdfArray)enc.get(PdfName.RECIPIENTS); 691 } 692 693 for (int i = 0; i<recipients.size(); i++) 694 { 695 PdfObject recipient = (PdfObject)recipients.getArrayList().get(i); 696 strings.remove(recipient); 697 698 CMSEnvelopedData data = null; 699 try { 700 data = new CMSEnvelopedData(recipient.getBytes()); 701 702 Iterator recipientCertificatesIt = 703 data.getRecipientInfos().getRecipients().iterator(); 704 705 while (recipientCertificatesIt.hasNext()) { 706 RecipientInformation recipientInfo = 707 (RecipientInformation)recipientCertificatesIt.next(); 708 709 if (recipientInfo.getRID().match(certificate) && !foundRecipient) { 710 711 envelopedData = recipientInfo.getContent(certificateKey, certificateKeyProvider); 712 foundRecipient = true; 713 } 714 } 715 } catch (Exception f) { 716 throw new ExceptionConverter(f); 717 } 718 } 719 720 if(!foundRecipient || envelopedData == null) 721 { 722 throw new IOException ("Bad certificate and key."); 723 } 724 725 MessageDigest md = null; 726 727 try { 728 md = MessageDigest.getInstance("SHA-1"); 729 md.update(envelopedData, 0, 20); 730 for (int i=0; i<recipients.size(); i++) 731 { 732 byte[] encodedRecipient = ((PdfObject)recipients.getArrayList().get(i)).getBytes(); 733 md.update(encodedRecipient); 734 } 735 if ((cryptoMode & PdfWriter.DO_NOT_ENCRYPT_METADATA) != 0) 736 md.update(new byte[]{(byte)255, (byte)255, (byte)255, (byte)255}); 737 encryptionKey = md.digest(); 738 739 } catch (Exception f) { 740 throw new ExceptionConverter(f); 741 } 742 } 743 744 745 decrypt = new PdfEncryption(); 746 decrypt.setCryptoMode(cryptoMode, lengthValue); 747 748 if (filter.equals(PdfName.STANDARD)) 749 { 750 decrypt.setupByOwnerPassword(documentID, password, uValue, oValue, pValue); 752 if (!equalsArray(uValue, decrypt.userKey, (rValue == 3 || rValue == 4) ? 16 : 32)) { 753 decrypt.setupByUserPassword(documentID, password, oValue, pValue); 755 if (!equalsArray(uValue, decrypt.userKey, (rValue == 3 || rValue == 4) ? 16 : 32)) { 756 throw new IOException ("Bad user password"); 757 } 758 } 759 else 760 ownerPasswordUsed = true; 761 } else if (filter.equals(PdfName.PUBSEC)) { 762 decrypt.setupByEncryptionKey(encryptionKey, lengthValue); 763 ownerPasswordUsed = true; 764 } 765 766 for (int k = 0; k < strings.size(); ++k) { 767 PdfString str = (PdfString)strings.get(k); 768 str.decrypt(this); 769 } 770 771 if (encDic.isIndirect()) { 772 cryptoRef = (PRIndirectReference)encDic; 773 xrefObj.set(cryptoRef.getNumber(), null); 774 } 775 } 776 777 781 public static PdfObject getPdfObjectRelease(PdfObject obj) { 782 PdfObject obj2 = getPdfObject(obj); 783 releaseLastXrefPartial(obj); 784 return obj2; 785 } 786 787 788 794 public static PdfObject getPdfObject(PdfObject obj) { 795 if (obj == null) 796 return null; 797 if (!obj.isIndirect()) 798 return obj; 799 try { 800 PRIndirectReference ref = (PRIndirectReference)obj; 801 int idx = ref.getNumber(); 802 boolean appendable = ref.getReader().appendable; 803 obj = ref.getReader().getPdfObject(idx); 804 if (obj == null) { 805 return null; 806 } 807 else { 808 if (appendable) { 809 switch (obj.type()) { 810 case PdfObject.NULL: 811 obj = new PdfNull(); 812 break; 813 case PdfObject.BOOLEAN: 814 obj = new PdfBoolean(((PdfBoolean)obj).booleanValue()); 815 break; 816 case PdfObject.NAME: 817 obj = new PdfName(obj.getBytes()); 818 break; 819 } 820 obj.setIndRef(ref); 821 } 822 return obj; 823 } 824 } 825 catch (Exception e) { 826 throw new ExceptionConverter(e); 827 } 828 } 829 830 838 public static PdfObject getPdfObjectRelease(PdfObject obj, PdfObject parent) { 839 PdfObject obj2 = getPdfObject(obj, parent); 840 releaseLastXrefPartial(obj); 841 return obj2; 842 } 843 844 849 public static PdfObject getPdfObject(PdfObject obj, PdfObject parent) { 850 if (obj == null) 851 return null; 852 if (!obj.isIndirect()) { 853 PRIndirectReference ref = null; 854 if (parent != null && (ref = parent.getIndRef()) != null && ref.getReader().isAppendable()) { 855 switch (obj.type()) { 856 case PdfObject.NULL: 857 obj = new PdfNull(); 858 break; 859 case PdfObject.BOOLEAN: 860 obj = new PdfBoolean(((PdfBoolean)obj).booleanValue()); 861 break; 862 case PdfObject.NAME: 863 obj = new PdfName(obj.getBytes()); 864 break; 865 } 866 obj.setIndRef(ref); 867 } 868 return obj; 869 } 870 return getPdfObject(obj); 871 } 872 873 877 public PdfObject getPdfObjectRelease(int idx) { 878 PdfObject obj = getPdfObject(idx); 879 releaseLastXrefPartial(); 880 return obj; 881 } 882 883 887 public PdfObject getPdfObject(int idx) { 888 try { 889 lastXrefPartial = -1; 890 if (idx < 0 || idx >= xrefObj.size()) 891 return null; 892 PdfObject obj = (PdfObject)xrefObj.get(idx); 893 if (!partial || obj != null) 894 return obj; 895 if (idx * 2 >= xref.length) 896 return null; 897 obj = readSingleObject(idx); 898 lastXrefPartial = -1; 899 if (obj != null) 900 lastXrefPartial = idx; 901 return obj; 902 } 903 catch (Exception e) { 904 throw new ExceptionConverter(e); 905 } 906 } 907 908 911 public void resetLastXrefPartial() { 912 lastXrefPartial = -1; 913 } 914 915 918 public void releaseLastXrefPartial() { 919 if (partial && lastXrefPartial != -1) { 920 xrefObj.set(lastXrefPartial, null); 921 lastXrefPartial = -1; 922 } 923 } 924 925 928 public static void releaseLastXrefPartial(PdfObject obj) { 929 if (obj == null) 930 return; 931 if (!obj.isIndirect()) 932 return; 933 PRIndirectReference ref = (PRIndirectReference)obj; 934 PdfReader reader = ref.getReader(); 935 if (reader.partial && reader.lastXrefPartial != -1 && reader.lastXrefPartial == ref.getNumber()) { 936 reader.xrefObj.set(reader.lastXrefPartial, null); 937 } 938 reader.lastXrefPartial = -1; 939 } 940 941 private void setXrefPartialObject(int idx, PdfObject obj) { 942 if (!partial || idx < 0) 943 return; 944 xrefObj.set(idx, obj); 945 } 946 947 951 public PRIndirectReference addPdfObject(PdfObject obj) { 952 xrefObj.add(obj); 953 return new PRIndirectReference(this, xrefObj.size() - 1); 954 } 955 956 protected void readPages() throws IOException { 957 catalog = (PdfDictionary)getPdfObject(trailer.get(PdfName.ROOT)); 958 rootPages = (PdfDictionary)getPdfObject(catalog.get(PdfName.PAGES)); 959 pageRefs = new PageRefs(this); 960 } 961 962 protected void readDocObjPartial() throws IOException { 963 xrefObj = new ArrayList (xref.length / 2); 964 xrefObj.addAll(Collections.nCopies(xref.length / 2, null)); 965 readDecryptedDocObj(); 966 if (objStmToOffset != null) { 967 int keys[] = objStmToOffset.getKeys(); 968 for (int k = 0; k < keys.length; ++k) { 969 int n = keys[k]; 970 objStmToOffset.put(n, xref[n * 2]); 971 xref[n * 2] = -1; 972 } 973 } 974 } 975 976 protected PdfObject readSingleObject(int k) throws IOException { 977 strings.clear(); 978 int k2 = k * 2; 979 int pos = xref[k2]; 980 if (pos < 0) 981 return null; 982 if (xref[k2 + 1] > 0) 983 pos = objStmToOffset.get(xref[k2 + 1]); 984 if (pos == 0) 985 return null; 986 tokens.seek(pos); 987 tokens.nextValidToken(); 988 if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) 989 tokens.throwError("Invalid object number."); 990 objNum = tokens.intValue(); 991 tokens.nextValidToken(); 992 if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) 993 tokens.throwError("Invalid generation number."); 994 objGen = tokens.intValue(); 995 tokens.nextValidToken(); 996 if (!tokens.getStringValue().equals("obj")) 997 tokens.throwError("Token 'obj' expected."); 998 PdfObject obj; 999 try { 1000 obj = readPRObject(); 1001 for (int j = 0; j < strings.size(); ++j) { 1002 PdfString str = (PdfString)strings.get(j); 1003 str.decrypt(this); 1004 } 1005 if (obj.isStream()) { 1006 checkPRStreamLength((PRStream)obj); 1007 } 1008 } 1009 catch (Exception e) { 1010 obj = null; 1011 } 1012 if (xref[k2 + 1] > 0) { 1013 obj = readOneObjStm((PRStream)obj, xref[k2]); 1014 } 1015 xrefObj.set(k, obj); 1016 return obj; 1017 } 1018 1019 protected PdfObject readOneObjStm(PRStream stream, int idx) throws IOException { 1020 int first = ((PdfNumber)getPdfObject(stream.get(PdfName.FIRST))).intValue(); 1021 byte b[] = getStreamBytes(stream, tokens.getFile()); 1022 PRTokeniser saveTokens = tokens; 1023 tokens = new PRTokeniser(b); 1024 try { 1025 int address = 0; 1026 boolean ok = true; 1027 ++idx; 1028 for (int k = 0; k < idx; ++k) { 1029 ok = tokens.nextToken(); 1030 if (!ok) 1031 break; 1032 if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) { 1033 ok = false; 1034 break; 1035 } 1036 ok = tokens.nextToken(); 1037 if (!ok) 1038 break; 1039 if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) { 1040 ok = false; 1041 break; 1042 } 1043 address = tokens.intValue() + first; 1044 } 1045 if (!ok) 1046 throw new IOException ("Error reading ObjStm"); 1047 tokens.seek(address); 1048 return readPRObject(); 1049 } 1050 finally { 1051 tokens = saveTokens; 1052 } 1053 } 1054 1055 1058 public double dumpPerc() { 1059 int total = 0; 1060 for (int k = 0; k < xrefObj.size(); ++k) { 1061 if (xrefObj.get(k) != null) 1062 ++total; 1063 } 1064 return (total * 100.0 / xrefObj.size()); 1065 } 1066 1067 protected void readDocObj() throws IOException { 1068 ArrayList streams = new ArrayList (); 1069 xrefObj = new ArrayList (xref.length / 2); 1070 xrefObj.addAll(Collections.nCopies(xref.length / 2, null)); 1071 for (int k = 2; k < xref.length; k += 2) { 1072 int pos = xref[k]; 1073 if (pos <= 0 || xref[k + 1] > 0) 1074 continue; 1075 tokens.seek(pos); 1076 tokens.nextValidToken(); 1077 if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) 1078 tokens.throwError("Invalid object number."); 1079 objNum = tokens.intValue(); 1080 tokens.nextValidToken(); 1081 if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) 1082 tokens.throwError("Invalid generation number."); 1083 objGen = tokens.intValue(); 1084 tokens.nextValidToken(); 1085 if (!tokens.getStringValue().equals("obj")) 1086 tokens.throwError("Token 'obj' expected."); 1087 PdfObject obj; 1088 try { 1089 obj = readPRObject(); 1090 if (obj.isStream()) { 1091 streams.add(obj); 1092 } 1093 } 1094 catch (Exception e) { 1095 obj = null; 1096 } 1097 xrefObj.set(k / 2, obj); 1098 } 1099 for (int k = 0; k < streams.size(); ++k) { 1100 checkPRStreamLength((PRStream)streams.get(k)); 1101 } 1102 readDecryptedDocObj(); 1103 if (objStmMark != null) { 1104 for (Iterator i = objStmMark.entrySet().iterator(); i.hasNext();) { 1105 Map.Entry entry = (Map.Entry )i.next(); 1106 int n = ((Integer )entry.getKey()).intValue(); 1107 IntHashtable h = (IntHashtable)entry.getValue(); 1108 readObjStm((PRStream)xrefObj.get(n), h); 1109 xrefObj.set(n, null); 1110 } 1111 objStmMark = null; 1112 } 1113 xref = null; 1114 } 1115 1116 private void checkPRStreamLength(PRStream stream) throws IOException { 1117 int fileLength = tokens.length(); 1118 int start = stream.getOffset(); 1119 boolean calc = false; 1120 int streamLength = 0; 1121 PdfObject obj = getPdfObjectRelease(stream.get(PdfName.LENGTH)); 1122 if (obj != null && obj.type() == PdfObject.NUMBER) { 1123 streamLength = ((PdfNumber)obj).intValue(); 1124 if (streamLength + start > fileLength - 20) 1125 calc = true; 1126 else { 1127 tokens.seek(start + streamLength); 1128 String line = tokens.readString(20); 1129 if (!line.startsWith("\nendstream") && 1130 !line.startsWith("\r\nendstream") && 1131 !line.startsWith("\rendstream") && 1132 !line.startsWith("endstream")) 1133 calc = true; 1134 } 1135 } 1136 else 1137 calc = true; 1138 if (calc) { 1139 byte tline[] = new byte[16]; 1140 tokens.seek(start); 1141 while (true) { 1142 int pos = tokens.getFilePointer(); 1143 if (!tokens.readLineSegment(tline)) 1144 break; 1145 if (equalsn(tline, endstream)) { 1146 streamLength = pos - start; 1147 break; 1148 } 1149 if (equalsn(tline, endobj)) { 1150 tokens.seek(pos - 16); 1151 String s = tokens.readString(16); 1152 int index = s.indexOf("endstream"); 1153 if (index >= 0) 1154 pos = pos - 16 + index; 1155 streamLength = pos - start; 1156 break; 1157 } 1158 } 1159 } 1160 stream.setLength(streamLength); 1161 } 1162 1163 protected void readObjStm(PRStream stream, IntHashtable map) throws IOException { 1164 int first = ((PdfNumber)getPdfObject(stream.get(PdfName.FIRST))).intValue(); 1165 int n = ((PdfNumber)getPdfObject(stream.get(PdfName.N))).intValue(); 1166 byte b[] = getStreamBytes(stream, tokens.getFile()); 1167 PRTokeniser saveTokens = tokens; 1168 tokens = new PRTokeniser(b); 1169 try { 1170 int address[] = new int[n]; 1171 int objNumber[] = new int[n]; 1172 boolean ok = true; 1173 for (int k = 0; k < n; ++k) { 1174 ok = tokens.nextToken(); 1175 if (!ok) 1176 break; 1177 if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) { 1178 ok = false; 1179 break; 1180 } 1181 objNumber[k] = tokens.intValue(); 1182 ok = tokens.nextToken(); 1183 if (!ok) 1184 break; 1185 if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) { 1186 ok = false; 1187 break; 1188 } 1189 address[k] = tokens.intValue() + first; 1190 } 1191 if (!ok) 1192 throw new IOException ("Error reading ObjStm"); 1193 for (int k = 0; k < n; ++k) { 1194 if (map.containsKey(k)) { 1195 tokens.seek(address[k]); 1196 PdfObject obj = readPRObject(); 1197 xrefObj.set(objNumber[k], obj); 1198 } 1199 } 1200 } 1201 finally { 1202 tokens = saveTokens; 1203 } 1204 } 1205 1206 1212 public static PdfObject killIndirect(PdfObject obj) { 1213 if (obj == null || obj.isNull()) 1214 return null; 1215 PdfObject ret = getPdfObjectRelease(obj); 1216 if (obj.isIndirect()) { 1217 PRIndirectReference ref = (PRIndirectReference)obj; 1218 PdfReader reader = ref.getReader(); 1219 int n = ref.getNumber(); 1220 reader.xrefObj.set(n, null); 1221 if (reader.partial) 1222 reader.xref[n * 2] = -1; 1223 } 1224 return ret; 1225 } 1226 1227 private void ensureXrefSize(int size) { 1228 if (size == 0) 1229 return; 1230 if (xref == null) 1231 xref = new int[size]; 1232 else { 1233 if (xref.length < size) { 1234 int xref2[] = new int[size]; 1235 System.arraycopy(xref, 0, xref2, 0, xref.length); 1236 xref = xref2; 1237 } 1238 } 1239 } 1240 1241 protected void readXref() throws IOException { 1242 hybridXref = false; 1243 newXrefType = false; 1244 tokens.seek(tokens.getStartxref()); 1245 tokens.nextToken(); 1246 if (!tokens.getStringValue().equals("startxref")) 1247 throw new IOException ("startxref not found."); 1248 tokens.nextToken(); 1249 if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) 1250 throw new IOException ("startxref is not followed by a number."); 1251 int startxref = tokens.intValue(); 1252 lastXref = startxref; 1253 eofPos = tokens.getFilePointer(); 1254 try { 1255 if (readXRefStream(startxref)) { 1256 newXrefType = true; 1257 return; 1258 } 1259 } 1260 catch (Exception e) {} 1261 xref = null; 1262 tokens.seek(startxref); 1263 trailer = readXrefSection(); 1264 PdfDictionary trailer2 = trailer; 1265 while (true) { 1266 PdfNumber prev = (PdfNumber)trailer2.get(PdfName.PREV); 1267 if (prev == null) 1268 break; 1269 tokens.seek(prev.intValue()); 1270 trailer2 = readXrefSection(); 1271 } 1272 } 1273 1274 protected PdfDictionary readXrefSection() throws IOException { 1275 tokens.nextValidToken(); 1276 if (!tokens.getStringValue().equals("xref")) 1277 tokens.throwError("xref subsection not found"); 1278 int start = 0; 1279 int end = 0; 1280 int pos = 0; 1281 int gen = 0; 1282 while (true) { 1283 tokens.nextValidToken(); 1284 if (tokens.getStringValue().equals("trailer")) 1285 break; 1286 if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) 1287 tokens.throwError("Object number of the first object in this xref subsection not found"); 1288 start = tokens.intValue(); 1289 tokens.nextValidToken(); 1290 if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) 1291 tokens.throwError("Number of entries in this xref subsection not found"); 1292 end = tokens.intValue() + start; 1293 if (start == 1) { int back = tokens.getFilePointer(); 1295 tokens.nextValidToken(); 1296 pos = tokens.intValue(); 1297 tokens.nextValidToken(); 1298 gen = tokens.intValue(); 1299 if (pos == 0 && gen == 65535) { 1300 --start; 1301 --end; 1302 } 1303 tokens.seek(back); 1304 } 1305 ensureXrefSize(end * 2); 1306 for (int k = start; k < end; ++k) { 1307 tokens.nextValidToken(); 1308 pos = tokens.intValue(); 1309 tokens.nextValidToken(); 1310 gen = tokens.intValue(); 1311 tokens.nextValidToken(); 1312 int p = k * 2; 1313 if (tokens.getStringValue().equals("n")) { 1314 if (xref[p] == 0 && xref[p + 1] == 0) { 1315 xref[p] = pos; 1318 } 1319 } 1320 else if (tokens.getStringValue().equals("f")) { 1321 if (xref[p] == 0 && xref[p + 1] == 0) 1322 xref[p] = -1; 1323 } 1324 else 1325 tokens.throwError("Invalid cross-reference entry in this xref subsection"); 1326 } 1327 } 1328 PdfDictionary trailer = (PdfDictionary)readPRObject(); 1329 PdfNumber xrefSize = (PdfNumber)trailer.get(PdfName.SIZE); 1330 ensureXrefSize(xrefSize.intValue() * 2); 1331 PdfObject xrs = trailer.get(PdfName.XREFSTM); 1332 if (xrs != null && xrs.isNumber()) { 1333 int loc = ((PdfNumber)xrs).intValue(); 1334 try { 1335 readXRefStream(loc); 1336 newXrefType = true; 1337 hybridXref = true; 1338 } 1339 catch (IOException e) { 1340 xref = null; 1341 throw e; 1342 } 1343 } 1344 return trailer; 1345 } 1346 1347 protected boolean readXRefStream(int ptr) throws IOException { 1348 tokens.seek(ptr); 1349 int thisStream = 0; 1350 if (!tokens.nextToken()) 1351 return false; 1352 if (tokens.getTokenType() != PRTokeniser.TK_NUMBER) 1353 return false; 1354 thisStream = tokens.intValue(); 1355 if (!tokens.nextToken() || tokens.getTokenType() != PRTokeniser.TK_NUMBER) 1356 return false; 1357 if (!tokens.nextToken() || !tokens.getStringValue().equals("obj")) 1358 return false; 1359 PdfObject object = readPRObject(); 1360 PRStream stm = null; 1361 if (object.isStream()) { 1362 stm = (PRStream)object; 1363 if (!PdfName.XREF.equals(stm.get(PdfName.TYPE))) 1364 return false; 1365 } 1366 else 1367 return false; 1368 if (trailer == null) { 1369 trailer = new PdfDictionary(); 1370 trailer.putAll(stm); 1371 } 1372 stm.setLength(((PdfNumber)stm.get(PdfName.LENGTH)).intValue()); 1373 int size = ((PdfNumber)stm.get(PdfName.SIZE)).intValue(); 1374 PdfArray index; 1375 PdfObject obj = stm.get(PdfName.INDEX); 1376 if (obj == null) { 1377 index = new PdfArray(); 1378 index.add(new int[]{0, size}); 1379 } 1380 else 1381 index = (PdfArray)obj; 1382 PdfArray w = (PdfArray)stm.get(PdfName.W); 1383 int prev = -1; 1384 obj = stm.get(PdfName.PREV); 1385 if (obj != null) 1386 prev = ((PdfNumber)obj).intValue(); 1387 ensureXrefSize(size * 2); 1392 if (objStmMark == null && !partial) 1393 objStmMark = new HashMap (); 1394 if (objStmToOffset == null && partial) 1395 objStmToOffset = new IntHashtable(); 1396 byte b[] = getStreamBytes(stm, tokens.getFile()); 1397 int bptr = 0; 1398 ArrayList wa = w.getArrayList(); 1399 int wc[] = new int[3]; 1400 for (int k = 0; k < 3; ++k) 1401 wc[k] = ((PdfNumber)wa.get(k)).intValue(); 1402 ArrayList sections = index.getArrayList(); 1403 for (int idx = 0; idx < sections.size(); idx += 2) { 1404 int start = ((PdfNumber)sections.get(idx)).intValue(); 1405 int length = ((PdfNumber)sections.get(idx + 1)).intValue(); 1406 ensureXrefSize((start + length) * 2); 1407 while (length-- > 0) { 1408 int type = 1; 1409 if (wc[0] > 0) { 1410 type = 0; 1411 for (int k = 0; k < wc[0]; ++k) 1412 type = (type << 8) + (b[bptr++] & 0xff); 1413 } 1414 int field2 = 0; 1415 for (int k = 0; k < wc[1]; ++k) 1416 field2 = (field2 << 8) + (b[bptr++] & 0xff); 1417 int field3 = 0; 1418 for (int k = 0; k < wc[2]; ++k) 1419 field3 = (field3 << 8) + (b[bptr++] & 0xff); 1420 int base = start * 2; 1421 if (xref[base] == 0 && xref[base + 1] == 0) { 1422 switch (type) { 1423 case 0: 1424 xref[base] = -1; 1425 break; 1426 case 1: 1427 xref[base] = field2; 1428 break; 1429 case 2: 1430 xref[base] = field3; 1431 xref[base + 1] = field2; 1432 if (partial) { 1433 objStmToOffset.put(field2, 0); 1434 } 1435 else { 1436 Integer on = new Integer (field2); 1437 IntHashtable seq = (IntHashtable)objStmMark.get(on); 1438 if (seq == null) { 1439 seq = new IntHashtable(); 1440 seq.put(field3, 1); 1441 objStmMark.put(on, seq); 1442 } 1443 else 1444 seq.put(field3, 1); 1445 } 1446 break; 1447 } 1448 } 1449 ++start; 1450 } 1451 } 1452 thisStream *= 2; 1453 if (thisStream < xref.length) 1454 xref[thisStream] = -1; 1455 1456 if (prev == -1) 1457 return true; 1458 return readXRefStream(prev); 1459 } 1460 1461 protected void rebuildXref() throws IOException { 1462 hybridXref = false; 1463 newXrefType = false; 1464 tokens.seek(0); 1465 int xr[][] = new int[1024][]; 1466 int top = 0; 1467 trailer = null; 1468 byte line[] = new byte[64]; 1469 for (;;) { 1470 int pos = tokens.getFilePointer(); 1471 if (!tokens.readLineSegment(line)) 1472 break; 1473 if (line[0] == 't') { 1474 if (!PdfEncodings.convertToString(line, null).startsWith("trailer")) 1475 continue; 1476 tokens.seek(pos); 1477 tokens.nextToken(); 1478 pos = tokens.getFilePointer(); 1479 try { 1480 PdfDictionary dic = (PdfDictionary)readPRObject(); 1481 if (dic.get(PdfName.ROOT) != null) 1482 trailer = dic; 1483 else 1484 tokens.seek(pos); 1485 } 1486 catch (Exception e) { 1487 tokens.seek(pos); 1488 } 1489 } 1490 else if (line[0] >= '0' && line[0] <= '9') { 1491 int obj[] = PRTokeniser.checkObjectStart(line); 1492 if (obj == null) 1493 continue; 1494 int num = obj[0]; 1495 int gen = obj[1]; 1496 if (num >= xr.length) { 1497 int newLength = num * 2; 1498 int xr2[][] = new int[newLength][]; 1499 System.arraycopy(xr, 0, xr2, 0, top); 1500 xr = xr2; 1501 } 1502 if (num >= top) 1503 top = num + 1; 1504 if (xr[num] == null || gen >= xr[num][1]) { 1505 obj[0] = pos; 1506 xr[num] = obj; 1507 } 1508 } 1509 } 1510 if (trailer == null) 1511 throw new IOException ("trailer not found."); 1512 xref = new int[top * 2]; 1513 for (int k = 0; k < top; ++k) { 1514 int obj[] = xr[k]; 1515 if (obj != null) 1516 xref[k * 2] = obj[0]; 1517 } 1518 } 1519 1520 protected PdfDictionary readDictionary() throws IOException { 1521 PdfDictionary dic = new PdfDictionary(); 1522 while (true) { 1523 tokens.nextValidToken(); 1524 if (tokens.getTokenType() == PRTokeniser.TK_END_DIC) 1525 break; 1526 if (tokens.getTokenType() != PRTokeniser.TK_NAME) 1527 tokens.throwError("Dictionary key is not a name."); 1528 PdfName name = new PdfName(tokens.getStringValue(), false); 1529 PdfObject obj = readPRObject(); 1530 int type = obj.type(); 1531 if (-type == PRTokeniser.TK_END_DIC) 1532 tokens.throwError("Unexpected '>>'"); 1533 if (-type == PRTokeniser.TK_END_ARRAY) 1534 tokens.throwError("Unexpected ']'"); 1535 dic.put(name, obj); 1536 } 1537 return dic; 1538 } 1539 1540 protected PdfArray readArray() throws IOException { 1541 PdfArray array = new PdfArray(); 1542 while (true) { 1543 PdfObject obj = readPRObject(); 1544 int type = obj.type(); 1545 if (-type == PRTokeniser.TK_END_ARRAY) 1546 break; 1547 if (-type == PRTokeniser.TK_END_DIC) 1548 tokens.throwError("Unexpected '>>'"); 1549 array.add(obj); 1550 } 1551 return array; 1552 } 1553 1554 protected PdfObject readPRObject() throws IOException { 1555 tokens.nextValidToken(); 1556 int type = tokens.getTokenType(); 1557 switch (type) { 1558 case PRTokeniser.TK_START_DIC: { 1559 PdfDictionary dic = readDictionary(); 1560 int pos = tokens.getFilePointer(); 1561 if (tokens.nextToken() && tokens.getStringValue().equals("stream")) { 1563 int ch = tokens.read(); 1564 if (ch != '\n') 1565 ch = tokens.read(); 1566 if (ch != '\n') 1567 tokens.backOnePosition(ch); 1568 PRStream stream = new PRStream(this, tokens.getFilePointer()); 1569 stream.putAll(dic); 1570 stream.setObjNum(objNum, objGen); 1571 return stream; 1572 } 1573 else { 1574 tokens.seek(pos); 1575 return dic; 1576 } 1577 } 1578 case PRTokeniser.TK_START_ARRAY: 1579 return readArray(); 1580 case PRTokeniser.TK_NUMBER: 1581 return new PdfNumber(tokens.getStringValue()); 1582 case PRTokeniser.TK_STRING: 1583 PdfString str = new PdfString(tokens.getStringValue(), null).setHexWriting(tokens.isHexString()); 1584 str.setObjNum(objNum, objGen); 1585 if (strings != null) 1586 strings.add(str); 1587 return str; 1588 case PRTokeniser.TK_NAME: 1589 return new PdfName(tokens.getStringValue(), false); 1590 case PRTokeniser.TK_REF: 1591 int num = tokens.getReference(); 1592 PRIndirectReference ref = new PRIndirectReference(this, num, tokens.getGeneration()); 1593 return ref; 1594 default: 1595 String sv = tokens.getStringValue(); 1596 if ("null".equals(sv)) 1597 return PdfNull.PDFNULL; 1598 else if ("true".equals(sv)) 1599 return PdfBoolean.PDFTRUE; 1600 else if ("false".equals(sv)) 1601 return PdfBoolean.PDFFALSE; 1602 return new PdfLiteral(-type, tokens.getStringValue()); 1603 } 1604 } 1605 1606 1610 public static byte[] FlateDecode(byte in[]) { 1611 byte b[] = FlateDecode(in, true); 1612 if (b == null) 1613 return FlateDecode(in, false); 1614 return b; 1615 } 1616 1617 1622 public static byte[] decodePredictor(byte in[], PdfObject dicPar) { 1623 if (dicPar == null || !dicPar.isDictionary()) 1624 return in; 1625 PdfDictionary dic = (PdfDictionary)dicPar; 1626 PdfObject obj = getPdfObject(dic.get(PdfName.PREDICTOR)); 1627 if (obj == null || !obj.isNumber()) 1628 return in; 1629 int predictor = ((PdfNumber)obj).intValue(); 1630 if (predictor < 10) 1631 return in; 1632 int width = 1; 1633 obj = getPdfObject(dic.get(PdfName.COLUMNS)); 1634 if (obj != null && obj.isNumber()) 1635 width = ((PdfNumber)obj).intValue(); 1636 int colors = 1; 1637 obj = getPdfObject(dic.get(PdfName.COLORS)); 1638 if (obj != null && obj.isNumber()) 1639 colors = ((PdfNumber)obj).intValue(); 1640 int bpc = 8; 1641 obj = getPdfObject(dic.get(PdfName.BITSPERCOMPONENT)); 1642 if (obj != null && obj.isNumber()) 1643 bpc = ((PdfNumber)obj).intValue(); 1644 DataInputStream dataStream = new DataInputStream (new ByteArrayInputStream (in)); 1645 ByteArrayOutputStream fout = new ByteArrayOutputStream (in.length); 1646 int bytesPerPixel = colors * bpc / 8; 1647 int bytesPerRow = (colors*width*bpc + 7)/8; 1648 byte[] curr = new byte[bytesPerRow]; 1649 byte[] prior = new byte[bytesPerRow]; 1650 1651 while (true) { 1653 int filter = 0; 1655 try { 1656 filter = dataStream.read(); 1657 if (filter < 0) { 1658 return fout.toByteArray(); 1659 } 1660 dataStream.readFully(curr, 0, bytesPerRow); 1661 } catch (Exception e) { 1662 return fout.toByteArray(); 1663 } 1664 1665 switch (filter) { 1666 case 0: break; 1668 case 1: for (int i = bytesPerPixel; i < bytesPerRow; i++) { 1670 curr[i] += curr[i - bytesPerPixel]; 1671 } 1672 break; 1673 case 2: for (int i = 0; i < bytesPerRow; i++) { 1675 curr[i] += prior[i]; 1676 } 1677 break; 1678 case 3: for (int i = 0; i < bytesPerPixel; i++) { 1680 curr[i] += prior[i] / 2; 1681 } 1682 for (int i = bytesPerPixel; i < bytesPerRow; i++) { 1683 curr[i] += ((curr[i - bytesPerPixel] & 0xff) + (prior[i] & 0xff))/2; 1684 } 1685 break; 1686 case 4: for (int i = 0; i < bytesPerPixel; i++) { 1688 curr[i] += prior[i]; 1689 } 1690 1691 for (int i = bytesPerPixel; i < bytesPerRow; i++) { 1692 int a = curr[i - bytesPerPixel] & 0xff; 1693 int b = prior[i] & 0xff; 1694 int c = prior[i - bytesPerPixel] & 0xff; 1695 1696 int p = a + b - c; 1697 int pa = Math.abs(p - a); 1698 int pb = Math.abs(p - b); 1699 int pc = Math.abs(p - c); 1700 1701 int ret; 1702 1703 if ((pa <= pb) && (pa <= pc)) { 1704 ret = a; 1705 } else if (pb <= pc) { 1706 ret = b; 1707 } else { 1708 ret = c; 1709 } 1710 curr[i] += (byte)(ret); 1711 } 1712 break; 1713 default: 1714 throw new RuntimeException ("PNG filter unknown."); 1716 } 1717 try { 1718 fout.write(curr); 1719 } 1720 catch (IOException ioe) { 1721 } 1723 1724 byte[] tmp = prior; 1726 prior = curr; 1727 curr = tmp; 1728 } 1729 } 1730 1731 1737 public static byte[] FlateDecode(byte in[], boolean strict) { 1738 ByteArrayInputStream stream = new ByteArrayInputStream (in); 1739 InflaterInputStream zip = new InflaterInputStream (stream); 1740 ByteArrayOutputStream out = new ByteArrayOutputStream (); 1741 byte b[] = new byte[strict ? 4092 : 1]; 1742 try { 1743 int n; 1744 while ((n = zip.read(b)) >= 0) { 1745 out.write(b, 0, n); 1746 } 1747 zip.close(); 1748 out.close(); 1749 return out.toByteArray(); 1750 } 1751 catch (Exception e) { 1752 if (strict) 1753 return null; 1754 return out.toByteArray(); 1755 } 1756 } 1757 1758 1762 public static byte[] ASCIIHexDecode(byte in[]) { 1763 ByteArrayOutputStream out = new ByteArrayOutputStream (); 1764 boolean first = true; 1765 int n1 = 0; 1766 for (int k = 0; k < in.length; ++k) { 1767 int ch = in[k] & 0xff; 1768 if (ch == '>') 1769 break; 1770 if (PRTokeniser.isWhitespace(ch)) 1771 continue; 1772 int n = PRTokeniser.getHex(ch); 1773 if (n == -1) 1774 throw new RuntimeException ("Illegal character in ASCIIHexDecode."); 1775 if (first) 1776 n1 = n; 1777 else 1778 out.write((byte)((n1 << 4) + n)); 1779 first = !first; 1780 } 1781 if (!first) 1782 out.write((byte)(n1 << 4)); 1783 return out.toByteArray(); 1784 } 1785 1786 1790 public static byte[] ASCII85Decode(byte in[]) { 1791 ByteArrayOutputStream out = new ByteArrayOutputStream (); 1792 int state = 0; 1793 int chn[] = new int[5]; 1794 for (int k = 0; k < in.length; ++k) { 1795 int ch = in[k] & 0xff; 1796 if (ch == '~') 1797 break; 1798 if (PRTokeniser.isWhitespace(ch)) 1799 continue; 1800 if (ch == 'z' && state == 0) { 1801 out.write(0); 1802 out.write(0); 1803 out.write(0); 1804 out.write(0); 1805 continue; 1806 } 1807 if (ch < '!' || ch > 'u') 1808 throw new RuntimeException ("Illegal character in ASCII85Decode."); 1809 chn[state] = ch - '!'; 1810 ++state; 1811 if (state == 5) { 1812 state = 0; 1813 int r = 0; 1814 for (int j = 0; j < 5; ++j) 1815 r = r * 85 + chn[j]; 1816 out.write((byte)(r >> 24)); 1817 out.write((byte)(r >> 16)); 1818 out.write((byte)(r >> 8)); 1819 out.write((byte)r); 1820 } 1821 } 1822 int r = 0; 1823 if (state == 1) 1824 throw new RuntimeException ("Illegal length in ASCII85Decode."); 1825 if (state == 2) { 1826 r = chn[0] * 85 * 85 * 85 * 85 + chn[1] * 85 * 85 * 85; 1827 out.write((byte)(r >> 24)); 1828 } 1829 else if (state == 3) { 1830 r = chn[0] * 85 * 85 * 85 * 85 + chn[1] * 85 * 85 * 85 + chn[2] * 85 * 85; 1831 out.write((byte)(r >> 24)); 1832 out.write((byte)(r >> 16)); 1833 } 1834 else if (state == 4) { 1835 r = chn[0] * 85 * 85 * 85 * 85 + chn[1] * 85 * 85 * 85 + chn[2] * 85 * 85 + chn[3] * 85 ; 1836 out.write((byte)(r >> 24)); 1837 out.write((byte)(r >> 16)); 1838 out.write((byte)(r >> 8)); 1839 } 1840 return out.toByteArray(); 1841 } 1842 1843 1847 public static byte[] LZWDecode(byte in[]) { 1848 ByteArrayOutputStream out = new ByteArrayOutputStream (); 1849 LZWDecoder lzw = new LZWDecoder(); 1850 lzw.decode(in, out); 1851 return out.toByteArray(); 1852 } 1853 1854 1858 public boolean isRebuilt() { 1859 return this.rebuilt; 1860 } 1861 1862 1866 public PdfDictionary getPageN(int pageNum) { 1867 PdfDictionary dic = pageRefs.getPageN(pageNum); 1868 if (dic == null) 1869 return null; 1870 if (appendable) 1871 dic.setIndRef(pageRefs.getPageOrigRef(pageNum)); 1872 return dic; 1873 } 1874 1875 1879 public PdfDictionary getPageNRelease(int pageNum) { 1880 PdfDictionary dic = getPageN(pageNum); 1881 pageRefs.releasePage(pageNum); 1882 return dic; 1883 } 1884 1885 1888 public void releasePage(int pageNum) { 1889 pageRefs.releasePage(pageNum); 1890 } 1891 1892 1895 public void resetReleasePage() { 1896 pageRefs.resetReleasePage(); 1897 } 1898 1899 1903 public PRIndirectReference getPageOrigRef(int pageNum) { 1904 return pageRefs.getPageOrigRef(pageNum); 1905 } 1906 1907 1913 public byte[] getPageContent(int pageNum, RandomAccessFileOrArray file) throws IOException { 1914 PdfDictionary page = getPageNRelease(pageNum); 1915 if (page == null) 1916 return null; 1917 PdfObject contents = getPdfObjectRelease(page.get(PdfName.CONTENTS)); 1918 if (contents == null) 1919 return new byte[0]; 1920 ByteArrayOutputStream bout = null; 1921 if (contents.isStream()) { 1922 return getStreamBytes((PRStream)contents, file); 1923 } 1924 else if (contents.isArray()) { 1925 PdfArray array = (PdfArray)contents; 1926 ArrayList list = array.getArrayList(); 1927 bout = new ByteArrayOutputStream (); 1928 for (int k = 0; k < list.size(); ++k) { 1929 PdfObject item = getPdfObjectRelease((PdfObject)list.get(k)); 1930 if (item == null || !item.isStream()) 1931 continue; 1932 byte[] b = getStreamBytes((PRStream)item, file); 1933 bout.write(b); 1934 if (k != list.size() - 1) 1935 bout.write('\n'); 1936 } 1937 return bout.toByteArray(); 1938 } 1939 else 1940 return new byte[0]; 1941 } 1942 1943 1948 public byte[] getPageContent(int pageNum) throws IOException { 1949 RandomAccessFileOrArray rf = getSafeFile(); 1950 try { 1951 rf.reOpen(); 1952 return getPageContent(pageNum, rf); 1953 } 1954 finally { 1955 try{rf.close();}catch(Exception e){} 1956 } 1957 } 1958 1959 protected void killXref(PdfObject obj) { 1960 if (obj == null) 1961 return; 1962 if ((obj instanceof PdfIndirectReference) && !obj.isIndirect()) 1963 return; 1964 switch (obj.type()) { 1965 case PdfObject.INDIRECT: { 1966 int xr = ((PRIndirectReference)obj).getNumber(); 1967 obj = (PdfObject)xrefObj.get(xr); 1968 xrefObj.set(xr, null); 1969 freeXref = xr; 1970 killXref(obj); 1971 break; 1972 } 1973 case PdfObject.ARRAY: { 1974 ArrayList t = ((PdfArray)obj).getArrayList(); 1975 for (int i = 0; i < t.size(); ++i) 1976 killXref((PdfObject)t.get(i)); 1977 break; 1978 } 1979 case PdfObject.STREAM: 1980 case PdfObject.DICTIONARY: { 1981 PdfDictionary dic = (PdfDictionary)obj; 1982 for (Iterator i = dic.getKeys().iterator(); i.hasNext();){ 1983 killXref(dic.get((PdfName)i.next())); 1984 } 1985 break; 1986 } 1987 } 1988 } 1989 1990 1994 public void setPageContent(int pageNum, byte content[]) { 1995 PdfDictionary page = getPageN(pageNum); 1996 if (page == null) 1997 return; 1998 PdfObject contents = page.get(PdfName.CONTENTS); 1999 freeXref = -1; 2000 killXref(contents); 2001 if (freeXref == -1) { 2002 xrefObj.add(null); 2003 freeXref = xrefObj.size() - 1; 2004 } 2005 page.put(PdfName.CONTENTS, new PRIndirectReference(this, freeXref)); 2006 xrefObj.set(freeXref, new PRStream(this, content)); 2007 } 2008 2009 2015 public static byte[] getStreamBytes(PRStream stream, RandomAccessFileOrArray file) throws IOException { 2016 PdfObject filter = getPdfObjectRelease(stream.get(PdfName.FILTER)); 2017 byte[] b = getStreamBytesRaw(stream, file); 2018 ArrayList filters = new ArrayList (); 2019 if (filter != null) { 2020 if (filter.isName()) 2021 filters.add(filter); 2022 else if (filter.isArray()) 2023 filters = ((PdfArray)filter).getArrayList(); 2024 } 2025 ArrayList dp = new ArrayList (); 2026 PdfObject dpo = getPdfObjectRelease(stream.get(PdfName.DECODEPARMS)); 2027 if (dpo == null || (!dpo.isDictionary() && !dpo.isArray())) 2028 dpo = getPdfObjectRelease(stream.get(PdfName.DP)); 2029 if (dpo != null) { 2030 if (dpo.isDictionary()) 2031 dp.add(dpo); 2032 else if (dpo.isArray()) 2033 dp = ((PdfArray)dpo).getArrayList(); 2034 } 2035 String name; 2036 for (int j = 0; j < filters.size(); ++j) { 2037 name = ((PdfName)getPdfObjectRelease((PdfObject)filters.get(j))).toString(); 2038 if (name.equals("/FlateDecode") || name.equals("/Fl")) { 2039 b = FlateDecode(b); 2040 PdfObject dicParam = null; 2041 if (j < dp.size()) { 2042 dicParam = (PdfObject)dp.get(j); 2043 b = decodePredictor(b, dicParam); 2044 } 2045 } 2046 else if (name.equals("/ASCIIHexDecode") || name.equals("/AHx")) 2047 b = ASCIIHexDecode(b); 2048 else if (name.equals("/ASCII85Decode") || name.equals("/A85")) 2049 b = ASCII85Decode(b); 2050 else if (name.equals("/LZWDecode")) { 2051 b = LZWDecode(b); 2052 PdfObject dicParam = null; 2053 if (j < dp.size()) { 2054 dicParam = (PdfObject)dp.get(j); 2055 b = decodePredictor(b, dicParam); 2056 } 2057 } 2058 else if (name.equals("/Crypt")) { 2059 } 2060 else 2061 throw new IOException ("The filter " + name + " is not supported."); 2062 } 2063 return b; 2064 } 2065 2066 2071 public static byte[] getStreamBytes(PRStream stream) throws IOException { 2072 RandomAccessFileOrArray rf = stream.getReader().getSafeFile(); 2073 try { 2074 rf.reOpen(); 2075 return getStreamBytes(stream, rf); 2076 } 2077 finally { 2078 try{rf.close();}catch(Exception e){} 2079 } 2080 } 2081 2082 2088 public static byte[] getStreamBytesRaw(PRStream stream, RandomAccessFileOrArray file) throws IOException { 2089 PdfReader reader = stream.getReader(); 2090 byte b[]; 2091 if (stream.getOffset() < 0) 2092 b = stream.getBytes(); 2093 else { 2094 b = new byte[stream.getLength()]; 2095 file.seek(stream.getOffset()); 2096 file.readFully(b); 2097 PdfEncryption decrypt = reader.getDecrypt(); 2098 if (decrypt != null) { 2099 PdfObject filter = getPdfObjectRelease(stream.get(PdfName.FILTER)); 2100 ArrayList filters = new ArrayList (); 2101 if (filter != null) { 2102 if (filter.isName()) 2103 filters.add(filter); 2104 else if (filter.isArray()) 2105 filters = ((PdfArray)filter).getArrayList(); 2106 } 2107 boolean skip = false; 2108 for (int k = 0; k < filters.size(); ++k) { 2109 PdfObject obj = getPdfObjectRelease((PdfObject)filters.get(k)); 2110 if (obj != null && obj.toString().equals("/Crypt")) { 2111 skip = true; 2112 break; 2113 } 2114 } 2115 if (!skip) { 2116 decrypt.setHashKey(stream.getObjNum(), stream.getObjGen()); 2117 b = decrypt.decryptByteArray(b); 2118 } 2119 } 2120 } 2121 return b; 2122 } 2123 2124 2129 public static byte[] getStreamBytesRaw(PRStream stream) throws IOException { 2130 RandomAccessFileOrArray rf = stream.getReader().getSafeFile(); 2131 try { 2132 rf.reOpen(); 2133 return getStreamBytesRaw(stream, rf); 2134 } 2135 finally { 2136 try{rf.close();}catch(Exception e){} 2137 } 2138 } 2139 2140 2141 public void eliminateSharedStreams() { 2142 if (!sharedStreams) 2143 return; 2144 sharedStreams = false; 2145 if (pageRefs.size() == 1) 2146 return; 2147 ArrayList newRefs = new ArrayList (); 2148 ArrayList newStreams = new ArrayList (); 2149 IntHashtable visited = new IntHashtable(); 2150 for (int k = 1; k <= pageRefs.size(); ++k) { 2151 PdfDictionary page = pageRefs.getPageN(k); 2152 if (page == null) 2153 continue; 2154 PdfObject contents = getPdfObject(page.get(PdfName.CONTENTS)); 2155 if (contents == null) 2156 continue; 2157 if (contents.isStream()) { 2158 PRIndirectReference ref = (PRIndirectReference)page.get(PdfName.CONTENTS); 2159 if (visited.containsKey(ref.getNumber())) { 2160 newRefs.add(ref); 2162 newStreams.add(new PRStream((PRStream)contents, null)); 2163 } 2164 else 2165 visited.put(ref.getNumber(), 1); 2166 } 2167 else if (contents.isArray()) { 2168 PdfArray array = (PdfArray)contents; 2169 ArrayList list = array.getArrayList(); 2170 for (int j = 0; j < list.size(); ++j) { 2171 PRIndirectReference ref = (PRIndirectReference)list.get(j); 2172 if (visited.containsKey(ref.getNumber())) { 2173 newRefs.add(ref); 2175 newStreams.add(new PRStream((PRStream)getPdfObject(ref), null)); 2176 } 2177 else 2178 visited.put(ref.getNumber(), 1); 2179 } 2180 } 2181 } 2182 if (newStreams.isEmpty()) 2183 return; 2184 for (int k = 0; k < newStreams.size(); ++k) { 2185 xrefObj.add(newStreams.get(k)); 2186 PRIndirectReference ref = (PRIndirectReference)newRefs.get(k); 2187 ref.setNumber(xrefObj.size() - 1, 0); 2188 } 2189 } 2190 2191 2195 public boolean isTampered() { 2196 return tampered; 2197 } 2198 2199 2203 public void setTampered(boolean tampered) { 2204 this.tampered = tampered; 2205 } 2206 2207 2211 public byte[] getMetadata() throws IOException { 2212 PdfObject obj = getPdfObject(catalog.get(PdfName.METADATA)); 2213 if (!(obj instanceof PRStream)) 2214 return null; 2215 RandomAccessFileOrArray rf = getSafeFile(); 2216 byte b[] = null; 2217 try { 2218 rf.reOpen(); 2219 b = getStreamBytes((PRStream)obj, rf); 2220 } 2221 finally { 2222 try { 2223 rf.close(); 2224 } 2225 catch (Exception e) { 2226 } 2228 } 2229 return b; 2230 } 2231 2232 2236 public int getLastXref() { 2237 return lastXref; 2238 } 2239 2240 2244 public int getXrefSize() { 2245 return xrefObj.size(); 2246 } 2247 2248 2252 public int getEofPos() { 2253 return eofPos; 2254 } 2255 2256 2261 public char getPdfVersion() { 2262 return pdfVersion; 2263 } 2264 2265 2269 public boolean isEncrypted() { 2270 return encrypted; 2271 } 2272 2273 2278 public int getPermissions() { 2279 return pValue; 2280 } 2281 2282 2286 public boolean is128Key() { 2287 return rValue == 3; 2288 } 2289 2290 2294 public PdfDictionary getTrailer() { 2295 return trailer; 2296 } 2297 2298 PdfEncryption getDecrypt() { 2299 return decrypt; 2300 } 2301 2302 static boolean equalsn(byte a1[], byte a2[]) { 2303 int length = a2.length; 2304 for (int k = 0; k < length; ++k) { 2305 if (a1[k] != a2[k]) 2306 return false; 2307 } 2308 return true; 2309 } 2310 2311 static boolean existsName(PdfDictionary dic, PdfName key, PdfName value) { 2312 PdfObject type = getPdfObjectRelease(dic.get(key)); 2313 if (type == null || !type.isName()) 2314 return false; 2315 PdfName name = (PdfName)type; 2316 return name.equals(value); 2317 } 2318 2319 static String getFontName(PdfDictionary dic) { 2320 if (dic == null) 2321 return null; 2322 PdfObject type = getPdfObjectRelease(dic.get(PdfName.BASEFONT)); 2323 if (type == null || !type.isName()) 2324 return null; 2325 return PdfName.decodeName(type.toString()); 2326 } 2327 2328 static String getSubsetPrefix(PdfDictionary dic) { 2329 if (dic == null) 2330 return null; 2331 String s = getFontName(dic); 2332 if (s == null) 2333 return null; 2334 if (s.length() < 8 || s.charAt(6) != '+') 2335 return null; 2336 for (int k = 0; k < 6; ++k) { 2337 char c = s.charAt(k); 2338 if (c < 'A' || c > 'Z') 2339 return null; 2340 } 2341 return s; 2342 } 2343 2344 2348 public int shuffleSubsetNames() { 2349 int total = 0; 2350 for (int k = 1; k < xrefObj.size(); ++k) { 2351 PdfObject obj = getPdfObjectRelease(k); 2352 if (obj == null || !obj.isDictionary()) 2353 continue; 2354 PdfDictionary dic = (PdfDictionary)obj; 2355 if (!existsName(dic, PdfName.TYPE, PdfName.FONT)) 2356 continue; 2357 if (existsName(dic, PdfName.SUBTYPE, PdfName.TYPE1) 2358 || existsName(dic, PdfName.SUBTYPE, PdfName.MMTYPE1) 2359 || existsName(dic, PdfName.SUBTYPE, PdfName.TRUETYPE)) { 2360 String s = getSubsetPrefix(dic); 2361 if (s == null) 2362 continue; 2363 String ns = BaseFont.createSubsetPrefix() + s.substring(7); 2364 PdfName newName = new PdfName(ns); 2365 dic.put(PdfName.BASEFONT, newName); 2366 setXrefPartialObject(k, dic); 2367 ++total; 2368 PdfDictionary fd = (PdfDictionary)getPdfObject(dic.get(PdfName.FONTDESCRIPTOR)); 2369 if (fd == null) 2370 continue; 2371 fd.put(PdfName.FONTNAME, newName); 2372 } 2373 else if (existsName(dic, PdfName.SUBTYPE, PdfName.TYPE0)) { 2374 String s = getSubsetPrefix(dic); 2375 PdfArray arr = (PdfArray)getPdfObject(dic.get(PdfName.DESCENDANTFONTS)); 2376 if (arr == null) 2377 continue; 2378 ArrayList list = arr.getArrayList(); 2379 if (list.isEmpty()) 2380 continue; 2381 PdfDictionary desc = (PdfDictionary)getPdfObject((PdfObject)list.get(0)); 2382 String sde = getSubsetPrefix(desc); 2383 if (sde == null) 2384 continue; 2385 String ns = BaseFont.createSubsetPrefix(); 2386 if (s != null) 2387 dic.put(PdfName.BASEFONT, new PdfName(ns + s.substring(7))); 2388 setXrefPartialObject(k, dic); 2389 PdfName newName = new PdfName(ns + sde.substring(7)); 2390 desc.put(PdfName.BASEFONT, newName); 2391 ++total; 2392 PdfDictionary fd = (PdfDictionary)getPdfObject(desc.get(PdfName.FONTDESCRIPTOR)); 2393 if (fd == null) 2394 continue; 2395 fd.put(PdfName.FONTNAME, newName); 2396 } 2397 } 2398 return total; 2399 } 2400 2401 2404 public int createFakeFontSubsets() { 2405 int total = 0; 2406 for (int k = 1; k < xrefObj.size(); ++k) { 2407 PdfObject obj = getPdfObjectRelease(k); 2408 if (obj == null || !obj.isDictionary()) 2409 continue; 2410 PdfDictionary dic = (PdfDictionary)obj; 2411 if (!existsName(dic, PdfName.TYPE, PdfName.FONT)) 2412 continue; 2413 if (existsName(dic, PdfName.SUBTYPE, PdfName.TYPE1) 2414 || existsName(dic, PdfName.SUBTYPE, PdfName.MMTYPE1) 2415 || existsName(dic, PdfName.SUBTYPE, PdfName.TRUETYPE)) { 2416 String s = getSubsetPrefix(dic); 2417 if (s != null) 2418 continue; 2419 s = getFontName(dic); 2420 if (s == null) 2421 continue; 2422 String ns = BaseFont.createSubsetPrefix() + s; 2423 PdfDictionary fd = (PdfDictionary)getPdfObjectRelease(dic.get(PdfName.FONTDESCRIPTOR)); 2424 if (fd == null) 2425 continue; 2426 if (fd.get(PdfName.FONTFILE) == null && fd.get(PdfName.FONTFILE2) == null 2427 && fd.get(PdfName.FONTFILE3) == null) 2428 continue; 2429 fd = (PdfDictionary)getPdfObject(dic.get(PdfName.FONTDESCRIPTOR)); 2430 PdfName newName = new PdfName(ns); 2431 dic.put(PdfName.BASEFONT, newName); 2432 fd.put(PdfName.FONTNAME, newName); 2433 setXrefPartialObject(k, dic); 2434 ++total; 2435 } 2436 } 2437 return total; 2438 } 2439 2440 private static PdfArray getNameArray(PdfObject obj) { 2441 if (obj == null) 2442 return null; 2443 obj = getPdfObjectRelease(obj); 2444 if (obj == null) 2445 return null; 2446 if (obj.isArray()) 2447 return (PdfArray)obj; 2448 else if (obj.isDictionary()) { 2449 PdfObject arr2 = getPdfObjectRelease(((PdfDictionary)obj).get(PdfName.D)); 2450 if (arr2 != null && arr2.isArray()) 2451 return (PdfArray)arr2; 2452 } 2453 return null; 2454 } 2455 2456 2461 public HashMap getNamedDestination() { 2462 HashMap names = getNamedDestinationFromNames(); 2463 names.putAll(getNamedDestinationFromStrings()); 2464 return names; 2465 } 2466 2467 2472 public HashMap getNamedDestinationFromNames() { 2473 HashMap names = new HashMap (); 2474 if (catalog.get(PdfName.DESTS) != null) { 2475 PdfDictionary dic = (PdfDictionary)getPdfObjectRelease(catalog.get(PdfName.DESTS)); 2476 if (dic == null) 2477 return names; 2478 Set keys = dic.getKeys(); 2479 for (Iterator it = keys.iterator(); it.hasNext();) { 2480 PdfName key = (PdfName)it.next(); 2481 String name = PdfName.decodeName(key.toString()); 2482 PdfArray arr = getNameArray(dic.get(key)); 2483 if (arr != null) 2484 names.put(name, arr); 2485 } 2486 } 2487 return names; 2488 } 2489 2490 2495 public HashMap getNamedDestinationFromStrings() { 2496 if (catalog.get(PdfName.NAMES) != null) { 2497 PdfDictionary dic = (PdfDictionary)getPdfObjectRelease(catalog.get(PdfName.NAMES)); 2498 if (dic != null) { 2499 dic = (PdfDictionary)getPdfObjectRelease(dic.get(PdfName.DESTS)); 2500 if (dic != null) { 2501 HashMap names = PdfNameTree.readTree(dic); 2502 for (Iterator it = names.entrySet().iterator(); it.hasNext();) { 2503 Map.Entry entry = (Map.Entry )it.next(); 2504 PdfArray arr = getNameArray((PdfObject)entry.getValue()); 2505 if (arr != null) 2506 entry.setValue(arr); 2507 else 2508 it.remove(); 2509 } 2510 return names; 2511 } 2512 } 2513 } 2514 return new HashMap (); 2515 } 2516 2517 private boolean replaceNamedDestination(PdfObject obj, HashMap names) { 2518 obj = getPdfObject(obj); 2519 int objIdx = lastXrefPartial; 2520 releaseLastXrefPartial(); 2521 if (obj != null && obj.isDictionary()) { 2522 PdfObject ob2 = getPdfObjectRelease(((PdfDictionary)obj).get(PdfName.DEST)); 2523 String name = null; 2524 if (ob2 != null) { 2525 if (ob2.isName()) 2526 name = PdfName.decodeName(ob2.toString()); 2527 else if (ob2.isString()) 2528 name = ob2.toString(); 2529 PdfArray dest = (PdfArray)names.get(name); 2530 if (dest != null) { 2531 ((PdfDictionary)obj).put(PdfName.DEST, dest); 2532 setXrefPartialObject(objIdx, obj); 2533 return true; 2534 } 2535 } 2536 else if ((ob2 = getPdfObject(((PdfDictionary)obj).get(PdfName.A))) != null) { 2537 int obj2Idx = lastXrefPartial; 2538 releaseLastXrefPartial(); 2539 PdfDictionary dic = (PdfDictionary)ob2; 2540 PdfName type = (PdfName)getPdfObjectRelease(dic.get(PdfName.S)); 2541 if (PdfName.GOTO.equals(type)) { 2542 PdfObject ob3 = getPdfObjectRelease(dic.get(PdfName.D)); 2543 if (ob3 != null) { 2544 if (ob3.isName()) 2545 name = PdfName.decodeName(ob3.toString()); 2546 else if (ob3.isString()) 2547 name = ob3.toString(); 2548 } 2549 PdfArray dest = (PdfArray)names.get(name); 2550 if (dest != null) { 2551 dic.put(PdfName.D, dest); 2552 setXrefPartialObject(obj2Idx, ob2); 2553 setXrefPartialObject(objIdx, obj); 2554 return true; 2555 } 2556 } 2557 } 2558 } 2559 return false; 2560 } 2561 2562 2565 public void removeFields() { 2566 pageRefs.resetReleasePage(); 2567 for (int k = 1; k <= pageRefs.size(); ++k) { 2568 PdfDictionary page = pageRefs.getPageN(k); 2569 PdfArray annots = (PdfArray)getPdfObject(page.get(PdfName.ANNOTS)); 2570 if (annots == null) { 2571 pageRefs.releasePage(k); 2572 continue; 2573 } 2574 ArrayList arr = annots.getArrayList(); 2575 for (int j = 0; j < arr.size(); ++j) { 2576 PdfDictionary annot = (PdfDictionary)getPdfObjectRelease((PdfObject)arr.get(j)); 2577 if (PdfName.WIDGET.equals(annot.get(PdfName.SUBTYPE))) 2578 arr.remove(j--); 2579 } 2580 if (arr.isEmpty()) 2581 page.remove(PdfName.ANNOTS); 2582 else 2583 pageRefs.releasePage(k); 2584 } 2585 catalog.remove(PdfName.ACROFORM); 2586 pageRefs.resetReleasePage(); 2587 } 2588 2589 2592 public void removeAnnotations() { 2593 pageRefs.resetReleasePage(); 2594 for (int k = 1; k <= pageRefs.size(); ++k) { 2595 PdfDictionary page = pageRefs.getPageN(k); 2596 if (page.get(PdfName.ANNOTS) == null) 2597 pageRefs.releasePage(k); 2598 else 2599 page.remove(PdfName.ANNOTS); 2600 } 2601 catalog.remove(PdfName.ACROFORM); 2602 pageRefs.resetReleasePage(); 2603 } 2604 2605 private void iterateBookmarks(PdfObject outlineRef, HashMap names) { 2606 while (outlineRef != null) { 2607 replaceNamedDestination(outlineRef, names); 2608 PdfDictionary outline = (PdfDictionary)getPdfObjectRelease(outlineRef); 2609 PdfObject first = outline.get(PdfName.FIRST); 2610 if (first != null) { 2611 iterateBookmarks(first, names); 2612 } 2613 outlineRef = outline.get(PdfName.NEXT); 2614 } 2615 } 2616 2617 2618 public void consolidateNamedDestinations() { 2619 if (consolidateNamedDestinations) 2620 return; 2621 consolidateNamedDestinations = true; 2622 HashMap names = getNamedDestination(); 2623 if (names.isEmpty()) 2624 return; 2625 for (int k = 1; k <= pageRefs.size(); ++k) { 2626 PdfDictionary page = pageRefs.getPageN(k); 2627 PdfObject annotsRef; 2628 PdfArray annots = (PdfArray)getPdfObject(annotsRef = page.get(PdfName.ANNOTS)); 2629 int annotIdx = lastXrefPartial; 2630 releaseLastXrefPartial(); 2631 if (annots == null) { 2632 pageRefs.releasePage(k); 2633 continue; 2634 } 2635 ArrayList list = annots.getArrayList(); 2636 boolean commitAnnots = false; 2637 for (int an = 0; an < list.size(); ++an) { 2638 PdfObject objRef = (PdfObject)list.get(an); 2639 if (replaceNamedDestination(objRef, names) && !objRef.isIndirect()) 2640 commitAnnots = true; 2641 } 2642 if (commitAnnots) 2643 setXrefPartialObject(annotIdx, annots); 2644 if (!commitAnnots || annotsRef.isIndirect()) 2645 pageRefs.releasePage(k); 2646 } 2647 PdfDictionary outlines = (PdfDictionary)getPdfObjectRelease(catalog.get(PdfName.OUTLINES)); 2648 if (outlines == null) 2649 return; 2650 iterateBookmarks(outlines.get(PdfName.FIRST), names); 2651 } 2652 2653 protected static PdfDictionary duplicatePdfDictionary(PdfDictionary original, PdfDictionary copy, PdfReader newReader) { 2654 if (copy == null) 2655 copy = new PdfDictionary(); 2656 for (Iterator it = original.getKeys().iterator(); it.hasNext();) { 2657 PdfName key = (PdfName)it.next(); 2658 copy.put(key, duplicatePdfObject(original.get(key), newReader)); 2659 } 2660 return copy; 2661 } 2662 2663 protected static PdfObject duplicatePdfObject(PdfObject original, PdfReader newReader) { 2664 if (original == null) 2665 return null; 2666 switch (original.type()) { 2667 case PdfObject.DICTIONARY: { 2668 return duplicatePdfDictionary((PdfDictionary)original, null, newReader); 2669 } 2670 case PdfObject.STREAM: { 2671 PRStream org = (PRStream)original; 2672 PRStream stream = new PRStream(org, null, newReader); 2673 duplicatePdfDictionary(org, stream, newReader); 2674 return stream; 2675 } 2676 case PdfObject.ARRAY: { 2677 ArrayList list = ((PdfArray)original).getArrayList(); 2678 PdfArray arr = new PdfArray(); 2679 for (Iterator it = list.iterator(); it.hasNext();) { 2680 arr.add(duplicatePdfObject((PdfObject)it.next(), newReader)); 2681 } 2682 return arr; 2683 } 2684 case PdfObject.INDIRECT: { 2685 PRIndirectReference org = (PRIndirectReference)original; 2686 return new PRIndirectReference(newReader, org.getNumber(), org.getGeneration()); 2687 } 2688 default: 2689 return original; 2690 } 2691 } 2692 2693 2696 public void close() { 2697 if (!partial) 2698 return; 2699 try { 2700 tokens.close(); 2701 } 2702 catch (IOException e) { 2703 throw new ExceptionConverter(e); 2704 } 2705 } 2706 2707 protected void removeUnusedNode(PdfObject obj, boolean hits[]) { 2708 Stack state = new Stack (); 2709 state.push(obj); 2710 while (!state.empty()) { 2711 Object current = state.pop(); 2712 if (current == null) 2713 continue; 2714 ArrayList ar = null; 2715 PdfDictionary dic = null; 2716 PdfName[] keys = null; 2717 Object [] objs = null; 2718 int idx = 0; 2719 if (current instanceof PdfObject) { 2720 obj = (PdfObject)current; 2721 switch (obj.type()) { 2722 case PdfObject.DICTIONARY: 2723 case PdfObject.STREAM: 2724 dic = (PdfDictionary)obj; 2725 keys = new PdfName[dic.size()]; 2726 dic.getKeys().toArray(keys); 2727 break; 2728 case PdfObject.ARRAY: 2729 ar = ((PdfArray)obj).getArrayList(); 2730 break; 2731 case PdfObject.INDIRECT: 2732 PRIndirectReference ref = (PRIndirectReference)obj; 2733 int num = ref.getNumber(); 2734 if (!hits[num]) { 2735 hits[num] = true; 2736 state.push(getPdfObjectRelease(ref)); 2737 } 2738 continue; 2739 default: 2740 continue; 2741 } 2742 } 2743 else { 2744 objs = (Object [])current; 2745 if (objs[0] instanceof ArrayList ) { 2746 ar = (ArrayList )objs[0]; 2747 idx = ((Integer )objs[1]).intValue(); 2748 } 2749 else { 2750 keys = (PdfName[])objs[0]; 2751 dic = (PdfDictionary)objs[1]; 2752 idx = ((Integer )objs[2]).intValue(); 2753 } 2754 } 2755 if (ar != null) { 2756 for (int k = idx; k < ar.size(); ++k) { 2757 PdfObject v = (PdfObject)ar.get(k); 2758 if (v.isIndirect()) { 2759 int num = ((PRIndirectReference)v).getNumber(); 2760 if (num >= xrefObj.size() || (!partial && xrefObj.get(num) == null)) { 2761 ar.set(k, PdfNull.PDFNULL); 2762 continue; 2763 } 2764 } 2765 if (objs == null) 2766 state.push(new Object []{ar, new Integer (k + 1)}); 2767 else { 2768 objs[1] = new Integer (k + 1); 2769 state.push(objs); 2770 } 2771 state.push(v); 2772 break; 2773 } 2774 } 2775 else { 2776 for (int k = idx; k < keys.length; ++k) { 2777 PdfName key = keys[k]; 2778 PdfObject v = dic.get(key); 2779 if (v.isIndirect()) { 2780 int num = ((PRIndirectReference)v).getNumber(); 2781 if (num >= xrefObj.size() || (!partial && xrefObj.get(num) == null)) { 2782 dic.put(key, PdfNull.PDFNULL); 2783 continue; 2784 } 2785 } 2786 if (objs == null) 2787 state.push(new Object []{keys, dic, new Integer (k + 1)}); 2788 else { 2789 objs[2] = new Integer (k + 1); 2790 state.push(objs); 2791 } 2792 state.push(v); 2793 break; 2794 } 2795 } 2796 } 2797 } 2798 2799 2802 public int removeUnusedObjects() { 2803 boolean hits[] = new boolean[xrefObj.size()]; 2804 removeUnusedNode(trailer, hits); 2805 int total = 0; 2806 if (partial) { 2807 for (int k = 1; k < hits.length; ++k) { 2808 if (!hits[k]) { 2809 xref[k * 2] = -1; 2810 xref[k * 2 + 1] = 0; 2811 xrefObj.set(k, null); 2812 ++total; 2813 } 2814 } 2815 } 2816 else { 2817 for (int k = 1; k < hits.length; ++k) { 2818 if (!hits[k]) { 2819 xrefObj.set(k, null); 2820 ++total; 2821 } 2822 } 2823 } 2824 return total; 2825 } 2826 2827 2830 public AcroFields getAcroFields() { 2831 return new AcroFields(this, null); 2832 } 2833 2834 2840 public String getJavaScript(RandomAccessFileOrArray file) throws IOException { 2841 PdfDictionary names = (PdfDictionary)getPdfObjectRelease(catalog.get(PdfName.NAMES)); 2842 if (names == null) 2843 return null; 2844 PdfDictionary js = (PdfDictionary)getPdfObjectRelease(names.get(PdfName.JAVASCRIPT)); 2845 if (js == null) 2846 return null; 2847 HashMap jscript = PdfNameTree.readTree(js); 2848 String sortedNames[] = new String [jscript.size()]; 2849 sortedNames = (String [])jscript.keySet().toArray(sortedNames); 2850 Arrays.sort(sortedNames); 2851 StringBuffer buf = new StringBuffer (); 2852 for (int k = 0; k < sortedNames.length; ++k) { 2853 PdfDictionary j = (PdfDictionary)getPdfObjectRelease((PdfIndirectReference)jscript.get(sortedNames[k])); 2854 if (j == null) 2855 continue; 2856 PdfObject obj = getPdfObjectRelease(j.get(PdfName.JS)); 2857 if (obj != null) { 2858 if (obj.isString()) 2859 buf.append(((PdfString)obj).toUnicodeString()).append('\n'); 2860 else if (obj.isStream()) { 2861 byte bytes[] = getStreamBytes((PRStream)obj, file); 2862 if (bytes.length >= 2 && bytes[0] == (byte)254 && bytes[1] == (byte)255) 2863 buf.append(PdfEncodings.convertToString(bytes, PdfObject.TEXT_UNICODE)); 2864 else 2865 buf.append(PdfEncodings.convertToString(bytes, PdfObject.TEXT_PDFDOCENCODING)); 2866 buf.append('\n'); 2867 } 2868 } 2869 } 2870 return buf.toString(); 2871 } 2872 2873 2878 public String getJavaScript() throws IOException { 2879 RandomAccessFileOrArray rf = getSafeFile(); 2880 try { 2881 rf.reOpen(); 2882 return getJavaScript(rf); 2883 } 2884 finally { 2885 try{rf.close();}catch(Exception e){} 2886 } 2887 } 2888 2889 2895 public void selectPages(String ranges) { 2896 selectPages(SequenceList.expand(ranges, getNumberOfPages())); 2897 } 2898 2899 2905 public void selectPages(List pagesToKeep) { 2906 pageRefs.selectPages(pagesToKeep); 2907 removeUnusedObjects(); 2908 } 2909 2910 2914 public void setViewerPreferences(int preferences) { 2915 this.viewerPreferences.setViewerPreferences(preferences); 2916 setViewerPreferences(this.viewerPreferences); 2917 } 2918 2919 2924 2925 public void addViewerPreference(PdfName key, PdfObject value) { 2926 this.viewerPreferences.addViewerPreference(key, value); 2927 setViewerPreferences(this.viewerPreferences); 2928 } 2929 2930 void setViewerPreferences(PdfViewerPreferencesImp vp) { 2931 vp.addToCatalog(catalog); 2932 } 2933 2934 2937 public int getSimpleViewerPreferences() { 2938 return PdfViewerPreferencesImp.getViewerPreferences(catalog).getPageLayoutAndMode(); 2939 } 2940 2941 2945 public boolean isAppendable() { 2946 return this.appendable; 2947 } 2948 2949 2953 public void setAppendable(boolean appendable) { 2954 this.appendable = appendable; 2955 if (appendable) 2956 getPdfObject(trailer.get(PdfName.ROOT)); 2957 } 2958 2959 2963 public boolean isNewXrefType() { 2964 return newXrefType; 2965 } 2966 2967 2971 public int getFileLength() { 2972 return fileLength; 2973 } 2974 2975 2979 public boolean isHybridXref() { 2980 return hybridXref; 2981 } 2982 2983 static class PageRefs { 2984 private PdfReader reader; 2985 private IntHashtable refsp; 2986 private ArrayList refsn; 2987 private ArrayList pageInh; 2988 private int lastPageRead = -1; 2989 private int sizep; 2990 2991 private PageRefs(PdfReader reader) throws IOException { 2992 this.reader = reader; 2993 if (reader.partial) { 2994 refsp = new IntHashtable(); 2995 PdfNumber npages = (PdfNumber)PdfReader.getPdfObjectRelease(reader.rootPages.get(PdfName.COUNT)); 2996 sizep = npages.intValue(); 2997 } 2998 else { 2999 readPages(); 3000 } 3001 } 3002 3003 PageRefs(PageRefs other, PdfReader reader) { 3004 this.reader = reader; 3005 this.sizep = other.sizep; 3006 if (other.refsn != null) { 3007 refsn = new ArrayList (other.refsn); 3008 for (int k = 0; k < refsn.size(); ++k) { 3009 refsn.set(k, duplicatePdfObject((PdfObject)refsn.get(k), reader)); 3010 } 3011 } 3012 else 3013 this.refsp = (IntHashtable)other.refsp.clone(); 3014 } 3015 3016 int size() { 3017 if (refsn != null) 3018 return refsn.size(); 3019 else 3020 return sizep; 3021 } 3022 3023 void readPages() throws IOException { 3024 if (refsn != null) 3025 return; 3026 refsp = null; 3027 refsn = new ArrayList (); 3028 pageInh = new ArrayList (); 3029 iteratePages((PRIndirectReference)reader.catalog.get(PdfName.PAGES)); 3030 pageInh = null; 3031 reader.rootPages.put(PdfName.COUNT, new PdfNumber(refsn.size())); 3032 } 3033 3034 void reReadPages() throws IOException { 3035 refsn = null; 3036 readPages(); 3037 } 3038 3039 3043 public PdfDictionary getPageN(int pageNum) { 3044 PRIndirectReference ref = getPageOrigRef(pageNum); 3045 return (PdfDictionary)PdfReader.getPdfObject(ref); 3046 } 3047 3048 3052 public PdfDictionary getPageNRelease(int pageNum) { 3053 PdfDictionary page = getPageN(pageNum); 3054 releasePage(pageNum); 3055 return page; 3056 } 3057 3058 3062 public PRIndirectReference getPageOrigRefRelease(int pageNum) { 3063 PRIndirectReference ref = getPageOrigRef(pageNum); 3064 releasePage(pageNum); 3065 return ref; 3066 } 3067 3068 3072 public PRIndirectReference getPageOrigRef(int pageNum) { 3073 try { 3074 --pageNum; 3075 if (pageNum < 0 || pageNum >= size()) 3076 return null; 3077 if (refsn != null) 3078 return (PRIndirectReference)refsn.get(pageNum); 3079 else { 3080 int n = refsp.get(pageNum); 3081 if (n == 0) { 3082 PRIndirectReference ref = getSinglePage(pageNum); 3083 if (reader.lastXrefPartial == -1) 3084 lastPageRead = -1; 3085 else 3086 lastPageRead = pageNum; 3087 reader.lastXrefPartial = -1; 3088 refsp.put(pageNum, ref.getNumber()); 3089 return ref; 3090 } 3091 else { 3092 if (lastPageRead != pageNum) 3093 lastPageRead = -1; 3094 return new PRIndirectReference(reader, n); 3095 } 3096 } 3097 } 3098 catch (Exception e) { 3099 throw new ExceptionConverter(e); 3100 } 3101 } 3102 3103 3106 public void releasePage(int pageNum) { 3107 if (refsp == null) 3108 return; 3109 --pageNum; 3110 if (pageNum < 0 || pageNum >= size()) 3111 return; 3112 if (pageNum != lastPageRead) 3113 return; 3114 lastPageRead = -1; 3115 reader.lastXrefPartial = refsp.get(pageNum); 3116 reader.releaseLastXrefPartial(); 3117 refsp.remove(pageNum); 3118 } 3119 3120 3123 public void resetReleasePage() { 3124 if (refsp == null) 3125 return; 3126 lastPageRead = -1; 3127 } 3128 3129 void insertPage(int pageNum, PRIndirectReference ref) { 3130 --pageNum; 3131 if (refsn != null) { 3132 if (pageNum >= refsn.size()) 3133 refsn.add(ref); 3134 else 3135 refsn.add(pageNum, ref); 3136 } 3137 else { 3138 ++sizep; 3139 lastPageRead = -1; 3140 if (pageNum >= size()) { 3141 refsp.put(size(), ref.getNumber()); 3142 } 3143 else { 3144 IntHashtable refs2 = new IntHashtable((refsp.size() + 1) * 2); 3145 for (Iterator it = refsp.getEntryIterator(); it.hasNext();) { 3146 IntHashtable.Entry entry = (IntHashtable.Entry)it.next(); 3147 int p = entry.getKey(); 3148 refs2.put(p >= pageNum ? p + 1 : p, entry.getValue()); 3149 } 3150 refs2.put(pageNum, ref.getNumber()); 3151 refsp = refs2; 3152 } 3153 } 3154 } 3155 3156 private void pushPageAttributes(PdfDictionary nodePages) { 3157 PdfDictionary dic = new PdfDictionary(); 3158 if (!pageInh.isEmpty()) { 3159 dic.putAll((PdfDictionary)pageInh.get(pageInh.size() - 1)); 3160 } 3161 for (int k = 0; k < pageInhCandidates.length; ++k) { 3162 PdfObject obj = nodePages.get(pageInhCandidates[k]); 3163 if (obj != null) 3164 dic.put(pageInhCandidates[k], obj); 3165 } 3166 pageInh.add(dic); 3167 } 3168 3169 private void popPageAttributes() { 3170 pageInh.remove(pageInh.size() - 1); 3171 } 3172 3173 private void iteratePages(PRIndirectReference rpage) throws IOException { 3174 PdfDictionary page = (PdfDictionary)getPdfObject(rpage); 3175 PdfArray kidsPR = (PdfArray)getPdfObject(page.get(PdfName.KIDS)); 3176 if (kidsPR == null) { 3177 page.put(PdfName.TYPE, PdfName.PAGE); 3178 PdfDictionary dic = (PdfDictionary)pageInh.get(pageInh.size() - 1); 3179 PdfName key; 3180 for (Iterator i = dic.getKeys().iterator(); i.hasNext();) { 3181 key = (PdfName)i.next(); 3182 if (page.get(key) == null) 3183 page.put(key, dic.get(key)); 3184 } 3185 if (page.get(PdfName.MEDIABOX) == null) { 3186 PdfArray arr = new PdfArray(new float[]{0,0,PageSize.LETTER.getRight(),PageSize.LETTER.getTop()}); 3187 page.put(PdfName.MEDIABOX, arr); 3188 } 3189 refsn.add(rpage); 3190 } 3191 else { 3192 page.put(PdfName.TYPE, PdfName.PAGES); 3193 pushPageAttributes(page); 3194 ArrayList kids = kidsPR.getArrayList(); 3195 for (int k = 0; k < kids.size(); ++k){ 3196 PdfObject obj = (PdfObject)kids.get(k); 3197 if (!obj.isIndirect()) { 3198 while (k < kids.size()) 3199 kids.remove(k); 3200 break; 3201 } 3202 iteratePages((PRIndirectReference)obj); 3203 } 3204 popPageAttributes(); 3205 } 3206 } 3207 3208 protected PRIndirectReference getSinglePage(int n) { 3209 PdfDictionary acc = new PdfDictionary(); 3210 PdfDictionary top = reader.rootPages; 3211 int base = 0; 3212 while (true) { 3213 for (int k = 0; k < pageInhCandidates.length; ++k) { 3214 PdfObject obj = top.get(pageInhCandidates[k]); 3215 if (obj != null) 3216 acc.put(pageInhCandidates[k], obj); 3217 } 3218 PdfArray kids = (PdfArray)PdfReader.getPdfObjectRelease(top.get(PdfName.KIDS)); 3219 for (Iterator it = kids.listIterator(); it.hasNext();) { 3220 PRIndirectReference ref = (PRIndirectReference)it.next(); 3221 PdfDictionary dic = (PdfDictionary)getPdfObject(ref); 3222 int last = reader.lastXrefPartial; 3223 PdfObject count = getPdfObjectRelease(dic.get(PdfName.COUNT)); 3224 reader.lastXrefPartial = last; 3225 int acn = 1; 3226 if (count != null && count.type() == PdfObject.NUMBER) 3227 acn = ((PdfNumber)count).intValue(); 3228 if (n < base + acn) { 3229 if (count == null) { 3230 dic.mergeDifferent(acc); 3231 return ref; 3232 } 3233 reader.releaseLastXrefPartial(); 3234 top = dic; 3235 break; 3236 } 3237 reader.releaseLastXrefPartial(); 3238 base += acn; 3239 } 3240 } 3241 } 3242 3243 private void selectPages(List pagesToKeep) { 3244 IntHashtable pg = new IntHashtable(); 3245 ArrayList finalPages = new ArrayList (); 3246 int psize = size(); 3247 for (Iterator it = pagesToKeep.iterator(); it.hasNext();) { 3248 Integer pi = (Integer )it.next(); 3249 int p = pi.intValue(); 3250 if (p >= 1 && p <= psize && pg.put(p, 1) == 0) 3251 finalPages.add(pi); 3252 } 3253 if (reader.partial) { 3254 for (int k = 1; k <= psize; ++k) { 3255 getPageOrigRef(k); 3256 resetReleasePage(); 3257 } 3258 } 3259 PRIndirectReference parent = (PRIndirectReference)reader.catalog.get(PdfName.PAGES); 3260 PdfDictionary topPages = (PdfDictionary)PdfReader.getPdfObject(parent); 3261 ArrayList newPageRefs = new ArrayList (finalPages.size()); 3262 PdfArray kids = new PdfArray(); 3263 for (int k = 0; k < finalPages.size(); ++k) { 3264 int p = ((Integer )finalPages.get(k)).intValue(); 3265 PRIndirectReference pref = getPageOrigRef(p); 3266 resetReleasePage(); 3267 kids.add(pref); 3268 newPageRefs.add(pref); 3269 getPageN(p).put(PdfName.PARENT, parent); 3270 } 3271 AcroFields af = reader.getAcroFields(); 3272 boolean removeFields = (af.getFields().size() > 0); 3273 for (int k = 1; k <= psize; ++k) { 3274 if (!pg.containsKey(k)) { 3275 if (removeFields) 3276 af.removeFieldsFromPage(k); 3277 PRIndirectReference pref = getPageOrigRef(k); 3278 int nref = pref.getNumber(); 3279 reader.xrefObj.set(nref, null); 3280 if (reader.partial) { 3281 reader.xref[nref * 2] = -1; 3282 reader.xref[nref * 2 + 1] = 0; 3283 } 3284 } 3285 } 3286 topPages.put(PdfName.COUNT, new PdfNumber(finalPages.size())); 3287 topPages.put(PdfName.KIDS, kids); 3288 refsp = null; 3289 refsn = newPageRefs; 3290 } 3291 } 3292 3293 PdfIndirectReference getCryptoRef() { 3294 if (cryptoRef == null) 3295 return null; 3296 return new PdfIndirectReference(0, cryptoRef.getNumber(), cryptoRef.getGeneration()); 3297 } 3298 3299 3304 public void removeUsageRights() { 3305 PdfDictionary perms = (PdfDictionary)getPdfObject(catalog.get(PdfName.PERMS)); 3306 if (perms == null) 3307 return; 3308 perms.remove(PdfName.UR); 3309 perms.remove(PdfName.UR3); 3310 if (perms.size() == 0) 3311 catalog.remove(PdfName.PERMS); 3312 } 3313 3314 3324 public int getCertificationLevel() { 3325 PdfDictionary dic = (PdfDictionary)getPdfObject(catalog.get(PdfName.PERMS)); 3326 if (dic == null) 3327 return PdfSignatureAppearance.NOT_CERTIFIED; 3328 dic = (PdfDictionary)getPdfObject(dic.get(PdfName.DOCMDP)); 3329 if (dic == null) 3330 return PdfSignatureAppearance.NOT_CERTIFIED; 3331 PdfArray arr = (PdfArray)getPdfObject(dic.get(PdfName.REFERENCE)); 3332 if (arr == null || arr.size() == 0) 3333 return PdfSignatureAppearance.NOT_CERTIFIED; 3334 dic = (PdfDictionary)getPdfObject((PdfObject)(arr.getArrayList().get(0))); 3335 if (dic == null) 3336 return PdfSignatureAppearance.NOT_CERTIFIED; 3337 dic = (PdfDictionary)getPdfObject(dic.get(PdfName.TRANSFORMPARAMS)); 3338 if (dic == null) 3339 return PdfSignatureAppearance.NOT_CERTIFIED; 3340 PdfNumber p = (PdfNumber)getPdfObject(dic.get(PdfName.P)); 3341 if (p == null) 3342 return PdfSignatureAppearance.NOT_CERTIFIED; 3343 return p.intValue(); 3344 } 3345 3346 3353 public final boolean isOpenedWithFullPermissions() { 3354 return !encrypted || ownerPasswordUsed; 3355 } 3356 3357 public int getCryptoMode() { 3358 if (decrypt == null) 3359 return -1; 3360 else 3361 return decrypt.getCryptoMode(); 3362 } 3363 3364 public boolean isMetadataEncrypted() { 3365 if (decrypt == null) 3366 return false; 3367 else 3368 return decrypt.isMetadataEncrypted(); 3369 } 3370 3371 public byte[] computeUserPassword() { 3372 if (!encrypted || !ownerPasswordUsed) return null; 3373 return decrypt.computeUserPassword(password); 3374 } 3375} | Popular Tags |