001/* 002 * $Id: PdfCopy.java 4784 2011-03-15 08:33:00Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.pdf; 045 046import java.io.IOException; 047import java.io.OutputStream; 048import java.util.ArrayList; 049import java.util.HashMap; 050import java.util.HashSet; 051import java.util.Iterator; 052 053import com.itextpdf.text.Document; 054import com.itextpdf.text.DocumentException; 055import com.itextpdf.text.ExceptionConverter; 056import com.itextpdf.text.Rectangle; 057 058/** 059 * Make copies of PDF documents. Documents can be edited after reading and 060 * before writing them out. 061 * @author Mark Thompson 062 */ 063 064public class PdfCopy extends PdfWriter { 065 /** 066 * This class holds information about indirect references, since they are 067 * renumbered by iText. 068 */ 069 static class IndirectReferences { 070 PdfIndirectReference theRef; 071 boolean hasCopied; 072 IndirectReferences(PdfIndirectReference ref) { 073 theRef = ref; 074 hasCopied = false; 075 } 076 void setCopied() { hasCopied = true; } 077 boolean getCopied() { return hasCopied; } 078 PdfIndirectReference getRef() { return theRef; } 079 }; 080 protected HashMap<RefKey, IndirectReferences> indirects; 081 protected HashMap<PdfReader, HashMap<RefKey, IndirectReferences>> indirectMap; 082 protected int currentObjectNum = 1; 083 protected PdfReader reader; 084 protected PdfIndirectReference acroForm; 085 protected int[] namePtr = {0}; 086 /** Holds value of property rotateContents. */ 087 private boolean rotateContents = true; 088 protected PdfArray fieldArray; 089 protected HashSet<PdfTemplate> fieldTemplates; 090 091 /** 092 * A key to allow us to hash indirect references 093 */ 094 protected static class RefKey { 095 int num; 096 int gen; 097 RefKey(int num, int gen) { 098 this.num = num; 099 this.gen = gen; 100 } 101 RefKey(PdfIndirectReference ref) { 102 num = ref.getNumber(); 103 gen = ref.getGeneration(); 104 } 105 RefKey(PRIndirectReference ref) { 106 num = ref.getNumber(); 107 gen = ref.getGeneration(); 108 } 109 @Override 110 public int hashCode() { 111 return (gen<<16)+num; 112 } 113 @Override 114 public boolean equals(Object o) { 115 if (!(o instanceof RefKey)) return false; 116 RefKey other = (RefKey)o; 117 return this.gen == other.gen && this.num == other.num; 118 } 119 @Override 120 public String toString() { 121 return Integer.toString(num) + ' ' + gen; 122 } 123 } 124 125 /** 126 * Constructor 127 * @param document 128 * @param os outputstream 129 */ 130 public PdfCopy(Document document, OutputStream os) throws DocumentException { 131 super(new PdfDocument(), os); 132 document.addDocListener(pdf); 133 pdf.addWriter(this); 134 indirectMap = new HashMap<PdfReader, HashMap<RefKey, IndirectReferences>>(); 135 } 136 137 /** Getter for property rotateContents. 138 * @return Value of property rotateContents. 139 * 140 */ 141 public boolean isRotateContents() { 142 return this.rotateContents; 143 } 144 145 /** Setter for property rotateContents. 146 * @param rotateContents New value of property rotateContents. 147 * 148 */ 149 public void setRotateContents(boolean rotateContents) { 150 this.rotateContents = rotateContents; 151 } 152 153 /** 154 * Grabs a page from the input document 155 * @param reader the reader of the document 156 * @param pageNumber which page to get 157 * @return the page 158 */ 159 @Override 160 public PdfImportedPage getImportedPage(PdfReader reader, int pageNumber) { 161 if (currentPdfReaderInstance != null) { 162 if (currentPdfReaderInstance.getReader() != reader) { 163 try { 164 currentPdfReaderInstance.getReader().close(); 165 currentPdfReaderInstance.getReaderFile().close(); 166 } 167 catch (IOException ioe) { 168 // empty on purpose 169 } 170 currentPdfReaderInstance = super.getPdfReaderInstance(reader); 171 } 172 } 173 else { 174 currentPdfReaderInstance = super.getPdfReaderInstance(reader); 175 } 176 //currentPdfReaderInstance.setOutputToPdf(false); 177 return currentPdfReaderInstance.getImportedPage(pageNumber); 178 } 179 180 181 /** 182 * Translate a PRIndirectReference to a PdfIndirectReference 183 * In addition, translates the object numbers, and copies the 184 * referenced object to the output file. 185 * NB: PRIndirectReferences (and PRIndirectObjects) really need to know what 186 * file they came from, because each file has its own namespace. The translation 187 * we do from their namespace to ours is *at best* heuristic, and guaranteed to 188 * fail under some circumstances. 189 */ 190 protected PdfIndirectReference copyIndirect(PRIndirectReference in) throws IOException, BadPdfFormatException { 191 PdfIndirectReference theRef; 192 RefKey key = new RefKey(in); 193 IndirectReferences iRef = indirects.get(key); 194 if (iRef != null) { 195 theRef = iRef.getRef(); 196 if (iRef.getCopied()) { 197 return theRef; 198 } 199 } 200 else { 201 theRef = body.getPdfIndirectReference(); 202 iRef = new IndirectReferences(theRef); 203 indirects.put(key, iRef); 204 } 205 PdfObject obj = PdfReader.getPdfObjectRelease(in); 206 if (obj != null && obj.isDictionary()) { 207 PdfObject type = PdfReader.getPdfObjectRelease(((PdfDictionary)obj).get(PdfName.TYPE)); 208 if (type != null && PdfName.PAGE.equals(type)) { 209 return theRef; 210 } 211 } 212 iRef.setCopied(); 213 obj = copyObject(obj); 214 addToBody(obj, theRef); 215 return theRef; 216 } 217 218 /** 219 * Translate a PRDictionary to a PdfDictionary. Also translate all of the 220 * objects contained in it. 221 */ 222 protected PdfDictionary copyDictionary(PdfDictionary in) 223 throws IOException, BadPdfFormatException { 224 PdfDictionary out = new PdfDictionary(); 225 PdfObject type = PdfReader.getPdfObjectRelease(in.get(PdfName.TYPE)); 226 227 for (Object element : in.getKeys()) { 228 PdfName key = (PdfName)element; 229 PdfObject value = in.get(key); 230 // System.out.println("Copy " + key); 231 if (type != null && PdfName.PAGE.equals(type)) { 232 if (!key.equals(PdfName.B) && !key.equals(PdfName.PARENT)) 233 out.put(key, copyObject(value)); 234 } 235 else 236 out.put(key, copyObject(value)); 237 } 238 return out; 239 } 240 241 /** 242 * Translate a PRStream to a PdfStream. The data part copies itself. 243 */ 244 protected PdfStream copyStream(PRStream in) throws IOException, BadPdfFormatException { 245 PRStream out = new PRStream(in, null); 246 247 for (Object element : in.getKeys()) { 248 PdfName key = (PdfName) element; 249 PdfObject value = in.get(key); 250 out.put(key, copyObject(value)); 251 } 252 253 return out; 254 } 255 256 257 /** 258 * Translate a PRArray to a PdfArray. Also translate all of the objects contained 259 * in it 260 */ 261 protected PdfArray copyArray(PdfArray in) throws IOException, BadPdfFormatException { 262 PdfArray out = new PdfArray(); 263 264 for (Iterator<PdfObject> i = in.listIterator(); i.hasNext();) { 265 PdfObject value = i.next(); 266 out.add(copyObject(value)); 267 } 268 return out; 269 } 270 271 /** 272 * Translate a PR-object to a Pdf-object 273 */ 274 protected PdfObject copyObject(PdfObject in) throws IOException,BadPdfFormatException { 275 if (in == null) 276 return PdfNull.PDFNULL; 277 switch (in.type) { 278 case PdfObject.DICTIONARY: 279 // System.out.println("Dictionary: " + in.toString()); 280 return copyDictionary((PdfDictionary)in); 281 case PdfObject.INDIRECT: 282 return copyIndirect((PRIndirectReference)in); 283 case PdfObject.ARRAY: 284 return copyArray((PdfArray)in); 285 case PdfObject.NUMBER: 286 case PdfObject.NAME: 287 case PdfObject.STRING: 288 case PdfObject.NULL: 289 case PdfObject.BOOLEAN: 290 case 0: 291 return in; 292 case PdfObject.STREAM: 293 return copyStream((PRStream)in); 294 // return in; 295 default: 296 if (in.type < 0) { 297 String lit = ((PdfLiteral)in).toString(); 298 if (lit.equals("true") || lit.equals("false")) { 299 return new PdfBoolean(lit); 300 } 301 return new PdfLiteral(lit); 302 } 303 System.out.println("CANNOT COPY type " + in.type); 304 return null; 305 } 306 } 307 308 /** 309 * convenience method. Given an imported page, set our "globals" 310 */ 311 protected int setFromIPage(PdfImportedPage iPage) { 312 int pageNum = iPage.getPageNumber(); 313 PdfReaderInstance inst = currentPdfReaderInstance = iPage.getPdfReaderInstance(); 314 reader = inst.getReader(); 315 setFromReader(reader); 316 return pageNum; 317 } 318 319 /** 320 * convenience method. Given a reader, set our "globals" 321 */ 322 protected void setFromReader(PdfReader reader) { 323 this.reader = reader; 324 indirects = indirectMap.get(reader); 325 if (indirects == null) { 326 indirects = new HashMap<RefKey, IndirectReferences>(); 327 indirectMap.put(reader,indirects); 328 PdfDictionary catalog = reader.getCatalog(); 329 PRIndirectReference ref = null; 330 PdfObject o = catalog.get(PdfName.ACROFORM); 331 if (o == null || o.type() != PdfObject.INDIRECT) 332 return; 333 ref = (PRIndirectReference)o; 334 if (acroForm == null) acroForm = body.getPdfIndirectReference(); 335 indirects.put(new RefKey(ref), new IndirectReferences(acroForm)); 336 } 337 } 338 /** 339 * Add an imported page to our output 340 * @param iPage an imported page 341 * @throws IOException, BadPdfFormatException 342 */ 343 public void addPage(PdfImportedPage iPage) throws IOException, BadPdfFormatException { 344 int pageNum = setFromIPage(iPage); 345 346 PdfDictionary thePage = reader.getPageN(pageNum); 347 PRIndirectReference origRef = reader.getPageOrigRef(pageNum); 348 reader.releasePage(pageNum); 349 RefKey key = new RefKey(origRef); 350 PdfIndirectReference pageRef; 351 IndirectReferences iRef = indirects.get(key); 352 if (iRef != null && !iRef.getCopied()) { 353 pageReferences.add(iRef.getRef()); 354 iRef.setCopied(); 355 } 356 pageRef = getCurrentPage(); 357 if (iRef == null) { 358 iRef = new IndirectReferences(pageRef); 359 indirects.put(key, iRef); 360 } 361 iRef.setCopied(); 362 PdfDictionary newPage = copyDictionary(thePage); 363 root.addPage(newPage); 364 iPage.setCopied(); 365 ++currentPageNumber; 366 } 367 368 /** 369 * Adds a blank page. 370 * @param rect The page dimension 371 * @param rotation The rotation angle in degrees 372 * @since 2.1.5 373 */ 374 public void addPage(Rectangle rect, int rotation) { 375 PdfRectangle mediabox = new PdfRectangle(rect, rotation); 376 PageResources resources = new PageResources(); 377 PdfPage page = new PdfPage(mediabox, new HashMap<String, PdfRectangle>(), resources.getResources(), 0); 378 page.put(PdfName.TABS, getTabs()); 379 root.addPage(page); 380 ++currentPageNumber; 381 } 382 383 /** 384 * Copy the acroform for an input document. Note that you can only have one, 385 * we make no effort to merge them. 386 * @param reader The reader of the input file that is being copied 387 * @throws IOException, BadPdfFormatException 388 */ 389 public void copyAcroForm(PdfReader reader) throws IOException, BadPdfFormatException { 390 setFromReader(reader); 391 392 PdfDictionary catalog = reader.getCatalog(); 393 PRIndirectReference hisRef = null; 394 PdfObject o = catalog.get(PdfName.ACROFORM); 395 if (o != null && o.type() == PdfObject.INDIRECT) 396 hisRef = (PRIndirectReference)o; 397 if (hisRef == null) return; // bugfix by John Englar 398 RefKey key = new RefKey(hisRef); 399 PdfIndirectReference myRef; 400 IndirectReferences iRef = indirects.get(key); 401 if (iRef != null) { 402 acroForm = myRef = iRef.getRef(); 403 } 404 else { 405 acroForm = myRef = body.getPdfIndirectReference(); 406 iRef = new IndirectReferences(myRef); 407 indirects.put(key, iRef); 408 } 409 if (! iRef.getCopied()) { 410 iRef.setCopied(); 411 PdfDictionary theForm = copyDictionary((PdfDictionary)PdfReader.getPdfObject(hisRef)); 412 addToBody(theForm, myRef); 413 } 414 } 415 416 /* 417 * the getCatalog method is part of PdfWriter. 418 * we wrap this so that we can extend it 419 */ 420 @Override 421 protected PdfDictionary getCatalog(PdfIndirectReference rootObj) { 422 try { 423 PdfDictionary theCat = pdf.getCatalog(rootObj); 424 if (fieldArray == null) { 425 if (acroForm != null) theCat.put(PdfName.ACROFORM, acroForm); 426 } 427 else 428 addFieldResources(theCat); 429 return theCat; 430 } 431 catch (IOException e) { 432 throw new ExceptionConverter(e); 433 } 434 } 435 436 private void addFieldResources(PdfDictionary catalog) throws IOException { 437 if (fieldArray == null) 438 return; 439 PdfDictionary acroForm = new PdfDictionary(); 440 catalog.put(PdfName.ACROFORM, acroForm); 441 acroForm.put(PdfName.FIELDS, fieldArray); 442 acroForm.put(PdfName.DA, new PdfString("/Helv 0 Tf 0 g ")); 443 if (fieldTemplates.isEmpty()) 444 return; 445 PdfDictionary dr = new PdfDictionary(); 446 acroForm.put(PdfName.DR, dr); 447 for (PdfTemplate template: fieldTemplates) { 448 PdfFormField.mergeResources(dr, (PdfDictionary)template.getResources()); 449 } 450 // if (dr.get(PdfName.ENCODING) == null) dr.put(PdfName.ENCODING, PdfName.WIN_ANSI_ENCODING); 451 PdfDictionary fonts = dr.getAsDict(PdfName.FONT); 452 if (fonts == null) { 453 fonts = new PdfDictionary(); 454 dr.put(PdfName.FONT, fonts); 455 } 456 if (!fonts.contains(PdfName.HELV)) { 457 PdfDictionary dic = new PdfDictionary(PdfName.FONT); 458 dic.put(PdfName.BASEFONT, PdfName.HELVETICA); 459 dic.put(PdfName.ENCODING, PdfName.WIN_ANSI_ENCODING); 460 dic.put(PdfName.NAME, PdfName.HELV); 461 dic.put(PdfName.SUBTYPE, PdfName.TYPE1); 462 fonts.put(PdfName.HELV, addToBody(dic).getIndirectReference()); 463 } 464 if (!fonts.contains(PdfName.ZADB)) { 465 PdfDictionary dic = new PdfDictionary(PdfName.FONT); 466 dic.put(PdfName.BASEFONT, PdfName.ZAPFDINGBATS); 467 dic.put(PdfName.NAME, PdfName.ZADB); 468 dic.put(PdfName.SUBTYPE, PdfName.TYPE1); 469 fonts.put(PdfName.ZADB, addToBody(dic).getIndirectReference()); 470 } 471 } 472 473 /** 474 * Signals that the <CODE>Document</CODE> was closed and that no other 475 * <CODE>Elements</CODE> will be added. 476 * <P> 477 * The pages-tree is built and written to the outputstream. 478 * A Catalog is constructed, as well as an Info-object, 479 * the reference table is composed and everything is written 480 * to the outputstream embedded in a Trailer. 481 */ 482 483 @Override 484 public void close() { 485 if (open) { 486 PdfReaderInstance ri = currentPdfReaderInstance; 487 pdf.close(); 488 super.close(); 489 if (ri != null) { 490 try { 491 ri.getReader().close(); 492 ri.getReaderFile().close(); 493 } 494 catch (IOException ioe) { 495 // empty on purpose 496 } 497 } 498 } 499 } 500 public PdfIndirectReference add(PdfOutline outline) { return null; } 501 @Override 502 public void addAnnotation(PdfAnnotation annot) { } 503 @Override 504 PdfIndirectReference add(PdfPage page, PdfContents contents) throws PdfException { return null; } 505 506 @Override 507 public void freeReader(PdfReader reader) throws IOException { 508 indirectMap.remove(reader); 509 if (currentPdfReaderInstance != null) { 510 if (currentPdfReaderInstance.getReader() == reader) { 511 try { 512 currentPdfReaderInstance.getReader().close(); 513 currentPdfReaderInstance.getReaderFile().close(); 514 } 515 catch (IOException ioe) { 516 // empty on purpose 517 } 518 currentPdfReaderInstance = null; 519 } 520 } 521 super.freeReader(reader); 522 } 523 524 /** 525 * Create a page stamp. New content and annotations, including new fields, are allowed. 526 * The fields added cannot have parents in another pages. This method modifies the PdfReader instance.<p> 527 * The general usage to stamp something in a page is: 528 * <p> 529 * <pre> 530 * PdfImportedPage page = copy.getImportedPage(reader, 1); 531 * PdfCopy.PageStamp ps = copy.createPageStamp(page); 532 * ps.addAnnotation(PdfAnnotation.createText(copy, new Rectangle(50, 180, 70, 200), "Hello", "No Thanks", true, "Comment")); 533 * PdfContentByte under = ps.getUnderContent(); 534 * under.addImage(img); 535 * PdfContentByte over = ps.getOverContent(); 536 * over.beginText(); 537 * over.setFontAndSize(bf, 18); 538 * over.setTextMatrix(30, 30); 539 * over.showText("total page " + totalPage); 540 * over.endText(); 541 * ps.alterContents(); 542 * copy.addPage(page); 543 * </pre> 544 * @param iPage an imported page 545 * @return the <CODE>PageStamp</CODE> 546 */ 547 public PageStamp createPageStamp(PdfImportedPage iPage) { 548 int pageNum = iPage.getPageNumber(); 549 PdfReader reader = iPage.getPdfReaderInstance().getReader(); 550 PdfDictionary pageN = reader.getPageN(pageNum); 551 return new PageStamp(reader, pageN, this); 552 } 553 554 public static class PageStamp { 555 556 PdfDictionary pageN; 557 PdfCopy.StampContent under; 558 PdfCopy.StampContent over; 559 PageResources pageResources; 560 PdfReader reader; 561 PdfCopy cstp; 562 563 PageStamp(PdfReader reader, PdfDictionary pageN, PdfCopy cstp) { 564 this.pageN = pageN; 565 this.reader = reader; 566 this.cstp = cstp; 567 } 568 569 public PdfContentByte getUnderContent(){ 570 if (under == null) { 571 if (pageResources == null) { 572 pageResources = new PageResources(); 573 PdfDictionary resources = pageN.getAsDict(PdfName.RESOURCES); 574 pageResources.setOriginalResources(resources, cstp.namePtr); 575 } 576 under = new PdfCopy.StampContent(cstp, pageResources); 577 } 578 return under; 579 } 580 581 public PdfContentByte getOverContent(){ 582 if (over == null) { 583 if (pageResources == null) { 584 pageResources = new PageResources(); 585 PdfDictionary resources = pageN.getAsDict(PdfName.RESOURCES); 586 pageResources.setOriginalResources(resources, cstp.namePtr); 587 } 588 over = new PdfCopy.StampContent(cstp, pageResources); 589 } 590 return over; 591 } 592 593 public void alterContents() throws IOException { 594 if (over == null && under == null) 595 return; 596 PdfArray ar = null; 597 PdfObject content = PdfReader.getPdfObject(pageN.get(PdfName.CONTENTS), pageN); 598 if (content == null) { 599 ar = new PdfArray(); 600 pageN.put(PdfName.CONTENTS, ar); 601 } else if (content.isArray()) { 602 ar = (PdfArray)content; 603 } else if (content.isStream()) { 604 ar = new PdfArray(); 605 ar.add(pageN.get(PdfName.CONTENTS)); 606 pageN.put(PdfName.CONTENTS, ar); 607 } else { 608 ar = new PdfArray(); 609 pageN.put(PdfName.CONTENTS, ar); 610 } 611 ByteBuffer out = new ByteBuffer(); 612 if (under != null) { 613 out.append(PdfContents.SAVESTATE); 614 applyRotation(pageN, out); 615 out.append(under.getInternalBuffer()); 616 out.append(PdfContents.RESTORESTATE); 617 } 618 if (over != null) 619 out.append(PdfContents.SAVESTATE); 620 PdfStream stream = new PdfStream(out.toByteArray()); 621 stream.flateCompress(cstp.getCompressionLevel()); 622 PdfIndirectReference ref1 = cstp.addToBody(stream).getIndirectReference(); 623 ar.addFirst(ref1); 624 out.reset(); 625 if (over != null) { 626 out.append(' '); 627 out.append(PdfContents.RESTORESTATE); 628 out.append(PdfContents.SAVESTATE); 629 applyRotation(pageN, out); 630 out.append(over.getInternalBuffer()); 631 out.append(PdfContents.RESTORESTATE); 632 stream = new PdfStream(out.toByteArray()); 633 stream.flateCompress(cstp.getCompressionLevel()); 634 ar.add(cstp.addToBody(stream).getIndirectReference()); 635 } 636 pageN.put(PdfName.RESOURCES, pageResources.getResources()); 637 } 638 639 void applyRotation(PdfDictionary pageN, ByteBuffer out) { 640 if (!cstp.rotateContents) 641 return; 642 Rectangle page = reader.getPageSizeWithRotation(pageN); 643 int rotation = page.getRotation(); 644 switch (rotation) { 645 case 90: 646 out.append(PdfContents.ROTATE90); 647 out.append(page.getTop()); 648 out.append(' ').append('0').append(PdfContents.ROTATEFINAL); 649 break; 650 case 180: 651 out.append(PdfContents.ROTATE180); 652 out.append(page.getRight()); 653 out.append(' '); 654 out.append(page.getTop()); 655 out.append(PdfContents.ROTATEFINAL); 656 break; 657 case 270: 658 out.append(PdfContents.ROTATE270); 659 out.append('0').append(' '); 660 out.append(page.getRight()); 661 out.append(PdfContents.ROTATEFINAL); 662 break; 663 } 664 } 665 666 private void addDocumentField(PdfIndirectReference ref) { 667 if (cstp.fieldArray == null) 668 cstp.fieldArray = new PdfArray(); 669 cstp.fieldArray.add(ref); 670 } 671 672 private void expandFields(PdfFormField field, ArrayList<PdfAnnotation> allAnnots) { 673 allAnnots.add(field); 674 ArrayList<PdfFormField> kids = field.getKids(); 675 if (kids != null) { 676 for (PdfFormField f : kids) 677 expandFields(f, allAnnots); 678 } 679 } 680 681 public void addAnnotation(PdfAnnotation annot) { 682 try { 683 ArrayList<PdfAnnotation> allAnnots = new ArrayList<PdfAnnotation>(); 684 if (annot.isForm()) { 685 PdfFormField field = (PdfFormField)annot; 686 if (field.getParent() != null) 687 return; 688 expandFields(field, allAnnots); 689 if (cstp.fieldTemplates == null) 690 cstp.fieldTemplates = new HashSet<PdfTemplate>(); 691 } 692 else 693 allAnnots.add(annot); 694 for (int k = 0; k < allAnnots.size(); ++k) { 695 annot = allAnnots.get(k); 696 if (annot.isForm()) { 697 if (!annot.isUsed()) { 698 HashSet<PdfTemplate> templates = annot.getTemplates(); 699 if (templates != null) 700 cstp.fieldTemplates.addAll(templates); 701 } 702 PdfFormField field = (PdfFormField)annot; 703 if (field.getParent() == null) 704 addDocumentField(field.getIndirectReference()); 705 } 706 if (annot.isAnnotation()) { 707 PdfObject pdfobj = PdfReader.getPdfObject(pageN.get(PdfName.ANNOTS), pageN); 708 PdfArray annots = null; 709 if (pdfobj == null || !pdfobj.isArray()) { 710 annots = new PdfArray(); 711 pageN.put(PdfName.ANNOTS, annots); 712 } 713 else 714 annots = (PdfArray)pdfobj; 715 annots.add(annot.getIndirectReference()); 716 if (!annot.isUsed()) { 717 PdfRectangle rect = (PdfRectangle)annot.get(PdfName.RECT); 718 if (rect != null && (rect.left() != 0 || rect.right() != 0 || rect.top() != 0 || rect.bottom() != 0)) { 719 int rotation = reader.getPageRotation(pageN); 720 Rectangle pageSize = reader.getPageSizeWithRotation(pageN); 721 switch (rotation) { 722 case 90: 723 annot.put(PdfName.RECT, new PdfRectangle( 724 pageSize.getTop() - rect.bottom(), 725 rect.left(), 726 pageSize.getTop() - rect.top(), 727 rect.right())); 728 break; 729 case 180: 730 annot.put(PdfName.RECT, new PdfRectangle( 731 pageSize.getRight() - rect.left(), 732 pageSize.getTop() - rect.bottom(), 733 pageSize.getRight() - rect.right(), 734 pageSize.getTop() - rect.top())); 735 break; 736 case 270: 737 annot.put(PdfName.RECT, new PdfRectangle( 738 rect.bottom(), 739 pageSize.getRight() - rect.left(), 740 rect.top(), 741 pageSize.getRight() - rect.right())); 742 break; 743 } 744 } 745 } 746 } 747 if (!annot.isUsed()) { 748 annot.setUsed(); 749 cstp.addToBody(annot, annot.getIndirectReference()); 750 } 751 } 752 } 753 catch (IOException e) { 754 throw new ExceptionConverter(e); 755 } 756 } 757 } 758 759 public static class StampContent extends PdfContentByte { 760 PageResources pageResources; 761 762 /** Creates a new instance of StampContent */ 763 StampContent(PdfWriter writer, PageResources pageResources) { 764 super(writer); 765 this.pageResources = pageResources; 766 } 767 768 /** 769 * Gets a duplicate of this <CODE>PdfContentByte</CODE>. All 770 * the members are copied by reference but the buffer stays different. 771 * 772 * @return a copy of this <CODE>PdfContentByte</CODE> 773 */ 774 @Override 775 public PdfContentByte getDuplicate() { 776 return new PdfCopy.StampContent(writer, pageResources); 777 } 778 779 @Override 780 PageResources getPageResources() { 781 return pageResources; 782 } 783 } 784}