1 49 50 package com.lowagie.text.pdf; 51 52 import java.awt.Color ; 53 import java.io.ByteArrayOutputStream ; 54 import java.io.IOException ; 55 import java.io.OutputStream ; 56 import java.util.ArrayList ; 57 import java.util.HashMap ; 58 import java.util.HashSet ; 59 import java.util.Iterator ; 60 import java.util.Map ; 61 import java.util.TreeMap ; 62 import java.util.TreeSet ; 63 import java.security.cert.Certificate ; 64 65 import com.lowagie.text.DocListener; 66 import com.lowagie.text.DocWriter; 67 import com.lowagie.text.Document; 68 import com.lowagie.text.DocumentException; 69 import com.lowagie.text.ExceptionConverter; 70 import com.lowagie.text.Image; 71 import com.lowagie.text.ImgWMF; 72 import com.lowagie.text.Rectangle; 73 import com.lowagie.text.Table; 74 import com.lowagie.text.pdf.collection.PdfCollection; 75 import com.lowagie.text.pdf.events.PdfPageEventForwarder; 76 import com.lowagie.text.pdf.interfaces.PdfAnnotations; 77 import com.lowagie.text.pdf.interfaces.PdfDocumentActions; 78 import com.lowagie.text.pdf.interfaces.PdfEncryptionSettings; 79 import com.lowagie.text.pdf.interfaces.PdfPageActions; 80 import com.lowagie.text.pdf.interfaces.PdfVersion; 81 import com.lowagie.text.pdf.interfaces.PdfViewerPreferences; 82 import com.lowagie.text.pdf.interfaces.PdfXConformance; 83 import com.lowagie.text.pdf.interfaces.PdfRunDirection; 84 import com.lowagie.text.pdf.internal.PdfVersionImp; 85 import com.lowagie.text.pdf.internal.PdfXConformanceImp; 86 import com.lowagie.text.xml.xmp.XmpWriter; 87 88 95 96 public class PdfWriter extends DocWriter implements 97 PdfViewerPreferences, 98 PdfEncryptionSettings, 99 PdfVersion, 100 PdfDocumentActions, 101 PdfPageActions, 102 PdfXConformance, 103 PdfRunDirection, 104 PdfAnnotations { 105 106 108 119 120 public static class PdfBody { 121 122 124 127 128 static class PdfCrossReference implements Comparable { 129 130 private int type; 132 133 134 private int offset; 135 136 private int refnum; 137 138 private int generation; 139 140 147 148 PdfCrossReference(int refnum, int offset, int generation) { 149 type = 0; 150 this.offset = offset; 151 this.refnum = refnum; 152 this.generation = generation; 153 } 154 155 160 161 PdfCrossReference(int refnum, int offset) { 162 type = 1; 163 this.offset = offset; 164 this.refnum = refnum; 165 this.generation = 0; 166 } 167 168 PdfCrossReference(int type, int refnum, int offset, int generation) { 169 this.type = type; 170 this.offset = offset; 171 this.refnum = refnum; 172 this.generation = generation; 173 } 174 175 int getRefnum() { 176 return refnum; 177 } 178 179 184 185 public void toPdf(OutputStream os) throws IOException { 186 StringBuffer off = new StringBuffer ("0000000000").append(offset); 187 off.delete(0, off.length() - 10); 188 StringBuffer gen = new StringBuffer ("00000").append(generation); 189 gen.delete(0, gen.length() - 5); 190 191 off.append(' ').append(gen).append(generation == 65535 ? " f \n" : " n \n"); 192 os.write(getISOBytes(off.toString())); 193 } 194 195 201 public void toPdf(int midSize, OutputStream os) throws IOException { 202 os.write((byte)type); 203 while (--midSize >= 0) 204 os.write((byte)((offset >>> (8 * midSize)) & 0xff)); 205 os.write((byte)((generation >>> 8) & 0xff)); 206 os.write((byte)(generation & 0xff)); 207 } 208 209 212 public int compareTo(Object o) { 213 PdfCrossReference other = (PdfCrossReference)o; 214 return (refnum < other.refnum ? -1 : (refnum==other.refnum ? 0 : 1)); 215 } 216 217 220 public boolean equals(Object obj) { 221 if (obj instanceof PdfCrossReference) { 222 PdfCrossReference other = (PdfCrossReference)obj; 223 return (refnum == other.refnum); 224 } 225 else 226 return false; 227 } 228 229 232 public int hashCode() { 233 return refnum; 234 } 235 236 } 237 238 private static final int OBJSINSTREAM = 200; 239 240 242 243 private TreeSet xrefs; 244 private int refnum; 245 246 private int position; 247 private PdfWriter writer; 248 private ByteBuffer index; 249 private ByteBuffer streamObjects; 250 private int currentObjNum; 251 private int numObj = 0; 252 253 255 259 PdfBody(PdfWriter writer) { 260 xrefs = new TreeSet (); 261 xrefs.add(new PdfCrossReference(0, 0, 65535)); 262 position = writer.getOs().getCounter(); 263 refnum = 1; 264 this.writer = writer; 265 } 266 267 269 void setRefnum(int refnum) { 270 this.refnum = refnum; 271 } 272 273 private PdfWriter.PdfBody.PdfCrossReference addToObjStm(PdfObject obj, int nObj) throws IOException { 274 if (numObj >= OBJSINSTREAM) 275 flushObjStm(); 276 if (index == null) { 277 index = new ByteBuffer(); 278 streamObjects = new ByteBuffer(); 279 currentObjNum = getIndirectReferenceNumber(); 280 numObj = 0; 281 } 282 int p = streamObjects.size(); 283 int idx = numObj++; 284 PdfEncryption enc = writer.crypto; 285 writer.crypto = null; 286 obj.toPdf(writer, streamObjects); 287 writer.crypto = enc; 288 streamObjects.append(' '); 289 index.append(nObj).append(' ').append(p).append(' '); 290 return new PdfWriter.PdfBody.PdfCrossReference(2, nObj, currentObjNum, idx); 291 } 292 293 private void flushObjStm() throws IOException { 294 if (numObj == 0) 295 return; 296 int first = index.size(); 297 index.append(streamObjects); 298 PdfStream stream = new PdfStream(index.toByteArray()); 299 stream.flateCompress(); 300 stream.put(PdfName.TYPE, PdfName.OBJSTM); 301 stream.put(PdfName.N, new PdfNumber(numObj)); 302 stream.put(PdfName.FIRST, new PdfNumber(first)); 303 add(stream, currentObjNum); 304 index = null; 305 streamObjects = null; 306 numObj = 0; 307 } 308 309 322 323 PdfIndirectObject add(PdfObject object) throws IOException { 324 return add(object, getIndirectReferenceNumber()); 325 } 326 327 PdfIndirectObject add(PdfObject object, boolean inObjStm) throws IOException { 328 return add(object, getIndirectReferenceNumber(), inObjStm); 329 } 330 331 335 336 PdfIndirectReference getPdfIndirectReference() { 337 return new PdfIndirectReference(0, getIndirectReferenceNumber()); 338 } 339 340 int getIndirectReferenceNumber() { 341 int n = refnum++; 342 xrefs.add(new PdfCrossReference(n, 0, 65536)); 343 return n; 344 } 345 346 361 362 PdfIndirectObject add(PdfObject object, PdfIndirectReference ref) throws IOException { 363 return add(object, ref.getNumber()); 364 } 365 366 PdfIndirectObject add(PdfObject object, PdfIndirectReference ref, boolean inObjStm) throws IOException { 367 return add(object, ref.getNumber(), inObjStm); 368 } 369 370 PdfIndirectObject add(PdfObject object, int refNumber) throws IOException { 371 return add(object, refNumber, true); } 373 374 PdfIndirectObject add(PdfObject object, int refNumber, boolean inObjStm) throws IOException { 375 if (inObjStm && object.canBeInObjStm() && writer.isFullCompression()) { 376 PdfCrossReference pxref = addToObjStm(object, refNumber); 377 PdfIndirectObject indirect = new PdfIndirectObject(refNumber, object, writer); 378 if (!xrefs.add(pxref)) { 379 xrefs.remove(pxref); 380 xrefs.add(pxref); 381 } 382 return indirect; 383 } 384 else { 385 PdfIndirectObject indirect = new PdfIndirectObject(refNumber, object, writer); 386 PdfCrossReference pxref = new PdfCrossReference(refNumber, position); 387 if (!xrefs.add(pxref)) { 388 xrefs.remove(pxref); 389 xrefs.add(pxref); 390 } 391 indirect.writeTo(writer.getOs()); 392 position = writer.getOs().getCounter(); 393 return indirect; 394 } 395 } 396 397 402 403 int offset() { 404 return position; 405 } 406 407 412 413 int size() { 414 return Math.max(((PdfCrossReference)xrefs.last()).getRefnum() + 1, refnum); 415 } 416 417 427 428 void writeCrossReferenceTable(OutputStream os, PdfIndirectReference root, PdfIndirectReference info, PdfIndirectReference encryption, PdfObject fileID, int prevxref) throws IOException { 429 int refNumber = 0; 430 if (writer.isFullCompression()) { 431 flushObjStm(); 432 refNumber = getIndirectReferenceNumber(); 433 xrefs.add(new PdfCrossReference(refNumber, position)); 434 } 435 PdfCrossReference entry = (PdfCrossReference)xrefs.first(); 436 int first = entry.getRefnum(); 437 int len = 0; 438 ArrayList sections = new ArrayList (); 439 for (Iterator i = xrefs.iterator(); i.hasNext(); ) { 440 entry = (PdfCrossReference)i.next(); 441 if (first + len == entry.getRefnum()) 442 ++len; 443 else { 444 sections.add(new Integer (first)); 445 sections.add(new Integer (len)); 446 first = entry.getRefnum(); 447 len = 1; 448 } 449 } 450 sections.add(new Integer (first)); 451 sections.add(new Integer (len)); 452 if (writer.isFullCompression()) { 453 int mid = 4; 454 int mask = 0xff000000; 455 for (; mid > 1; --mid) { 456 if ((mask & position) != 0) 457 break; 458 mask >>>= 8; 459 } 460 ByteBuffer buf = new ByteBuffer(); 461 462 for (Iterator i = xrefs.iterator(); i.hasNext(); ) { 463 entry = (PdfCrossReference) i.next(); 464 entry.toPdf(mid, buf); 465 } 466 PdfStream xr = new PdfStream(buf.toByteArray()); 467 buf = null; 468 xr.flateCompress(); 469 xr.put(PdfName.SIZE, new PdfNumber(size())); 470 xr.put(PdfName.ROOT, root); 471 if (info != null) { 472 xr.put(PdfName.INFO, info); 473 } 474 if (encryption != null) 475 xr.put(PdfName.ENCRYPT, encryption); 476 if (fileID != null) 477 xr.put(PdfName.ID, fileID); 478 xr.put(PdfName.W, new PdfArray(new int[]{1, mid, 2})); 479 xr.put(PdfName.TYPE, PdfName.XREF); 480 PdfArray idx = new PdfArray(); 481 for (int k = 0; k < sections.size(); ++k) 482 idx.add(new PdfNumber(((Integer )sections.get(k)).intValue())); 483 xr.put(PdfName.INDEX, idx); 484 if (prevxref > 0) 485 xr.put(PdfName.PREV, new PdfNumber(prevxref)); 486 PdfEncryption enc = writer.crypto; 487 writer.crypto = null; 488 PdfIndirectObject indirect = new PdfIndirectObject(refNumber, xr, writer); 489 indirect.writeTo(writer.getOs()); 490 writer.crypto = enc; 491 } 492 else { 493 os.write(getISOBytes("xref\n")); 494 Iterator i = xrefs.iterator(); 495 for (int k = 0; k < sections.size(); k += 2) { 496 first = ((Integer )sections.get(k)).intValue(); 497 len = ((Integer )sections.get(k + 1)).intValue(); 498 os.write(getISOBytes(String.valueOf(first))); 499 os.write(getISOBytes(" ")); 500 os.write(getISOBytes(String.valueOf(len))); 501 os.write('\n'); 502 while (len-- > 0) { 503 entry = (PdfCrossReference) i.next(); 504 entry.toPdf(os); 505 } 506 } 507 } 508 } 509 } 510 511 517 518 static class PdfTrailer extends PdfDictionary { 519 520 522 int offset; 523 524 526 537 538 PdfTrailer(int size, int offset, PdfIndirectReference root, PdfIndirectReference info, PdfIndirectReference encryption, PdfObject fileID, int prevxref) { 539 this.offset = offset; 540 put(PdfName.SIZE, new PdfNumber(size)); 541 put(PdfName.ROOT, root); 542 if (info != null) { 543 put(PdfName.INFO, info); 544 } 545 if (encryption != null) 546 put(PdfName.ENCRYPT, encryption); 547 if (fileID != null) 548 put(PdfName.ID, fileID); 549 if (prevxref > 0) 550 put(PdfName.PREV, new PdfNumber(prevxref)); 551 } 552 553 559 public void toPdf(PdfWriter writer, OutputStream os) throws IOException { 560 os.write(getISOBytes("trailer\n")); 561 super.toPdf(null, os); 562 os.write(getISOBytes("\nstartxref\n")); 563 os.write(getISOBytes(String.valueOf(offset))); 564 os.write(getISOBytes("\n%%EOF\n")); 565 } 566 } 567 568 570 572 575 protected PdfWriter() { 576 } 577 578 587 588 protected PdfWriter(PdfDocument document, OutputStream os) { 589 super(document, os); 590 pdf = document; 591 directContent = new PdfContentByte(this); 592 directContentUnder = new PdfContentByte(this); 593 } 594 595 604 605 public static PdfWriter getInstance(Document document, OutputStream os) 606 throws DocumentException { 607 PdfDocument pdf = new PdfDocument(); 608 document.addDocListener(pdf); 609 PdfWriter writer = new PdfWriter(pdf, os); 610 pdf.addWriter(writer); 611 return writer; 612 } 613 614 623 624 public static PdfWriter getInstance(Document document, OutputStream os, DocListener listener) 625 throws DocumentException { 626 PdfDocument pdf = new PdfDocument(); 627 pdf.addDocListener(listener); 628 document.addDocListener(pdf); 629 PdfWriter writer = new PdfWriter(pdf, os); 630 pdf.addWriter(writer); 631 return writer; 632 } 633 634 636 637 protected PdfDocument pdf; 638 639 643 644 PdfDocument getPdfDocument() { 645 return pdf; 646 } 647 648 653 public PdfDictionary getInfo() { 654 return pdf.getInfo(); 655 } 656 657 664 public float getVerticalPosition(boolean ensureNewLine) { 665 return pdf.getVerticalPosition(ensureNewLine); 666 } 667 668 670 678 679 680 protected PdfContentByte directContent; 681 682 683 protected PdfContentByte directContentUnder; 684 685 691 692 public PdfContentByte getDirectContent() { 693 if (!open) 694 throw new RuntimeException ("The document is not open."); 695 return directContent; 696 } 697 698 704 705 public PdfContentByte getDirectContentUnder() { 706 if (!open) 707 throw new RuntimeException ("The document is not open."); 708 return directContentUnder; 709 } 710 711 715 void resetContent() { 716 directContent.reset(); 717 directContentUnder.reset(); 718 } 719 720 722 729 730 731 protected PdfBody body; 732 733 738 739 void addLocalDestinations(TreeMap dest) throws IOException { 740 for (Iterator i = dest.entrySet().iterator(); i.hasNext();) { 741 Map.Entry entry = (Map.Entry ) i.next(); 742 String name = (String ) entry.getKey(); 743 Object obj[] = (Object []) entry.getValue(); 744 PdfDestination destination = (PdfDestination)obj[2]; 745 if (destination == null) 746 throw new RuntimeException ("The name '" + name + "' has no local destination."); 747 if (obj[1] == null) 748 obj[1] = getPdfIndirectReference(); 749 addToBody(destination, (PdfIndirectReference)obj[1]); 750 } 751 } 752 753 760 public PdfIndirectObject addToBody(PdfObject object) throws IOException { 761 PdfIndirectObject iobj = body.add(object); 762 return iobj; 763 } 764 765 773 public PdfIndirectObject addToBody(PdfObject object, boolean inObjStm) throws IOException { 774 PdfIndirectObject iobj = body.add(object, inObjStm); 775 return iobj; 776 } 777 778 786 public PdfIndirectObject addToBody(PdfObject object, PdfIndirectReference ref) throws IOException { 787 PdfIndirectObject iobj = body.add(object, ref); 788 return iobj; 789 } 790 791 800 public PdfIndirectObject addToBody(PdfObject object, PdfIndirectReference ref, boolean inObjStm) throws IOException { 801 PdfIndirectObject iobj = body.add(object, ref, inObjStm); 802 return iobj; 803 } 804 805 813 public PdfIndirectObject addToBody(PdfObject object, int refNumber) throws IOException { 814 PdfIndirectObject iobj = body.add(object, refNumber); 815 return iobj; 816 } 817 818 827 public PdfIndirectObject addToBody(PdfObject object, int refNumber, boolean inObjStm) throws IOException { 828 PdfIndirectObject iobj = body.add(object, refNumber, inObjStm); 829 return iobj; 830 } 831 832 838 839 public PdfIndirectReference getPdfIndirectReference() { 840 return body.getPdfIndirectReference(); 841 } 842 843 int getIndirectReferenceNumber() { 844 return body.getIndirectReferenceNumber(); 845 } 846 847 851 OutputStreamCounter getOs() { 852 return os; 853 } 854 855 856 858 865 866 protected PdfDictionary getCatalog(PdfIndirectReference rootObj) 867 { 868 PdfDictionary catalog = pdf.getCatalog(rootObj); 869 if (tagged) { 871 try { 872 getStructureTreeRoot().buildTree(); 873 } 874 catch (Exception e) { 875 throw new ExceptionConverter(e); 876 } 877 catalog.put(PdfName.STRUCTTREEROOT, structureTreeRoot.getReference()); 878 PdfDictionary mi = new PdfDictionary(); 879 mi.put(PdfName.MARKED, PdfBoolean.PDFTRUE); 880 if (userProperties) 881 mi.put(PdfName.USERPROPERTIES, PdfBoolean.PDFTRUE); 882 catalog.put(PdfName.MARKINFO, mi); 883 } 884 if (!documentOCG.isEmpty()) { 886 fillOCProperties(false); 887 catalog.put(PdfName.OCPROPERTIES, OCProperties); 888 } 889 return catalog; 890 } 891 892 893 protected PdfDictionary extraCatalog; 894 895 899 public PdfDictionary getExtraCatalog() { 900 if (extraCatalog == null) 901 extraCatalog = new PdfDictionary(); 902 return this.extraCatalog; 903 } 904 905 907 913 914 915 protected PdfPages root = new PdfPages(this); 916 917 protected ArrayList pageReferences = new ArrayList (); 918 919 protected int currentPageNumber = 1; 920 921 926 public void setLinearPageMode() { 927 root.setLinearMode(null); 928 } 929 930 939 public int reorderPages(int order[]) throws DocumentException { 940 return root.reorderPages(order); 941 } 942 943 952 public PdfIndirectReference getPageReference(int page) { 953 --page; 954 if (page < 0) 955 throw new IndexOutOfBoundsException ("The page numbers start at 1."); 956 PdfIndirectReference ref; 957 if (page < pageReferences.size()) { 958 ref = (PdfIndirectReference)pageReferences.get(page); 959 if (ref == null) { 960 ref = body.getPdfIndirectReference(); 961 pageReferences.set(page, ref); 962 } 963 } 964 else { 965 int empty = page - pageReferences.size(); 966 for (int k = 0; k < empty; ++k) 967 pageReferences.add(null); 968 ref = body.getPdfIndirectReference(); 969 pageReferences.add(ref); 970 } 971 return ref; 972 } 973 974 980 981 public int getPageNumber() { 982 return pdf.getPageNumber(); 983 } 984 985 PdfIndirectReference getCurrentPage() { 986 return getPageReference(currentPageNumber); 987 } 988 989 public int getCurrentPageNumber() { 990 return currentPageNumber; 991 } 992 993 1004 1005 PdfIndirectReference add(PdfPage page, PdfContents contents) throws PdfException { 1006 if (!open) { 1007 throw new PdfException("The document isn't open."); 1008 } 1009 PdfIndirectObject object; 1010 try { 1011 object = addToBody(contents); 1012 } 1013 catch(IOException ioe) { 1014 throw new ExceptionConverter(ioe); 1015 } 1016 page.add(object.getIndirectReference()); 1017 if (group != null) { 1019 page.put(PdfName.GROUP, group); 1020 group = null; 1021 } 1022 root.addPage(page); 1023 currentPageNumber++; 1024 return null; 1025 } 1026 1027 1029 1035 1036 1037 private PdfPageEvent pageEvent; 1038 1039 1043 1044 public void setPageEvent(PdfPageEvent event) { 1045 if (event == null) this.pageEvent = null; 1046 else if (this.pageEvent == null) this.pageEvent = event; 1047 else if (this.pageEvent instanceof PdfPageEventForwarder) ((PdfPageEventForwarder)this.pageEvent).addPageEvent(event); 1048 else { 1049 PdfPageEventForwarder forward = new PdfPageEventForwarder(); 1050 forward.addPageEvent(this.pageEvent); 1051 forward.addPageEvent(event); 1052 this.pageEvent = forward; 1053 } 1054 } 1055 1056 1062 1063 public PdfPageEvent getPageEvent() { 1064 return pageEvent; 1065 } 1066 1067 1069 1070 protected int prevxref = 0; 1071 1072 1080 public void open() { 1081 super.open(); 1082 try { 1083 pdf_version.writeHeader(os); 1084 body = new PdfBody(this); 1085 if (pdfxConformance.isPdfX32002()) { 1086 PdfDictionary sec = new PdfDictionary(); 1087 sec.put(PdfName.GAMMA, new PdfArray(new float[]{2.2f,2.2f,2.2f})); 1088 sec.put(PdfName.MATRIX, new PdfArray(new float[]{0.4124f,0.2126f,0.0193f,0.3576f,0.7152f,0.1192f,0.1805f,0.0722f,0.9505f})); 1089 sec.put(PdfName.WHITEPOINT, new PdfArray(new float[]{0.9505f,1f,1.089f})); 1090 PdfArray arr = new PdfArray(PdfName.CALRGB); 1091 arr.add(sec); 1092 setDefaultColorspace(PdfName.DEFAULTRGB, addToBody(arr).getIndirectReference()); 1093 } 1094 } 1095 catch(IOException ioe) { 1096 throw new ExceptionConverter(ioe); 1097 } 1098 } 1099 1100 1110 public synchronized void close() { 1111 if (open) { 1112 if ((currentPageNumber - 1) != pageReferences.size()) 1113 throw new RuntimeException ("The page " + pageReferences.size() + 1114 " was requested but the document has only " + (currentPageNumber - 1) + " pages."); 1115 pdf.close(); 1116 try { 1117 addSharedObjectsToBody(); 1118 PdfIndirectReference rootRef = root.writePageTree(); 1120 PdfDictionary catalog = getCatalog(rootRef); 1122 if (xmpMetadata != null) { 1124 PdfStream xmp = new PdfStream(xmpMetadata); 1125 xmp.put(PdfName.TYPE, PdfName.METADATA); 1126 xmp.put(PdfName.SUBTYPE, PdfName.XML); 1127 if (crypto != null && !crypto.isMetadataEncrypted()) { 1128 PdfArray ar = new PdfArray(); 1129 ar.add(PdfName.CRYPT); 1130 xmp.put(PdfName.FILTER, ar); 1131 } 1132 catalog.put(PdfName.METADATA, body.add(xmp).getIndirectReference()); 1133 } 1134 if (isPdfX()) { 1136 pdfxConformance.completeInfoDictionary(getInfo()); 1137 pdfxConformance.completeExtraCatalog(getExtraCatalog()); 1138 } 1139 if (extraCatalog != null) { 1141 catalog.mergeDifferent(extraCatalog); 1142 } 1143 1144 PdfIndirectObject indirectCatalog = addToBody(catalog, false); 1146 PdfIndirectObject infoObj = addToBody(getInfo(), false); 1148 1149 PdfIndirectReference encryption = null; 1151 PdfObject fileID = null; 1152 body.flushObjStm(); 1153 if (crypto != null) { 1154 PdfIndirectObject encryptionObject = addToBody(crypto.getEncryptionDictionary(), false); 1155 encryption = encryptionObject.getIndirectReference(); 1156 fileID = crypto.getFileID(); 1157 } 1158 else 1159 fileID = PdfEncryption.createInfoId(PdfEncryption.createDocumentId()); 1160 1161 body.writeCrossReferenceTable(os, indirectCatalog.getIndirectReference(), 1163 infoObj.getIndirectReference(), encryption, fileID, prevxref); 1164 1165 if (fullCompression) { 1168 os.write(getISOBytes("startxref\n")); 1169 os.write(getISOBytes(String.valueOf(body.offset()))); 1170 os.write(getISOBytes("\n%%EOF\n")); 1171 } 1172 else { 1173 PdfTrailer trailer = new PdfTrailer(body.size(), 1174 body.offset(), 1175 indirectCatalog.getIndirectReference(), 1176 infoObj.getIndirectReference(), 1177 encryption, 1178 fileID, prevxref); 1179 trailer.toPdf(this, os); 1180 } 1181 super.close(); 1182 } 1183 catch(IOException ioe) { 1184 throw new ExceptionConverter(ioe); 1185 } 1186 } 1187 } 1188 1189 protected void addSharedObjectsToBody() throws IOException { 1190 for (Iterator it = documentFonts.values().iterator(); it.hasNext();) { 1192 FontDetails details = (FontDetails)it.next(); 1193 details.writeFont(this); 1194 } 1195 for (Iterator it = formXObjects.values().iterator(); it.hasNext();) { 1197 Object objs[] = (Object [])it.next(); 1198 PdfTemplate template = (PdfTemplate)objs[1]; 1199 if (template != null && template.getIndirectReference() instanceof PRIndirectReference) 1200 continue; 1201 if (template != null && template.getType() == PdfTemplate.TYPE_TEMPLATE) { 1202 addToBody(template.getFormXObject(), template.getIndirectReference()); 1203 } 1204 } 1205 for (Iterator it = importedPages.values().iterator(); it.hasNext();) { 1207 currentPdfReaderInstance = (PdfReaderInstance)it.next(); 1208 currentPdfReaderInstance.writeAllPages(); 1209 } 1210 currentPdfReaderInstance = null; 1211 for (Iterator it = documentColors.values().iterator(); it.hasNext();) { 1213 ColorDetails color = (ColorDetails)it.next(); 1214 addToBody(color.getSpotColor(this), color.getIndirectReference()); 1215 } 1216 for (Iterator it = documentPatterns.keySet().iterator(); it.hasNext();) { 1218 PdfPatternPainter pat = (PdfPatternPainter)it.next(); 1219 addToBody(pat.getPattern(), pat.getIndirectReference()); 1220 } 1221 for (Iterator it = documentShadingPatterns.keySet().iterator(); it.hasNext();) { 1223 PdfShadingPattern shadingPattern = (PdfShadingPattern)it.next(); 1224 shadingPattern.addToBody(); 1225 } 1226 for (Iterator it = documentShadings.keySet().iterator(); it.hasNext();) { 1228 PdfShading shading = (PdfShading)it.next(); 1229 shading.addToBody(); 1230 } 1231 for (Iterator it = documentExtGState.entrySet().iterator(); it.hasNext();) { 1233 Map.Entry entry = (Map.Entry ) it.next(); 1234 PdfDictionary gstate = (PdfDictionary) entry.getKey(); 1235 PdfObject obj[] = (PdfObject[]) entry.getValue(); 1236 addToBody(gstate, (PdfIndirectReference)obj[1]); 1237 } 1238 for (Iterator it = documentProperties.entrySet().iterator(); it.hasNext();) { 1240 Map.Entry entry = (Map.Entry ) it.next(); 1241 Object prop = entry.getKey(); 1242 PdfObject[] obj = (PdfObject[]) entry.getValue(); 1243 if (prop instanceof PdfLayerMembership){ 1244 PdfLayerMembership layer = (PdfLayerMembership)prop; 1245 addToBody(layer.getPdfObject(), layer.getRef()); 1246 } 1247 else if ((prop instanceof PdfDictionary) && !(prop instanceof PdfLayer)){ 1248 addToBody((PdfDictionary)prop, (PdfIndirectReference)obj[1]); 1249 } 1250 } 1251 for (Iterator it = documentOCG.iterator(); it.hasNext();) { 1253 PdfOCG layer = (PdfOCG)it.next(); 1254 addToBody(layer.getPdfObject(), layer.getRef()); 1255 } 1256 } 1257 1258 1260 1262 1267 1268 public PdfOutline getRootOutline() { 1269 return directContent.getRootOutline(); 1270 } 1271 1272 1274 public static final char VERSION_1_2 = '2'; 1275 1276 public static final char VERSION_1_3 = '3'; 1277 1278 public static final char VERSION_1_4 = '4'; 1279 1280 public static final char VERSION_1_5 = '5'; 1281 1282 public static final char VERSION_1_6 = '6'; 1283 1284 public static final char VERSION_1_7 = '7'; 1285 1286 1287 public static final PdfName PDF_VERSION_1_2 = new PdfName("1.2"); 1288 1289 public static final PdfName PDF_VERSION_1_3 = new PdfName("1.3"); 1290 1291 public static final PdfName PDF_VERSION_1_4 = new PdfName("1.4"); 1292 1293 public static final PdfName PDF_VERSION_1_5 = new PdfName("1.5"); 1294 1295 public static final PdfName PDF_VERSION_1_6 = new PdfName("1.6"); 1296 1297 public static final PdfName PDF_VERSION_1_7 = new PdfName("1.7"); 1298 1299 1300 protected PdfVersionImp pdf_version = new PdfVersionImp(); 1301 1302 1303 public void setPdfVersion(char version) { 1304 pdf_version.setPdfVersion(version); 1305 } 1306 1307 1308 public void setAtLeastPdfVersion(char version) { 1309 pdf_version.setAtLeastPdfVersion(version); 1310 } 1311 1312 1313 public void setPdfVersion(PdfName version) { 1314 pdf_version.setPdfVersion(version); 1315 } 1316 1317 1320 PdfVersionImp getPdfVersion() { 1321 return pdf_version; 1322 } 1323 1324 1326 1328 1329 public static final int PageLayoutSinglePage = 1; 1330 1331 public static final int PageLayoutOneColumn = 2; 1332 1333 public static final int PageLayoutTwoColumnLeft = 4; 1334 1335 public static final int PageLayoutTwoColumnRight = 8; 1336 1337 public static final int PageLayoutTwoPageLeft = 16; 1338 1339 public static final int PageLayoutTwoPageRight = 32; 1340 1341 1343 1344 public static final int PageModeUseNone = 64; 1345 1346 public static final int PageModeUseOutlines = 128; 1347 1348 public static final int PageModeUseThumbs = 256; 1349 1350 public static final int PageModeFullScreen = 512; 1351 1352 public static final int PageModeUseOC = 1024; 1353 1354 public static final int PageModeUseAttachments = 2048; 1355 1356 1358 1359 public static final int HideToolbar = 1 << 12; 1360 1361 public static final int HideMenubar = 1 << 13; 1362 1363 public static final int HideWindowUI = 1 << 14; 1364 1365 public static final int FitWindow = 1 << 15; 1366 1367 public static final int CenterWindow = 1 << 16; 1368 1369 public static final int DisplayDocTitle = 1 << 17; 1370 1371 1372 public static final int NonFullScreenPageModeUseNone = 1 << 18; 1373 1374 public static final int NonFullScreenPageModeUseOutlines = 1 << 19; 1375 1376 public static final int NonFullScreenPageModeUseThumbs = 1 << 20; 1377 1378 public static final int NonFullScreenPageModeUseOC = 1 << 21; 1379 1380 1381 public static final int DirectionL2R = 1 << 22; 1382 1383 public static final int DirectionR2L = 1 << 23; 1384 1385 1386 public static final int PrintScalingNone = 1 << 24; 1387 1388 1389 public void setViewerPreferences(int preferences) { 1390 pdf.setViewerPreferences(preferences); 1391 } 1392 1393 1394 public void addViewerPreference(PdfName key, PdfObject value) { 1395 pdf.addViewerPreference(key, value); 1396 } 1397 1398 1400 1404 public void setPageLabels(PdfPageLabels pageLabels) { 1405 pdf.setPageLabels(pageLabels); 1406 } 1407 1408 1410 1415 public void addJavaScript(PdfAction js) { 1416 pdf.addJavaScript(js); 1417 } 1418 1419 1427 public void addJavaScript(String code, boolean unicode) { 1428 addJavaScript(PdfAction.javaScript(code, this, unicode)); 1429 } 1430 1431 1436 public void addJavaScript(String code) { 1437 addJavaScript(code, false); 1438 } 1439 1440 1450 public void addFileAttachment(String description, byte fileStore[], String file, String fileDisplay) throws IOException { 1451 addFileAttachment(description, PdfFileSpecification.fileEmbedded(this, file, fileDisplay, fileStore)); 1452 } 1453 1454 1459 public void addFileAttachment(String description, PdfFileSpecification fs) throws IOException { 1460 pdf.addFileAttachment(description, fs); 1461 } 1462 1463 1467 public void addFileAttachment(PdfFileSpecification fs) throws IOException { 1468 addFileAttachment(null, fs); 1469 } 1470 1471 1473 1474 public static final PdfName DOCUMENT_CLOSE = PdfName.WC; 1475 1476 public static final PdfName WILL_SAVE = PdfName.WS; 1477 1478 public static final PdfName DID_SAVE = PdfName.DS; 1479 1480 public static final PdfName WILL_PRINT = PdfName.WP; 1481 1482 public static final PdfName DID_PRINT = PdfName.DP; 1483 1484 1485 public void setOpenAction(String name) { 1486 pdf.setOpenAction(name); 1487 } 1488 1489 1490 public void setOpenAction(PdfAction action) { 1491 pdf.setOpenAction(action); 1492 } 1493 1494 1495 public void setAdditionalAction(PdfName actionType, PdfAction action) throws DocumentException { 1496 if (!(actionType.equals(DOCUMENT_CLOSE) || 1497 actionType.equals(WILL_SAVE) || 1498 actionType.equals(DID_SAVE) || 1499 actionType.equals(WILL_PRINT) || 1500 actionType.equals(DID_PRINT))) { 1501 throw new DocumentException("Invalid additional action type: " + actionType.toString()); 1502 } 1503 pdf.addAdditionalAction(actionType, action); 1504 } 1505 1506 1508 1512 public void setCollection(PdfCollection collection) { 1513 setAtLeastPdfVersion(VERSION_1_7); 1514 pdf.setCollection(collection); 1515 } 1516 1517 1519 1520 public static final int SIGNATURE_EXISTS = 1; 1521 1522 public static final int SIGNATURE_APPEND_ONLY = 2; 1523 1524 1525 public PdfAcroForm getAcroForm() { 1526 return pdf.getAcroForm(); 1527 } 1528 1529 1530 public void addAnnotation(PdfAnnotation annot) { 1531 pdf.addAnnotation(annot); 1532 } 1533 1534 void addAnnotation(PdfAnnotation annot, int page) { 1535 addAnnotation(annot); 1536 } 1537 1538 1539 public void addCalculationOrder(PdfFormField annot) { 1540 pdf.addCalculationOrder(annot); 1541 } 1542 1543 1544 public void setSigFlags(int f) { 1545 pdf.setSigFlags(f); 1546 } 1547 1548 1550 1551 protected byte[] xmpMetadata = null; 1552 1553 1557 public void setXmpMetadata(byte[] xmpMetadata) { 1558 this.xmpMetadata = xmpMetadata; 1559 } 1560 1561 1565 public void createXmpMetadata() { 1566 setXmpMetadata(createXmpMetadataBytes()); 1567 } 1568 1569 1572 private byte[] createXmpMetadataBytes() { 1573 ByteArrayOutputStream baos = new ByteArrayOutputStream (); 1574 try { 1575 XmpWriter xmp = new XmpWriter(baos, pdf.getInfo()); 1576 xmp.close(); 1577 } 1578 catch(IOException ioe) { 1579 ioe.printStackTrace(); 1580 } 1581 return baos.toByteArray(); 1582 } 1583 1584 1586 public static final int PDFXNONE = 0; 1587 1588 public static final int PDFX1A2001 = 1; 1589 1590 public static final int PDFX32002 = 2; 1591 1592 1593 private PdfXConformanceImp pdfxConformance = new PdfXConformanceImp(); 1594 1595 1596 public void setPDFXConformance(int pdfx) { 1597 if (pdfxConformance.getPDFXConformance() == pdfx) 1598 return; 1599 if (pdf.isOpen()) 1600 throw new PdfXConformanceException("PDFX conformance can only be set before opening the document."); 1601 if (crypto != null) 1602 throw new PdfXConformanceException("A PDFX conforming document cannot be encrypted."); 1603 if (pdfx!= PDFXNONE) 1604 setPdfVersion(VERSION_1_3); 1605 pdfxConformance.setPDFXConformance(pdfx); 1606 } 1607 1608 1609 public int getPDFXConformance() { 1610 return pdfxConformance.getPDFXConformance(); 1611 } 1612 1613 1614 public boolean isPdfX() { 1615 return pdfxConformance.isPdfX(); 1616 } 1617 1618 1620 1630 public void setOutputIntents(String outputConditionIdentifier, String outputCondition, String registryName, String info, byte destOutputProfile[]) throws IOException { 1631 getExtraCatalog(); 1632 PdfDictionary out = new PdfDictionary(PdfName.OUTPUTINTENT); 1633 if (outputCondition != null) 1634 out.put(PdfName.OUTPUTCONDITION, new PdfString(outputCondition, PdfObject.TEXT_UNICODE)); 1635 if (outputConditionIdentifier != null) 1636 out.put(PdfName.OUTPUTCONDITIONIDENTIFIER, new PdfString(outputConditionIdentifier, PdfObject.TEXT_UNICODE)); 1637 if (registryName != null) 1638 out.put(PdfName.REGISTRYNAME, new PdfString(registryName, PdfObject.TEXT_UNICODE)); 1639 if (info != null) 1640 out.put(PdfName.INFO, new PdfString(info, PdfObject.TEXT_UNICODE)); 1641 if (destOutputProfile != null) { 1642 PdfStream stream = new PdfStream(destOutputProfile); 1643 stream.flateCompress(); 1644 out.put(PdfName.DESTOUTPUTPROFILE, addToBody(stream).getIndirectReference()); 1645 } 1646 out.put(PdfName.S, PdfName.GTS_PDFX); 1647 extraCatalog.put(PdfName.OUTPUTINTENTS, new PdfArray(out)); 1648 } 1649 1650 1660 public boolean setOutputIntents(PdfReader reader, boolean checkExistence) throws IOException { 1661 PdfDictionary catalog = reader.getCatalog(); 1662 PdfArray outs = (PdfArray)PdfReader.getPdfObject(catalog.get(PdfName.OUTPUTINTENTS)); 1663 if (outs == null) 1664 return false; 1665 ArrayList arr = outs.getArrayList(); 1666 if (arr.isEmpty()) 1667 return false; 1668 PdfDictionary out = (PdfDictionary)PdfReader.getPdfObject((PdfObject)arr.get(0)); 1669 PdfObject obj = PdfReader.getPdfObject(out.get(PdfName.S)); 1670 if (obj == null || !PdfName.GTS_PDFX.equals(obj)) 1671 return false; 1672 if (checkExistence) 1673 return true; 1674 PRStream stream = (PRStream)PdfReader.getPdfObject(out.get(PdfName.DESTOUTPUTPROFILE)); 1675 byte destProfile[] = null; 1676 if (stream != null) { 1677 destProfile = PdfReader.getStreamBytes(stream); 1678 } 1679 setOutputIntents(getNameString(out, PdfName.OUTPUTCONDITIONIDENTIFIER), getNameString(out, PdfName.OUTPUTCONDITION), 1680 getNameString(out, PdfName.REGISTRYNAME), getNameString(out, PdfName.INFO), destProfile); 1681 return true; 1682 } 1683 1684 private static String getNameString(PdfDictionary dic, PdfName key) { 1685 PdfObject obj = PdfReader.getPdfObject(dic.get(key)); 1686 if (obj == null || !obj.isString()) 1687 return null; 1688 return ((PdfString)obj).toUnicodeString(); 1689 } 1690 1691 1693 1695 1697 1698 public static final int STANDARD_ENCRYPTION_40 = 0; 1699 1700 public static final int STANDARD_ENCRYPTION_128 = 1; 1701 1702 public static final int ENCRYPTION_AES_128 = 2; 1703 1704 static final int ENCRYPTION_MASK = 7; 1705 1706 public static final int DO_NOT_ENCRYPT_METADATA = 8; 1707 1708 1710 1711 public static final int AllowPrinting = 4 + 2048; 1712 1713 public static final int AllowModifyContents = 8; 1714 1715 public static final int AllowCopy = 16; 1716 1717 public static final int AllowModifyAnnotations = 32; 1718 1719 public static final int AllowFillIn = 256; 1720 1721 public static final int AllowScreenReaders = 512; 1722 1723 public static final int AllowAssembly = 1024; 1724 1725 public static final int AllowDegradedPrinting = 4; 1726 1727 1729 public static final boolean STRENGTH40BITS = false; 1730 1731 public static final boolean STRENGTH128BITS = true; 1732 1733 1734 protected PdfEncryption crypto; 1735 PdfEncryption getEncryption() { 1736 return crypto; 1737 } 1738 1739 1740 public void setEncryption(byte userPassword[], byte ownerPassword[], int permissions, int encryptionType) throws DocumentException { 1741 if (pdf.isOpen()) 1742 throw new DocumentException("Encryption can only be added before opening the document."); 1743 crypto = new PdfEncryption(); 1744 crypto.setCryptoMode(encryptionType, 0); 1745 crypto.setupAllKeys(userPassword, ownerPassword, permissions); 1746 } 1747 1748 1749 public void setEncryption(Certificate [] certs, int[] permissions, int encryptionType) throws DocumentException { 1750 if (pdf.isOpen()) 1751 throw new DocumentException("Encryption can only be added before opening the document."); 1752 crypto = new PdfEncryption(); 1753 if (certs != null) { 1754 for (int i=0; i < certs.length; i++) { 1755 crypto.addRecipient(certs[i], permissions[i]); 1756 } 1757 } 1758 crypto.setCryptoMode(encryptionType, 0); 1759 crypto.getEncryptionDictionary(); 1760 } 1761 1762 1776 public void setEncryption(byte userPassword[], byte ownerPassword[], int permissions, boolean strength128Bits) throws DocumentException { 1777 setEncryption(userPassword, ownerPassword, permissions, strength128Bits ? STANDARD_ENCRYPTION_128 : STANDARD_ENCRYPTION_40); 1778 } 1779 1780 1794 public void setEncryption(boolean strength, String userPassword, String ownerPassword, int permissions) throws DocumentException { 1795 setEncryption(getISOBytes(userPassword), getISOBytes(ownerPassword), permissions, strength ? STANDARD_ENCRYPTION_128 : STANDARD_ENCRYPTION_40); 1796 } 1797 1798 1813 public void setEncryption(int encryptionType, String userPassword, String ownerPassword, int permissions) throws DocumentException { 1814 setEncryption(getISOBytes(userPassword), getISOBytes(ownerPassword), permissions, encryptionType); 1815 } 1816 1817 1819 1820 protected boolean fullCompression = false; 1821 1822 1826 public boolean isFullCompression() { 1827 return this.fullCompression; 1828 } 1829 1830 1837 public void setFullCompression() { 1838 this.fullCompression = true; 1839 setAtLeastPdfVersion(VERSION_1_5); 1840 } 1841 1842 1844 1845 protected HashMap documentFonts = new HashMap (); 1846 1847 1848 protected int fontNumber = 1; 1849 1850 1857 1858 FontDetails addSimple(BaseFont bf) { 1859 if (bf.getFontType() == BaseFont.FONT_TYPE_DOCUMENT) { 1860 return new FontDetails(new PdfName("F" + (fontNumber++)), ((DocumentFont)bf).getIndirectReference(), bf); 1861 } 1862 FontDetails ret = (FontDetails)documentFonts.get(bf); 1863 if (ret == null) { 1864 PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_FONT, bf); 1865 ret = new FontDetails(new PdfName("F" + (fontNumber++)), body.getPdfIndirectReference(), bf); 1866 documentFonts.put(bf, ret); 1867 } 1868 return ret; 1869 } 1870 1871 void eliminateFontSubset(PdfDictionary fonts) { 1872 for (Iterator it = documentFonts.values().iterator(); it.hasNext();) { 1873 FontDetails ft = (FontDetails)it.next(); 1874 if (fonts.get(ft.getFontName()) != null) 1875 ft.setSubset(false); 1876 } 1877 } 1878 1879 1881 1883 protected HashMap formXObjects = new HashMap (); 1884 1885 1886 protected int formXObjectsCounter = 1; 1887 1888 1894 1895 PdfName addDirectTemplateSimple(PdfTemplate template, PdfName forcedName) { 1896 PdfIndirectReference ref = template.getIndirectReference(); 1897 Object obj[] = (Object [])formXObjects.get(ref); 1898 PdfName name = null; 1899 try { 1900 if (obj == null) { 1901 if (forcedName == null) { 1902 name = new PdfName("Xf" + formXObjectsCounter); 1903 ++formXObjectsCounter; 1904 } 1905 else 1906 name = forcedName; 1907 if (template.getType() == PdfTemplate.TYPE_IMPORTED) 1908 template = null; 1909 formXObjects.put(ref, new Object []{name, template}); 1910 } 1911 else 1912 name = (PdfName)obj[0]; 1913 } 1914 catch (Exception e) { 1915 throw new ExceptionConverter(e); 1916 } 1917 return name; 1918 } 1919 1920 1928 public void releaseTemplate(PdfTemplate tp) throws IOException { 1929 PdfIndirectReference ref = tp.getIndirectReference(); 1930 Object [] objs = (Object [])formXObjects.get(ref); 1931 if (objs == null || objs[1] == null) 1932 return; 1933 PdfTemplate template = (PdfTemplate)objs[1]; 1934 if (template.getIndirectReference() instanceof PRIndirectReference) 1935 return; 1936 if (template.getType() == PdfTemplate.TYPE_TEMPLATE) { 1937 addToBody(template.getFormXObject(), template.getIndirectReference()); 1938 objs[1] = null; 1939 } 1940 } 1941 1942 1944 protected HashMap importedPages = new HashMap (); 1945 1946 1955 public PdfImportedPage getImportedPage(PdfReader reader, int pageNumber) { 1956 PdfReaderInstance inst = (PdfReaderInstance)importedPages.get(reader); 1957 if (inst == null) { 1958 inst = reader.getPdfReaderInstance(this); 1959 importedPages.put(reader, inst); 1960 } 1961 return inst.getImportedPage(pageNumber); 1962 } 1963 1964 1973 public void freeReader(PdfReader reader) throws IOException { 1974 currentPdfReaderInstance = (PdfReaderInstance)importedPages.get(reader); 1975 if (currentPdfReaderInstance == null) 1976 return; 1977 currentPdfReaderInstance.writeAllPages(); 1978 currentPdfReaderInstance = null; 1979 importedPages.remove(reader); 1980 } 1981 1982 1991 public int getCurrentDocumentSize() { 1992 return body.offset() + body.size() * 20 + 0x48; 1993 } 1994 1995 protected PdfReaderInstance currentPdfReaderInstance; 1996 1997 protected int getNewObjectNumber(PdfReader reader, int number, int generation) { 1998 return currentPdfReaderInstance.getNewObjectNumber(number, generation); 1999 } 2000 2001 RandomAccessFileOrArray getReaderFile(PdfReader reader) { 2002 return currentPdfReaderInstance.getReaderFile(); 2003 } 2004 2005 2007 2008 protected HashMap documentColors = new HashMap (); 2009 2010 2011 protected int colorNumber = 1; 2012 2013 PdfName getColorspaceName() { 2014 return new PdfName("CS" + (colorNumber++)); 2015 } 2016 2017 2023 ColorDetails addSimple(PdfSpotColor spc) { 2024 ColorDetails ret = (ColorDetails)documentColors.get(spc); 2025 if (ret == null) { 2026 ret = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), spc); 2027 documentColors.put(spc, ret); 2028 } 2029 return ret; 2030 } 2031 2032 2034 2035 protected HashMap documentPatterns = new HashMap (); 2036 2037 2038 protected int patternNumber = 1; 2039 2040 PdfName addSimplePattern(PdfPatternPainter painter) { 2041 PdfName name = (PdfName)documentPatterns.get(painter); 2042 try { 2043 if ( name == null ) { 2044 name = new PdfName("P" + patternNumber); 2045 ++patternNumber; 2046 documentPatterns.put(painter, name); 2047 } 2048 } catch (Exception e) { 2049 throw new ExceptionConverter(e); 2050 } 2051 return name; 2052 } 2053 2054 2056 protected HashMap documentShadingPatterns = new HashMap (); 2057 2058 void addSimpleShadingPattern(PdfShadingPattern shading) { 2059 if (!documentShadingPatterns.containsKey(shading)) { 2060 shading.setName(patternNumber); 2061 ++patternNumber; 2062 documentShadingPatterns.put(shading, null); 2063 addSimpleShading(shading.getShading()); 2064 } 2065 } 2066 2067 2069 protected HashMap documentShadings = new HashMap (); 2070 2071 void addSimpleShading(PdfShading shading) { 2072 if (!documentShadings.containsKey(shading)) { 2073 documentShadings.put(shading, null); 2074 shading.setName(documentShadings.size()); 2075 } 2076 } 2077 2078 2080 protected HashMap documentExtGState = new HashMap (); 2081 2082 PdfObject[] addSimpleExtGState(PdfDictionary gstate) { 2083 if (!documentExtGState.containsKey(gstate)) { 2084 PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_GSTATE, gstate); 2085 documentExtGState.put(gstate, new PdfObject[]{new PdfName("GS" + (documentExtGState.size() + 1)), getPdfIndirectReference()}); 2086 } 2087 return (PdfObject[])documentExtGState.get(gstate); 2088 } 2089 2090 2092 protected HashMap documentProperties = new HashMap (); 2093 PdfObject[] addSimpleProperty(Object prop, PdfIndirectReference refi) { 2094 if (!documentProperties.containsKey(prop)) { 2095 if (prop instanceof PdfOCG) 2096 PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_LAYER, null); 2097 documentProperties.put(prop, new PdfObject[]{new PdfName("Pr" + (documentProperties.size() + 1)), refi}); 2098 } 2099 return (PdfObject[])documentProperties.get(prop); 2100 } 2101 2102 boolean propertyExists(Object prop) { 2103 return documentProperties.containsKey(prop); 2104 } 2105 2106 2108 protected boolean tagged = false; 2109 protected PdfStructureTreeRoot structureTreeRoot; 2110 2111 2114 public void setTagged() { 2115 if (open) 2116 throw new IllegalArgumentException ("Tagging must be set before opening the document."); 2117 tagged = true; 2118 } 2119 2120 2124 public boolean isTagged() { 2125 return tagged; 2126 } 2127 2128 2132 public PdfStructureTreeRoot getStructureTreeRoot() { 2133 if (tagged && structureTreeRoot == null) 2134 structureTreeRoot = new PdfStructureTreeRoot(this); 2135 return structureTreeRoot; 2136 } 2137 2138 protected HashSet documentOCG = new HashSet (); 2140 protected ArrayList documentOCGorder = new ArrayList (); 2141 protected PdfOCProperties OCProperties; 2142 protected PdfArray OCGRadioGroup = new PdfArray(); 2143 2144 2151 public PdfOCProperties getOCProperties() { 2152 fillOCProperties(true); 2153 return OCProperties; 2154 } 2155 2156 2164 public void addOCGRadioGroup(ArrayList group) { 2165 PdfArray ar = new PdfArray(); 2166 for (int k = 0; k < group.size(); ++k) { 2167 PdfLayer layer = (PdfLayer)group.get(k); 2168 if (layer.getTitle() == null) 2169 ar.add(layer.getRef()); 2170 } 2171 if (ar.size() == 0) 2172 return; 2173 OCGRadioGroup.add(ar); 2174 } 2175 2176 private static void getOCGOrder(PdfArray order, PdfLayer layer) { 2177 if (!layer.isOnPanel()) 2178 return; 2179 if (layer.getTitle() == null) 2180 order.add(layer.getRef()); 2181 ArrayList children = layer.getChildren(); 2182 if (children == null) 2183 return; 2184 PdfArray kids = new PdfArray(); 2185 if (layer.getTitle() != null) 2186 kids.add(new PdfString(layer.getTitle(), PdfObject.TEXT_UNICODE)); 2187 for (int k = 0; k < children.size(); ++k) { 2188 getOCGOrder(kids, (PdfLayer)children.get(k)); 2189 } 2190 if (kids.size() > 0) 2191 order.add(kids); 2192 } 2193 2194 private void addASEvent(PdfName event, PdfName category) { 2195 PdfArray arr = new PdfArray(); 2196 for (Iterator it = documentOCG.iterator(); it.hasNext();) { 2197 PdfLayer layer = (PdfLayer)it.next(); 2198 PdfDictionary usage = (PdfDictionary)layer.get(PdfName.USAGE); 2199 if (usage != null && usage.get(category) != null) 2200 arr.add(layer.getRef()); 2201 } 2202 if (arr.size() == 0) 2203 return; 2204 PdfDictionary d = (PdfDictionary)OCProperties.get(PdfName.D); 2205 PdfArray arras = (PdfArray)d.get(PdfName.AS); 2206 if (arras == null) { 2207 arras = new PdfArray(); 2208 d.put(PdfName.AS, arras); 2209 } 2210 PdfDictionary as = new PdfDictionary(); 2211 as.put(PdfName.EVENT, event); 2212 as.put(PdfName.CATEGORY, new PdfArray(category)); 2213 as.put(PdfName.OCGS, arr); 2214 arras.add(as); 2215 } 2216 2217 private void fillOCProperties(boolean erase) { 2218 if (OCProperties == null) 2219 OCProperties = new PdfOCProperties(); 2220 if (erase) { 2221 OCProperties.remove(PdfName.OCGS); 2222 OCProperties.remove(PdfName.D); 2223 } 2224 if (OCProperties.get(PdfName.OCGS) == null) { 2225 PdfArray gr = new PdfArray(); 2226 for (Iterator it = documentOCG.iterator(); it.hasNext();) { 2227 PdfLayer layer = (PdfLayer)it.next(); 2228 gr.add(layer.getRef()); 2229 } 2230 OCProperties.put(PdfName.OCGS, gr); 2231 } 2232 if (OCProperties.get(PdfName.D) != null) 2233 return; 2234 ArrayList docOrder = new ArrayList (documentOCGorder); 2235 for (Iterator it = docOrder.iterator(); it.hasNext();) { 2236 PdfLayer layer = (PdfLayer)it.next(); 2237 if (layer.getParent() != null) 2238 it.remove(); 2239 } 2240 PdfArray order = new PdfArray(); 2241 for (Iterator it = docOrder.iterator(); it.hasNext();) { 2242 PdfLayer layer = (PdfLayer)it.next(); 2243 getOCGOrder(order, layer); 2244 } 2245 PdfDictionary d = new PdfDictionary(); 2246 OCProperties.put(PdfName.D, d); 2247 d.put(PdfName.ORDER, order); 2248 PdfArray gr = new PdfArray(); 2249 for (Iterator it = documentOCG.iterator(); it.hasNext();) { 2250 PdfLayer layer = (PdfLayer)it.next(); 2251 if (!layer.isOn()) 2252 gr.add(layer.getRef()); 2253 } 2254 if (gr.size() > 0) 2255 d.put(PdfName.OFF, gr); 2256 if (OCGRadioGroup.size() > 0) 2257 d.put(PdfName.RBGROUPS, OCGRadioGroup); 2258 addASEvent(PdfName.VIEW, PdfName.ZOOM); 2259 addASEvent(PdfName.VIEW, PdfName.VIEW); 2260 addASEvent(PdfName.PRINT, PdfName.PRINT); 2261 addASEvent(PdfName.EXPORT, PdfName.EXPORT); 2262 d.put(PdfName.LISTMODE, PdfName.VISIBLEPAGES); 2263 } 2264 2265 void registerLayer(PdfOCG layer) { 2266 PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_LAYER, null); 2267 if (layer instanceof PdfLayer) { 2268 PdfLayer la = (PdfLayer)layer; 2269 if (la.getTitle() == null) { 2270 if (!documentOCG.contains(layer)) { 2271 documentOCG.add(layer); 2272 documentOCGorder.add(layer); 2273 } 2274 } 2275 else { 2276 documentOCGorder.add(layer); 2277 } 2278 } 2279 else 2280 throw new IllegalArgumentException ("Only PdfLayer is accepted."); 2281 } 2282 2283 2285 2287 2291 public Rectangle getPageSize() { 2292 return pdf.getPageSize(); 2293 } 2294 2295 2301 public void setCropBoxSize(Rectangle crop) { 2302 pdf.setCropBoxSize(crop); 2303 } 2304 2305 2311 public void setBoxSize(String boxName, Rectangle size) { 2312 pdf.setBoxSize(boxName, size); 2313 } 2314 2315 2320 public Rectangle getBoxSize(String boxName) { 2321 return pdf.getBoxSize(boxName); 2322 } 2323 2324 2326 2332 public void setPageEmpty(boolean pageEmpty) { 2333 pdf.setPageEmpty(pageEmpty); 2334 } 2335 2336 2338 2339 public static final PdfName PAGE_OPEN = PdfName.O; 2340 2341 public static final PdfName PAGE_CLOSE = PdfName.C; 2342 2343 2344 public void setPageAction(PdfName actionType, PdfAction action) throws DocumentException { 2345 if (!actionType.equals(PAGE_OPEN) && !actionType.equals(PAGE_CLOSE)) 2346 throw new DocumentException("Invalid page additional action type: " + actionType.toString()); 2347 pdf.setPageAction(actionType, action); 2348 } 2349 2350 2351 public void setDuration(int seconds) { 2352 pdf.setDuration(seconds); 2353 } 2354 2355 2356 public void setTransition(PdfTransition transition) { 2357 pdf.setTransition(transition); 2358 } 2359 2360 2362 2368 public void setThumbnail(Image image) throws PdfException, DocumentException { 2369 pdf.setThumbnail(image); 2370 } 2371 2372 2374 2379 protected PdfDictionary group; 2380 2381 2385 public PdfDictionary getGroup() { 2386 return this.group; 2387 } 2388 2389 2393 public void setGroup(PdfDictionary group) { 2394 this.group = group; 2395 } 2396 2397 2399 2400 public static final float SPACE_CHAR_RATIO_DEFAULT = 2.5f; 2401 2402 public static final float NO_SPACE_CHAR_RATIO = 10000000f; 2403 2404 2408 private float spaceCharRatio = SPACE_CHAR_RATIO_DEFAULT; 2409 2410 2415 public float getSpaceCharRatio() { 2416 return spaceCharRatio; 2417 } 2418 2419 2427 public void setSpaceCharRatio(float spaceCharRatio) { 2428 if (spaceCharRatio < 0.001f) 2429 this.spaceCharRatio = 0.001f; 2430 else 2431 this.spaceCharRatio = spaceCharRatio; 2432 } 2433 2434 2436 2437 public static final int RUN_DIRECTION_DEFAULT = 0; 2438 2439 public static final int RUN_DIRECTION_NO_BIDI = 1; 2440 2443 public static final int RUN_DIRECTION_LTR = 2; 2444 2447 public static final int RUN_DIRECTION_RTL = 3; 2448 2449 protected int runDirection = RUN_DIRECTION_NO_BIDI; 2450 2451 2456 public void setRunDirection(int runDirection) { 2457 if (runDirection < RUN_DIRECTION_NO_BIDI || runDirection > RUN_DIRECTION_RTL) 2458 throw new RuntimeException ("Invalid run direction: " + runDirection); 2459 this.runDirection = runDirection; 2460 } 2461 2462 2466 public int getRunDirection() { 2467 return runDirection; 2468 } 2469 2470 2472 protected float userunit = 0f; 2473 2481 public float getUserunit() { 2482 return userunit; 2483 } 2484 2493 public void setUserunit(float userunit) throws DocumentException { 2494 if (userunit < 1f || userunit > 75000f) throw new DocumentException("UserUnit should be a value between 1 and 75000."); 2495 this.userunit = userunit; 2496 setAtLeastPdfVersion(VERSION_1_6); 2497 } 2498 2499 2501 2503 protected PdfDictionary defaultColorspace = new PdfDictionary(); 2504 2508 public PdfDictionary getDefaultColorspace() { 2509 return defaultColorspace; 2510 } 2511 2512 2523 public void setDefaultColorspace(PdfName key, PdfObject cs) { 2524 if (cs == null || cs.isNull()) 2525 defaultColorspace.remove(key); 2526 defaultColorspace.put(key, cs); 2527 } 2528 2529 2531 protected HashMap documentSpotPatterns = new HashMap (); 2532 protected ColorDetails patternColorspaceRGB; 2533 protected ColorDetails patternColorspaceGRAY; 2534 protected ColorDetails patternColorspaceCMYK; 2535 2536 ColorDetails addSimplePatternColorspace(Color color) { 2537 int type = ExtendedColor.getType(color); 2538 if (type == ExtendedColor.TYPE_PATTERN || type == ExtendedColor.TYPE_SHADING) 2539 throw new RuntimeException ("An uncolored tile pattern can not have another pattern or shading as color."); 2540 try { 2541 switch (type) { 2542 case ExtendedColor.TYPE_RGB: 2543 if (patternColorspaceRGB == null) { 2544 patternColorspaceRGB = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null); 2545 PdfArray array = new PdfArray(PdfName.PATTERN); 2546 array.add(PdfName.DEVICERGB); 2547 addToBody(array, patternColorspaceRGB.getIndirectReference()); 2548 } 2549 return patternColorspaceRGB; 2550 case ExtendedColor.TYPE_CMYK: 2551 if (patternColorspaceCMYK == null) { 2552 patternColorspaceCMYK = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null); 2553 PdfArray array = new PdfArray(PdfName.PATTERN); 2554 array.add(PdfName.DEVICECMYK); 2555 addToBody(array, patternColorspaceCMYK.getIndirectReference()); 2556 } 2557 return patternColorspaceCMYK; 2558 case ExtendedColor.TYPE_GRAY: 2559 if (patternColorspaceGRAY == null) { 2560 patternColorspaceGRAY = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null); 2561 PdfArray array = new PdfArray(PdfName.PATTERN); 2562 array.add(PdfName.DEVICEGRAY); 2563 addToBody(array, patternColorspaceGRAY.getIndirectReference()); 2564 } 2565 return patternColorspaceGRAY; 2566 case ExtendedColor.TYPE_SEPARATION: { 2567 ColorDetails details = addSimple(((SpotColor)color).getPdfSpotColor()); 2568 ColorDetails patternDetails = (ColorDetails)documentSpotPatterns.get(details); 2569 if (patternDetails == null) { 2570 patternDetails = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null); 2571 PdfArray array = new PdfArray(PdfName.PATTERN); 2572 array.add(details.getIndirectReference()); 2573 addToBody(array, patternDetails.getIndirectReference()); 2574 documentSpotPatterns.put(details, patternDetails); 2575 } 2576 return patternDetails; 2577 } 2578 default: 2579 throw new RuntimeException ("Invalid color type in PdfWriter.addSimplePatternColorspace()."); 2580 } 2581 } 2582 catch (Exception e) { 2583 throw new RuntimeException (e.getMessage()); 2584 } 2585 } 2586 2587 2589 2593 public boolean isStrictImageSequence() { 2594 return pdf.isStrictImageSequence(); 2595 } 2596 2597 2603 public void setStrictImageSequence(boolean strictImageSequence) { 2604 pdf.setStrictImageSequence(strictImageSequence); 2605 } 2606 2607 2611 public void clearTextWrap() throws DocumentException { 2612 pdf.clearTextWrap(); 2613 } 2614 2615 2616 protected PdfDictionary imageDictionary = new PdfDictionary(); 2617 2618 2619 private HashMap images = new HashMap (); 2620 2621 2631 public PdfName addDirectImageSimple(Image image) throws PdfException, DocumentException { 2632 return addDirectImageSimple(image, null); 2633 } 2634 2635 2646 public PdfName addDirectImageSimple(Image image, PdfIndirectReference fixedRef) throws PdfException, DocumentException { 2647 PdfName name; 2648 if (images.containsKey(image.getMySerialId())) { 2650 name = (PdfName) images.get(image.getMySerialId()); 2651 } 2652 else { 2654 if (image.isImgTemplate()) { 2655 name = new PdfName("img" + images.size()); 2656 if (image.getTemplateData() == null) { 2657 if(image instanceof ImgWMF){ 2658 try { 2659 ImgWMF wmf = (ImgWMF)image; 2660 wmf.readWMF(PdfTemplate.createTemplate(this, 0, 0)); 2661 } 2662 catch (Exception e) { 2663 throw new DocumentException(e); 2664 } 2665 } 2666 } 2667 } 2668 else { 2669 PdfIndirectReference dref = image.getDirectReference(); 2670 if (dref != null) { 2671 PdfName rname = new PdfName("img" + images.size()); 2672 images.put(image.getMySerialId(), rname); 2673 imageDictionary.put(rname, dref); 2674 return rname; 2675 } 2676 Image maskImage = image.getImageMask(); 2677 PdfIndirectReference maskRef = null; 2678 if (maskImage != null) { 2679 PdfName mname = (PdfName)images.get(maskImage.getMySerialId()); 2680 maskRef = getImageReference(mname); 2681 } 2682 PdfImage i = new PdfImage(image, "img" + images.size(), maskRef); 2683 if (image.hasICCProfile()) { 2684 PdfICCBased icc = new PdfICCBased(image.getICCProfile()); 2685 PdfIndirectReference iccRef = add(icc); 2686 PdfArray iccArray = new PdfArray(); 2687 iccArray.add(PdfName.ICCBASED); 2688 iccArray.add(iccRef); 2689 PdfObject colorspace = i.get(PdfName.COLORSPACE); 2690 if (colorspace != null && colorspace.isArray()) { 2691 ArrayList ar = ((PdfArray)colorspace).getArrayList(); 2692 if (ar.size() > 1 && PdfName.INDEXED.equals(ar.get(0))) 2693 ar.set(1, iccArray); 2694 else 2695 i.put(PdfName.COLORSPACE, iccArray); 2696 } 2697 else 2698 i.put(PdfName.COLORSPACE, iccArray); 2699 } 2700 add(i, fixedRef); 2701 name = i.name(); 2702 } 2703 images.put(image.getMySerialId(), name); 2704 } 2705 return name; 2706 } 2707 2708 2715 2716 PdfIndirectReference add(PdfImage pdfImage, PdfIndirectReference fixedRef) throws PdfException { 2717 if (! imageDictionary.contains(pdfImage.name())) { 2718 PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_IMAGE, pdfImage); 2719 if (fixedRef instanceof PRIndirectReference) { 2720 PRIndirectReference r2 = (PRIndirectReference)fixedRef; 2721 fixedRef = new PdfIndirectReference(0, getNewObjectNumber(r2.getReader(), r2.getNumber(), r2.getGeneration())); 2722 } 2723 try { 2724 if (fixedRef == null) 2725 fixedRef = addToBody(pdfImage).getIndirectReference(); 2726 else 2727 addToBody(pdfImage, fixedRef); 2728 } 2729 catch(IOException ioe) { 2730 throw new ExceptionConverter(ioe); 2731 } 2732 imageDictionary.put(pdfImage.name(), fixedRef); 2733 return fixedRef; 2734 } 2735 return (PdfIndirectReference) imageDictionary.get(pdfImage.name()); 2736 } 2737 2738 2744 2745 PdfIndirectReference getImageReference(PdfName name) { 2746 return (PdfIndirectReference) imageDictionary.get(name); 2747 } 2748 2749 protected PdfIndirectReference add(PdfICCBased icc) { 2750 PdfIndirectObject object; 2751 try { 2752 object = addToBody(icc); 2753 } 2754 catch(IOException ioe) { 2755 throw new ExceptionConverter(ioe); 2756 } 2757 return object.getIndirectReference(); 2758 } 2759 2760 2762 2776 2777 public float getTableBottom(Table table) { 2778 return pdf.bottom(table) - pdf.indentBottom(); 2779 } 2780 2781 2788 2789 public PdfTable getPdfTable(Table table) { 2790 return pdf.getPdfTable(table, true); 2791 } 2792 2793 2806 2807 public boolean breakTableIfDoesntFit(PdfTable table) throws DocumentException { 2808 return pdf.breakTableIfDoesntFit(table); 2809 } 2810 2811 2819 2820 public boolean fitsPage(Table table, float margin) { 2821 return pdf.bottom(table) > pdf.indentBottom() + margin; 2822 } 2823 2824 2831 2832 public boolean fitsPage(Table table) { 2833 return fitsPage(table, 0); 2834 } 2835 2836 2844 public boolean fitsPage(PdfPTable table, float margin) { 2845 return pdf.fitsPage(table, margin); 2846 } 2847 2848 2855 public boolean fitsPage(PdfPTable table) { 2856 return pdf.fitsPage(table, 0); 2857 } 2858 2859 2862 private boolean userProperties; 2863 2864 2868 public boolean isUserProperties() { 2869 return this.userProperties; 2870 } 2871 2872 2876 public void setUserProperties(boolean userProperties) { 2877 this.userProperties = userProperties; 2878 } 2879} 2880 | Popular Tags |