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