1 /*
2 * $Id: PdfCopy.java 3912 2009-04-26 08:38:15Z blowagie $
3 *
4 * Copyright (C) 2002 Mark Thompson
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 package com.lowagie.text.pdf;
50
51 import java.io.IOException;
52 import java.io.OutputStream;
53 import java.util.HashMap;
54 import java.util.Iterator;
55
56 import com.lowagie.text.Document;
57 import com.lowagie.text.DocumentException;
58 import com.lowagie.text.ExceptionConverter;
59 import com.lowagie.text.Rectangle;
60 import java.util.ArrayList;
61
62 /**
63 * Make copies of PDF documents. Documents can be edited after reading and
64 * before writing them out.
65 * @author Mark Thompson
66 */
67
68 public class PdfCopy extends PdfWriter {
69 /**
70 * This class holds information about indirect references, since they are
71 * renumbered by iText.
72 */
73 static class IndirectReferences {
74 PdfIndirectReference theRef;
75 boolean hasCopied;
76 IndirectReferences(PdfIndirectReference ref) {
77 theRef = ref;
78 hasCopied = false;
79 }
80 void setCopied() { hasCopied = true; }
81 boolean getCopied() { return hasCopied; }
82 PdfIndirectReference getRef() { return theRef; }
83 };
84 protected HashMap indirects;
85 protected HashMap indirectMap;
86 protected int currentObjectNum = 1;
87 protected PdfReader reader;
88 protected PdfIndirectReference acroForm;
89 protected int[] namePtr = {0};
90 /** Holds value of property rotateContents. */
91 private boolean rotateContents = true;
92 protected PdfArray fieldArray;
93 protected HashMap fieldTemplates;
94
95 /**
96 * A key to allow us to hash indirect references
97 */
98 protected static class RefKey {
99 int num;
100 int gen;
101 RefKey(int num, int gen) {
102 this.num = num;
103 this.gen = gen;
104 }
105 RefKey(PdfIndirectReference ref) {
106 num = ref.getNumber();
107 gen = ref.getGeneration();
108 }
109 RefKey(PRIndirectReference ref) {
110 num = ref.getNumber();
111 gen = ref.getGeneration();
112 }
113 public int hashCode() {
114 return (gen<<16)+num;
115 }
116 public boolean equals(Object o) {
117 if (!(o instanceof RefKey)) return false;
118 RefKey other = (RefKey)o;
119 return this.gen == other.gen && this.num == other.num;
120 }
121 public String toString() {
122 return Integer.toString(num) + ' ' + gen;
123 }
124 }
125
126 /**
127 * Constructor
128 * @param document
129 * @param os outputstream
130 */
131 public PdfCopy(Document document, OutputStream os) throws DocumentException {
132 super(new PdfDocument(), os);
133 document.addDocListener(pdf);
134 pdf.addWriter(this);
135 indirectMap = new HashMap();
136 }
137
138 /** Getter for property rotateContents.
139 * @return Value of property rotateContents.
140 *
141 */
142 public boolean isRotateContents() {
143 return this.rotateContents;
144 }
145
146 /** Setter for property rotateContents.
147 * @param rotateContents New value of property rotateContents.
148 *
149 */
150 public void setRotateContents(boolean rotateContents) {
151 this.rotateContents = rotateContents;
152 }
153
154 /**
155 * Grabs a page from the input document
156 * @param reader the reader of the document
157 * @param pageNumber which page to get
158 * @return the page
159 */
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 = reader.getPdfReaderInstance(this);
171 }
172 }
173 else {
174 currentPdfReaderInstance = reader.getPdfReaderInstance(this);
175 }
176 return currentPdfReaderInstance.getImportedPage(pageNumber);
177 }
178
179
180 /**
181 * Translate a PRIndirectReference to a PdfIndirectReference
182 * In addition, translates the object numbers, and copies the
183 * referenced object to the output file.
184 * NB: PRIndirectReferences (and PRIndirectObjects) really need to know what
185 * file they came from, because each file has its own namespace. The translation
186 * we do from their namespace to ours is *at best* heuristic, and guaranteed to
187 * fail under some circumstances.
188 */
189 protected PdfIndirectReference copyIndirect(PRIndirectReference in) throws IOException, BadPdfFormatException {
190 PdfIndirectReference theRef;
191 RefKey key = new RefKey(in);
192 IndirectReferences iRef = (IndirectReferences)indirects.get(key);
193 if (iRef != null) {
194 theRef = iRef.getRef();
195 if (iRef.getCopied()) {
196 return theRef;
197 }
198 }
199 else {
200 theRef = body.getPdfIndirectReference();
201 iRef = new IndirectReferences(theRef);
202 indirects.put(key, iRef);
203 }
204 PdfObject obj = PdfReader.getPdfObjectRelease(in);
205 if (obj != null && obj.isDictionary()) {
206 PdfObject type = PdfReader.getPdfObjectRelease(((PdfDictionary)obj).get(PdfName.TYPE));
207 if (type != null && PdfName.PAGE.equals(type)) {
208 return theRef;
209 }
210 }
211 iRef.setCopied();
212 obj = copyObject(obj);
213 addToBody(obj, theRef);
214 return theRef;
215 }
216
217 /**
218 * Translate a PRDictionary to a PdfDictionary. Also translate all of the
219 * objects contained in it.
220 */
221 protected PdfDictionary copyDictionary(PdfDictionary in)
222 throws IOException, BadPdfFormatException {
223 PdfDictionary out = new PdfDictionary();
224 PdfObject type = PdfReader.getPdfObjectRelease(in.get(PdfName.TYPE));
225
226 for (Iterator it = in.getKeys().iterator(); it.hasNext();) {
227 PdfName key = (PdfName)it.next();
228 PdfObject value = in.get(key);
229 // System.out.println("Copy " + key);
230 if (type != null && PdfName.PAGE.equals(type)) {
231 if (!key.equals(PdfName.B) && !key.equals(PdfName.PARENT))
232 out.put(key, copyObject(value));
233 }
234 else
235 out.put(key, copyObject(value));
236 }
237 return out;
238 }
239
240 /**
241 * Translate a PRStream to a PdfStream. The data part copies itself.
242 */
243 protected PdfStream copyStream(PRStream in) throws IOException, BadPdfFormatException {
244 PRStream out = new PRStream(in, null);
245
246 for (Iterator it = in.getKeys().iterator(); it.hasNext();) {
247 PdfName key = (PdfName) it.next();
248 PdfObject value = in.get(key);
249 out.put(key, copyObject(value));
250 }
251
252 return out;
253 }
254
255
256 /**
257 * Translate a PRArray to a PdfArray. Also translate all of the objects contained
258 * in it
259 */
260 protected PdfArray copyArray(PdfArray in) throws IOException, BadPdfFormatException {
261 PdfArray out = new PdfArray();
262
263 for (Iterator i = in.listIterator(); i.hasNext();) {
264 PdfObject value = (PdfObject)i.next();
265 out.add(copyObject(value));
266 }
267 return out;
268 }
269
270 /**
271 * Translate a PR-object to a Pdf-object
272 */
273 protected PdfObject copyObject(PdfObject in) throws IOException,BadPdfFormatException {
274 if (in == null)
275 return PdfNull.PDFNULL;
276 switch (in.type) {
277 case PdfObject.DICTIONARY:
278 // System.out.println("Dictionary: " + in.toString());
279 return copyDictionary((PdfDictionary)in);
280 case PdfObject.INDIRECT:
281 return copyIndirect((PRIndirectReference)in);
282 case PdfObject.ARRAY:
283 return copyArray((PdfArray)in);
284 case PdfObject.NUMBER:
285 case PdfObject.NAME:
286 case PdfObject.STRING:
287 case PdfObject.NULL:
288 case PdfObject.BOOLEAN:
289 case 0:
290 return in;
291 case PdfObject.STREAM:
292 return copyStream((PRStream)in);
293 // return in;
294 default:
295 if (in.type < 0) {
296 String lit = ((PdfLiteral)in).toString();
297 if (lit.equals("true") || lit.equals("false")) {
298 return new PdfBoolean(lit);
299 }
300 return new PdfLiteral(lit);
301 }
302 System.out.println("CANNOT COPY type " + in.type);
303 return null;
304 }
305 }
306
307 /**
308 * convenience method. Given an imported page, set our "globals"
309 */
310 protected int setFromIPage(PdfImportedPage iPage) {
311 int pageNum = iPage.getPageNumber();
312 PdfReaderInstance inst = currentPdfReaderInstance = iPage.getPdfReaderInstance();
313 reader = inst.getReader();
314 setFromReader(reader);
315 return pageNum;
316 }
317
318 /**
319 * convenience method. Given a reader, set our "globals"
320 */
321 protected void setFromReader(PdfReader reader) {
322 this.reader = reader;
323 indirects = (HashMap)indirectMap.get(reader);
324 if (indirects == null) {
325 indirects = new HashMap();
326 indirectMap.put(reader,indirects);
327 PdfDictionary catalog = reader.getCatalog();
328 PRIndirectReference ref = null;
329 PdfObject o = catalog.get(PdfName.ACROFORM);
330 if (o == null || o.type() != PdfObject.INDIRECT)
331 return;
332 ref = (PRIndirectReference)o;
333 if (acroForm == null) acroForm = body.getPdfIndirectReference();
334 indirects.put(new RefKey(ref), new IndirectReferences(acroForm));
335 }
336 }
337 /**
338 * Add an imported page to our output
339 * @param iPage an imported page
340 * @throws IOException, BadPdfFormatException
341 */
342 public void addPage(PdfImportedPage iPage) throws IOException, BadPdfFormatException {
343 int pageNum = setFromIPage(iPage);
344
345 PdfDictionary thePage = reader.getPageN(pageNum);
346 PRIndirectReference origRef = reader.getPageOrigRef(pageNum);
347 reader.releasePage(pageNum);
348 RefKey key = new RefKey(origRef);
349 PdfIndirectReference pageRef;
350 IndirectReferences iRef = (IndirectReferences)indirects.get(key);
351 if (iRef != null && !iRef.getCopied()) {
352 pageReferences.add(iRef.getRef());
353 iRef.setCopied();
354 }
355 pageRef = getCurrentPage();
356 if (iRef == null) {
357 iRef = new IndirectReferences(pageRef);
358 indirects.put(key, iRef);
359 }
360 iRef.setCopied();
361 PdfDictionary newPage = copyDictionary(thePage);
362 root.addPage(newPage);
363 ++currentPageNumber;
364 }
365
366 /**
367 * Adds a blank page.
368 * @param rect The page dimension
369 * @param rotation The rotation angle in degrees
370 * @since 2.1.5
371 */
372 public void addPage(Rectangle rect, int rotation) {
373 PdfRectangle mediabox = new PdfRectangle(rect, rotation);
374 PageResources resources = new PageResources();
375 PdfPage page = new PdfPage(mediabox, new HashMap(), resources.getResources(), 0);
376 page.put(PdfName.TABS, getTabs());
377 root.addPage(page);
378 ++currentPageNumber;
379 }
380
381 /**
382 * Copy the acroform for an input document. Note that you can only have one,
383 * we make no effort to merge them.
384 * @param reader The reader of the input file that is being copied
385 * @throws IOException, BadPdfFormatException
386 */
387 public void copyAcroForm(PdfReader reader) throws IOException, BadPdfFormatException {
388 setFromReader(reader);
389
390 PdfDictionary catalog = reader.getCatalog();
391 PRIndirectReference hisRef = null;
392 PdfObject o = catalog.get(PdfName.ACROFORM);
393 if (o != null && o.type() == PdfObject.INDIRECT)
394 hisRef = (PRIndirectReference)o;
395 if (hisRef == null) return; // bugfix by John Englar
396 RefKey key = new RefKey(hisRef);
397 PdfIndirectReference myRef;
398 IndirectReferences iRef = (IndirectReferences)indirects.get(key);
399 if (iRef != null) {
400 acroForm = myRef = iRef.getRef();
401 }
402 else {
403 acroForm = myRef = body.getPdfIndirectReference();
404 iRef = new IndirectReferences(myRef);
405 indirects.put(key, iRef);
406 }
407 if (! iRef.getCopied()) {
408 iRef.setCopied();
409 PdfDictionary theForm = copyDictionary((PdfDictionary)PdfReader.getPdfObject(hisRef));
410 addToBody(theForm, myRef);
411 }
412 }
413
414 /*
415 * the getCatalog method is part of PdfWriter.
416 * we wrap this so that we can extend it
417 */
418 protected PdfDictionary getCatalog(PdfIndirectReference rootObj) {
419 try {
420 PdfDictionary theCat = pdf.getCatalog(rootObj);
421 if (fieldArray == null) {
422 if (acroForm != null) theCat.put(PdfName.ACROFORM, acroForm);
423 }
424 else
425 addFieldResources(theCat);
426 return theCat;
427 }
428 catch (IOException e) {
429 throw new ExceptionConverter(e);
430 }
431 }
432
433 private void addFieldResources(PdfDictionary catalog) throws IOException {
434 if (fieldArray == null)
435 return;
436 PdfDictionary acroForm = new PdfDictionary();
437 catalog.put(PdfName.ACROFORM, acroForm);
438 acroForm.put(PdfName.FIELDS, fieldArray);
439 acroForm.put(PdfName.DA, new PdfString("/Helv 0 Tf 0 g "));
440 if (fieldTemplates.isEmpty())
441 return;
442 PdfDictionary dr = new PdfDictionary();
443 acroForm.put(PdfName.DR, dr);
444 for (Iterator it = fieldTemplates.keySet().iterator(); it.hasNext();) {
445 PdfTemplate template = (PdfTemplate)it.next();
446 PdfFormField.mergeResources(dr, (PdfDictionary)template.getResources());
447 }
448 // if (dr.get(PdfName.ENCODING) == null) dr.put(PdfName.ENCODING, PdfName.WIN_ANSI_ENCODING);
449 PdfDictionary fonts = dr.getAsDict(PdfName.FONT);
450 if (fonts == null) {
451 fonts = new PdfDictionary();
452 dr.put(PdfName.FONT, fonts);
453 }
454 if (!fonts.contains(PdfName.HELV)) {
455 PdfDictionary dic = new PdfDictionary(PdfName.FONT);
456 dic.put(PdfName.BASEFONT, PdfName.HELVETICA);
457 dic.put(PdfName.ENCODING, PdfName.WIN_ANSI_ENCODING);
458 dic.put(PdfName.NAME, PdfName.HELV);
459 dic.put(PdfName.SUBTYPE, PdfName.TYPE1);
460 fonts.put(PdfName.HELV, addToBody(dic).getIndirectReference());
461 }
462 if (!fonts.contains(PdfName.ZADB)) {
463 PdfDictionary dic = new PdfDictionary(PdfName.FONT);
464 dic.put(PdfName.BASEFONT, PdfName.ZAPFDINGBATS);
465 dic.put(PdfName.NAME, PdfName.ZADB);
466 dic.put(PdfName.SUBTYPE, PdfName.TYPE1);
467 fonts.put(PdfName.ZADB, addToBody(dic).getIndirectReference());
468 }
469 }
470
471 /**
472 * Signals that the <CODE>Document</CODE> was closed and that no other
473 * <CODE>Elements</CODE> will be added.
474 * <P>
475 * The pages-tree is built and written to the outputstream.
476 * A Catalog is constructed, as well as an Info-object,
477 * the reference table is composed and everything is written
478 * to the outputstream embedded in a Trailer.
479 */
480
481 public void close() {
482 if (open) {
483 PdfReaderInstance ri = currentPdfReaderInstance;
484 pdf.close();
485 super.close();
486 if (ri != null) {
487 try {
488 ri.getReader().close();
489 ri.getReaderFile().close();
490 }
491 catch (IOException ioe) {
492 // empty on purpose
493 }
494 }
495 }
496 }
497 public PdfIndirectReference add(PdfOutline outline) { return null; }
498 public void addAnnotation(PdfAnnotation annot) { }
499 PdfIndirectReference add(PdfPage page, PdfContents contents) throws PdfException { return null; }
500
501 public void freeReader(PdfReader reader) throws IOException {
502 indirectMap.remove(reader);
503 if (currentPdfReaderInstance != null) {
504 if (currentPdfReaderInstance.getReader() == reader) {
505 try {
506 currentPdfReaderInstance.getReader().close();
507 currentPdfReaderInstance.getReaderFile().close();
508 }
509 catch (IOException ioe) {
510 // empty on purpose
511 }
512 currentPdfReaderInstance = null;
513 }
514 }
515 }
516
517 /**
518 * Create a page stamp. New content and annotations, including new fields, are allowed.
519 * The fields added cannot have parents in another pages. This method modifies the PdfReader instance.<p>
520 * The general usage to stamp something in a page is:
521 * <p>
522 * <pre>
523 * PdfImportedPage page = copy.getImportedPage(reader, 1);
524 * PdfCopy.PageStamp ps = copy.createPageStamp(page);
525 * ps.addAnnotation(PdfAnnotation.createText(copy, new Rectangle(50, 180, 70, 200), "Hello", "No Thanks", true, "Comment"));
526 * PdfContentByte under = ps.getUnderContent();
527 * under.addImage(img);
528 * PdfContentByte over = ps.getOverContent();
529 * over.beginText();
530 * over.setFontAndSize(bf, 18);
531 * over.setTextMatrix(30, 30);
532 * over.showText("total page " + totalPage);
533 * over.endText();
534 * ps.alterContents();
535 * copy.addPage(page);
536 * </pre>
537 * @param iPage an imported page
538 * @return the <CODE>PageStamp</CODE>
539 */
540 public PageStamp createPageStamp(PdfImportedPage iPage) {
541 int pageNum = iPage.getPageNumber();
542 PdfReader reader = iPage.getPdfReaderInstance().getReader();
543 PdfDictionary pageN = reader.getPageN(pageNum);
544 return new PageStamp(reader, pageN, this);
545 }
546
547 public static class PageStamp {
548
549 PdfDictionary pageN;
550 PdfCopy.StampContent under;
551 PdfCopy.StampContent over;
552 PageResources pageResources;
553 PdfReader reader;
554 PdfCopy cstp;
555
556 PageStamp(PdfReader reader, PdfDictionary pageN, PdfCopy cstp) {
557 this.pageN = pageN;
558 this.reader = reader;
559 this.cstp = cstp;
560 }
561
562 public PdfContentByte getUnderContent(){
563 if (under == null) {
564 if (pageResources == null) {
565 pageResources = new PageResources();
566 PdfDictionary resources = pageN.getAsDict(PdfName.RESOURCES);
567 pageResources.setOriginalResources(resources, cstp.namePtr);
568 }
569 under = new PdfCopy.StampContent(cstp, pageResources);
570 }
571 return under;
572 }
573
574 public PdfContentByte getOverContent(){
575 if (over == null) {
576 if (pageResources == null) {
577 pageResources = new PageResources();
578 PdfDictionary resources = pageN.getAsDict(PdfName.RESOURCES);
579 pageResources.setOriginalResources(resources, cstp.namePtr);
580 }
581 over = new PdfCopy.StampContent(cstp, pageResources);
582 }
583 return over;
584 }
585
586 public void alterContents() throws IOException {
587 if (over == null && under == null)
588 return;
589 PdfArray ar = null;
590 PdfObject content = PdfReader.getPdfObject(pageN.get(PdfName.CONTENTS), pageN);
591 if (content == null) {
592 ar = new PdfArray();
593 pageN.put(PdfName.CONTENTS, ar);
594 } else if (content.isArray()) {
595 ar = (PdfArray)content;
596 } else if (content.isStream()) {
597 ar = new PdfArray();
598 ar.add(pageN.get(PdfName.CONTENTS));
599 pageN.put(PdfName.CONTENTS, ar);
600 } else {
601 ar = new PdfArray();
602 pageN.put(PdfName.CONTENTS, ar);
603 }
604 ByteBuffer out = new ByteBuffer();
605 if (under != null) {
606 out.append(PdfContents.SAVESTATE);
607 applyRotation(pageN, out);
608 out.append(under.getInternalBuffer());
609 out.append(PdfContents.RESTORESTATE);
610 }
611 if (over != null)
612 out.append(PdfContents.SAVESTATE);
613 PdfStream stream = new PdfStream(out.toByteArray());
614 stream.flateCompress(cstp.getCompressionLevel());
615 PdfIndirectReference ref1 = cstp.addToBody(stream).getIndirectReference();
616 ar.addFirst(ref1);
617 out.reset();
618 if (over != null) {
619 out.append(' ');
620 out.append(PdfContents.RESTORESTATE);
621 out.append(PdfContents.SAVESTATE);
622 applyRotation(pageN, out);
623 out.append(over.getInternalBuffer());
624 out.append(PdfContents.RESTORESTATE);
625 stream = new PdfStream(out.toByteArray());
626 stream.flateCompress(cstp.getCompressionLevel());
627 ar.add(cstp.addToBody(stream).getIndirectReference());
628 }
629 pageN.put(PdfName.RESOURCES, pageResources.getResources());
630 }
631
632 void applyRotation(PdfDictionary pageN, ByteBuffer out) {
633 if (!cstp.rotateContents)
634 return;
635 Rectangle page = reader.getPageSizeWithRotation(pageN);
636 int rotation = page.getRotation();
637 switch (rotation) {
638 case 90:
639 out.append(PdfContents.ROTATE90);
640 out.append(page.getTop());
641 out.append(' ').append('0').append(PdfContents.ROTATEFINAL);
642 break;
643 case 180:
644 out.append(PdfContents.ROTATE180);
645 out.append(page.getRight());
646 out.append(' ');
647 out.append(page.getTop());
648 out.append(PdfContents.ROTATEFINAL);
649 break;
650 case 270:
651 out.append(PdfContents.ROTATE270);
652 out.append('0').append(' ');
653 out.append(page.getRight());
654 out.append(PdfContents.ROTATEFINAL);
655 break;
656 }
657 }
658
659 private void addDocumentField(PdfIndirectReference ref) {
660 if (cstp.fieldArray == null)
661 cstp.fieldArray = new PdfArray();
662 cstp.fieldArray.add(ref);
663 }
664
665 private void expandFields(PdfFormField field, ArrayList allAnnots) {
666 allAnnots.add(field);
667 ArrayList kids = field.getKids();
668 if (kids != null) {
669 for (int k = 0; k < kids.size(); ++k)
670 expandFields((PdfFormField)kids.get(k), allAnnots);
671 }
672 }
673
674 public void addAnnotation(PdfAnnotation annot) {
675 try {
676 ArrayList allAnnots = new ArrayList();
677 if (annot.isForm()) {
678 PdfFormField field = (PdfFormField)annot;
679 if (field.getParent() != null)
680 return;
681 expandFields(field, allAnnots);
682 if (cstp.fieldTemplates == null)
683 cstp.fieldTemplates = new HashMap();
684 }
685 else
686 allAnnots.add(annot);
687 for (int k = 0; k < allAnnots.size(); ++k) {
688 annot = (PdfAnnotation)allAnnots.get(k);
689 if (annot.isForm()) {
690 if (!annot.isUsed()) {
691 HashMap templates = annot.getTemplates();
692 if (templates != null)
693 cstp.fieldTemplates.putAll(templates);
694 }
695 PdfFormField field = (PdfFormField)annot;
696 if (field.getParent() == null)
697 addDocumentField(field.getIndirectReference());
698 }
699 if (annot.isAnnotation()) {
700 PdfObject pdfobj = PdfReader.getPdfObject(pageN.get(PdfName.ANNOTS), pageN);
701 PdfArray annots = null;
702 if (pdfobj == null || !pdfobj.isArray()) {
703 annots = new PdfArray();
704 pageN.put(PdfName.ANNOTS, annots);
705 }
706 else
707 annots = (PdfArray)pdfobj;
708 annots.add(annot.getIndirectReference());
709 if (!annot.isUsed()) {
710 PdfRectangle rect = (PdfRectangle)annot.get(PdfName.RECT);
711 if (rect != null && (rect.left() != 0 || rect.right() != 0 || rect.top() != 0 || rect.bottom() != 0)) {
712 int rotation = reader.getPageRotation(pageN);
713 Rectangle pageSize = reader.getPageSizeWithRotation(pageN);
714 switch (rotation) {
715 case 90:
716 annot.put(PdfName.RECT, new PdfRectangle(
717 pageSize.getTop() - rect.bottom(),
718 rect.left(),
719 pageSize.getTop() - rect.top(),
720 rect.right()));
721 break;
722 case 180:
723 annot.put(PdfName.RECT, new PdfRectangle(
724 pageSize.getRight() - rect.left(),
725 pageSize.getTop() - rect.bottom(),
726 pageSize.getRight() - rect.right(),
727 pageSize.getTop() - rect.top()));
728 break;
729 case 270:
730 annot.put(PdfName.RECT, new PdfRectangle(
731 rect.bottom(),
732 pageSize.getRight() - rect.left(),
733 rect.top(),
734 pageSize.getRight() - rect.right()));
735 break;
736 }
737 }
738 }
739 }
740 if (!annot.isUsed()) {
741 annot.setUsed();
742 cstp.addToBody(annot, annot.getIndirectReference());
743 }
744 }
745 }
746 catch (IOException e) {
747 throw new ExceptionConverter(e);
748 }
749 }
750 }
751
752 public static class StampContent extends PdfContentByte {
753 PageResources pageResources;
754
755 /** Creates a new instance of StampContent */
756 StampContent(PdfWriter writer, PageResources pageResources) {
757 super(writer);
758 this.pageResources = pageResources;
759 }
760
761 /**
762 * Gets a duplicate of this <CODE>PdfContentByte</CODE>. All
763 * the members are copied by reference but the buffer stays different.
764 *
765 * @return a copy of this <CODE>PdfContentByte</CODE>
766 */
767 public PdfContentByte getDuplicate() {
768 return new PdfCopy.StampContent(writer, pageResources);
769 }
770
771 PageResources getPageResources() {
772 return pageResources;
773 }
774 }
775 }