001/* 002 * $Id: PdfWriter.java 4891 2011-06-01 18:22:36Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.pdf; 045 046import java.io.ByteArrayOutputStream; 047import java.io.IOException; 048import java.io.OutputStream; 049import java.security.cert.Certificate; 050import java.util.ArrayList; 051import java.util.Arrays; 052import java.util.HashMap; 053import java.util.HashSet; 054import java.util.Iterator; 055import java.util.LinkedHashMap; 056import java.util.List; 057import java.util.Map; 058import java.util.TreeMap; 059import java.util.TreeSet; 060 061import com.itextpdf.text.BaseColor; 062import com.itextpdf.text.DocListener; 063import com.itextpdf.text.DocWriter; 064import com.itextpdf.text.Document; 065import com.itextpdf.text.DocumentException; 066import com.itextpdf.text.ExceptionConverter; 067import com.itextpdf.text.Image; 068import com.itextpdf.text.ImgJBIG2; 069import com.itextpdf.text.ImgWMF; 070import com.itextpdf.text.Rectangle; 071import com.itextpdf.text.error_messages.MessageLocalization; 072import com.itextpdf.text.pdf.collection.PdfCollection; 073import com.itextpdf.text.pdf.events.PdfPageEventForwarder; 074import com.itextpdf.text.pdf.interfaces.PdfAnnotations; 075import com.itextpdf.text.pdf.interfaces.PdfDocumentActions; 076import com.itextpdf.text.pdf.interfaces.PdfEncryptionSettings; 077import com.itextpdf.text.pdf.interfaces.PdfPageActions; 078import com.itextpdf.text.pdf.interfaces.PdfRunDirection; 079import com.itextpdf.text.pdf.interfaces.PdfVersion; 080import com.itextpdf.text.pdf.interfaces.PdfViewerPreferences; 081import com.itextpdf.text.pdf.interfaces.PdfXConformance; 082import com.itextpdf.text.pdf.internal.PdfVersionImp; 083import com.itextpdf.text.pdf.internal.PdfXConformanceImp; 084import com.itextpdf.text.xml.xmp.XmpWriter; 085 086/** 087 * A <CODE>DocWriter</CODE> class for PDF. 088 * <P> 089 * When this <CODE>PdfWriter</CODE> is added 090 * to a certain <CODE>PdfDocument</CODE>, the PDF representation of every Element 091 * added to this Document will be written to the outputstream.</P> 092 */ 093 094public class PdfWriter extends DocWriter implements 095 PdfViewerPreferences, 096 PdfEncryptionSettings, 097 PdfVersion, 098 PdfDocumentActions, 099 PdfPageActions, 100 PdfXConformance, 101 PdfRunDirection, 102 PdfAnnotations { 103 104 /** 105 * The highest generation number possible. 106 * @since iText 2.1.6 107 */ 108 public static final int GENERATION_MAX = 65535; 109 110// INNER CLASSES 111 112 /** 113 * This class generates the structure of a PDF document. 114 * <P> 115 * This class covers the third section of Chapter 5 in the 'Portable Document Format 116 * Reference Manual version 1.3' (page 55-60). It contains the body of a PDF document 117 * (section 5.14) and it can also generate a Cross-reference Table (section 5.15). 118 * 119 * @see PdfWriter 120 * @see PdfObject 121 * @see PdfIndirectObject 122 */ 123 124 public static class PdfBody { 125 126 // inner classes 127 128 /** 129 * <CODE>PdfCrossReference</CODE> is an entry in the PDF Cross-Reference table. 130 */ 131 132 static class PdfCrossReference implements Comparable<PdfCrossReference> { 133 134 // membervariables 135 private final int type; 136 137 /** Byte offset in the PDF file. */ 138 private final int offset; 139 140 private final int refnum; 141 /** generation of the object. */ 142 private final int generation; 143 144 // constructors 145 /** 146 * Constructs a cross-reference element for a PdfIndirectObject. 147 * @param refnum 148 * @param offset byte offset of the object 149 * @param generation generation number of the object 150 */ 151 152 PdfCrossReference(final int refnum, final int offset, final int generation) { 153 type = 0; 154 this.offset = offset; 155 this.refnum = refnum; 156 this.generation = generation; 157 } 158 159 /** 160 * Constructs a cross-reference element for a PdfIndirectObject. 161 * @param refnum 162 * @param offset byte offset of the object 163 */ 164 165 PdfCrossReference(final int refnum, final int offset) { 166 type = 1; 167 this.offset = offset; 168 this.refnum = refnum; 169 this.generation = 0; 170 } 171 172 PdfCrossReference(final int type, final int refnum, final int offset, final int generation) { 173 this.type = type; 174 this.offset = offset; 175 this.refnum = refnum; 176 this.generation = generation; 177 } 178 179 int getRefnum() { 180 return refnum; 181 } 182 183 /** 184 * Returns the PDF representation of this <CODE>PdfObject</CODE>. 185 * @param os 186 * @throws IOException 187 */ 188 189 public void toPdf(final OutputStream os) throws IOException { 190 StringBuffer off = new StringBuffer("0000000000").append(offset); 191 off.delete(0, off.length() - 10); 192 StringBuffer gen = new StringBuffer("00000").append(generation); 193 gen.delete(0, gen.length() - 5); 194 195 off.append(' ').append(gen).append(generation == GENERATION_MAX ? " f \n" : " n \n"); 196 os.write(getISOBytes(off.toString())); 197 } 198 199 /** 200 * Writes PDF syntax to the OutputStream 201 * @param midSize 202 * @param os 203 * @throws IOException 204 */ 205 public void toPdf(int midSize, final OutputStream os) throws IOException { 206 os.write((byte)type); 207 while (--midSize >= 0) 208 os.write((byte)(offset >>> 8 * midSize & 0xff)); 209 os.write((byte)(generation >>> 8 & 0xff)); 210 os.write((byte)(generation & 0xff)); 211 } 212 213 /** 214 * @see java.lang.Comparable#compareTo(java.lang.Object) 215 */ 216 public int compareTo(final PdfCrossReference other) { 217 return refnum < other.refnum ? -1 : refnum==other.refnum ? 0 : 1; 218 } 219 220 /** 221 * @see java.lang.Object#equals(java.lang.Object) 222 */ 223 @Override 224 public boolean equals(final Object obj) { 225 if (obj instanceof PdfCrossReference) { 226 PdfCrossReference other = (PdfCrossReference)obj; 227 return refnum == other.refnum; 228 } 229 else 230 return false; 231 } 232 233 /** 234 * @see java.lang.Object#hashCode() 235 */ 236 @Override 237 public int hashCode() { 238 return refnum; 239 } 240 241 } 242 243 private static final int OBJSINSTREAM = 200; 244 245 // membervariables 246 247 /** array containing the cross-reference table of the normal objects. */ 248 private final TreeSet<PdfCrossReference> xrefs; 249 private int refnum; 250 /** the current byte position in the body. */ 251 private int position; 252 private final PdfWriter writer; 253 private ByteBuffer index; 254 private ByteBuffer streamObjects; 255 private int currentObjNum; 256 private int numObj = 0; 257 258 // constructors 259 260 /** 261 * Constructs a new <CODE>PdfBody</CODE>. 262 * @param writer 263 */ 264 PdfBody(final PdfWriter writer) { 265 xrefs = new TreeSet<PdfCrossReference>(); 266 xrefs.add(new PdfCrossReference(0, 0, GENERATION_MAX)); 267 position = writer.getOs().getCounter(); 268 refnum = 1; 269 this.writer = writer; 270 } 271 272 // methods 273 274 void setRefnum(final int refnum) { 275 this.refnum = refnum; 276 } 277 278 private PdfWriter.PdfBody.PdfCrossReference addToObjStm(final PdfObject obj, final int nObj) throws IOException { 279 if (numObj >= OBJSINSTREAM) 280 flushObjStm(); 281 if (index == null) { 282 index = new ByteBuffer(); 283 streamObjects = new ByteBuffer(); 284 currentObjNum = getIndirectReferenceNumber(); 285 numObj = 0; 286 } 287 int p = streamObjects.size(); 288 int idx = numObj++; 289 PdfEncryption enc = writer.crypto; 290 writer.crypto = null; 291 obj.toPdf(writer, streamObjects); 292 writer.crypto = enc; 293 streamObjects.append(' '); 294 index.append(nObj).append(' ').append(p).append(' '); 295 return new PdfWriter.PdfBody.PdfCrossReference(2, nObj, currentObjNum, idx); 296 } 297 298 private void flushObjStm() throws IOException { 299 if (numObj == 0) 300 return; 301 int first = index.size(); 302 index.append(streamObjects); 303 PdfStream stream = new PdfStream(index.toByteArray()); 304 stream.flateCompress(writer.getCompressionLevel()); 305 stream.put(PdfName.TYPE, PdfName.OBJSTM); 306 stream.put(PdfName.N, new PdfNumber(numObj)); 307 stream.put(PdfName.FIRST, new PdfNumber(first)); 308 add(stream, currentObjNum); 309 index = null; 310 streamObjects = null; 311 numObj = 0; 312 } 313 314 /** 315 * Adds a <CODE>PdfObject</CODE> to the body. 316 * <P> 317 * This methods creates a <CODE>PdfIndirectObject</CODE> with a 318 * certain number, containing the given <CODE>PdfObject</CODE>. 319 * It also adds a <CODE>PdfCrossReference</CODE> for this object 320 * to an <CODE>ArrayList</CODE> that will be used to build the 321 * Cross-reference Table. 322 * 323 * @param object a <CODE>PdfObject</CODE> 324 * @return a <CODE>PdfIndirectObject</CODE> 325 * @throws IOException 326 */ 327 328 PdfIndirectObject add(final PdfObject object) throws IOException { 329 return add(object, getIndirectReferenceNumber()); 330 } 331 332 PdfIndirectObject add(final PdfObject object, final boolean inObjStm) throws IOException { 333 return add(object, getIndirectReferenceNumber(), inObjStm); 334 } 335 336 /** 337 * Gets a PdfIndirectReference for an object that will be created in the future. 338 * @return a PdfIndirectReference 339 */ 340 341 PdfIndirectReference getPdfIndirectReference() { 342 return new PdfIndirectReference(0, getIndirectReferenceNumber()); 343 } 344 345 int getIndirectReferenceNumber() { 346 int n = refnum++; 347 xrefs.add(new PdfCrossReference(n, 0, GENERATION_MAX)); 348 return n; 349 } 350 351 /** 352 * Adds a <CODE>PdfObject</CODE> to the body given an already existing 353 * PdfIndirectReference. 354 * <P> 355 * This methods creates a <CODE>PdfIndirectObject</CODE> with the number given by 356 * <CODE>ref</CODE>, containing the given <CODE>PdfObject</CODE>. 357 * It also adds a <CODE>PdfCrossReference</CODE> for this object 358 * to an <CODE>ArrayList</CODE> that will be used to build the 359 * Cross-reference Table. 360 * 361 * @param object a <CODE>PdfObject</CODE> 362 * @param ref a <CODE>PdfIndirectReference</CODE> 363 * @return a <CODE>PdfIndirectObject</CODE> 364 * @throws IOException 365 */ 366 367 PdfIndirectObject add(final PdfObject object, final PdfIndirectReference ref) throws IOException { 368 return add(object, ref.getNumber()); 369 } 370 371 PdfIndirectObject add(final PdfObject object, final PdfIndirectReference ref, final boolean inObjStm) throws IOException { 372 return add(object, ref.getNumber(), inObjStm); 373 } 374 375 PdfIndirectObject add(final PdfObject object, final int refNumber) throws IOException { 376 return add(object, refNumber, true); // to false 377 } 378 379 PdfIndirectObject add(final PdfObject object, final int refNumber, final boolean inObjStm) throws IOException { 380 if (inObjStm && object.canBeInObjStm() && writer.isFullCompression()) { 381 PdfCrossReference pxref = addToObjStm(object, refNumber); 382 PdfIndirectObject indirect = new PdfIndirectObject(refNumber, object, writer); 383 if (!xrefs.add(pxref)) { 384 xrefs.remove(pxref); 385 xrefs.add(pxref); 386 } 387 return indirect; 388 } 389 else { 390 PdfIndirectObject indirect = new PdfIndirectObject(refNumber, object, writer); 391 PdfCrossReference pxref = new PdfCrossReference(refNumber, position); 392 if (!xrefs.add(pxref)) { 393 xrefs.remove(pxref); 394 xrefs.add(pxref); 395 } 396 indirect.writeTo(writer.getOs()); 397 position = writer.getOs().getCounter(); 398 return indirect; 399 } 400 } 401 402 /** 403 * Returns the offset of the Cross-Reference table. 404 * 405 * @return an offset 406 */ 407 408 int offset() { 409 return position; 410 } 411 412 /** 413 * Returns the total number of objects contained in the CrossReferenceTable of this <CODE>Body</CODE>. 414 * 415 * @return a number of objects 416 */ 417 418 int size() { 419 return Math.max(xrefs.last().getRefnum() + 1, refnum); 420 } 421 422 /** 423 * Returns the CrossReferenceTable of the <CODE>Body</CODE>. 424 * @param os 425 * @param root 426 * @param info 427 * @param encryption 428 * @param fileID 429 * @param prevxref 430 * @throws IOException 431 */ 432 433 void writeCrossReferenceTable(final OutputStream os, final PdfIndirectReference root, final PdfIndirectReference info, final PdfIndirectReference encryption, final PdfObject fileID, final int prevxref) throws IOException { 434 int refNumber = 0; 435 if (writer.isFullCompression()) { 436 flushObjStm(); 437 refNumber = getIndirectReferenceNumber(); 438 xrefs.add(new PdfCrossReference(refNumber, position)); 439 } 440 PdfCrossReference entry = xrefs.first(); 441 int first = entry.getRefnum(); 442 int len = 0; 443 ArrayList<Integer> sections = new ArrayList<Integer>(); 444 for (PdfCrossReference pdfCrossReference : xrefs) { 445 entry = pdfCrossReference; 446 if (first + len == entry.getRefnum()) 447 ++len; 448 else { 449 sections.add(Integer.valueOf(first)); 450 sections.add(Integer.valueOf(len)); 451 first = entry.getRefnum(); 452 len = 1; 453 } 454 } 455 sections.add(Integer.valueOf(first)); 456 sections.add(Integer.valueOf(len)); 457 if (writer.isFullCompression()) { 458 int mid = 4; 459 int mask = 0xff000000; 460 for (; mid > 1; --mid) { 461 if ((mask & position) != 0) 462 break; 463 mask >>>= 8; 464 } 465 ByteBuffer buf = new ByteBuffer(); 466 467 for (Object element : xrefs) { 468 entry = (PdfCrossReference) element; 469 entry.toPdf(mid, buf); 470 } 471 PdfStream xr = new PdfStream(buf.toByteArray()); 472 buf = null; 473 xr.flateCompress(writer.getCompressionLevel()); 474 xr.put(PdfName.SIZE, new PdfNumber(size())); 475 xr.put(PdfName.ROOT, root); 476 if (info != null) { 477 xr.put(PdfName.INFO, info); 478 } 479 if (encryption != null) 480 xr.put(PdfName.ENCRYPT, encryption); 481 if (fileID != null) 482 xr.put(PdfName.ID, fileID); 483 xr.put(PdfName.W, new PdfArray(new int[]{1, mid, 2})); 484 xr.put(PdfName.TYPE, PdfName.XREF); 485 PdfArray idx = new PdfArray(); 486 for (int k = 0; k < sections.size(); ++k) 487 idx.add(new PdfNumber(sections.get(k).intValue())); 488 xr.put(PdfName.INDEX, idx); 489 if (prevxref > 0) 490 xr.put(PdfName.PREV, new PdfNumber(prevxref)); 491 PdfEncryption enc = writer.crypto; 492 writer.crypto = null; 493 PdfIndirectObject indirect = new PdfIndirectObject(refNumber, xr, writer); 494 indirect.writeTo(writer.getOs()); 495 writer.crypto = enc; 496 } 497 else { 498 os.write(getISOBytes("xref\n")); 499 Iterator<PdfCrossReference> i = xrefs.iterator(); 500 for (int k = 0; k < sections.size(); k += 2) { 501 first = sections.get(k).intValue(); 502 len = sections.get(k + 1).intValue(); 503 os.write(getISOBytes(String.valueOf(first))); 504 os.write(getISOBytes(" ")); 505 os.write(getISOBytes(String.valueOf(len))); 506 os.write('\n'); 507 while (len-- > 0) { 508 entry = i.next(); 509 entry.toPdf(os); 510 } 511 } 512 } 513 } 514 } 515 516 /** 517 * <CODE>PdfTrailer</CODE> is the PDF Trailer object. 518 * <P> 519 * This object is described in the 'Portable Document Format Reference Manual version 1.3' 520 * section 5.16 (page 59-60). 521 */ 522 523 static class PdfTrailer extends PdfDictionary { 524 525 // membervariables 526 527 int offset; 528 529 // constructors 530 531 /** 532 * Constructs a PDF-Trailer. 533 * 534 * @param size the number of entries in the <CODE>PdfCrossReferenceTable</CODE> 535 * @param offset offset of the <CODE>PdfCrossReferenceTable</CODE> 536 * @param root an indirect reference to the root of the PDF document 537 * @param info an indirect reference to the info object of the PDF document 538 * @param encryption 539 * @param fileID 540 * @param prevxref 541 */ 542 543 PdfTrailer(final int size, final int offset, final PdfIndirectReference root, final PdfIndirectReference info, final PdfIndirectReference encryption, final PdfObject fileID, final int prevxref) { 544 this.offset = offset; 545 put(PdfName.SIZE, new PdfNumber(size)); 546 put(PdfName.ROOT, root); 547 if (info != null) { 548 put(PdfName.INFO, info); 549 } 550 if (encryption != null) 551 put(PdfName.ENCRYPT, encryption); 552 if (fileID != null) 553 put(PdfName.ID, fileID); 554 if (prevxref > 0) 555 put(PdfName.PREV, new PdfNumber(prevxref)); 556 } 557 558 /** 559 * Returns the PDF representation of this <CODE>PdfObject</CODE>. 560 * @param writer 561 * @param os 562 * @throws IOException 563 */ 564 @Override 565 public void toPdf(final PdfWriter writer, final OutputStream os) throws IOException { 566 os.write(getISOBytes("trailer\n")); 567 super.toPdf(null, os); 568 os.write(getISOBytes("\nstartxref\n")); 569 os.write(getISOBytes(String.valueOf(offset))); 570 os.write(getISOBytes("\n%%EOF\n")); 571 } 572 } 573 574// ESSENTIALS 575 576// Construct a PdfWriter instance 577 578 /** 579 * Constructs a <CODE>PdfWriter</CODE>. 580 */ 581 protected PdfWriter() { 582 } 583 584 /** 585 * Constructs a <CODE>PdfWriter</CODE>. 586 * <P> 587 * Remark: a PdfWriter can only be constructed by calling the method 588 * <CODE>getInstance(Document document, OutputStream os)</CODE>. 589 * 590 * @param document The <CODE>PdfDocument</CODE> that has to be written 591 * @param os The <CODE>OutputStream</CODE> the writer has to write to. 592 */ 593 594 protected PdfWriter(final PdfDocument document, final OutputStream os) { 595 super(document, os); 596 pdf = document; 597 directContent = new PdfContentByte(this); 598 directContentUnder = new PdfContentByte(this); 599 } 600 601 /** 602 * Use this method to get an instance of the <CODE>PdfWriter</CODE>. 603 * 604 * @param document The <CODE>Document</CODE> that has to be written 605 * @param os The <CODE>OutputStream</CODE> the writer has to write to. 606 * @return a new <CODE>PdfWriter</CODE> 607 * 608 * @throws DocumentException on error 609 */ 610 611 public static PdfWriter getInstance(final Document document, final OutputStream os) 612 throws DocumentException { 613 PdfDocument pdf = new PdfDocument(); 614 document.addDocListener(pdf); 615 PdfWriter writer = new PdfWriter(pdf, os); 616 pdf.addWriter(writer); 617 return writer; 618 } 619 620 /** 621 * Use this method to get an instance of the <CODE>PdfWriter</CODE>. 622 * 623 * @return a new <CODE>PdfWriter</CODE> 624 * @param document The <CODE>Document</CODE> that has to be written 625 * @param os The <CODE>OutputStream</CODE> the writer has to write to. 626 * @param listener A <CODE>DocListener</CODE> to pass to the PdfDocument. 627 * @throws DocumentException on error 628 */ 629 630 public static PdfWriter getInstance(final Document document, final OutputStream os, final DocListener listener) 631 throws DocumentException { 632 PdfDocument pdf = new PdfDocument(); 633 pdf.addDocListener(listener); 634 document.addDocListener(pdf); 635 PdfWriter writer = new PdfWriter(pdf, os); 636 pdf.addWriter(writer); 637 return writer; 638 } 639 640// the PdfDocument instance 641 642 /** the pdfdocument object. */ 643 protected PdfDocument pdf; 644 645 /** 646 * Gets the <CODE>PdfDocument</CODE> associated with this writer. 647 * @return the <CODE>PdfDocument</CODE> 648 */ 649 650 PdfDocument getPdfDocument() { 651 return pdf; 652 } 653 654 /** 655 * Use this method to get the info dictionary if you want to 656 * change it directly (add keys and values to the info dictionary). 657 * @return the info dictionary 658 */ 659 public PdfDictionary getInfo() { 660 return pdf.getInfo(); 661 } 662 663 /** 664 * Use this method to get the current vertical page position. 665 * @param ensureNewLine Tells whether a new line shall be enforced. This may cause side effects 666 * for elements that do not terminate the lines they've started because those lines will get 667 * terminated. 668 * @return The current vertical page position. 669 */ 670 public float getVerticalPosition(final boolean ensureNewLine) { 671 return pdf.getVerticalPosition(ensureNewLine); 672 } 673 674 /** 675 * Sets the initial leading for the PDF document. 676 * This has to be done before the document is opened. 677 * @param leading the initial leading 678 * @since 2.1.6 679 * @throws DocumentException if you try setting the leading after the document was opened. 680 */ 681 public void setInitialLeading(final float leading) throws DocumentException { 682 if (open) 683 throw new DocumentException(MessageLocalization.getComposedMessage("you.can.t.set.the.initial.leading.if.the.document.is.already.open")); 684 pdf.setLeading(leading); 685 } 686 687// the PdfDirectContentByte instances 688 689/* 690 * You should see Direct Content as a canvas on which you can draw 691 * graphics and text. One canvas goes on top of the page (getDirectContent), 692 * the other goes underneath (getDirectContentUnder). 693 * You can always the same object throughout your document, 694 * even if you have moved to a new page. Whatever you add on 695 * the canvas will be displayed on top or under the current page. 696 */ 697 698 /** The direct content in this document. */ 699 protected PdfContentByte directContent; 700 701 /** The direct content under in this document. */ 702 protected PdfContentByte directContentUnder; 703 704 /** 705 * Use this method to get the direct content for this document. 706 * There is only one direct content, multiple calls to this method 707 * will allways retrieve the same object. 708 * @return the direct content 709 */ 710 711 public PdfContentByte getDirectContent() { 712 if (!open) 713 throw new RuntimeException(MessageLocalization.getComposedMessage("the.document.is.not.open")); 714 return directContent; 715 } 716 717 /** 718 * Use this method to get the direct content under for this document. 719 * There is only one direct content, multiple calls to this method 720 * will always retrieve the same object. 721 * @return the direct content 722 */ 723 724 public PdfContentByte getDirectContentUnder() { 725 if (!open) 726 throw new RuntimeException(MessageLocalization.getComposedMessage("the.document.is.not.open")); 727 return directContentUnder; 728 } 729 730 /** 731 * Resets all the direct contents to empty. 732 * This happens when a new page is started. 733 */ 734 void resetContent() { 735 directContent.reset(); 736 directContentUnder.reset(); 737 } 738 739// PDF body 740 741/* 742 * A PDF file has 4 parts: a header, a body, a cross-reference table, and a trailer. 743 * The body contains all the PDF objects that make up the PDF document. 744 * Each element gets a reference (a set of numbers) and the byte position of 745 * every object is stored in the cross-reference table. 746 * Use these methods only if you know what you're doing. 747 */ 748 749 /** body of the PDF document */ 750 protected PdfBody body; 751 752 /** 753 * Adds the local destinations to the body of the document. 754 * @param desto the <CODE>HashMap</CODE> containing the destinations 755 * @throws IOException on error 756 */ 757 758 void addLocalDestinations(final TreeMap<String, PdfDocument.Destination> desto) throws IOException { 759 for (Map.Entry<String, PdfDocument.Destination> entry : desto.entrySet()) { 760 String name = entry.getKey(); 761 PdfDocument.Destination dest = entry.getValue(); 762 PdfDestination destination = dest.destination; 763 if (dest.reference == null) 764 dest.reference = getPdfIndirectReference(); 765 if (destination == null) 766 addToBody(new PdfString("invalid_" + name), dest.reference); 767 else 768 addToBody(destination, dest.reference); 769 } 770 } 771 772 /** 773 * Use this method to add a PDF object to the PDF body. 774 * Use this method only if you know what you're doing! 775 * @param object 776 * @return a PdfIndirectObject 777 * @throws IOException 778 */ 779 public PdfIndirectObject addToBody(final PdfObject object) throws IOException { 780 PdfIndirectObject iobj = body.add(object); 781 return iobj; 782 } 783 784 /** 785 * Use this method to add a PDF object to the PDF body. 786 * Use this method only if you know what you're doing! 787 * @param object 788 * @param inObjStm 789 * @return a PdfIndirectObject 790 * @throws IOException 791 */ 792 public PdfIndirectObject addToBody(final PdfObject object, final boolean inObjStm) throws IOException { 793 PdfIndirectObject iobj = body.add(object, inObjStm); 794 return iobj; 795 } 796 797 /** 798 * Use this method to add a PDF object to the PDF body. 799 * Use this method only if you know what you're doing! 800 * @param object 801 * @param ref 802 * @return a PdfIndirectObject 803 * @throws IOException 804 */ 805 public PdfIndirectObject addToBody(final PdfObject object, final PdfIndirectReference ref) throws IOException { 806 PdfIndirectObject iobj = body.add(object, ref); 807 return iobj; 808 } 809 810 /** 811 * Use this method to add a PDF object to the PDF body. 812 * Use this method only if you know what you're doing! 813 * @param object 814 * @param ref 815 * @param inObjStm 816 * @return a PdfIndirectObject 817 * @throws IOException 818 */ 819 public PdfIndirectObject addToBody(final PdfObject object, final PdfIndirectReference ref, final boolean inObjStm) throws IOException { 820 PdfIndirectObject iobj = body.add(object, ref, inObjStm); 821 return iobj; 822 } 823 824 /** 825 * Use this method to add a PDF object to the PDF body. 826 * Use this method only if you know what you're doing! 827 * @param object 828 * @param refNumber 829 * @return a PdfIndirectObject 830 * @throws IOException 831 */ 832 public PdfIndirectObject addToBody(final PdfObject object, final int refNumber) throws IOException { 833 PdfIndirectObject iobj = body.add(object, refNumber); 834 return iobj; 835 } 836 837 /** 838 * Use this method to add a PDF object to the PDF body. 839 * Use this method only if you know what you're doing! 840 * @param object 841 * @param refNumber 842 * @param inObjStm 843 * @return a PdfIndirectObject 844 * @throws IOException 845 */ 846 public PdfIndirectObject addToBody(final PdfObject object, final int refNumber, final boolean inObjStm) throws IOException { 847 PdfIndirectObject iobj = body.add(object, refNumber, inObjStm); 848 return iobj; 849 } 850 851 /** 852 * Use this to get an <CODE>PdfIndirectReference</CODE> for an object that 853 * will be created in the future. 854 * Use this method only if you know what you're doing! 855 * @return the <CODE>PdfIndirectReference</CODE> 856 */ 857 858 public PdfIndirectReference getPdfIndirectReference() { 859 return body.getPdfIndirectReference(); 860 } 861 862 int getIndirectReferenceNumber() { 863 return body.getIndirectReferenceNumber(); 864 } 865 866 /** 867 * Returns the outputStreamCounter. 868 * @return the outputStreamCounter 869 */ 870 OutputStreamCounter getOs() { 871 return os; 872 } 873 874 875// PDF Catalog 876 877/* 878 * The Catalog is also called the root object of the document. 879 * Whereas the Cross-Reference maps the objects number with the 880 * byte offset so that the viewer can find the objects, the 881 * Catalog tells the viewer the numbers of the objects needed 882 * to render the document. 883 */ 884 885 protected PdfDictionary getCatalog(final PdfIndirectReference rootObj) 886 { 887 PdfDictionary catalog = pdf.getCatalog(rootObj); 888 // [F12] tagged PDF 889 if (tagged) { 890 try { 891 getStructureTreeRoot().buildTree(); 892 } 893 catch (Exception e) { 894 throw new ExceptionConverter(e); 895 } 896 catalog.put(PdfName.STRUCTTREEROOT, structureTreeRoot.getReference()); 897 PdfDictionary mi = new PdfDictionary(); 898 mi.put(PdfName.MARKED, PdfBoolean.PDFTRUE); 899 if (userProperties) 900 mi.put(PdfName.USERPROPERTIES, PdfBoolean.PDFTRUE); 901 catalog.put(PdfName.MARKINFO, mi); 902 } 903 // [F13] OCG 904 if (!documentOCG.isEmpty()) { 905 fillOCProperties(false); 906 catalog.put(PdfName.OCPROPERTIES, OCProperties); 907 } 908 return catalog; 909 } 910 911 /** Holds value of property extraCatalog this is used for Output Intents. */ 912 protected PdfDictionary extraCatalog; 913 914 /** 915 * Sets extra keys to the catalog. 916 * @return the catalog to change 917 */ 918 public PdfDictionary getExtraCatalog() { 919 if (extraCatalog == null) 920 extraCatalog = new PdfDictionary(); 921 return this.extraCatalog; 922 } 923 924// PdfPages 925 926/* 927 * The page root keeps the complete page tree of the document. 928 * There's an entry in the Catalog that refers to the root 929 * of the page tree, the page tree contains the references 930 * to pages and other page trees. 931 */ 932 933 /** The root of the page tree. */ 934 protected PdfPages root = new PdfPages(this); 935 /** The PdfIndirectReference to the pages. */ 936 protected ArrayList<PdfIndirectReference> pageReferences = new ArrayList<PdfIndirectReference>(); 937 /** The current page number. */ 938 protected int currentPageNumber = 1; 939 /** 940 * The value of the Tabs entry in the page dictionary. 941 * @since 2.1.5 942 */ 943 protected PdfName tabs = null; 944 945 /** 946 * Additional page dictionary entries. 947 * @since 5.1.0 948 */ 949 protected PdfDictionary pageDictEntries = new PdfDictionary(); 950 951 /** 952 * Adds an additional entry for the page dictionary. 953 * @param key the key 954 * @param object the PdfObject for the given key 955 * @since 5.1.0 956 */ 957 public void addPageDictEntry(final PdfName key, final PdfObject object) { 958 pageDictEntries.put(key, object); 959 } 960 961 /** 962 * Gets the additional pageDictEntries. 963 * @return the page dictionary entries 964 * @since 5.1.0 965 */ 966 public PdfDictionary getPageDictEntries() { 967 return pageDictEntries; 968 } 969 970 /** 971 * Resets the additional pageDictEntries. 972 * @since 5.1.0 973 */ 974 public void resetPageDictEntries() { 975 pageDictEntries = new PdfDictionary(); 976 } 977 978 /** 979 * Use this method to make sure the page tree has a linear structure 980 * (every leave is attached directly to the root). 981 * Use this method to allow page reordering with method reorderPages. 982 */ 983 public void setLinearPageMode() { 984 root.setLinearMode(null); 985 } 986 987 /** 988 * Use this method to reorder the pages in the document. 989 * A <CODE>null</CODE> argument value only returns the number of pages to process. 990 * It is advisable to issue a <CODE>Document.newPage()</CODE> before using this method. 991 * @return the total number of pages 992 * @param order an array with the new page sequence. It must have the 993 * same size as the number of pages. 994 * @throws DocumentException if all the pages are not present in the array 995 */ 996 public int reorderPages(final int order[]) throws DocumentException { 997 return root.reorderPages(order); 998 } 999 1000 /** 1001 * Use this method to get a reference to a page existing or not. 1002 * If the page does not exist yet the reference will be created 1003 * in advance. If on closing the document, a page number greater 1004 * than the total number of pages was requested, an exception 1005 * is thrown. 1006 * @param page the page number. The first page is 1 1007 * @return the reference to the page 1008 */ 1009 public PdfIndirectReference getPageReference(int page) { 1010 --page; 1011 if (page < 0) 1012 throw new IndexOutOfBoundsException(MessageLocalization.getComposedMessage("the.page.number.must.be.gt.eq.1")); 1013 PdfIndirectReference ref; 1014 if (page < pageReferences.size()) { 1015 ref = pageReferences.get(page); 1016 if (ref == null) { 1017 ref = body.getPdfIndirectReference(); 1018 pageReferences.set(page, ref); 1019 } 1020 } 1021 else { 1022 int empty = page - pageReferences.size(); 1023 for (int k = 0; k < empty; ++k) 1024 pageReferences.add(null); 1025 ref = body.getPdfIndirectReference(); 1026 pageReferences.add(ref); 1027 } 1028 return ref; 1029 } 1030 1031 /** 1032 * Gets the pagenumber of this document. 1033 * This number can be different from the real pagenumber, 1034 * if you have (re)set the page number previously. 1035 * @return a page number 1036 */ 1037 1038 public int getPageNumber() { 1039 return pdf.getPageNumber(); 1040 } 1041 1042 PdfIndirectReference getCurrentPage() { 1043 return getPageReference(currentPageNumber); 1044 } 1045 1046 public int getCurrentPageNumber() { 1047 return currentPageNumber; 1048 } 1049 1050 /** 1051 * Sets the Viewport for the next page. 1052 * @param vp an array consisting of Viewport dictionaries. 1053 * @since 5.1.0 1054 */ 1055 public void setPageViewport(final PdfArray vp) { 1056 addPageDictEntry(PdfName.VP, vp); 1057 } 1058 1059 /** 1060 * Sets the value for the Tabs entry in the page tree. 1061 * @param tabs Can be PdfName.R, PdfName.C or PdfName.S. 1062 * Since the Adobe Extensions Level 3, it can also be PdfName.A 1063 * or PdfName.W 1064 * @since 2.1.5 1065 */ 1066 public void setTabs(final PdfName tabs) { 1067 this.tabs = tabs; 1068 } 1069 1070 /** 1071 * Returns the value to be used for the Tabs entry in the page tree. 1072 * @return the Tabs PdfName 1073 * @since 2.1.5 1074 */ 1075 public PdfName getTabs() { 1076 return tabs; 1077 } 1078 1079 /** 1080 * Adds some <CODE>PdfContents</CODE> to this Writer. 1081 * <P> 1082 * The document has to be open before you can begin to add content 1083 * to the body of the document. 1084 * 1085 * @return a <CODE>PdfIndirectReference</CODE> 1086 * @param page the <CODE>PdfPage</CODE> to add 1087 * @param contents the <CODE>PdfContents</CODE> of the page 1088 * @throws PdfException on error 1089 */ 1090 1091 PdfIndirectReference add(final PdfPage page, final PdfContents contents) throws PdfException { 1092 if (!open) { 1093 throw new PdfException(MessageLocalization.getComposedMessage("the.document.is.not.open")); 1094 } 1095 PdfIndirectObject object; 1096 try { 1097 object = addToBody(contents); 1098 } 1099 catch(IOException ioe) { 1100 throw new ExceptionConverter(ioe); 1101 } 1102 page.add(object.getIndirectReference()); 1103 // [U5] 1104 if (group != null) { 1105 page.put(PdfName.GROUP, group); 1106 group = null; 1107 } 1108 else if (rgbTransparencyBlending) { 1109 PdfDictionary pp = new PdfDictionary(); 1110 pp.put(PdfName.TYPE, PdfName.GROUP); 1111 pp.put(PdfName.S, PdfName.TRANSPARENCY); 1112 pp.put(PdfName.CS, PdfName.DEVICERGB); 1113 page.put(PdfName.GROUP, pp); 1114 } 1115 root.addPage(page); 1116 currentPageNumber++; 1117 return null; 1118 } 1119 1120// page events 1121 1122/* 1123 * Page events are specific for iText, not for PDF. 1124 * Upon specific events (for instance when a page starts 1125 * or ends), the corresponding method in the page event 1126 * implementation that is added to the writer is invoked. 1127 */ 1128 1129 /** The <CODE>PdfPageEvent</CODE> for this document. */ 1130 private PdfPageEvent pageEvent; 1131 1132 /** 1133 * Sets the <CODE>PdfPageEvent</CODE> for this document. 1134 * @param event the <CODE>PdfPageEvent</CODE> for this document 1135 */ 1136 1137 public void setPageEvent(final PdfPageEvent event) { 1138 if (event == null) this.pageEvent = null; 1139 else if (this.pageEvent == null) this.pageEvent = event; 1140 else if (this.pageEvent instanceof PdfPageEventForwarder) ((PdfPageEventForwarder)this.pageEvent).addPageEvent(event); 1141 else { 1142 PdfPageEventForwarder forward = new PdfPageEventForwarder(); 1143 forward.addPageEvent(this.pageEvent); 1144 forward.addPageEvent(event); 1145 this.pageEvent = forward; 1146 } 1147 } 1148 1149 /** 1150 * Gets the <CODE>PdfPageEvent</CODE> for this document or <CODE>null</CODE> 1151 * if none is set. 1152 * @return the <CODE>PdfPageEvent</CODE> for this document or <CODE>null</CODE> 1153 * if none is set 1154 */ 1155 1156 public PdfPageEvent getPageEvent() { 1157 return pageEvent; 1158 } 1159 1160// Open and Close methods + method that create the PDF 1161 1162 /** A number referring to the previous Cross-Reference Table. */ 1163 protected int prevxref = 0; 1164 1165 /** 1166 * Signals that the <CODE>Document</CODE> has been opened and that 1167 * <CODE>Elements</CODE> can be added. 1168 * <P> 1169 * When this method is called, the PDF-document header is 1170 * written to the outputstream. 1171 * @see com.itextpdf.text.DocWriter#open() 1172 */ 1173 @Override 1174 public void open() { 1175 super.open(); 1176 try { 1177 pdf_version.writeHeader(os); 1178 body = new PdfBody(this); 1179 if (pdfxConformance.isPdfX32002()) { 1180 PdfDictionary sec = new PdfDictionary(); 1181 sec.put(PdfName.GAMMA, new PdfArray(new float[]{2.2f,2.2f,2.2f})); 1182 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})); 1183 sec.put(PdfName.WHITEPOINT, new PdfArray(new float[]{0.9505f,1f,1.089f})); 1184 PdfArray arr = new PdfArray(PdfName.CALRGB); 1185 arr.add(sec); 1186 setDefaultColorspace(PdfName.DEFAULTRGB, addToBody(arr).getIndirectReference()); 1187 } 1188 } 1189 catch(IOException ioe) { 1190 throw new ExceptionConverter(ioe); 1191 } 1192 } 1193 1194 /** 1195 * Signals that the <CODE>Document</CODE> was closed and that no other 1196 * <CODE>Elements</CODE> will be added. 1197 * <P> 1198 * The pages-tree is built and written to the outputstream. 1199 * A Catalog is constructed, as well as an Info-object, 1200 * the reference table is composed and everything is written 1201 * to the outputstream embedded in a Trailer. 1202 * @see com.itextpdf.text.DocWriter#close() 1203 */ 1204 @Override 1205 public void close() { 1206 if (open) { 1207 if (currentPageNumber - 1 != pageReferences.size()) 1208 throw new RuntimeException("The page " + pageReferences.size() + 1209 " was requested but the document has only " + (currentPageNumber - 1) + " pages."); 1210 pdf.close(); 1211 try { 1212 addSharedObjectsToBody(); 1213 for (PdfOCG layer : documentOCG) { 1214 addToBody(layer.getPdfObject(), layer.getRef()); 1215 } 1216 // add the root to the body 1217 PdfIndirectReference rootRef = root.writePageTree(); 1218 // make the catalog-object and add it to the body 1219 PdfDictionary catalog = getCatalog(rootRef); 1220 // [C9] if there is XMP data to add: add it 1221 if (xmpMetadata != null) { 1222 PdfStream xmp = new PdfStream(xmpMetadata); 1223 xmp.put(PdfName.TYPE, PdfName.METADATA); 1224 xmp.put(PdfName.SUBTYPE, PdfName.XML); 1225 if (crypto != null && !crypto.isMetadataEncrypted()) { 1226 PdfArray ar = new PdfArray(); 1227 ar.add(PdfName.CRYPT); 1228 xmp.put(PdfName.FILTER, ar); 1229 } 1230 catalog.put(PdfName.METADATA, body.add(xmp).getIndirectReference()); 1231 } 1232 // [C10] make pdfx conformant 1233 if (isPdfX()) { 1234 pdfxConformance.completeInfoDictionary(getInfo()); 1235 pdfxConformance.completeExtraCatalog(getExtraCatalog()); 1236 } 1237 // [C11] Output Intents 1238 if (extraCatalog != null) { 1239 catalog.mergeDifferent(extraCatalog); 1240 } 1241 1242 writeOutlines(catalog, false); 1243 1244 // add the Catalog to the body 1245 PdfIndirectObject indirectCatalog = addToBody(catalog, false); 1246 // add the info-object to the body 1247 PdfIndirectObject infoObj = addToBody(getInfo(), false); 1248 1249 // [F1] encryption 1250 PdfIndirectReference encryption = null; 1251 PdfObject fileID = null; 1252 body.flushObjStm(); 1253 if (crypto != null) { 1254 PdfIndirectObject encryptionObject = addToBody(crypto.getEncryptionDictionary(), false); 1255 encryption = encryptionObject.getIndirectReference(); 1256 fileID = crypto.getFileID(); 1257 } 1258 else 1259 fileID = PdfEncryption.createInfoId(PdfEncryption.createDocumentId()); 1260 1261 // write the cross-reference table of the body 1262 body.writeCrossReferenceTable(os, indirectCatalog.getIndirectReference(), 1263 infoObj.getIndirectReference(), encryption, fileID, prevxref); 1264 1265 // make the trailer 1266 // [F2] full compression 1267 if (fullCompression) { 1268 os.write(getISOBytes("startxref\n")); 1269 os.write(getISOBytes(String.valueOf(body.offset()))); 1270 os.write(getISOBytes("\n%%EOF\n")); 1271 } 1272 else { 1273 PdfTrailer trailer = new PdfTrailer(body.size(), 1274 body.offset(), 1275 indirectCatalog.getIndirectReference(), 1276 infoObj.getIndirectReference(), 1277 encryption, 1278 fileID, prevxref); 1279 trailer.toPdf(this, os); 1280 } 1281 super.close(); 1282 } 1283 catch(IOException ioe) { 1284 throw new ExceptionConverter(ioe); 1285 } 1286 } 1287 } 1288 1289 protected void addSharedObjectsToBody() throws IOException { 1290 // [F3] add the fonts 1291 for (FontDetails details : documentFonts.values()) { 1292 details.writeFont(this); 1293 } 1294 // [F4] add the form XObjects 1295 for (Object objs[] : formXObjects.values()) { 1296 PdfTemplate template = (PdfTemplate)objs[1]; 1297 if (template != null && template.getIndirectReference() instanceof PRIndirectReference) 1298 continue; 1299 if (template != null && template.getType() == PdfTemplate.TYPE_TEMPLATE) { 1300 addToBody(template.getFormXObject(compressionLevel), template.getIndirectReference()); 1301 } 1302 } 1303 // [F5] add all the dependencies in the imported pages 1304 for (PdfReaderInstance element : readerInstances.values()) { 1305 currentPdfReaderInstance= element; 1306 currentPdfReaderInstance.writeAllPages(); 1307 } 1308 currentPdfReaderInstance = null; 1309 // [F6] add the spotcolors 1310 for (ColorDetails color : documentColors.values()) { 1311 addToBody(color.getSpotColor(this), color.getIndirectReference()); 1312 } 1313 // [F7] add the pattern 1314 for (PdfPatternPainter pat : documentPatterns.keySet()) { 1315 addToBody(pat.getPattern(compressionLevel), pat.getIndirectReference()); 1316 } 1317 // [F8] add the shading patterns 1318 for (PdfShadingPattern shadingPattern : documentShadingPatterns) { 1319 shadingPattern.addToBody(); 1320 } 1321 // [F9] add the shadings 1322 for (PdfShading shading : documentShadings) { 1323 shading.addToBody(); 1324 } 1325 // [F10] add the extgstate 1326 for (Map.Entry<PdfDictionary, PdfObject[]>entry : documentExtGState.entrySet()) { 1327 PdfDictionary gstate = entry.getKey(); 1328 PdfObject obj[] = entry.getValue(); 1329 addToBody(gstate, (PdfIndirectReference)obj[1]); 1330 } 1331 // [F11] add the properties 1332 for (Map.Entry<Object, PdfObject[]>entry : documentProperties.entrySet()) { 1333 Object prop = entry.getKey(); 1334 PdfObject[] obj = entry.getValue(); 1335 if (prop instanceof PdfLayerMembership){ 1336 PdfLayerMembership layer = (PdfLayerMembership)prop; 1337 addToBody(layer.getPdfObject(), layer.getRef()); 1338 } 1339 else if (prop instanceof PdfDictionary && !(prop instanceof PdfLayer)){ 1340 addToBody((PdfDictionary)prop, (PdfIndirectReference)obj[1]); 1341 } 1342 } 1343 } 1344 1345// Root data for the PDF document (used when composing the Catalog) 1346 1347// [C1] Outlines (bookmarks) 1348 1349 /** 1350 * Use this method to get the root outline 1351 * and construct bookmarks. 1352 * @return the root outline 1353 */ 1354 1355 public PdfOutline getRootOutline() { 1356 return directContent.getRootOutline(); 1357 } 1358 1359 protected List<HashMap<String, Object>> newBookmarks; 1360 1361 /** 1362 * Sets the bookmarks. The list structure is defined in 1363 * {@link SimpleBookmark}. 1364 * @param outlines the bookmarks or <CODE>null</CODE> to remove any 1365 */ 1366 public void setOutlines(final List<HashMap<String, Object>> outlines) { 1367 newBookmarks = outlines; 1368 } 1369 1370 protected void writeOutlines(final PdfDictionary catalog, final boolean namedAsNames) throws IOException { 1371 if (newBookmarks == null || newBookmarks.isEmpty()) 1372 return; 1373 PdfDictionary top = new PdfDictionary(); 1374 PdfIndirectReference topRef = getPdfIndirectReference(); 1375 Object kids[] = SimpleBookmark.iterateOutlines(this, topRef, newBookmarks, namedAsNames); 1376 top.put(PdfName.FIRST, (PdfIndirectReference)kids[0]); 1377 top.put(PdfName.LAST, (PdfIndirectReference)kids[1]); 1378 top.put(PdfName.COUNT, new PdfNumber(((Integer)kids[2]).intValue())); 1379 addToBody(top, topRef); 1380 catalog.put(PdfName.OUTLINES, topRef); 1381 } 1382 1383// [C2] PdfVersion interface 1384 /** possible PDF version (header) */ 1385 public static final char VERSION_1_2 = '2'; 1386 /** possible PDF version (header) */ 1387 public static final char VERSION_1_3 = '3'; 1388 /** possible PDF version (header) */ 1389 public static final char VERSION_1_4 = '4'; 1390 /** possible PDF version (header) */ 1391 public static final char VERSION_1_5 = '5'; 1392 /** possible PDF version (header) */ 1393 public static final char VERSION_1_6 = '6'; 1394 /** possible PDF version (header) */ 1395 public static final char VERSION_1_7 = '7'; 1396 1397 /** possible PDF version (catalog) */ 1398 public static final PdfName PDF_VERSION_1_2 = new PdfName("1.2"); 1399 /** possible PDF version (catalog) */ 1400 public static final PdfName PDF_VERSION_1_3 = new PdfName("1.3"); 1401 /** possible PDF version (catalog) */ 1402 public static final PdfName PDF_VERSION_1_4 = new PdfName("1.4"); 1403 /** possible PDF version (catalog) */ 1404 public static final PdfName PDF_VERSION_1_5 = new PdfName("1.5"); 1405 /** possible PDF version (catalog) */ 1406 public static final PdfName PDF_VERSION_1_6 = new PdfName("1.6"); 1407 /** possible PDF version (catalog) */ 1408 public static final PdfName PDF_VERSION_1_7 = new PdfName("1.7"); 1409 1410 /** Stores the version information for the header and the catalog. */ 1411 protected PdfVersionImp pdf_version = new PdfVersionImp(); 1412 1413 /** @see com.itextpdf.text.pdf.interfaces.PdfVersion#setPdfVersion(char) */ 1414 public void setPdfVersion(final char version) { 1415 pdf_version.setPdfVersion(version); 1416 } 1417 1418 /** @see com.itextpdf.text.pdf.interfaces.PdfVersion#setAtLeastPdfVersion(char) */ 1419 public void setAtLeastPdfVersion(final char version) { 1420 pdf_version.setAtLeastPdfVersion(version); 1421 } 1422 1423 /** @see com.itextpdf.text.pdf.interfaces.PdfVersion#setPdfVersion(com.itextpdf.text.pdf.PdfName) */ 1424 public void setPdfVersion(final PdfName version) { 1425 pdf_version.setPdfVersion(version); 1426 } 1427 1428 /** 1429 * @see com.itextpdf.text.pdf.interfaces.PdfVersion#addDeveloperExtension(com.itextpdf.text.pdf.PdfDeveloperExtension) 1430 * @since 2.1.6 1431 */ 1432 public void addDeveloperExtension(final PdfDeveloperExtension de) { 1433 pdf_version.addDeveloperExtension(de); 1434 } 1435 1436 /** 1437 * Returns the version information. 1438 * @return the PdfVersion 1439 */ 1440 PdfVersionImp getPdfVersion() { 1441 return pdf_version; 1442 } 1443 1444// [C3] PdfViewerPreferences interface 1445 1446 // page layout (section 13.1.1 of "iText in Action") 1447 1448 /** A viewer preference */ 1449 public static final int PageLayoutSinglePage = 1; 1450 /** A viewer preference */ 1451 public static final int PageLayoutOneColumn = 2; 1452 /** A viewer preference */ 1453 public static final int PageLayoutTwoColumnLeft = 4; 1454 /** A viewer preference */ 1455 public static final int PageLayoutTwoColumnRight = 8; 1456 /** A viewer preference */ 1457 public static final int PageLayoutTwoPageLeft = 16; 1458 /** A viewer preference */ 1459 public static final int PageLayoutTwoPageRight = 32; 1460 1461 // page mode (section 13.1.2 of "iText in Action") 1462 1463 /** A viewer preference */ 1464 public static final int PageModeUseNone = 64; 1465 /** A viewer preference */ 1466 public static final int PageModeUseOutlines = 128; 1467 /** A viewer preference */ 1468 public static final int PageModeUseThumbs = 256; 1469 /** A viewer preference */ 1470 public static final int PageModeFullScreen = 512; 1471 /** A viewer preference */ 1472 public static final int PageModeUseOC = 1024; 1473 /** A viewer preference */ 1474 public static final int PageModeUseAttachments = 2048; 1475 1476 // values for setting viewer preferences in iText versions older than 2.x 1477 1478 /** A viewer preference */ 1479 public static final int HideToolbar = 1 << 12; 1480 /** A viewer preference */ 1481 public static final int HideMenubar = 1 << 13; 1482 /** A viewer preference */ 1483 public static final int HideWindowUI = 1 << 14; 1484 /** A viewer preference */ 1485 public static final int FitWindow = 1 << 15; 1486 /** A viewer preference */ 1487 public static final int CenterWindow = 1 << 16; 1488 /** A viewer preference */ 1489 public static final int DisplayDocTitle = 1 << 17; 1490 1491 /** A viewer preference */ 1492 public static final int NonFullScreenPageModeUseNone = 1 << 18; 1493 /** A viewer preference */ 1494 public static final int NonFullScreenPageModeUseOutlines = 1 << 19; 1495 /** A viewer preference */ 1496 public static final int NonFullScreenPageModeUseThumbs = 1 << 20; 1497 /** A viewer preference */ 1498 public static final int NonFullScreenPageModeUseOC = 1 << 21; 1499 1500 /** A viewer preference */ 1501 public static final int DirectionL2R = 1 << 22; 1502 /** A viewer preference */ 1503 public static final int DirectionR2L = 1 << 23; 1504 1505 /** A viewer preference */ 1506 public static final int PrintScalingNone = 1 << 24; 1507 1508 /** @see com.itextpdf.text.pdf.interfaces.PdfViewerPreferences#setViewerPreferences(int) */ 1509 public void setViewerPreferences(final int preferences) { 1510 pdf.setViewerPreferences(preferences); 1511 } 1512 1513 /** @see com.itextpdf.text.pdf.interfaces.PdfViewerPreferences#addViewerPreference(com.itextpdf.text.pdf.PdfName, com.itextpdf.text.pdf.PdfObject) */ 1514 public void addViewerPreference(final PdfName key, final PdfObject value) { 1515 pdf.addViewerPreference(key, value); 1516 } 1517 1518// [C4] Page labels 1519 1520 /** 1521 * Use this method to add page labels 1522 * @param pageLabels the page labels 1523 */ 1524 public void setPageLabels(final PdfPageLabels pageLabels) { 1525 pdf.setPageLabels(pageLabels); 1526 } 1527 1528// [C5] named objects: named destinations, javascript, embedded files 1529 1530 /** 1531 * Adds named destinations in bulk. 1532 * Valid keys and values of the map can be found in the map 1533 * that is created by SimpleNamedDestination. 1534 * @param map a map with strings as keys for the names, 1535 * and structured strings as values for the destinations 1536 * @param page_offset number of pages that has to be added to 1537 * the page numbers in the destinations (useful if you 1538 * use this method in combination with PdfCopy). 1539 * @since iText 5.0 1540 */ 1541 public void addNamedDestinations(final Map<String, String> map, final int page_offset) { 1542 int page; 1543 String dest; 1544 PdfDestination destination; 1545 for (Map.Entry<String, String> entry : map.entrySet()) { 1546 dest = entry.getValue(); 1547 page = Integer.parseInt(dest.substring(0, dest.indexOf(" "))); 1548 destination = new PdfDestination(dest.substring(dest.indexOf(" ") + 1)); 1549 addNamedDestination(entry.getKey(), page + page_offset, destination); 1550 } 1551 } 1552 1553 /** 1554 * Adds one named destination. 1555 * @param name the name for the destination 1556 * @param page the page number where you want to jump to 1557 * @param dest an explicit destination 1558 * @since iText 5.0 1559 */ 1560 public void addNamedDestination(final String name, final int page, final PdfDestination dest) { 1561 dest.addPage(getPageReference(page)); 1562 pdf.localDestination(name, dest); 1563 } 1564 1565 /** 1566 * Use this method to add a JavaScript action at the document level. 1567 * When the document opens, all this JavaScript runs. 1568 * @param js The JavaScript action 1569 */ 1570 public void addJavaScript(final PdfAction js) { 1571 pdf.addJavaScript(js); 1572 } 1573 1574 /** 1575 * Use this method to add a JavaScript action at the document level. 1576 * When the document opens, all this JavaScript runs. 1577 * @param code the JavaScript code 1578 * @param unicode select JavaScript unicode. Note that the internal 1579 * Acrobat JavaScript engine does not support unicode, 1580 * so this may or may not work for you 1581 */ 1582 public void addJavaScript(final String code, final boolean unicode) { 1583 addJavaScript(PdfAction.javaScript(code, this, unicode)); 1584 } 1585 1586 /** 1587 * Use this method to adds a JavaScript action at the document level. 1588 * When the document opens, all this JavaScript runs. 1589 * @param code the JavaScript code 1590 */ 1591 public void addJavaScript(final String code) { 1592 addJavaScript(code, false); 1593 } 1594 /** 1595 * Use this method to add a JavaScript action at the document level. 1596 * When the document opens, all this JavaScript runs. 1597 * @param name The name of the JS Action in the name tree 1598 * @param js The JavaScript action 1599 */ 1600 public void addJavaScript(final String name, final PdfAction js) { 1601 pdf.addJavaScript(name, js); 1602 } 1603 1604 /** 1605 * Use this method to add a JavaScript action at the document level. 1606 * When the document opens, all this JavaScript runs. 1607 * @param name The name of the JS Action in the name tree 1608 * @param code the JavaScript code 1609 * @param unicode select JavaScript unicode. Note that the internal 1610 * Acrobat JavaScript engine does not support unicode, 1611 * so this may or may not work for you 1612 */ 1613 public void addJavaScript(final String name, final String code, final boolean unicode) { 1614 addJavaScript(name, PdfAction.javaScript(code, this, unicode)); 1615 } 1616 1617 /** 1618 * Use this method to adds a JavaScript action at the document level. 1619 * When the document opens, all this JavaScript runs. 1620 * @param name The name of the JS Action in the name tree 1621 * @param code the JavaScript code 1622 */ 1623 public void addJavaScript(final String name, final String code) { 1624 addJavaScript(name, code, false); 1625 } 1626 1627 /** 1628 * Use this method to add a file attachment at the document level. 1629 * @param description the file description 1630 * @param fileStore an array with the file. If it's <CODE>null</CODE> 1631 * the file will be read from the disk 1632 * @param file the path to the file. It will only be used if 1633 * <CODE>fileStore</CODE> is not <CODE>null</CODE> 1634 * @param fileDisplay the actual file name stored in the pdf 1635 * @throws IOException on error 1636 */ 1637 public void addFileAttachment(final String description, final byte fileStore[], final String file, final String fileDisplay) throws IOException { 1638 addFileAttachment(description, PdfFileSpecification.fileEmbedded(this, file, fileDisplay, fileStore)); 1639 } 1640 1641 /** 1642 * Use this method to add a file attachment at the document level. 1643 * @param description the file description 1644 * @param fs the file specification 1645 * @throws IOException if the file attachment could not be added to the document 1646 */ 1647 public void addFileAttachment(final String description, final PdfFileSpecification fs) throws IOException { 1648 pdf.addFileAttachment(description, fs); 1649 } 1650 1651 /** 1652 * Use this method to add a file attachment at the document level. 1653 * @param fs the file specification 1654 * @throws IOException if the file attachment could not be added to the document 1655 */ 1656 public void addFileAttachment(final PdfFileSpecification fs) throws IOException { 1657 addFileAttachment(null, fs); 1658 } 1659 1660// [C6] Actions (open and additional) 1661 1662 /** action value */ 1663 public static final PdfName DOCUMENT_CLOSE = PdfName.WC; 1664 /** action value */ 1665 public static final PdfName WILL_SAVE = PdfName.WS; 1666 /** action value */ 1667 public static final PdfName DID_SAVE = PdfName.DS; 1668 /** action value */ 1669 public static final PdfName WILL_PRINT = PdfName.WP; 1670 /** action value */ 1671 public static final PdfName DID_PRINT = PdfName.DP; 1672 1673 /** @see com.itextpdf.text.pdf.interfaces.PdfDocumentActions#setOpenAction(java.lang.String) */ 1674 public void setOpenAction(final String name) { 1675 pdf.setOpenAction(name); 1676 } 1677 1678 /** @see com.itextpdf.text.pdf.interfaces.PdfDocumentActions#setOpenAction(com.itextpdf.text.pdf.PdfAction) */ 1679 public void setOpenAction(final PdfAction action) { 1680 pdf.setOpenAction(action); 1681 } 1682 1683 /** @see com.itextpdf.text.pdf.interfaces.PdfDocumentActions#setAdditionalAction(com.itextpdf.text.pdf.PdfName, com.itextpdf.text.pdf.PdfAction) */ 1684 public void setAdditionalAction(final PdfName actionType, final PdfAction action) throws DocumentException { 1685 if (!(actionType.equals(DOCUMENT_CLOSE) || 1686 actionType.equals(WILL_SAVE) || 1687 actionType.equals(DID_SAVE) || 1688 actionType.equals(WILL_PRINT) || 1689 actionType.equals(DID_PRINT))) { 1690 throw new DocumentException(MessageLocalization.getComposedMessage("invalid.additional.action.type.1", actionType.toString())); 1691 } 1692 pdf.addAdditionalAction(actionType, action); 1693 } 1694 1695// [C7] portable collections 1696 1697 /** 1698 * Use this method to add the Collection dictionary. 1699 * @param collection a dictionary of type PdfCollection 1700 */ 1701 public void setCollection(final PdfCollection collection) { 1702 setAtLeastPdfVersion(VERSION_1_7); 1703 pdf.setCollection(collection); 1704 } 1705 1706// [C8] AcroForm 1707 1708 /** signature value */ 1709 public static final int SIGNATURE_EXISTS = 1; 1710 /** signature value */ 1711 public static final int SIGNATURE_APPEND_ONLY = 2; 1712 1713 /** @see com.itextpdf.text.pdf.interfaces.PdfAnnotations#getAcroForm() */ 1714 public PdfAcroForm getAcroForm() { 1715 return pdf.getAcroForm(); 1716 } 1717 1718 /** @see com.itextpdf.text.pdf.interfaces.PdfAnnotations#addAnnotation(com.itextpdf.text.pdf.PdfAnnotation) */ 1719 public void addAnnotation(final PdfAnnotation annot) { 1720 pdf.addAnnotation(annot); 1721 } 1722 1723 void addAnnotation(final PdfAnnotation annot, final int page) { 1724 addAnnotation(annot); 1725 } 1726 1727 /** @see com.itextpdf.text.pdf.interfaces.PdfAnnotations#addCalculationOrder(com.itextpdf.text.pdf.PdfFormField) */ 1728 public void addCalculationOrder(final PdfFormField annot) { 1729 pdf.addCalculationOrder(annot); 1730 } 1731 1732 /** @see com.itextpdf.text.pdf.interfaces.PdfAnnotations#setSigFlags(int) */ 1733 public void setSigFlags(final int f) { 1734 pdf.setSigFlags(f); 1735 } 1736 1737// [C9] Metadata 1738 1739 /** XMP Metadata for the document. */ 1740 protected byte[] xmpMetadata = null; 1741 1742 /** 1743 * Use this method to set the XMP Metadata. 1744 * @param xmpMetadata The xmpMetadata to set. 1745 */ 1746 public void setXmpMetadata(final byte[] xmpMetadata) { 1747 this.xmpMetadata = xmpMetadata; 1748 } 1749 1750 /** 1751 * Use this method to set the XMP Metadata for each page. 1752 * @param xmpMetadata The xmpMetadata to set. 1753 * @throws IOException 1754 */ 1755 public void setPageXmpMetadata(final byte[] xmpMetadata) throws IOException { 1756 pdf.setXmpMetadata(xmpMetadata); 1757 } 1758 1759 /** 1760 * Use this method to creates XMP Metadata based 1761 * on the metadata in the PdfDocument. 1762 */ 1763 public void createXmpMetadata() { 1764 setXmpMetadata(createXmpMetadataBytes()); 1765 } 1766 1767 /** 1768 * @return an XmpMetadata byte array 1769 */ 1770 private byte[] createXmpMetadataBytes() { 1771 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 1772 try { 1773 XmpWriter xmp = new XmpWriter(baos, pdf.getInfo(), pdfxConformance.getPDFXConformance()); 1774 xmp.close(); 1775 } 1776 catch (IOException ioe) { 1777 ioe.printStackTrace(); 1778 } 1779 return baos.toByteArray(); 1780 } 1781 1782// [C10] PDFX Conformance 1783 /** A PDF/X level. */ 1784 public static final int PDFXNONE = 0; 1785 /** A PDF/X level. */ 1786 public static final int PDFX1A2001 = 1; 1787 /** A PDF/X level. */ 1788 public static final int PDFX32002 = 2; 1789 /** PDFA-1A level. */ 1790 public static final int PDFA1A = 3; 1791 /** PDFA-1B level. */ 1792 public static final int PDFA1B = 4; 1793 1794 /** Stores the PDF/X level. */ 1795 private final PdfXConformanceImp pdfxConformance = new PdfXConformanceImp(); 1796 1797 /** @see com.itextpdf.text.pdf.interfaces.PdfXConformance#setPDFXConformance(int) */ 1798 public void setPDFXConformance(final int pdfx) { 1799 if (pdfxConformance.getPDFXConformance() == pdfx) 1800 return; 1801 if (pdf.isOpen()) 1802 throw new PdfXConformanceException(MessageLocalization.getComposedMessage("pdfx.conformance.can.only.be.set.before.opening.the.document")); 1803 if (crypto != null) 1804 throw new PdfXConformanceException(MessageLocalization.getComposedMessage("a.pdfx.conforming.document.cannot.be.encrypted")); 1805 if (pdfx == PDFA1A || pdfx == PDFA1B) 1806 setPdfVersion(VERSION_1_4); 1807 else if (pdfx != PDFXNONE) 1808 setPdfVersion(VERSION_1_3); 1809 pdfxConformance.setPDFXConformance(pdfx); 1810 } 1811 1812 /** @see com.itextpdf.text.pdf.interfaces.PdfXConformance#getPDFXConformance() */ 1813 public int getPDFXConformance() { 1814 return pdfxConformance.getPDFXConformance(); 1815 } 1816 1817 /** @see com.itextpdf.text.pdf.interfaces.PdfXConformance#isPdfX() */ 1818 public boolean isPdfX() { 1819 return pdfxConformance.isPdfX(); 1820 } 1821 1822// [C11] Output intents 1823 /** 1824 * Sets the values of the output intent dictionary. Null values are allowed to 1825 * suppress any key. 1826 * 1827 * @param outputConditionIdentifier a value 1828 * @param outputCondition a value, "PDFA/A" to force GTS_PDFA1, otherwise cued by pdfxConformance. 1829 * @param registryName a value 1830 * @param info a value 1831 * @param colorProfile a value 1832 * @since 2.1.5 1833 * @throws IOException on error 1834 */ 1835 public void setOutputIntents(final String outputConditionIdentifier, final String outputCondition, final String registryName, final String info, final ICC_Profile colorProfile) throws IOException { 1836 getExtraCatalog(); 1837 PdfDictionary out = new PdfDictionary(PdfName.OUTPUTINTENT); 1838 if (outputCondition != null) 1839 out.put(PdfName.OUTPUTCONDITION, new PdfString(outputCondition, PdfObject.TEXT_UNICODE)); 1840 if (outputConditionIdentifier != null) 1841 out.put(PdfName.OUTPUTCONDITIONIDENTIFIER, new PdfString(outputConditionIdentifier, PdfObject.TEXT_UNICODE)); 1842 if (registryName != null) 1843 out.put(PdfName.REGISTRYNAME, new PdfString(registryName, PdfObject.TEXT_UNICODE)); 1844 if (info != null) 1845 out.put(PdfName.INFO, new PdfString(info, PdfObject.TEXT_UNICODE)); 1846 if (colorProfile != null) { 1847 PdfStream stream = new PdfICCBased(colorProfile, compressionLevel); 1848 out.put(PdfName.DESTOUTPUTPROFILE, addToBody(stream).getIndirectReference()); 1849 } 1850 1851 PdfName intentSubtype; 1852 if (pdfxConformance.isPdfA1() || "PDFA/1".equals(outputCondition)) { 1853 intentSubtype = PdfName.GTS_PDFA1; 1854 } 1855 else { 1856 intentSubtype = PdfName.GTS_PDFX; 1857 } 1858 1859 out.put(PdfName.S, intentSubtype); 1860 1861 extraCatalog.put(PdfName.OUTPUTINTENTS, new PdfArray(out)); 1862 } 1863 1864 /** 1865 * Sets the values of the output intent dictionary. Null values are allowed to 1866 * suppress any key. 1867 * 1868 * Prefer the <CODE>ICC_Profile</CODE>-based version of this method. 1869 * @param outputConditionIdentifier a value 1870 * @param outputCondition a value, "PDFA/A" to force GTS_PDFA1, otherwise cued by pdfxConformance. 1871 * @param registryName a value 1872 * @param info a value 1873 * @param destOutputProfile a value 1874 * @since 1.x 1875 * 1876 * @throws IOException 1877 */ 1878 public void setOutputIntents(final String outputConditionIdentifier, final String outputCondition, final String registryName, final String info, final byte destOutputProfile[]) throws IOException { 1879 ICC_Profile colorProfile = destOutputProfile == null ? null : ICC_Profile.getInstance(destOutputProfile); 1880 setOutputIntents(outputConditionIdentifier, outputCondition, registryName, info, colorProfile); 1881 } 1882 1883 1884 /** 1885 * Use this method to copy the output intent dictionary 1886 * from another document to this one. 1887 * @param reader the other document 1888 * @param checkExistence <CODE>true</CODE> to just check for the existence of a valid output intent 1889 * dictionary, <CODE>false</CODE> to insert the dictionary if it exists 1890 * @throws IOException on error 1891 * @return <CODE>true</CODE> if the output intent dictionary exists, <CODE>false</CODE> 1892 * otherwise 1893 */ 1894 public boolean setOutputIntents(final PdfReader reader, final boolean checkExistence) throws IOException { 1895 PdfDictionary catalog = reader.getCatalog(); 1896 PdfArray outs = catalog.getAsArray(PdfName.OUTPUTINTENTS); 1897 if (outs == null) 1898 return false; 1899 if (outs.isEmpty()) 1900 return false; 1901 PdfDictionary out = outs.getAsDict(0); 1902 PdfObject obj = PdfReader.getPdfObject(out.get(PdfName.S)); 1903 if (obj == null || !PdfName.GTS_PDFX.equals(obj)) 1904 return false; 1905 if (checkExistence) 1906 return true; 1907 PRStream stream = (PRStream)PdfReader.getPdfObject(out.get(PdfName.DESTOUTPUTPROFILE)); 1908 byte destProfile[] = null; 1909 if (stream != null) { 1910 destProfile = PdfReader.getStreamBytes(stream); 1911 } 1912 setOutputIntents(getNameString(out, PdfName.OUTPUTCONDITIONIDENTIFIER), getNameString(out, PdfName.OUTPUTCONDITION), 1913 getNameString(out, PdfName.REGISTRYNAME), getNameString(out, PdfName.INFO), destProfile); 1914 return true; 1915 } 1916 1917 private static String getNameString(final PdfDictionary dic, final PdfName key) { 1918 PdfObject obj = PdfReader.getPdfObject(dic.get(key)); 1919 if (obj == null || !obj.isString()) 1920 return null; 1921 return ((PdfString)obj).toUnicodeString(); 1922 } 1923 1924// PDF Objects that have an impact on the PDF body 1925 1926// [F1] PdfEncryptionSettings interface 1927 1928 // types of encryption 1929 1930 /** Type of encryption */ 1931 public static final int STANDARD_ENCRYPTION_40 = 0; 1932 /** Type of encryption */ 1933 public static final int STANDARD_ENCRYPTION_128 = 1; 1934 /** Type of encryption */ 1935 public static final int ENCRYPTION_AES_128 = 2; 1936 /** Type of encryption */ 1937 public static final int ENCRYPTION_AES_256 = 3; 1938 /** Mask to separate the encryption type from the encryption mode. */ 1939 static final int ENCRYPTION_MASK = 7; 1940 /** Add this to the mode to keep the metadata in clear text */ 1941 public static final int DO_NOT_ENCRYPT_METADATA = 8; 1942 /** 1943 * Add this to the mode to keep encrypt only the embedded files. 1944 * @since 2.1.3 1945 */ 1946 public static final int EMBEDDED_FILES_ONLY = 24; 1947 1948 // permissions 1949 1950 /** The operation permitted when the document is opened with the user password 1951 * 1952 * @since 2.0.7 1953 */ 1954 public static final int ALLOW_PRINTING = 4 + 2048; 1955 1956 /** The operation permitted when the document is opened with the user password 1957 * 1958 * @since 2.0.7 1959 */ 1960 public static final int ALLOW_MODIFY_CONTENTS = 8; 1961 1962 /** The operation permitted when the document is opened with the user password 1963 * 1964 * @since 2.0.7 1965 */ 1966 public static final int ALLOW_COPY = 16; 1967 1968 /** The operation permitted when the document is opened with the user password 1969 * 1970 * @since 2.0.7 1971 */ 1972 public static final int ALLOW_MODIFY_ANNOTATIONS = 32; 1973 1974 /** The operation permitted when the document is opened with the user password 1975 * 1976 * @since 2.0.7 1977 */ 1978 public static final int ALLOW_FILL_IN = 256; 1979 1980 /** The operation permitted when the document is opened with the user password 1981 * 1982 * @since 2.0.7 1983 */ 1984 public static final int ALLOW_SCREENREADERS = 512; 1985 1986 /** The operation permitted when the document is opened with the user password 1987 * 1988 * @since 2.0.7 1989 */ 1990 public static final int ALLOW_ASSEMBLY = 1024; 1991 1992 /** The operation permitted when the document is opened with the user password 1993 * 1994 * @since 2.0.7 1995 */ 1996 public static final int ALLOW_DEGRADED_PRINTING = 4; 1997 1998 /** @deprecated As of iText 2.0.7, use {@link #ALLOW_PRINTING} instead. Scheduled for removal at or after 2.2.0 */ 1999 @Deprecated 2000 public static final int AllowPrinting = ALLOW_PRINTING; 2001 /** @deprecated As of iText 2.0.7, use {@link #ALLOW_MODIFY_CONTENTS} instead. Scheduled for removal at or after 2.2.0 */ 2002 @Deprecated 2003 public static final int AllowModifyContents = ALLOW_MODIFY_CONTENTS; 2004 /** @deprecated As of iText 2.0.7, use {@link #ALLOW_COPY} instead. Scheduled for removal at or after 2.2.0 */ 2005 @Deprecated 2006 public static final int AllowCopy = ALLOW_COPY; 2007 /** @deprecated As of iText 2.0.7, use {@link #ALLOW_MODIFY_ANNOTATIONS} instead. Scheduled for removal at or after 2.2.0 */ 2008 @Deprecated 2009 public static final int AllowModifyAnnotations = ALLOW_MODIFY_ANNOTATIONS; 2010 /** @deprecated As of iText 2.0.7, use {@link #ALLOW_FILL_IN} instead. Scheduled for removal at or after 2.2.0 */ 2011 @Deprecated 2012 public static final int AllowFillIn = ALLOW_FILL_IN; 2013 /** @deprecated As of iText 2.0.7, use {@link #ALLOW_SCREENREADERS} instead. Scheduled for removal at or after 2.2.0 */ 2014 @Deprecated 2015 public static final int AllowScreenReaders = ALLOW_SCREENREADERS; 2016 /** @deprecated As of iText 2.0.7, use {@link #ALLOW_ASSEMBLY} instead. Scheduled for removal at or after 2.2.0 */ 2017 @Deprecated 2018 public static final int AllowAssembly = ALLOW_ASSEMBLY; 2019 /** @deprecated As of iText 2.0.7, use {@link #ALLOW_DEGRADED_PRINTING} instead. Scheduled for removal at or after 2.2.0 */ 2020 @Deprecated 2021 public static final int AllowDegradedPrinting = ALLOW_DEGRADED_PRINTING; 2022 2023 // Strength of the encryption (kept for historical reasons) 2024 /** @deprecated As of iText 2.0.7, use {@link #STANDARD_ENCRYPTION_40} instead. Scheduled for removal at or after 2.2.0 */ 2025 @Deprecated 2026 public static final boolean STRENGTH40BITS = false; 2027 /** @deprecated As of iText 2.0.7, use {@link #STANDARD_ENCRYPTION_128} instead. Scheduled for removal at or after 2.2.0 */ 2028 @Deprecated 2029 public static final boolean STRENGTH128BITS = true; 2030 2031 /** Contains the business logic for cryptography. */ 2032 protected PdfEncryption crypto; 2033 PdfEncryption getEncryption() { 2034 return crypto; 2035 } 2036 2037 /** @see com.itextpdf.text.pdf.interfaces.PdfEncryptionSettings#setEncryption(byte[], byte[], int, int) */ 2038 public void setEncryption(final byte userPassword[], final byte ownerPassword[], final int permissions, final int encryptionType) throws DocumentException { 2039 if (pdf.isOpen()) 2040 throw new DocumentException(MessageLocalization.getComposedMessage("encryption.can.only.be.added.before.opening.the.document")); 2041 crypto = new PdfEncryption(); 2042 crypto.setCryptoMode(encryptionType, 0); 2043 crypto.setupAllKeys(userPassword, ownerPassword, permissions); 2044 } 2045 2046 /** @see com.itextpdf.text.pdf.interfaces.PdfEncryptionSettings#setEncryption(java.security.cert.Certificate[], int[], int) */ 2047 public void setEncryption(final Certificate[] certs, final int[] permissions, final int encryptionType) throws DocumentException { 2048 if (pdf.isOpen()) 2049 throw new DocumentException(MessageLocalization.getComposedMessage("encryption.can.only.be.added.before.opening.the.document")); 2050 crypto = new PdfEncryption(); 2051 if (certs != null) { 2052 for (int i=0; i < certs.length; i++) { 2053 crypto.addRecipient(certs[i], permissions[i]); 2054 } 2055 } 2056 crypto.setCryptoMode(encryptionType, 0); 2057 crypto.getEncryptionDictionary(); 2058 } 2059 2060 /** 2061 * Sets the encryption options for this document. The userPassword and the 2062 * ownerPassword can be null or have zero length. In this case the ownerPassword 2063 * is replaced by a random string. The open permissions for the document can be 2064 * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations, 2065 * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting. 2066 * The permissions can be combined by ORing them. 2067 * @param userPassword the user password. Can be null or empty 2068 * @param ownerPassword the owner password. Can be null or empty 2069 * @param permissions the user permissions 2070 * @param strength128Bits <code>true</code> for 128 bit key length, <code>false</code> for 40 bit key length 2071 * @throws DocumentException if the document is already open 2072 * @deprecated As of iText 2.0.3, replaced by (@link #setEncryption(byte[], byte[], int, int)}. Scheduled for removal at or after 2.2.0 2073 */ 2074 @Deprecated 2075 public void setEncryption(final byte userPassword[], final byte ownerPassword[], final int permissions, final boolean strength128Bits) throws DocumentException { 2076 setEncryption(userPassword, ownerPassword, permissions, strength128Bits ? STANDARD_ENCRYPTION_128 : STANDARD_ENCRYPTION_40); 2077 } 2078 2079 /** 2080 * Sets the encryption options for this document. The userPassword and the 2081 * ownerPassword can be null or have zero length. In this case the ownerPassword 2082 * is replaced by a random string. The open permissions for the document can be 2083 * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations, 2084 * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting. 2085 * The permissions can be combined by ORing them. 2086 * @param strength <code>true</code> for 128 bit key length, <code>false</code> for 40 bit key length 2087 * @param userPassword the user password. Can be null or empty 2088 * @param ownerPassword the owner password. Can be null or empty 2089 * @param permissions the user permissions 2090 * @throws DocumentException if the document is already open 2091 * @deprecated As of iText 2.0.3, replaced by (@link #setEncryption(byte[], byte[], int, int)}. Scheduled for removal at or after 2.2.0 2092 */ 2093 @Deprecated 2094 public void setEncryption(final boolean strength, final String userPassword, final String ownerPassword, final int permissions) throws DocumentException { 2095 setEncryption(getISOBytes(userPassword), getISOBytes(ownerPassword), permissions, strength ? STANDARD_ENCRYPTION_128 : STANDARD_ENCRYPTION_40); 2096 } 2097 2098 /** 2099 * Sets the encryption options for this document. The userPassword and the 2100 * ownerPassword can be null or have zero length. In this case the ownerPassword 2101 * is replaced by a random string. The open permissions for the document can be 2102 * AllowPrinting, AllowModifyContents, AllowCopy, AllowModifyAnnotations, 2103 * AllowFillIn, AllowScreenReaders, AllowAssembly and AllowDegradedPrinting. 2104 * The permissions can be combined by ORing them. 2105 * @param encryptionType the type of encryption. It can be one of STANDARD_ENCRYPTION_40, STANDARD_ENCRYPTION_128 or ENCRYPTION_AES128. 2106 * Optionally DO_NOT_ENCRYPT_METADATA can be ored to output the metadata in cleartext 2107 * @param userPassword the user password. Can be null or empty 2108 * @param ownerPassword the owner password. Can be null or empty 2109 * @param permissions the user permissions 2110 * @throws DocumentException if the document is already open 2111 * @deprecated As of iText 2.0.3, replaced by (@link #setEncryption(byte[], byte[], int, int)}. Scheduled for removal at or after 2.2.0 2112 */ 2113 @Deprecated 2114 public void setEncryption(final int encryptionType, final String userPassword, final String ownerPassword, final int permissions) throws DocumentException { 2115 setEncryption(getISOBytes(userPassword), getISOBytes(ownerPassword), permissions, encryptionType); 2116 } 2117 2118// [F2] compression 2119 2120 /** Holds value of property fullCompression. */ 2121 protected boolean fullCompression = false; 2122 2123 /** 2124 * Use this method to find out if 1.5 compression is on. 2125 * @return the 1.5 compression status 2126 */ 2127 public boolean isFullCompression() { 2128 return this.fullCompression; 2129 } 2130 2131 /** 2132 * Use this method to set the document's compression to the 2133 * PDF 1.5 mode with object streams and xref streams. 2134 * It can be set at any time but once set it can't be unset. 2135 * <p> 2136 * If set before opening the document it will also set the pdf version to 1.5. 2137 */ 2138 public void setFullCompression() { 2139 this.fullCompression = true; 2140 setAtLeastPdfVersion(VERSION_1_5); 2141 } 2142 2143 /** 2144 * The compression level of the content streams. 2145 * @since 2.1.3 2146 */ 2147 protected int compressionLevel = PdfStream.DEFAULT_COMPRESSION; 2148 2149 /** 2150 * Returns the compression level used for streams written by this writer. 2151 * @return the compression level (0 = best speed, 9 = best compression, -1 is default) 2152 * @since 2.1.3 2153 */ 2154 public int getCompressionLevel() { 2155 return compressionLevel; 2156 } 2157 2158 /** 2159 * Sets the compression level to be used for streams written by this writer. 2160 * @param compressionLevel a value between 0 (best speed) and 9 (best compression) 2161 * @since 2.1.3 2162 */ 2163 public void setCompressionLevel(final int compressionLevel) { 2164 if (compressionLevel < PdfStream.NO_COMPRESSION || compressionLevel > PdfStream.BEST_COMPRESSION) 2165 this.compressionLevel = PdfStream.DEFAULT_COMPRESSION; 2166 else 2167 this.compressionLevel = compressionLevel; 2168 } 2169 2170// [F3] adding fonts 2171 2172 /** The fonts of this document */ 2173 protected LinkedHashMap<BaseFont, FontDetails> documentFonts = new LinkedHashMap<BaseFont, FontDetails>(); 2174 2175 /** The font number counter for the fonts in the document. */ 2176 protected int fontNumber = 1; 2177 2178 /** 2179 * Adds a <CODE>BaseFont</CODE> to the document but not to the page resources. 2180 * It is used for templates. 2181 * @param bf the <CODE>BaseFont</CODE> to add 2182 * @return an <CODE>Object[]</CODE> where position 0 is a <CODE>PdfName</CODE> 2183 * and position 1 is an <CODE>PdfIndirectReference</CODE> 2184 */ 2185 2186 FontDetails addSimple(final BaseFont bf) { 2187 if (bf.getFontType() == BaseFont.FONT_TYPE_DOCUMENT) { 2188 return new FontDetails(new PdfName("F" + fontNumber++), ((DocumentFont)bf).getIndirectReference(), bf); 2189 } 2190 FontDetails ret = documentFonts.get(bf); 2191 if (ret == null) { 2192 PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_FONT, bf); 2193 ret = new FontDetails(new PdfName("F" + fontNumber++), body.getPdfIndirectReference(), bf); 2194 documentFonts.put(bf, ret); 2195 } 2196 return ret; 2197 } 2198 2199 void eliminateFontSubset(final PdfDictionary fonts) { 2200 for (Object element : documentFonts.values()) { 2201 FontDetails ft = (FontDetails)element; 2202 if (fonts.get(ft.getFontName()) != null) 2203 ft.setSubset(false); 2204 } 2205 } 2206 2207// [F4] adding (and releasing) form XObjects 2208 2209 /** The form XObjects in this document. The key is the xref and the value 2210 is Object[]{PdfName, template}.*/ 2211 protected HashMap<PdfIndirectReference, Object[]> formXObjects = new HashMap<PdfIndirectReference, Object[]>(); 2212 2213 /** The name counter for the form XObjects name. */ 2214 protected int formXObjectsCounter = 1; 2215 2216 /** 2217 * Adds a template to the document but not to the page resources. 2218 * @param template the template to add 2219 * @param forcedName the template name, rather than a generated one. Can be null 2220 * @return the <CODE>PdfName</CODE> for this template 2221 */ 2222 2223 PdfName addDirectTemplateSimple(PdfTemplate template, final PdfName forcedName) { 2224 PdfIndirectReference ref = template.getIndirectReference(); 2225 Object obj[] = formXObjects.get(ref); 2226 PdfName name = null; 2227 try { 2228 if (obj == null) { 2229 if (forcedName == null) { 2230 name = new PdfName("Xf" + formXObjectsCounter); 2231 ++formXObjectsCounter; 2232 } 2233 else 2234 name = forcedName; 2235 if (template.getType() == PdfTemplate.TYPE_IMPORTED) { 2236 // If we got here from PdfCopy we'll have to fill importedPages 2237 PdfImportedPage ip = (PdfImportedPage)template; 2238 PdfReader r = ip.getPdfReaderInstance().getReader(); 2239 if (!readerInstances.containsKey(r)) { 2240 readerInstances.put(r, ip.getPdfReaderInstance()); 2241 } 2242 template = null; 2243 } 2244 formXObjects.put(ref, new Object[]{name, template}); 2245 } 2246 else 2247 name = (PdfName)obj[0]; 2248 } 2249 catch (Exception e) { 2250 throw new ExceptionConverter(e); 2251 } 2252 return name; 2253 } 2254 2255 /** 2256 * Use this method to releases the memory used by a template. 2257 * This method writes the template to the output. 2258 * The template can still be added to any content 2259 * but changes to the template itself won't have any effect. 2260 * @param tp the template to release 2261 * @throws IOException on error 2262 */ 2263 public void releaseTemplate(final PdfTemplate tp) throws IOException { 2264 PdfIndirectReference ref = tp.getIndirectReference(); 2265 Object[] objs = formXObjects.get(ref); 2266 if (objs == null || objs[1] == null) 2267 return; 2268 PdfTemplate template = (PdfTemplate)objs[1]; 2269 if (template.getIndirectReference() instanceof PRIndirectReference) 2270 return; 2271 if (template.getType() == PdfTemplate.TYPE_TEMPLATE) { 2272 addToBody(template.getFormXObject(compressionLevel), template.getIndirectReference()); 2273 objs[1] = null; 2274 } 2275 } 2276 2277// [F5] adding pages imported form other PDF documents 2278 2279 /** 2280 * Instances of PdfReader/PdfReaderInstance that are used to import pages. 2281 * @since 5.0.3 2282 */ 2283 protected HashMap<PdfReader, PdfReaderInstance> readerInstances = new HashMap<PdfReader, PdfReaderInstance>(); 2284 2285 /** 2286 * Use this method to get a page from other PDF document. 2287 * The page can be used as any other PdfTemplate. 2288 * Note that calling this method more than once with the same parameters 2289 * will retrieve the same object. 2290 * @param reader the PDF document where the page is 2291 * @param pageNumber the page number. The first page is 1 2292 * @return the template representing the imported page 2293 */ 2294 public PdfImportedPage getImportedPage(final PdfReader reader, final int pageNumber) { 2295 return getPdfReaderInstance(reader).getImportedPage(pageNumber); 2296 } 2297 2298 /** 2299 * Returns the PdfReaderInstance associated with the specified reader. 2300 * Multiple calls with the same reader object will return the same 2301 * PdfReaderInstance. 2302 * @param reader the PDF reader that you want an instance for 2303 * @return the instance for the provided reader 2304 * @since 5.0.3 2305 */ 2306 protected PdfReaderInstance getPdfReaderInstance(final PdfReader reader){ 2307 PdfReaderInstance inst = readerInstances.get(reader); 2308 if (inst == null) { 2309 inst = reader.getPdfReaderInstance(this); 2310 readerInstances.put(reader, inst); 2311 } 2312 return inst; 2313 } 2314 2315 /** 2316 * Use this method to writes the reader to the document 2317 * and free the memory used by it. 2318 * The main use is when concatenating multiple documents 2319 * to keep the memory usage restricted to the current 2320 * appending document. 2321 * @param reader the <CODE>PdfReader</CODE> to free 2322 * @throws IOException on error 2323 */ 2324 public void freeReader(final PdfReader reader) throws IOException { 2325 currentPdfReaderInstance = readerInstances.get(reader); 2326 if (currentPdfReaderInstance == null) 2327 return; 2328 currentPdfReaderInstance.writeAllPages(); 2329 currentPdfReaderInstance = null; 2330 readerInstances.remove(reader); 2331 } 2332 2333 /** 2334 * Use this method to gets the current document size. 2335 * This size only includes the data already written 2336 * to the output stream, it does not include templates or fonts. 2337 * It is useful if used with <CODE>freeReader()</CODE> 2338 * when concatenating many documents and an idea of 2339 * the current size is needed. 2340 * @return the approximate size without fonts or templates 2341 */ 2342 public int getCurrentDocumentSize() { 2343 return body.offset() + body.size() * 20 + 0x48; 2344 } 2345 2346 protected PdfReaderInstance currentPdfReaderInstance; 2347 2348 protected int getNewObjectNumber(final PdfReader reader, final int number, final int generation) { 2349 if (currentPdfReaderInstance == null) { 2350 currentPdfReaderInstance = getPdfReaderInstance(reader); 2351 } 2352 return currentPdfReaderInstance.getNewObjectNumber(number, generation); 2353 } 2354 2355 RandomAccessFileOrArray getReaderFile(final PdfReader reader) { 2356 return currentPdfReaderInstance.getReaderFile(); 2357 } 2358 2359// [F6] spot colors 2360 2361 /** The colors of this document */ 2362 protected HashMap<PdfSpotColor, ColorDetails> documentColors = new HashMap<PdfSpotColor, ColorDetails>(); 2363 2364 /** The color number counter for the colors in the document. */ 2365 protected int colorNumber = 1; 2366 2367 PdfName getColorspaceName() { 2368 return new PdfName("CS" + colorNumber++); 2369 } 2370 2371 /** 2372 * Adds a <CODE>SpotColor</CODE> to the document but not to the page resources. 2373 * @param spc the <CODE>SpotColor</CODE> to add 2374 * @return an <CODE>Object[]</CODE> where position 0 is a <CODE>PdfName</CODE> 2375 * and position 1 is an <CODE>PdfIndirectReference</CODE> 2376 */ 2377 ColorDetails addSimple(final PdfSpotColor spc) { 2378 ColorDetails ret = documentColors.get(spc); 2379 if (ret == null) { 2380 ret = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), spc); 2381 documentColors.put(spc, ret); 2382 } 2383 return ret; 2384 } 2385 2386// [F7] document patterns 2387 2388 /** The patterns of this document */ 2389 protected HashMap<PdfPatternPainter, PdfName> documentPatterns = new HashMap<PdfPatternPainter, PdfName>(); 2390 2391 /** The pattern number counter for the colors in the document. */ 2392 protected int patternNumber = 1; 2393 2394 PdfName addSimplePattern(final PdfPatternPainter painter) { 2395 PdfName name = documentPatterns.get(painter); 2396 try { 2397 if ( name == null ) { 2398 name = new PdfName("P" + patternNumber); 2399 ++patternNumber; 2400 documentPatterns.put(painter, name); 2401 } 2402 } catch (Exception e) { 2403 throw new ExceptionConverter(e); 2404 } 2405 return name; 2406 } 2407 2408// [F8] shading patterns 2409 2410 protected HashSet<PdfShadingPattern> documentShadingPatterns = new HashSet<PdfShadingPattern>(); 2411 2412 void addSimpleShadingPattern(final PdfShadingPattern shading) { 2413 if (!documentShadingPatterns.contains(shading)) { 2414 shading.setName(patternNumber); 2415 ++patternNumber; 2416 documentShadingPatterns.add(shading); 2417 addSimpleShading(shading.getShading()); 2418 } 2419 } 2420 2421// [F9] document shadings 2422 2423 protected HashSet<PdfShading> documentShadings = new HashSet<PdfShading>(); 2424 2425 void addSimpleShading(final PdfShading shading) { 2426 if (!documentShadings.contains(shading)) { 2427 documentShadings.add(shading); 2428 shading.setName(documentShadings.size()); 2429 } 2430 } 2431 2432// [F10] extended graphics state (for instance for transparency) 2433 2434 protected HashMap<PdfDictionary, PdfObject[]> documentExtGState = new HashMap<PdfDictionary, PdfObject[]>(); 2435 2436 PdfObject[] addSimpleExtGState(final PdfDictionary gstate) { 2437 if (!documentExtGState.containsKey(gstate)) { 2438 PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_GSTATE, gstate); 2439 documentExtGState.put(gstate, new PdfObject[]{new PdfName("GS" + (documentExtGState.size() + 1)), getPdfIndirectReference()}); 2440 } 2441 return documentExtGState.get(gstate); 2442 } 2443 2444// [F11] adding properties (OCG, marked content) 2445 2446 protected HashMap<Object, PdfObject[]> documentProperties = new HashMap<Object, PdfObject[]>(); 2447 PdfObject[] addSimpleProperty(final Object prop, final PdfIndirectReference refi) { 2448 if (!documentProperties.containsKey(prop)) { 2449 if (prop instanceof PdfOCG) 2450 PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_LAYER, null); 2451 documentProperties.put(prop, new PdfObject[]{new PdfName("Pr" + (documentProperties.size() + 1)), refi}); 2452 } 2453 return documentProperties.get(prop); 2454 } 2455 2456 boolean propertyExists(final Object prop) { 2457 return documentProperties.containsKey(prop); 2458 } 2459 2460// [F12] tagged PDF 2461 2462 protected boolean tagged = false; 2463 protected PdfStructureTreeRoot structureTreeRoot; 2464 2465 /** 2466 * Mark this document for tagging. It must be called before open. 2467 */ 2468 public void setTagged() { 2469 if (open) 2470 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("tagging.must.be.set.before.opening.the.document")); 2471 tagged = true; 2472 } 2473 2474 /** 2475 * Check if the document is marked for tagging. 2476 * @return <CODE>true</CODE> if the document is marked for tagging 2477 */ 2478 public boolean isTagged() { 2479 return tagged; 2480 } 2481 2482 /** 2483 * Gets the structure tree root. If the document is not marked for tagging it will return <CODE>null</CODE>. 2484 * @return the structure tree root 2485 */ 2486 public PdfStructureTreeRoot getStructureTreeRoot() { 2487 if (tagged && structureTreeRoot == null) 2488 structureTreeRoot = new PdfStructureTreeRoot(this); 2489 return structureTreeRoot; 2490 } 2491 2492// [F13] Optional Content Groups 2493 /** A hashSet containing all the PdfLayer objects. */ 2494 protected HashSet<PdfOCG> documentOCG = new HashSet<PdfOCG>(); 2495 /** An array list used to define the order of an OCG tree. */ 2496 protected ArrayList<PdfOCG> documentOCGorder = new ArrayList<PdfOCG>(); 2497 /** The OCProperties in a catalog dictionary. */ 2498 protected PdfOCProperties OCProperties; 2499 /** The RBGroups array in an OCG dictionary */ 2500 protected PdfArray OCGRadioGroup = new PdfArray(); 2501 /** 2502 * The locked array in an OCG dictionary 2503 * @since 2.1.2 2504 */ 2505 protected PdfArray OCGLocked = new PdfArray(); 2506 2507 /** 2508 * Use this method to get the <B>Optional Content Properties Dictionary</B>. 2509 * Each call fills the dictionary with the current layer state. 2510 * It's advisable to only call this method right before close 2511 * and do any modifications at that time. 2512 * @return the Optional Content Properties Dictionary 2513 */ 2514 public PdfOCProperties getOCProperties() { 2515 fillOCProperties(true); 2516 return OCProperties; 2517 } 2518 2519 /** 2520 * Use this method to set a collection of optional content groups 2521 * whose states are intended to follow a "radio button" paradigm. 2522 * That is, the state of at most one optional content group 2523 * in the array should be ON at a time: if one group is turned 2524 * ON, all others must be turned OFF. 2525 * @param group the radio group 2526 */ 2527 public void addOCGRadioGroup(final ArrayList<PdfLayer> group) { 2528 PdfArray ar = new PdfArray(); 2529 for (int k = 0; k < group.size(); ++k) { 2530 PdfLayer layer = group.get(k); 2531 if (layer.getTitle() == null) 2532 ar.add(layer.getRef()); 2533 } 2534 if (ar.size() == 0) 2535 return; 2536 OCGRadioGroup.add(ar); 2537 } 2538 2539 /** 2540 * Use this method to lock an optional content group. 2541 * The state of a locked group cannot be changed through the user interface 2542 * of a viewer application. Producers can use this entry to prevent the visibility 2543 * of content that depends on these groups from being changed by users. 2544 * @param layer the layer that needs to be added to the array of locked OCGs 2545 * @since 2.1.2 2546 */ 2547 public void lockLayer(final PdfLayer layer) { 2548 OCGLocked.add(layer.getRef()); 2549 } 2550 2551 private static void getOCGOrder(final PdfArray order, final PdfLayer layer) { 2552 if (!layer.isOnPanel()) 2553 return; 2554 if (layer.getTitle() == null) 2555 order.add(layer.getRef()); 2556 ArrayList<PdfLayer> children = layer.getChildren(); 2557 if (children == null) 2558 return; 2559 PdfArray kids = new PdfArray(); 2560 if (layer.getTitle() != null) 2561 kids.add(new PdfString(layer.getTitle(), PdfObject.TEXT_UNICODE)); 2562 for (int k = 0; k < children.size(); ++k) { 2563 getOCGOrder(kids, children.get(k)); 2564 } 2565 if (kids.size() > 0) 2566 order.add(kids); 2567 } 2568 2569 private void addASEvent(final PdfName event, final PdfName category) { 2570 PdfArray arr = new PdfArray(); 2571 for (Object element : documentOCG) { 2572 PdfLayer layer = (PdfLayer)element; 2573 PdfDictionary usage = layer.getAsDict(PdfName.USAGE); 2574 if (usage != null && usage.get(category) != null) 2575 arr.add(layer.getRef()); 2576 } 2577 if (arr.size() == 0) 2578 return; 2579 PdfDictionary d = OCProperties.getAsDict(PdfName.D); 2580 PdfArray arras = d.getAsArray(PdfName.AS); 2581 if (arras == null) { 2582 arras = new PdfArray(); 2583 d.put(PdfName.AS, arras); 2584 } 2585 PdfDictionary as = new PdfDictionary(); 2586 as.put(PdfName.EVENT, event); 2587 as.put(PdfName.CATEGORY, new PdfArray(category)); 2588 as.put(PdfName.OCGS, arr); 2589 arras.add(as); 2590 } 2591 2592 /** 2593 * @param erase true to erase the {@link PdfName#OCGS} and {@link PdfName#D} from the OCProperties first. 2594 * @since 2.1.2 2595 */ 2596 protected void fillOCProperties(final boolean erase) { 2597 if (OCProperties == null) 2598 OCProperties = new PdfOCProperties(); 2599 if (erase) { 2600 OCProperties.remove(PdfName.OCGS); 2601 OCProperties.remove(PdfName.D); 2602 } 2603 if (OCProperties.get(PdfName.OCGS) == null) { 2604 PdfArray gr = new PdfArray(); 2605 for (Object element : documentOCG) { 2606 PdfLayer layer = (PdfLayer)element; 2607 gr.add(layer.getRef()); 2608 } 2609 OCProperties.put(PdfName.OCGS, gr); 2610 } 2611 if (OCProperties.get(PdfName.D) != null) 2612 return; 2613 ArrayList<PdfOCG> docOrder = new ArrayList<PdfOCG>(documentOCGorder); 2614 for (Iterator<PdfOCG> it = docOrder.iterator(); it.hasNext();) { 2615 PdfLayer layer = (PdfLayer)it.next(); 2616 if (layer.getParent() != null) 2617 it.remove(); 2618 } 2619 PdfArray order = new PdfArray(); 2620 for (Object element : docOrder) { 2621 PdfLayer layer = (PdfLayer)element; 2622 getOCGOrder(order, layer); 2623 } 2624 PdfDictionary d = new PdfDictionary(); 2625 OCProperties.put(PdfName.D, d); 2626 d.put(PdfName.ORDER, order); 2627 PdfArray gr = new PdfArray(); 2628 for (Object element : documentOCG) { 2629 PdfLayer layer = (PdfLayer)element; 2630 if (!layer.isOn()) 2631 gr.add(layer.getRef()); 2632 } 2633 if (gr.size() > 0) 2634 d.put(PdfName.OFF, gr); 2635 if (OCGRadioGroup.size() > 0) 2636 d.put(PdfName.RBGROUPS, OCGRadioGroup); 2637 if (OCGLocked.size() > 0) 2638 d.put(PdfName.LOCKED, OCGLocked); 2639 addASEvent(PdfName.VIEW, PdfName.ZOOM); 2640 addASEvent(PdfName.VIEW, PdfName.VIEW); 2641 addASEvent(PdfName.PRINT, PdfName.PRINT); 2642 addASEvent(PdfName.EXPORT, PdfName.EXPORT); 2643 d.put(PdfName.LISTMODE, PdfName.VISIBLEPAGES); 2644 } 2645 2646 void registerLayer(final PdfOCG layer) { 2647 PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_LAYER, null); 2648 if (layer instanceof PdfLayer) { 2649 PdfLayer la = (PdfLayer)layer; 2650 if (la.getTitle() == null) { 2651 if (!documentOCG.contains(layer)) { 2652 documentOCG.add(layer); 2653 documentOCGorder.add(layer); 2654 } 2655 } 2656 else { 2657 documentOCGorder.add(layer); 2658 } 2659 } 2660 else 2661 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("only.pdflayer.is.accepted")); 2662 } 2663 2664// User methods to change aspects of the page 2665 2666// [U1] page size 2667 2668 /** 2669 * Use this method to get the size of the media box. 2670 * @return a Rectangle 2671 */ 2672 public Rectangle getPageSize() { 2673 return pdf.getPageSize(); 2674 } 2675 2676 /** 2677 * Use this method to set the crop box. 2678 * The crop box should not be rotated even if the page is rotated. 2679 * This change only takes effect in the next page. 2680 * @param crop the crop box 2681 */ 2682 public void setCropBoxSize(final Rectangle crop) { 2683 pdf.setCropBoxSize(crop); 2684 } 2685 2686 /** 2687 * Use this method to set the page box sizes. 2688 * Allowed names are: "crop", "trim", "art" and "bleed". 2689 * @param boxName the box size 2690 * @param size the size 2691 */ 2692 public void setBoxSize(final String boxName, final Rectangle size) { 2693 pdf.setBoxSize(boxName, size); 2694 } 2695 2696 /** 2697 * Use this method to get the size of a trim, art, crop or bleed box, 2698 * or null if not defined. 2699 * @param boxName crop, trim, art or bleed 2700 */ 2701 public Rectangle getBoxSize(final String boxName) { 2702 return pdf.getBoxSize(boxName); 2703 } 2704 2705// [U2] take care of empty pages 2706 2707 /** 2708 * Use this method to make sure a page is added, 2709 * even if it's empty. If you use setPageEmpty(false), 2710 * invoking newPage() after a blank page will add a newPage. 2711 * setPageEmpty(true) won't have any effect. 2712 * @param pageEmpty the state 2713 */ 2714 public void setPageEmpty(final boolean pageEmpty) { 2715 if (pageEmpty) 2716 return; 2717 pdf.setPageEmpty(pageEmpty); 2718 } 2719 2720 /** 2721 * Checks if a newPage() will actually generate a new page. 2722 * @return true if a new page will be generated, false otherwise 2723 * @since 2.1.8 2724 */ 2725 public boolean isPageEmpty() { 2726 return pdf.isPageEmpty(); 2727 } 2728 2729// [U3] page actions (open and close) 2730 2731 /** action value */ 2732 public static final PdfName PAGE_OPEN = PdfName.O; 2733 /** action value */ 2734 public static final PdfName PAGE_CLOSE = PdfName.C; 2735 2736 /** @see com.itextpdf.text.pdf.interfaces.PdfPageActions#setPageAction(com.itextpdf.text.pdf.PdfName, com.itextpdf.text.pdf.PdfAction) */ 2737 public void setPageAction(final PdfName actionType, final PdfAction action) throws DocumentException { 2738 if (!actionType.equals(PAGE_OPEN) && !actionType.equals(PAGE_CLOSE)) 2739 throw new DocumentException(MessageLocalization.getComposedMessage("invalid.page.additional.action.type.1", actionType.toString())); 2740 pdf.setPageAction(actionType, action); 2741 } 2742 2743 /** @see com.itextpdf.text.pdf.interfaces.PdfPageActions#setDuration(int) */ 2744 public void setDuration(final int seconds) { 2745 pdf.setDuration(seconds); 2746 } 2747 2748 /** @see com.itextpdf.text.pdf.interfaces.PdfPageActions#setTransition(com.itextpdf.text.pdf.PdfTransition) */ 2749 public void setTransition(final PdfTransition transition) { 2750 pdf.setTransition(transition); 2751 } 2752 2753// [U4] Thumbnail image 2754 2755 /** 2756 * Use this method to set the thumbnail image for the current page. 2757 * @param image the image 2758 * @throws PdfException on error 2759 * @throws DocumentException or error 2760 */ 2761 public void setThumbnail(final Image image) throws PdfException, DocumentException { 2762 pdf.setThumbnail(image); 2763 } 2764 2765// [U5] Transparency groups 2766 2767 /** 2768 * A group attributes dictionary specifying the attributes 2769 * of the page's page group for use in the transparent 2770 * imaging model 2771 */ 2772 protected PdfDictionary group; 2773 2774 /** 2775 * Use this method to get the group dictionary. 2776 * @return Value of property group. 2777 */ 2778 public PdfDictionary getGroup() { 2779 return this.group; 2780 } 2781 2782 /** 2783 * Use this method to set the group dictionary. 2784 * @param group New value of property group. 2785 */ 2786 public void setGroup(final PdfDictionary group) { 2787 this.group = group; 2788 } 2789 2790// [U6] space char ratio 2791 2792 /** The default space-char ratio. */ 2793 public static final float SPACE_CHAR_RATIO_DEFAULT = 2.5f; 2794 /** Disable the inter-character spacing. */ 2795 public static final float NO_SPACE_CHAR_RATIO = 10000000f; 2796 2797 /** 2798 * The ratio between the extra word spacing and the extra character spacing. 2799 * Extra word spacing will grow <CODE>ratio</CODE> times more than extra character spacing. 2800 */ 2801 private float spaceCharRatio = SPACE_CHAR_RATIO_DEFAULT; 2802 2803 /** 2804 * Use this method to gets the space/character extra spacing ratio 2805 * for fully justified text. 2806 * @return the space/character extra spacing ratio 2807 */ 2808 public float getSpaceCharRatio() { 2809 return spaceCharRatio; 2810 } 2811 2812 /** 2813 * Use this method to set the ratio between the extra word spacing and 2814 * the extra character spacing when the text is fully justified. 2815 * Extra word spacing will grow <CODE>spaceCharRatio</CODE> times more 2816 * than extra character spacing. If the ratio is <CODE>PdfWriter.NO_SPACE_CHAR_RATIO</CODE> 2817 * then the extra character spacing will be zero. 2818 * @param spaceCharRatio the ratio between the extra word spacing and the extra character spacing 2819 */ 2820 public void setSpaceCharRatio(final float spaceCharRatio) { 2821 if (spaceCharRatio < 0.001f) 2822 this.spaceCharRatio = 0.001f; 2823 else 2824 this.spaceCharRatio = spaceCharRatio; 2825 } 2826 2827// [U7] run direction (doesn't actually do anything) 2828 2829 /** Use the default run direction. */ 2830 public static final int RUN_DIRECTION_DEFAULT = 0; 2831 /** Do not use bidirectional reordering. */ 2832 public static final int RUN_DIRECTION_NO_BIDI = 1; 2833 /** Use bidirectional reordering with left-to-right 2834 * preferential run direction. 2835 */ 2836 public static final int RUN_DIRECTION_LTR = 2; 2837 /** Use bidirectional reordering with right-to-left 2838 * preferential run direction. 2839 */ 2840 public static final int RUN_DIRECTION_RTL = 3; 2841 2842 protected int runDirection = RUN_DIRECTION_NO_BIDI; 2843 2844 /** 2845 * Use this method to set the run direction. 2846 * This is only used as a placeholder as it does not affect anything. 2847 * @param runDirection the run direction 2848 */ 2849 public void setRunDirection(final int runDirection) { 2850 if (runDirection < RUN_DIRECTION_NO_BIDI || runDirection > RUN_DIRECTION_RTL) 2851 throw new RuntimeException(MessageLocalization.getComposedMessage("invalid.run.direction.1", runDirection)); 2852 this.runDirection = runDirection; 2853 } 2854 2855 /** 2856 * Use this method to set the run direction. 2857 * @return the run direction 2858 */ 2859 public int getRunDirection() { 2860 return runDirection; 2861 } 2862 2863// [U8] user units 2864 /** 2865 * Use this method to set the user unit. 2866 * A UserUnit is a value that defines the default user space unit. 2867 * The minimum UserUnit is 1 (1 unit = 1/72 inch). 2868 * The maximum UserUnit is 75,000. 2869 * Note that this userunit only works starting with PDF1.6! 2870 * @param userunit The userunit to set. 2871 * @throws DocumentException on error 2872 */ 2873 public void setUserunit(final float userunit) throws DocumentException { 2874 if (userunit < 1f || userunit > 75000f) throw new DocumentException(MessageLocalization.getComposedMessage("userunit.should.be.a.value.between.1.and.75000")); 2875 addPageDictEntry(PdfName.USERUNIT, new PdfNumber(userunit)); 2876 setAtLeastPdfVersion(VERSION_1_6); 2877 } 2878 2879// Miscellaneous topics 2880 2881// [M1] Color settings 2882 2883 protected PdfDictionary defaultColorspace = new PdfDictionary(); 2884 /** 2885 * Use this method to get the default colorspaces. 2886 * @return the default colorspaces 2887 */ 2888 public PdfDictionary getDefaultColorspace() { 2889 return defaultColorspace; 2890 } 2891 2892 /** 2893 * Use this method to sets the default colorspace that will be applied 2894 * to all the document. The colorspace is only applied if another colorspace 2895 * with the same name is not present in the content. 2896 * <p> 2897 * The colorspace is applied immediately when creating templates and 2898 * at the page end for the main document content. 2899 * @param key the name of the colorspace. It can be <CODE>PdfName.DEFAULTGRAY</CODE>, <CODE>PdfName.DEFAULTRGB</CODE> 2900 * or <CODE>PdfName.DEFAULTCMYK</CODE> 2901 * @param cs the colorspace. A <CODE>null</CODE> or <CODE>PdfNull</CODE> removes any colorspace with the same name 2902 */ 2903 public void setDefaultColorspace(final PdfName key, final PdfObject cs) { 2904 if (cs == null || cs.isNull()) 2905 defaultColorspace.remove(key); 2906 defaultColorspace.put(key, cs); 2907 } 2908 2909// [M2] spot patterns 2910 2911 protected HashMap<ColorDetails, ColorDetails> documentSpotPatterns = new HashMap<ColorDetails, ColorDetails>(); 2912 protected ColorDetails patternColorspaceRGB; 2913 protected ColorDetails patternColorspaceGRAY; 2914 protected ColorDetails patternColorspaceCMYK; 2915 2916 ColorDetails addSimplePatternColorspace(final BaseColor color) { 2917 int type = ExtendedColor.getType(color); 2918 if (type == ExtendedColor.TYPE_PATTERN || type == ExtendedColor.TYPE_SHADING) 2919 throw new RuntimeException(MessageLocalization.getComposedMessage("an.uncolored.tile.pattern.can.not.have.another.pattern.or.shading.as.color")); 2920 try { 2921 switch (type) { 2922 case ExtendedColor.TYPE_RGB: 2923 if (patternColorspaceRGB == null) { 2924 patternColorspaceRGB = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null); 2925 PdfArray array = new PdfArray(PdfName.PATTERN); 2926 array.add(PdfName.DEVICERGB); 2927 addToBody(array, patternColorspaceRGB.getIndirectReference()); 2928 } 2929 return patternColorspaceRGB; 2930 case ExtendedColor.TYPE_CMYK: 2931 if (patternColorspaceCMYK == null) { 2932 patternColorspaceCMYK = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null); 2933 PdfArray array = new PdfArray(PdfName.PATTERN); 2934 array.add(PdfName.DEVICECMYK); 2935 addToBody(array, patternColorspaceCMYK.getIndirectReference()); 2936 } 2937 return patternColorspaceCMYK; 2938 case ExtendedColor.TYPE_GRAY: 2939 if (patternColorspaceGRAY == null) { 2940 patternColorspaceGRAY = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null); 2941 PdfArray array = new PdfArray(PdfName.PATTERN); 2942 array.add(PdfName.DEVICEGRAY); 2943 addToBody(array, patternColorspaceGRAY.getIndirectReference()); 2944 } 2945 return patternColorspaceGRAY; 2946 case ExtendedColor.TYPE_SEPARATION: { 2947 ColorDetails details = addSimple(((SpotColor)color).getPdfSpotColor()); 2948 ColorDetails patternDetails = documentSpotPatterns.get(details); 2949 if (patternDetails == null) { 2950 patternDetails = new ColorDetails(getColorspaceName(), body.getPdfIndirectReference(), null); 2951 PdfArray array = new PdfArray(PdfName.PATTERN); 2952 array.add(details.getIndirectReference()); 2953 addToBody(array, patternDetails.getIndirectReference()); 2954 documentSpotPatterns.put(details, patternDetails); 2955 } 2956 return patternDetails; 2957 } 2958 default: 2959 throw new RuntimeException(MessageLocalization.getComposedMessage("invalid.color.type")); 2960 } 2961 } 2962 catch (Exception e) { 2963 throw new RuntimeException(e.getMessage()); 2964 } 2965 } 2966 2967// [M3] Images 2968 2969 /** 2970 * Use this method to get the strictImageSequence status. 2971 * @return value of property strictImageSequence 2972 */ 2973 public boolean isStrictImageSequence() { 2974 return pdf.isStrictImageSequence(); 2975 } 2976 2977 /** 2978 * Use this method to set the image sequence, so that it follows 2979 * the text in strict order (or not). 2980 * @param strictImageSequence new value of property strictImageSequence 2981 * 2982 */ 2983 public void setStrictImageSequence(final boolean strictImageSequence) { 2984 pdf.setStrictImageSequence(strictImageSequence); 2985 } 2986 2987 /** 2988 * Use this method to clear text wrapping around images (if applicable). 2989 * @throws DocumentException 2990 */ 2991 public void clearTextWrap() throws DocumentException { 2992 pdf.clearTextWrap(); 2993 } 2994 2995 /** Dictionary, containing all the images of the PDF document */ 2996 protected PdfDictionary imageDictionary = new PdfDictionary(); 2997 2998 /** This is the list with all the images in the document. */ 2999 private final HashMap<Long, PdfName> images = new HashMap<Long, PdfName>(); 3000 3001 /** 3002 * Use this method to adds an image to the document 3003 * but not to the page resources. It is used with 3004 * templates and <CODE>Document.add(Image)</CODE>. 3005 * Use this method only if you know what you're doing! 3006 * @param image the <CODE>Image</CODE> to add 3007 * @return the name of the image added 3008 * @throws PdfException on error 3009 * @throws DocumentException on error 3010 */ 3011 public PdfName addDirectImageSimple(final Image image) throws PdfException, DocumentException { 3012 return addDirectImageSimple(image, null); 3013 } 3014 3015 /** 3016 * Adds an image to the document but not to the page resources. 3017 * It is used with templates and <CODE>Document.add(Image)</CODE>. 3018 * Use this method only if you know what you're doing! 3019 * @param image the <CODE>Image</CODE> to add 3020 * @param fixedRef the reference to used. It may be <CODE>null</CODE>, 3021 * a <CODE>PdfIndirectReference</CODE> or a <CODE>PRIndirectReference</CODE>. 3022 * @return the name of the image added 3023 * @throws PdfException on error 3024 * @throws DocumentException on error 3025 */ 3026 public PdfName addDirectImageSimple(final Image image, final PdfIndirectReference fixedRef) throws PdfException, DocumentException { 3027 PdfName name; 3028 // if the images is already added, just retrieve the name 3029 if (images.containsKey(image.getMySerialId())) { 3030 name = images.get(image.getMySerialId()); 3031 } 3032 // if it's a new image, add it to the document 3033 else { 3034 if (image.isImgTemplate()) { 3035 name = new PdfName("img" + images.size()); 3036 if(image instanceof ImgWMF){ 3037 try { 3038 ImgWMF wmf = (ImgWMF)image; 3039 wmf.readWMF(PdfTemplate.createTemplate(this, 0, 0)); 3040 } 3041 catch (Exception e) { 3042 throw new DocumentException(e); 3043 } 3044 } 3045 } 3046 else { 3047 PdfIndirectReference dref = image.getDirectReference(); 3048 if (dref != null) { 3049 PdfName rname = new PdfName("img" + images.size()); 3050 images.put(image.getMySerialId(), rname); 3051 imageDictionary.put(rname, dref); 3052 return rname; 3053 } 3054 Image maskImage = image.getImageMask(); 3055 PdfIndirectReference maskRef = null; 3056 if (maskImage != null) { 3057 PdfName mname = images.get(maskImage.getMySerialId()); 3058 maskRef = getImageReference(mname); 3059 } 3060 PdfImage i = new PdfImage(image, "img" + images.size(), maskRef); 3061 if (image instanceof ImgJBIG2) { 3062 byte[] globals = ((ImgJBIG2) image).getGlobalBytes(); 3063 if (globals != null) { 3064 PdfDictionary decodeparms = new PdfDictionary(); 3065 decodeparms.put(PdfName.JBIG2GLOBALS, getReferenceJBIG2Globals(globals)); 3066 i.put(PdfName.DECODEPARMS, decodeparms); 3067 } 3068 } 3069 if (image.hasICCProfile()) { 3070 PdfICCBased icc = new PdfICCBased(image.getICCProfile(), image.getCompressionLevel()); 3071 PdfIndirectReference iccRef = add(icc); 3072 PdfArray iccArray = new PdfArray(); 3073 iccArray.add(PdfName.ICCBASED); 3074 iccArray.add(iccRef); 3075 PdfArray colorspace = i.getAsArray(PdfName.COLORSPACE); 3076 if (colorspace != null) { 3077 if (colorspace.size() > 1 && PdfName.INDEXED.equals(colorspace.getPdfObject(0))) 3078 colorspace.set(1, iccArray); 3079 else 3080 i.put(PdfName.COLORSPACE, iccArray); 3081 } 3082 else 3083 i.put(PdfName.COLORSPACE, iccArray); 3084 } 3085 add(i, fixedRef); 3086 name = i.name(); 3087 } 3088 images.put(image.getMySerialId(), name); 3089 } 3090 return name; 3091 } 3092 3093 /** 3094 * Writes a <CODE>PdfImage</CODE> to the outputstream. 3095 * 3096 * @param pdfImage the image to be added 3097 * @param fixedRef the IndirectReference, may be null then a new indirect reference is returned 3098 * @return a <CODE>PdfIndirectReference</CODE> to the encapsulated image 3099 * @throws PdfException when a document isn't open yet, or has been closed 3100 */ 3101 3102 PdfIndirectReference add(final PdfImage pdfImage, PdfIndirectReference fixedRef) throws PdfException { 3103 if (! imageDictionary.contains(pdfImage.name())) { 3104 PdfXConformanceImp.checkPDFXConformance(this, PdfXConformanceImp.PDFXKEY_IMAGE, pdfImage); 3105 if (fixedRef instanceof PRIndirectReference) { 3106 PRIndirectReference r2 = (PRIndirectReference)fixedRef; 3107 fixedRef = new PdfIndirectReference(0, getNewObjectNumber(r2.getReader(), r2.getNumber(), r2.getGeneration())); 3108 } 3109 try { 3110 if (fixedRef == null) 3111 fixedRef = addToBody(pdfImage).getIndirectReference(); 3112 else 3113 addToBody(pdfImage, fixedRef); 3114 } 3115 catch(IOException ioe) { 3116 throw new ExceptionConverter(ioe); 3117 } 3118 imageDictionary.put(pdfImage.name(), fixedRef); 3119 return fixedRef; 3120 } 3121 return (PdfIndirectReference) imageDictionary.get(pdfImage.name()); 3122 } 3123 3124 /** 3125 * return the <CODE>PdfIndirectReference</CODE> to the image with a given name. 3126 * 3127 * @param name the name of the image 3128 * @return a <CODE>PdfIndirectReference</CODE> 3129 */ 3130 3131 PdfIndirectReference getImageReference(final PdfName name) { 3132 return (PdfIndirectReference) imageDictionary.get(name); 3133 } 3134 3135 protected PdfIndirectReference add(final PdfICCBased icc) { 3136 PdfIndirectObject object; 3137 try { 3138 object = addToBody(icc); 3139 } 3140 catch(IOException ioe) { 3141 throw new ExceptionConverter(ioe); 3142 } 3143 return object.getIndirectReference(); 3144 } 3145 3146 /** 3147 * A HashSet with Stream objects containing JBIG2 Globals 3148 * @since 2.1.5 3149 */ 3150 protected HashMap<PdfStream, PdfIndirectReference> JBIG2Globals = new HashMap<PdfStream, PdfIndirectReference>(); 3151 /** 3152 * Gets an indirect reference to a JBIG2 Globals stream. 3153 * Adds the stream if it hasn't already been added to the writer. 3154 * @param content a byte array that may already been added to the writer inside a stream object. 3155 * @return the PdfIndirectReference of the stream 3156 * @since 2.1.5 3157 */ 3158 protected PdfIndirectReference getReferenceJBIG2Globals(final byte[] content) { 3159 if (content == null) return null; 3160 for (PdfStream stream : JBIG2Globals.keySet()) { 3161 if (Arrays.equals(content, stream.getBytes())) { 3162 return JBIG2Globals.get(stream); 3163 } 3164 } 3165 PdfStream stream = new PdfStream(content); 3166 PdfIndirectObject ref; 3167 try { 3168 ref = addToBody(stream); 3169 } catch (IOException e) { 3170 return null; 3171 } 3172 JBIG2Globals.put(stream, ref.getIndirectReference()); 3173 return ref.getIndirectReference(); 3174 } 3175 3176// [F12] tagged PDF 3177 /** 3178 * A flag indicating the presence of structure elements that contain user properties attributes. 3179 */ 3180 private boolean userProperties; 3181 3182 /** 3183 * Gets the flag indicating the presence of structure elements that contain user properties attributes. 3184 * @return the user properties flag 3185 */ 3186 public boolean isUserProperties() { 3187 return this.userProperties; 3188 } 3189 3190 /** 3191 * Sets the flag indicating the presence of structure elements that contain user properties attributes. 3192 * @param userProperties the user properties flag 3193 */ 3194 public void setUserProperties(final boolean userProperties) { 3195 this.userProperties = userProperties; 3196 } 3197 3198 /** 3199 * Holds value of property RGBTranparency. 3200 */ 3201 private boolean rgbTransparencyBlending; 3202 3203 /** 3204 * Gets the transparency blending colorspace. 3205 * @return <code>true</code> if the transparency blending colorspace is RGB, <code>false</code> 3206 * if it is the default blending colorspace 3207 * @since 2.1.0 3208 */ 3209 public boolean isRgbTransparencyBlending() { 3210 return this.rgbTransparencyBlending; 3211 } 3212 3213 /** 3214 * Sets the transparency blending colorspace to RGB. The default blending colorspace is 3215 * CMYK and will result in faded colors in the screen and in printing. Calling this method 3216 * will return the RGB colors to what is expected. The RGB blending will be applied to all subsequent pages 3217 * until other value is set. 3218 * Note that this is a generic solution that may not work in all cases. 3219 * @param rgbTransparencyBlending <code>true</code> to set the transparency blending colorspace to RGB, <code>false</code> 3220 * to use the default blending colorspace 3221 * @since 2.1.0 3222 */ 3223 public void setRgbTransparencyBlending(final boolean rgbTransparencyBlending) { 3224 this.rgbTransparencyBlending = rgbTransparencyBlending; 3225 } 3226}