001/* 002 * $Id: PdfStamperImp.java 4888 2011-05-29 14:57:01Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.pdf; 045 046import java.io.ByteArrayOutputStream; 047import java.io.IOException; 048import java.io.OutputStream; 049import java.util.ArrayList; 050import java.util.HashMap; 051import java.util.HashSet; 052import java.util.Iterator; 053import java.util.Map; 054 055import org.xml.sax.SAXException; 056 057import com.itextpdf.text.Document; 058import com.itextpdf.text.DocumentException; 059import com.itextpdf.text.ExceptionConverter; 060import com.itextpdf.text.Image; 061import com.itextpdf.text.Rectangle; 062import com.itextpdf.text.error_messages.MessageLocalization; 063import com.itextpdf.text.exceptions.BadPasswordException; 064import com.itextpdf.text.pdf.AcroFields.Item; 065import com.itextpdf.text.pdf.collection.PdfCollection; 066import com.itextpdf.text.pdf.interfaces.PdfViewerPreferences; 067import com.itextpdf.text.pdf.internal.PdfViewerPreferencesImp; 068import com.itextpdf.text.xml.xmp.XmpReader; 069import com.itextpdf.text.xml.xmp.XmpWriter; 070 071class PdfStamperImp extends PdfWriter { 072 HashMap<PdfReader, IntHashtable> readers2intrefs = new HashMap<PdfReader, IntHashtable>(); 073 HashMap<PdfReader, RandomAccessFileOrArray> readers2file = new HashMap<PdfReader, RandomAccessFileOrArray>(); 074 RandomAccessFileOrArray file; 075 PdfReader reader; 076 IntHashtable myXref = new IntHashtable(); 077 /** Integer(page number) -> PageStamp */ 078 HashMap<PdfDictionary, PageStamp> pagesToContent = new HashMap<PdfDictionary, PageStamp>(); 079 boolean closed = false; 080 /** Holds value of property rotateContents. */ 081 private boolean rotateContents = true; 082 protected AcroFields acroFields; 083 protected boolean flat = false; 084 protected boolean flatFreeText = false; 085 protected int namePtr[] = {0}; 086 protected HashSet<String> partialFlattening = new HashSet<String>(); 087 protected boolean useVp = false; 088 protected PdfViewerPreferencesImp viewerPreferences = new PdfViewerPreferencesImp(); 089 protected HashSet<PdfTemplate> fieldTemplates = new HashSet<PdfTemplate>(); 090 protected boolean fieldsAdded = false; 091 protected int sigFlags = 0; 092 protected boolean append; 093 protected IntHashtable marked; 094 protected int initialXrefSize; 095 protected PdfAction openAction; 096 097 /** Creates new PdfStamperImp. 098 * @param reader the read PDF 099 * @param os the output destination 100 * @param pdfVersion the new pdf version or '\0' to keep the same version as the original 101 * document 102 * @param append 103 * @throws DocumentException on error 104 * @throws IOException 105 */ 106 PdfStamperImp(PdfReader reader, OutputStream os, char pdfVersion, boolean append) throws DocumentException, IOException { 107 super(new PdfDocument(), os); 108 if (!reader.isOpenedWithFullPermissions()) 109 throw new BadPasswordException(MessageLocalization.getComposedMessage("pdfreader.not.opened.with.owner.password")); 110 if (reader.isTampered()) 111 throw new DocumentException(MessageLocalization.getComposedMessage("the.original.document.was.reused.read.it.again.from.file")); 112 reader.setTampered(true); 113 this.reader = reader; 114 file = reader.getSafeFile(); 115 this.append = append; 116 if (append) { 117 if (reader.isRebuilt()) 118 throw new DocumentException(MessageLocalization.getComposedMessage("append.mode.requires.a.document.without.errors.even.if.recovery.was.possible")); 119 if (reader.isEncrypted()) 120 crypto = new PdfEncryption(reader.getDecrypt()); 121 pdf_version.setAppendmode(true); 122 file.reOpen(); 123 byte buf[] = new byte[8192]; 124 int n; 125 while ((n = file.read(buf)) > 0) 126 this.os.write(buf, 0, n); 127 file.close(); 128 prevxref = reader.getLastXref(); 129 reader.setAppendable(true); 130 } 131 else { 132 if (pdfVersion == 0) 133 super.setPdfVersion(reader.getPdfVersion()); 134 else 135 super.setPdfVersion(pdfVersion); 136 } 137 super.open(); 138 pdf.addWriter(this); 139 if (append) { 140 body.setRefnum(reader.getXrefSize()); 141 marked = new IntHashtable(); 142 if (reader.isNewXrefType()) 143 fullCompression = true; 144 if (reader.isHybridXref()) 145 fullCompression = false; 146 } 147 initialXrefSize = reader.getXrefSize(); 148 } 149 150 void close(Map<String, String> moreInfo) throws IOException { 151 if (closed) 152 return; 153 if (useVp) { 154 reader.setViewerPreferences(viewerPreferences); 155 markUsed(reader.getTrailer().get(PdfName.ROOT)); 156 } 157 if (flat) 158 flatFields(); 159 if (flatFreeText) 160 flatFreeTextFields(); 161 addFieldResources(); 162 PdfDictionary catalog = reader.getCatalog(); 163 PdfDictionary pages = (PdfDictionary)PdfReader.getPdfObject(catalog.get(PdfName.PAGES)); 164 pages.put(PdfName.ITXT, new PdfString(Document.getRelease())); 165 markUsed(pages); 166 PdfDictionary acroForm = (PdfDictionary)PdfReader.getPdfObject(catalog.get(PdfName.ACROFORM), reader.getCatalog()); 167 if (acroFields != null && acroFields.getXfa().isChanged()) { 168 markUsed(acroForm); 169 if (!flat) 170 acroFields.getXfa().setXfa(this); 171 } 172 if (sigFlags != 0) { 173 if (acroForm != null) { 174 acroForm.put(PdfName.SIGFLAGS, new PdfNumber(sigFlags)); 175 markUsed(acroForm); 176 markUsed(catalog); 177 } 178 } 179 closed = true; 180 addSharedObjectsToBody(); 181 setOutlines(); 182 setJavaScript(); 183 addFileAttachments(); 184 if (openAction != null) { 185 catalog.put(PdfName.OPENACTION, openAction); 186 } 187 if (pdf.pageLabels != null) 188 catalog.put(PdfName.PAGELABELS, pdf.pageLabels.getDictionary(this)); 189 // OCG 190 if (!documentOCG.isEmpty()) { 191 fillOCProperties(false); 192 PdfDictionary ocdict = catalog.getAsDict(PdfName.OCPROPERTIES); 193 if (ocdict == null) { 194 reader.getCatalog().put(PdfName.OCPROPERTIES, OCProperties); 195 } 196 else { 197 ocdict.put(PdfName.OCGS, OCProperties.get(PdfName.OCGS)); 198 PdfDictionary ddict = ocdict.getAsDict(PdfName.D); 199 if (ddict == null) { 200 ddict = new PdfDictionary(); 201 ocdict.put(PdfName.D, ddict); 202 } 203 ddict.put(PdfName.ORDER, OCProperties.getAsDict(PdfName.D).get(PdfName.ORDER)); 204 ddict.put(PdfName.RBGROUPS, OCProperties.getAsDict(PdfName.D).get(PdfName.RBGROUPS)); 205 ddict.put(PdfName.OFF, OCProperties.getAsDict(PdfName.D).get(PdfName.OFF)); 206 ddict.put(PdfName.AS, OCProperties.getAsDict(PdfName.D).get(PdfName.AS)); 207 } 208 } 209 // metadata 210 int skipInfo = -1; 211 PdfObject oInfo = reader.getTrailer().get(PdfName.INFO); 212 PRIndirectReference iInfo = null; 213 PdfDictionary oldInfo = null; 214 if (oInfo instanceof PRIndirectReference) 215 iInfo = (PRIndirectReference)oInfo; 216 if (iInfo != null) 217 oldInfo = (PdfDictionary)PdfReader.getPdfObject(iInfo); 218 else if (oInfo instanceof PdfDictionary) 219 oldInfo = (PdfDictionary)oInfo; 220 String producer = null; 221 if (iInfo != null) 222 skipInfo = iInfo.getNumber(); 223 if (oldInfo != null && oldInfo.get(PdfName.PRODUCER) != null) 224 producer = oldInfo.getAsString(PdfName.PRODUCER).toUnicodeString(); 225 if (producer == null) { 226 producer = Document.getVersion(); 227 } 228 else if (producer.indexOf(Document.getProduct()) == -1) { 229 StringBuffer buf = new StringBuffer(producer); 230 buf.append("; modified using "); 231 buf.append(Document.getVersion()); 232 producer = buf.toString(); 233 } 234 PdfIndirectReference info = null; 235 PdfDictionary newInfo = new PdfDictionary(); 236 if (oldInfo != null) { 237 for (Object element : oldInfo.getKeys()) { 238 PdfName key = (PdfName)element; 239 PdfObject value = PdfReader.getPdfObject(oldInfo.get(key)); 240 newInfo.put(key, value); 241 } 242 } 243 if (moreInfo != null) { 244 for (Map.Entry<String, String> entry: moreInfo.entrySet()) { 245 String key = entry.getKey(); 246 PdfName keyName = new PdfName(key); 247 String value = entry.getValue(); 248 if (value == null) 249 newInfo.remove(keyName); 250 else 251 newInfo.put(keyName, new PdfString(value, PdfObject.TEXT_UNICODE)); 252 } 253 } 254 PdfDate date = new PdfDate(); 255 newInfo.put(PdfName.MODDATE, date); 256 newInfo.put(PdfName.PRODUCER, new PdfString(producer, PdfObject.TEXT_UNICODE)); 257 if (append) { 258 if (iInfo == null) 259 info = addToBody(newInfo, false).getIndirectReference(); 260 else 261 info = addToBody(newInfo, iInfo.getNumber(), false).getIndirectReference(); 262 } 263 else { 264 info = addToBody(newInfo, false).getIndirectReference(); 265 } 266 // XMP 267 byte[] altMetadata = null; 268 PdfObject xmpo = PdfReader.getPdfObject(catalog.get(PdfName.METADATA)); 269 if (xmpo != null && xmpo.isStream()) { 270 altMetadata = PdfReader.getStreamBytesRaw((PRStream)xmpo); 271 PdfReader.killIndirect(catalog.get(PdfName.METADATA)); 272 } 273 if (xmpMetadata != null) { 274 altMetadata = xmpMetadata; 275 } 276 if (altMetadata != null) { 277 PdfStream xmp; 278 try { 279 XmpReader xmpr; 280 if (moreInfo == null) { 281 xmpr = new XmpReader(altMetadata); 282 if (!(xmpr.replaceNode("http://ns.adobe.com/pdf/1.3/", "Producer", producer) 283 || xmpr.replaceDescriptionAttribute("http://ns.adobe.com/pdf/1.3/", "Producer", producer))) 284 xmpr.add("rdf:Description", "http://ns.adobe.com/pdf/1.3/", "pdf:Producer", producer); 285 if (!(xmpr.replaceNode("http://ns.adobe.com/xap/1.0/", "ModifyDate", date.getW3CDate()) 286 || xmpr.replaceDescriptionAttribute("http://ns.adobe.com/xap/1.0/", "ModifyDate", date.getW3CDate()))) 287 xmpr.add("rdf:Description", "http://ns.adobe.com/xap/1.0/", "xmp:ModifyDate", date.getW3CDate()); 288 if (!(xmpr.replaceNode("http://ns.adobe.com/xap/1.0/", "MetadataDate", date.getW3CDate()) 289 || xmpr.replaceDescriptionAttribute("http://ns.adobe.com/xap/1.0/", "MetadataDate", date.getW3CDate()))) { 290 } 291 } 292 else { 293 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 294 try { 295 XmpWriter xmpw = new XmpWriter(baos, newInfo, getPDFXConformance()); 296 xmpw.close(); 297 } 298 catch (IOException ioe) { 299 ioe.printStackTrace(); 300 } 301 xmpr = new XmpReader(baos.toByteArray()); 302 } 303 xmp = new PdfStream(xmpr.serializeDoc()); 304 } 305 catch(SAXException e) { 306 xmp = new PdfStream(altMetadata); 307 } 308 catch(IOException e) { 309 xmp = new PdfStream(altMetadata); 310 } 311 xmp.put(PdfName.TYPE, PdfName.METADATA); 312 xmp.put(PdfName.SUBTYPE, PdfName.XML); 313 if (crypto != null && !crypto.isMetadataEncrypted()) { 314 PdfArray ar = new PdfArray(); 315 ar.add(PdfName.CRYPT); 316 xmp.put(PdfName.FILTER, ar); 317 } 318 if (append && xmpo != null) { 319 body.add(xmp, xmpo.getIndRef()); 320 } 321 else { 322 catalog.put(PdfName.METADATA, body.add(xmp).getIndirectReference()); 323 markUsed(catalog); 324 } 325 } 326 try { 327 file.reOpen(); 328 alterContents(); 329 int rootN = ((PRIndirectReference)reader.trailer.get(PdfName.ROOT)).getNumber(); 330 if (append) { 331 int keys[] = marked.getKeys(); 332 for (int k = 0; k < keys.length; ++k) { 333 int j = keys[k]; 334 PdfObject obj = reader.getPdfObjectRelease(j); 335 if (obj != null && skipInfo != j && j < initialXrefSize) { 336 addToBody(obj, j, j != rootN); 337 } 338 } 339 for (int k = initialXrefSize; k < reader.getXrefSize(); ++k) { 340 PdfObject obj = reader.getPdfObject(k); 341 if (obj != null) { 342 addToBody(obj, getNewObjectNumber(reader, k, 0)); 343 } 344 } 345 } 346 else { 347 for (int k = 1; k < reader.getXrefSize(); ++k) { 348 PdfObject obj = reader.getPdfObjectRelease(k); 349 if (obj != null && skipInfo != k) { 350 addToBody(obj, getNewObjectNumber(reader, k, 0), k != rootN); 351 } 352 } 353 } 354 } 355 finally { 356 try { 357 file.close(); 358 } 359 catch (Exception e) { 360 // empty on purpose 361 } 362 } 363 PdfIndirectReference encryption = null; 364 PdfObject fileID = null; 365 if (crypto != null) { 366 if (append) { 367 encryption = reader.getCryptoRef(); 368 } 369 else { 370 PdfIndirectObject encryptionObject = addToBody(crypto.getEncryptionDictionary(), false); 371 encryption = encryptionObject.getIndirectReference(); 372 } 373 fileID = crypto.getFileID(); 374 } 375 else 376 fileID = PdfEncryption.createInfoId(PdfEncryption.createDocumentId()); 377 PRIndirectReference iRoot = (PRIndirectReference)reader.trailer.get(PdfName.ROOT); 378 PdfIndirectReference root = new PdfIndirectReference(0, getNewObjectNumber(reader, iRoot.getNumber(), 0)); 379 // write the cross-reference table of the body 380 body.writeCrossReferenceTable(os, root, info, encryption, fileID, prevxref); 381 if (fullCompression) { 382 os.write(getISOBytes("startxref\n")); 383 os.write(getISOBytes(String.valueOf(body.offset()))); 384 os.write(getISOBytes("\n%%EOF\n")); 385 } 386 else { 387 PdfTrailer trailer = new PdfTrailer(body.size(), 388 body.offset(), 389 root, 390 info, 391 encryption, 392 fileID, prevxref); 393 trailer.toPdf(this, os); 394 } 395 os.flush(); 396 if (isCloseStream()) 397 os.close(); 398 reader.close(); 399 } 400 401 void applyRotation(PdfDictionary pageN, ByteBuffer out) { 402 if (!rotateContents) 403 return; 404 Rectangle page = reader.getPageSizeWithRotation(pageN); 405 int rotation = page.getRotation(); 406 switch (rotation) { 407 case 90: 408 out.append(PdfContents.ROTATE90); 409 out.append(page.getTop()); 410 out.append(' ').append('0').append(PdfContents.ROTATEFINAL); 411 break; 412 case 180: 413 out.append(PdfContents.ROTATE180); 414 out.append(page.getRight()); 415 out.append(' '); 416 out.append(page.getTop()); 417 out.append(PdfContents.ROTATEFINAL); 418 break; 419 case 270: 420 out.append(PdfContents.ROTATE270); 421 out.append('0').append(' '); 422 out.append(page.getRight()); 423 out.append(PdfContents.ROTATEFINAL); 424 break; 425 } 426 } 427 428 void alterContents() throws IOException { 429 for (Object element : pagesToContent.values()) { 430 PageStamp ps = (PageStamp)element; 431 PdfDictionary pageN = ps.pageN; 432 markUsed(pageN); 433 PdfArray ar = null; 434 PdfObject content = PdfReader.getPdfObject(pageN.get(PdfName.CONTENTS), pageN); 435 if (content == null) { 436 ar = new PdfArray(); 437 pageN.put(PdfName.CONTENTS, ar); 438 } 439 else if (content.isArray()) { 440 ar = (PdfArray)content; 441 markUsed(ar); 442 } 443 else if (content.isStream()) { 444 ar = new PdfArray(); 445 ar.add(pageN.get(PdfName.CONTENTS)); 446 pageN.put(PdfName.CONTENTS, ar); 447 } 448 else { 449 ar = new PdfArray(); 450 pageN.put(PdfName.CONTENTS, ar); 451 } 452 ByteBuffer out = new ByteBuffer(); 453 if (ps.under != null) { 454 out.append(PdfContents.SAVESTATE); 455 applyRotation(pageN, out); 456 out.append(ps.under.getInternalBuffer()); 457 out.append(PdfContents.RESTORESTATE); 458 } 459 if (ps.over != null) 460 out.append(PdfContents.SAVESTATE); 461 PdfStream stream = new PdfStream(out.toByteArray()); 462 stream.flateCompress(compressionLevel); 463 ar.addFirst(addToBody(stream).getIndirectReference()); 464 out.reset(); 465 if (ps.over != null) { 466 out.append(' '); 467 out.append(PdfContents.RESTORESTATE); 468 ByteBuffer buf = ps.over.getInternalBuffer(); 469 out.append(buf.getBuffer(), 0, ps.replacePoint); 470 out.append(PdfContents.SAVESTATE); 471 applyRotation(pageN, out); 472 out.append(buf.getBuffer(), ps.replacePoint, buf.size() - ps.replacePoint); 473 out.append(PdfContents.RESTORESTATE); 474 stream = new PdfStream(out.toByteArray()); 475 stream.flateCompress(compressionLevel); 476 ar.add(addToBody(stream).getIndirectReference()); 477 } 478 alterResources(ps); 479 } 480 } 481 482 void alterResources(PageStamp ps) { 483 ps.pageN.put(PdfName.RESOURCES, ps.pageResources.getResources()); 484 } 485 486 @Override 487 protected int getNewObjectNumber(PdfReader reader, int number, int generation) { 488 IntHashtable ref = readers2intrefs.get(reader); 489 if (ref != null) { 490 int n = ref.get(number); 491 if (n == 0) { 492 n = getIndirectReferenceNumber(); 493 ref.put(number, n); 494 } 495 return n; 496 } 497 if (currentPdfReaderInstance == null) { 498 if (append && number < initialXrefSize) 499 return number; 500 int n = myXref.get(number); 501 if (n == 0) { 502 n = getIndirectReferenceNumber(); 503 myXref.put(number, n); 504 } 505 return n; 506 } 507 else 508 return currentPdfReaderInstance.getNewObjectNumber(number, generation); 509 } 510 511 @Override 512 RandomAccessFileOrArray getReaderFile(PdfReader reader) { 513 if (readers2intrefs.containsKey(reader)) { 514 RandomAccessFileOrArray raf = readers2file.get(reader); 515 if (raf != null) 516 return raf; 517 return reader.getSafeFile(); 518 } 519 if (currentPdfReaderInstance == null) 520 return file; 521 else 522 return currentPdfReaderInstance.getReaderFile(); 523 } 524 525 /** 526 * @param reader 527 * @param openFile 528 * @throws IOException 529 */ 530 public void registerReader(PdfReader reader, boolean openFile) throws IOException { 531 if (readers2intrefs.containsKey(reader)) 532 return; 533 readers2intrefs.put(reader, new IntHashtable()); 534 if (openFile) { 535 RandomAccessFileOrArray raf = reader.getSafeFile(); 536 readers2file.put(reader, raf); 537 raf.reOpen(); 538 } 539 } 540 541 /** 542 * @param reader 543 */ 544 public void unRegisterReader(PdfReader reader) { 545 if (!readers2intrefs.containsKey(reader)) 546 return; 547 readers2intrefs.remove(reader); 548 RandomAccessFileOrArray raf = readers2file.get(reader); 549 if (raf == null) 550 return; 551 readers2file.remove(reader); 552 try{raf.close();}catch(Exception e){} 553 } 554 555 static void findAllObjects(PdfReader reader, PdfObject obj, IntHashtable hits) { 556 if (obj == null) 557 return; 558 switch (obj.type()) { 559 case PdfObject.INDIRECT: 560 PRIndirectReference iref = (PRIndirectReference)obj; 561 if (reader != iref.getReader()) 562 return; 563 if (hits.containsKey(iref.getNumber())) 564 return; 565 hits.put(iref.getNumber(), 1); 566 findAllObjects(reader, PdfReader.getPdfObject(obj), hits); 567 return; 568 case PdfObject.ARRAY: 569 PdfArray a = (PdfArray)obj; 570 for (int k = 0; k < a.size(); ++k) { 571 findAllObjects(reader, a.getPdfObject(k), hits); 572 } 573 return; 574 case PdfObject.DICTIONARY: 575 case PdfObject.STREAM: 576 PdfDictionary dic = (PdfDictionary)obj; 577 for (Object element : dic.getKeys()) { 578 PdfName name = (PdfName)element; 579 findAllObjects(reader, dic.get(name), hits); 580 } 581 return; 582 } 583 } 584 585 /** 586 * @param fdf 587 * @throws IOException 588 */ 589 public void addComments(FdfReader fdf) throws IOException{ 590 if (readers2intrefs.containsKey(fdf)) 591 return; 592 PdfDictionary catalog = fdf.getCatalog(); 593 catalog = catalog.getAsDict(PdfName.FDF); 594 if (catalog == null) 595 return; 596 PdfArray annots = catalog.getAsArray(PdfName.ANNOTS); 597 if (annots == null || annots.size() == 0) 598 return; 599 registerReader(fdf, false); 600 IntHashtable hits = new IntHashtable(); 601 HashMap<String, PdfObject> irt = new HashMap<String, PdfObject>(); 602 ArrayList<PdfObject> an = new ArrayList<PdfObject>(); 603 for (int k = 0; k < annots.size(); ++k) { 604 PdfObject obj = annots.getPdfObject(k); 605 PdfDictionary annot = (PdfDictionary)PdfReader.getPdfObject(obj); 606 PdfNumber page = annot.getAsNumber(PdfName.PAGE); 607 if (page == null || page.intValue() >= reader.getNumberOfPages()) 608 continue; 609 findAllObjects(fdf, obj, hits); 610 an.add(obj); 611 if (obj.type() == PdfObject.INDIRECT) { 612 PdfObject nm = PdfReader.getPdfObject(annot.get(PdfName.NM)); 613 if (nm != null && nm.type() == PdfObject.STRING) 614 irt.put(nm.toString(), obj); 615 } 616 } 617 int arhits[] = hits.getKeys(); 618 for (int k = 0; k < arhits.length; ++k) { 619 int n = arhits[k]; 620 PdfObject obj = fdf.getPdfObject(n); 621 if (obj.type() == PdfObject.DICTIONARY) { 622 PdfObject str = PdfReader.getPdfObject(((PdfDictionary)obj).get(PdfName.IRT)); 623 if (str != null && str.type() == PdfObject.STRING) { 624 PdfObject i = irt.get(str.toString()); 625 if (i != null) { 626 PdfDictionary dic2 = new PdfDictionary(); 627 dic2.merge((PdfDictionary)obj); 628 dic2.put(PdfName.IRT, i); 629 obj = dic2; 630 } 631 } 632 } 633 addToBody(obj, getNewObjectNumber(fdf, n, 0)); 634 } 635 for (int k = 0; k < an.size(); ++k) { 636 PdfObject obj = an.get(k); 637 PdfDictionary annot = (PdfDictionary)PdfReader.getPdfObject(obj); 638 PdfNumber page = annot.getAsNumber(PdfName.PAGE); 639 PdfDictionary dic = reader.getPageN(page.intValue() + 1); 640 PdfArray annotsp = (PdfArray)PdfReader.getPdfObject(dic.get(PdfName.ANNOTS), dic); 641 if (annotsp == null) { 642 annotsp = new PdfArray(); 643 dic.put(PdfName.ANNOTS, annotsp); 644 markUsed(dic); 645 } 646 markUsed(annotsp); 647 annotsp.add(obj); 648 } 649 } 650 651 PageStamp getPageStamp(int pageNum) { 652 PdfDictionary pageN = reader.getPageN(pageNum); 653 PageStamp ps = pagesToContent.get(pageN); 654 if (ps == null) { 655 ps = new PageStamp(this, reader, pageN); 656 pagesToContent.put(pageN, ps); 657 } 658 return ps; 659 } 660 661 PdfContentByte getUnderContent(int pageNum) { 662 if (pageNum < 1 || pageNum > reader.getNumberOfPages()) 663 return null; 664 PageStamp ps = getPageStamp(pageNum); 665 if (ps.under == null) 666 ps.under = new StampContent(this, ps); 667 return ps.under; 668 } 669 670 PdfContentByte getOverContent(int pageNum) { 671 if (pageNum < 1 || pageNum > reader.getNumberOfPages()) 672 return null; 673 PageStamp ps = getPageStamp(pageNum); 674 if (ps.over == null) 675 ps.over = new StampContent(this, ps); 676 return ps.over; 677 } 678 679 void correctAcroFieldPages(int page) { 680 if (acroFields == null) 681 return; 682 if (page > reader.getNumberOfPages()) 683 return; 684 Map<String, Item> fields = acroFields.getFields(); 685 for (AcroFields.Item item: fields.values()) { 686 for (int k = 0; k < item.size(); ++k) { 687 int p = item.getPage(k).intValue(); 688 if (p >= page) 689 item.forcePage(k, p + 1); 690 } 691 } 692 } 693 694 private static void moveRectangle(PdfDictionary dic2, PdfReader r, int pageImported, PdfName key, String name) { 695 Rectangle m = r.getBoxSize(pageImported, name); 696 if (m == null) 697 dic2.remove(key); 698 else 699 dic2.put(key, new PdfRectangle(m)); 700 } 701 702 void replacePage(PdfReader r, int pageImported, int pageReplaced) { 703 PdfDictionary pageN = reader.getPageN(pageReplaced); 704 if (pagesToContent.containsKey(pageN)) 705 throw new IllegalStateException(MessageLocalization.getComposedMessage("this.page.cannot.be.replaced.new.content.was.already.added")); 706 PdfImportedPage p = getImportedPage(r, pageImported); 707 PdfDictionary dic2 = reader.getPageNRelease(pageReplaced); 708 dic2.remove(PdfName.RESOURCES); 709 dic2.remove(PdfName.CONTENTS); 710 moveRectangle(dic2, r, pageImported, PdfName.MEDIABOX, "media"); 711 moveRectangle(dic2, r, pageImported, PdfName.CROPBOX, "crop"); 712 moveRectangle(dic2, r, pageImported, PdfName.TRIMBOX, "trim"); 713 moveRectangle(dic2, r, pageImported, PdfName.ARTBOX, "art"); 714 moveRectangle(dic2, r, pageImported, PdfName.BLEEDBOX, "bleed"); 715 dic2.put(PdfName.ROTATE, new PdfNumber(r.getPageRotation(pageImported))); 716 PdfContentByte cb = getOverContent(pageReplaced); 717 cb.addTemplate(p, 0, 0); 718 PageStamp ps = pagesToContent.get(pageN); 719 ps.replacePoint = ps.over.getInternalBuffer().size(); 720 } 721 722 void insertPage(int pageNumber, Rectangle mediabox) { 723 Rectangle media = new Rectangle(mediabox); 724 int rotation = media.getRotation() % 360; 725 PdfDictionary page = new PdfDictionary(PdfName.PAGE); 726 PdfDictionary resources = new PdfDictionary(); 727 PdfArray procset = new PdfArray(); 728 procset.add(PdfName.PDF); 729 procset.add(PdfName.TEXT); 730 procset.add(PdfName.IMAGEB); 731 procset.add(PdfName.IMAGEC); 732 procset.add(PdfName.IMAGEI); 733 resources.put(PdfName.PROCSET, procset); 734 page.put(PdfName.RESOURCES, resources); 735 page.put(PdfName.ROTATE, new PdfNumber(rotation)); 736 page.put(PdfName.MEDIABOX, new PdfRectangle(media, rotation)); 737 PRIndirectReference pref = reader.addPdfObject(page); 738 PdfDictionary parent; 739 PRIndirectReference parentRef; 740 if (pageNumber > reader.getNumberOfPages()) { 741 PdfDictionary lastPage = reader.getPageNRelease(reader.getNumberOfPages()); 742 parentRef = (PRIndirectReference)lastPage.get(PdfName.PARENT); 743 parentRef = new PRIndirectReference(reader, parentRef.getNumber()); 744 parent = (PdfDictionary)PdfReader.getPdfObject(parentRef); 745 PdfArray kids = (PdfArray)PdfReader.getPdfObject(parent.get(PdfName.KIDS), parent); 746 kids.add(pref); 747 markUsed(kids); 748 reader.pageRefs.insertPage(pageNumber, pref); 749 } 750 else { 751 if (pageNumber < 1) 752 pageNumber = 1; 753 PdfDictionary firstPage = reader.getPageN(pageNumber); 754 PRIndirectReference firstPageRef = reader.getPageOrigRef(pageNumber); 755 reader.releasePage(pageNumber); 756 parentRef = (PRIndirectReference)firstPage.get(PdfName.PARENT); 757 parentRef = new PRIndirectReference(reader, parentRef.getNumber()); 758 parent = (PdfDictionary)PdfReader.getPdfObject(parentRef); 759 PdfArray kids = (PdfArray)PdfReader.getPdfObject(parent.get(PdfName.KIDS), parent); 760 int len = kids.size(); 761 int num = firstPageRef.getNumber(); 762 for (int k = 0; k < len; ++k) { 763 PRIndirectReference cur = (PRIndirectReference)kids.getPdfObject(k); 764 if (num == cur.getNumber()) { 765 kids.add(k, pref); 766 break; 767 } 768 } 769 if (len == kids.size()) 770 throw new RuntimeException(MessageLocalization.getComposedMessage("internal.inconsistence")); 771 markUsed(kids); 772 reader.pageRefs.insertPage(pageNumber, pref); 773 correctAcroFieldPages(pageNumber); 774 } 775 page.put(PdfName.PARENT, parentRef); 776 while (parent != null) { 777 markUsed(parent); 778 PdfNumber count = (PdfNumber)PdfReader.getPdfObjectRelease(parent.get(PdfName.COUNT)); 779 parent.put(PdfName.COUNT, new PdfNumber(count.intValue() + 1)); 780 parent = parent.getAsDict(PdfName.PARENT); 781 } 782 } 783 784 /** Getter for property rotateContents. 785 * @return Value of property rotateContents. 786 * 787 */ 788 boolean isRotateContents() { 789 return this.rotateContents; 790 } 791 792 /** Setter for property rotateContents. 793 * @param rotateContents New value of property rotateContents. 794 * 795 */ 796 void setRotateContents(boolean rotateContents) { 797 this.rotateContents = rotateContents; 798 } 799 800 boolean isContentWritten() { 801 return body.size() > 1; 802 } 803 804 AcroFields getAcroFields() { 805 if (acroFields == null) { 806 acroFields = new AcroFields(reader, this); 807 } 808 return acroFields; 809 } 810 811 void setFormFlattening(boolean flat) { 812 this.flat = flat; 813 } 814 815 void setFreeTextFlattening(boolean flat) { 816 this.flatFreeText = flat; 817 } 818 819 boolean partialFormFlattening(String name) { 820 getAcroFields(); 821 if (acroFields.getXfa().isXfaPresent()) 822 throw new UnsupportedOperationException(MessageLocalization.getComposedMessage("partial.form.flattening.is.not.supported.with.xfa.forms")); 823 if (!acroFields.getFields().containsKey(name)) 824 return false; 825 partialFlattening.add(name); 826 return true; 827 } 828 829 void flatFields() { 830 if (append) 831 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("field.flattening.is.not.supported.in.append.mode")); 832 getAcroFields(); 833 Map<String, Item> fields = acroFields.getFields(); 834 if (fieldsAdded && partialFlattening.isEmpty()) { 835 for (String s: fields.keySet()) { 836 partialFlattening.add(s); 837 } 838 } 839 PdfDictionary acroForm = reader.getCatalog().getAsDict(PdfName.ACROFORM); 840 PdfArray acroFds = null; 841 if (acroForm != null) { 842 acroFds = (PdfArray)PdfReader.getPdfObject(acroForm.get(PdfName.FIELDS), acroForm); 843 } 844 for (Map.Entry<String, Item> entry: fields.entrySet()) { 845 String name = entry.getKey(); 846 if (!partialFlattening.isEmpty() && !partialFlattening.contains(name)) 847 continue; 848 AcroFields.Item item = entry.getValue(); 849 for (int k = 0; k < item.size(); ++k) { 850 PdfDictionary merged = item.getMerged(k); 851 PdfNumber ff = merged.getAsNumber(PdfName.F); 852 int flags = 0; 853 if (ff != null) 854 flags = ff.intValue(); 855 int page = item.getPage(k).intValue(); 856 PdfDictionary appDic = merged.getAsDict(PdfName.AP); 857 if (appDic != null && (flags & PdfFormField.FLAGS_PRINT) != 0 && (flags & PdfFormField.FLAGS_HIDDEN) == 0) { 858 PdfObject obj = appDic.get(PdfName.N); 859 PdfAppearance app = null; 860 if (obj != null) { 861 PdfObject objReal = PdfReader.getPdfObject(obj); 862 if (obj instanceof PdfIndirectReference && !obj.isIndirect()) 863 app = new PdfAppearance((PdfIndirectReference)obj); 864 else if (objReal instanceof PdfStream) { 865 ((PdfDictionary)objReal).put(PdfName.SUBTYPE, PdfName.FORM); 866 app = new PdfAppearance((PdfIndirectReference)obj); 867 } 868 else { 869 if (objReal != null && objReal.isDictionary()) { 870 PdfName as = merged.getAsName(PdfName.AS); 871 if (as != null) { 872 PdfIndirectReference iref = (PdfIndirectReference)((PdfDictionary)objReal).get(as); 873 if (iref != null) { 874 app = new PdfAppearance(iref); 875 if (iref.isIndirect()) { 876 objReal = PdfReader.getPdfObject(iref); 877 ((PdfDictionary)objReal).put(PdfName.SUBTYPE, PdfName.FORM); 878 } 879 } 880 } 881 } 882 } 883 } 884 if (app != null) { 885 Rectangle box = PdfReader.getNormalizedRectangle(merged.getAsArray(PdfName.RECT)); 886 PdfContentByte cb = getOverContent(page); 887 cb.setLiteral("Q "); 888 cb.addTemplate(app, box.getLeft(), box.getBottom()); 889 cb.setLiteral("q "); 890 } 891 } 892 if (partialFlattening.isEmpty()) 893 continue; 894 PdfDictionary pageDic = reader.getPageN(page); 895 PdfArray annots = pageDic.getAsArray(PdfName.ANNOTS); 896 if (annots == null) 897 continue; 898 for (int idx = 0; idx < annots.size(); ++idx) { 899 PdfObject ran = annots.getPdfObject(idx); 900 if (!ran.isIndirect()) 901 continue; 902 PdfObject ran2 = item.getWidgetRef(k); 903 if (!ran2.isIndirect()) 904 continue; 905 if (((PRIndirectReference)ran).getNumber() == ((PRIndirectReference)ran2).getNumber()) { 906 annots.remove(idx--); 907 PRIndirectReference wdref = (PRIndirectReference)ran2; 908 while (true) { 909 PdfDictionary wd = (PdfDictionary)PdfReader.getPdfObject(wdref); 910 PRIndirectReference parentRef = (PRIndirectReference)wd.get(PdfName.PARENT); 911 PdfReader.killIndirect(wdref); 912 if (parentRef == null) { // reached AcroForm 913 for (int fr = 0; fr < acroFds.size(); ++fr) { 914 PdfObject h = acroFds.getPdfObject(fr); 915 if (h.isIndirect() && ((PRIndirectReference)h).getNumber() == wdref.getNumber()) { 916 acroFds.remove(fr); 917 --fr; 918 } 919 } 920 break; 921 } 922 PdfDictionary parent = (PdfDictionary)PdfReader.getPdfObject(parentRef); 923 PdfArray kids = parent.getAsArray(PdfName.KIDS); 924 for (int fr = 0; fr < kids.size(); ++fr) { 925 PdfObject h = kids.getPdfObject(fr); 926 if (h.isIndirect() && ((PRIndirectReference)h).getNumber() == wdref.getNumber()) { 927 kids.remove(fr); 928 --fr; 929 } 930 } 931 if (!kids.isEmpty()) 932 break; 933 wdref = parentRef; 934 } 935 } 936 } 937 if (annots.isEmpty()) { 938 PdfReader.killIndirect(pageDic.get(PdfName.ANNOTS)); 939 pageDic.remove(PdfName.ANNOTS); 940 } 941 } 942 } 943 if (!fieldsAdded && partialFlattening.isEmpty()) { 944 for (int page = 1; page <= reader.getNumberOfPages(); ++page) { 945 PdfDictionary pageDic = reader.getPageN(page); 946 PdfArray annots = pageDic.getAsArray(PdfName.ANNOTS); 947 if (annots == null) 948 continue; 949 for (int idx = 0; idx < annots.size(); ++idx) { 950 PdfObject annoto = annots.getDirectObject(idx); 951 if (annoto instanceof PdfIndirectReference && !annoto.isIndirect()) 952 continue; 953 if (!annoto.isDictionary() || PdfName.WIDGET.equals(((PdfDictionary)annoto).get(PdfName.SUBTYPE))) { 954 annots.remove(idx); 955 --idx; 956 } 957 } 958 if (annots.isEmpty()) { 959 PdfReader.killIndirect(pageDic.get(PdfName.ANNOTS)); 960 pageDic.remove(PdfName.ANNOTS); 961 } 962 } 963 eliminateAcroformObjects(); 964 } 965 } 966 967 void eliminateAcroformObjects() { 968 PdfObject acro = reader.getCatalog().get(PdfName.ACROFORM); 969 if (acro == null) 970 return; 971 PdfDictionary acrodic = (PdfDictionary)PdfReader.getPdfObject(acro); 972 reader.killXref(acrodic.get(PdfName.XFA)); 973 acrodic.remove(PdfName.XFA); 974 PdfObject iFields = acrodic.get(PdfName.FIELDS); 975 if (iFields != null) { 976 PdfDictionary kids = new PdfDictionary(); 977 kids.put(PdfName.KIDS, iFields); 978 sweepKids(kids); 979 PdfReader.killIndirect(iFields); 980 acrodic.put(PdfName.FIELDS, new PdfArray()); 981 } 982 acrodic.remove(PdfName.SIGFLAGS); 983// PdfReader.killIndirect(acro); 984// reader.getCatalog().remove(PdfName.ACROFORM); 985 } 986 987 void sweepKids(PdfObject obj) { 988 PdfObject oo = PdfReader.killIndirect(obj); 989 if (oo == null || !oo.isDictionary()) 990 return; 991 PdfDictionary dic = (PdfDictionary)oo; 992 PdfArray kids = (PdfArray)PdfReader.killIndirect(dic.get(PdfName.KIDS)); 993 if (kids == null) 994 return; 995 for (int k = 0; k < kids.size(); ++k) { 996 sweepKids(kids.getPdfObject(k)); 997 } 998 } 999 1000 private void flatFreeTextFields() 1001 { 1002 if (append) 1003 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("freetext.flattening.is.not.supported.in.append.mode")); 1004 1005 for (int page = 1; page <= reader.getNumberOfPages(); ++page) 1006 { 1007 PdfDictionary pageDic = reader.getPageN(page); 1008 PdfArray annots = pageDic.getAsArray(PdfName.ANNOTS); 1009 if (annots == null) 1010 continue; 1011 for (int idx = 0; idx < annots.size(); ++idx) 1012 { 1013 PdfObject annoto = annots.getDirectObject(idx); 1014 if (annoto instanceof PdfIndirectReference && !annoto.isIndirect()) 1015 continue; 1016 1017 PdfDictionary annDic = (PdfDictionary)annoto; 1018 if (!((PdfName)annDic.get(PdfName.SUBTYPE)).equals(PdfName.FREETEXT)) 1019 continue; 1020 PdfNumber ff = annDic.getAsNumber(PdfName.F); 1021 int flags = ff != null ? ff.intValue() : 0; 1022 1023 if ( (flags & PdfFormField.FLAGS_PRINT) != 0 && (flags & PdfFormField.FLAGS_HIDDEN) == 0) 1024 { 1025 PdfObject obj1 = annDic.get(PdfName.AP); 1026 if (obj1 == null) 1027 continue; 1028 PdfDictionary appDic = obj1 instanceof PdfIndirectReference ? 1029 (PdfDictionary) PdfReader.getPdfObject(obj1) : (PdfDictionary) obj1; 1030 PdfObject obj = appDic.get(PdfName.N); 1031 PdfAppearance app = null; 1032 PdfObject objReal = PdfReader.getPdfObject(obj); 1033 1034 if (obj instanceof PdfIndirectReference && !obj.isIndirect()) 1035 app = new PdfAppearance((PdfIndirectReference)obj); 1036 else if (objReal instanceof PdfStream) 1037 { 1038 ((PdfDictionary)objReal).put(PdfName.SUBTYPE, PdfName.FORM); 1039 app = new PdfAppearance((PdfIndirectReference)obj); 1040 } 1041 else 1042 { 1043 if (objReal.isDictionary()) 1044 { 1045 PdfName as_p = appDic.getAsName(PdfName.AS); 1046 if (as_p != null) 1047 { 1048 PdfIndirectReference iref = (PdfIndirectReference)((PdfDictionary)objReal).get(as_p); 1049 if (iref != null) 1050 { 1051 app = new PdfAppearance(iref); 1052 if (iref.isIndirect()) 1053 { 1054 objReal = PdfReader.getPdfObject(iref); 1055 ((PdfDictionary)objReal).put(PdfName.SUBTYPE, PdfName.FORM); 1056 } 1057 } 1058 } 1059 } 1060 } 1061 if (app != null) 1062 { 1063 Rectangle box = PdfReader.getNormalizedRectangle(annDic.getAsArray(PdfName.RECT)); 1064 PdfContentByte cb = getOverContent(page); 1065 cb.setLiteral("Q "); 1066 cb.addTemplate(app, box.getLeft(), box.getBottom()); 1067 cb.setLiteral("q "); 1068 } 1069 } 1070 } 1071 for (int idx = 0; idx < annots.size(); ++idx) 1072 { 1073 PdfDictionary annot = annots.getAsDict(idx); 1074 if (annot != null) 1075 { 1076 if (PdfName.FREETEXT.equals(annot.get(PdfName.SUBTYPE))) 1077 { 1078 annots.remove(idx); 1079 --idx; 1080 } 1081 } 1082 } 1083 if (annots.isEmpty()) 1084 { 1085 PdfReader.killIndirect(pageDic.get(PdfName.ANNOTS)); 1086 pageDic.remove(PdfName.ANNOTS); 1087 } 1088 } 1089 } 1090 1091 /** 1092 * @see com.itextpdf.text.pdf.PdfWriter#getPageReference(int) 1093 */ 1094 @Override 1095 public PdfIndirectReference getPageReference(int page) { 1096 PdfIndirectReference ref = reader.getPageOrigRef(page); 1097 if (ref == null) 1098 throw new IllegalArgumentException(MessageLocalization.getComposedMessage("invalid.page.number.1", page)); 1099 return ref; 1100 } 1101 1102 /** 1103 * @see com.itextpdf.text.pdf.PdfWriter#addAnnotation(com.itextpdf.text.pdf.PdfAnnotation) 1104 */ 1105 @Override 1106 public void addAnnotation(PdfAnnotation annot) { 1107 throw new RuntimeException(MessageLocalization.getComposedMessage("unsupported.in.this.context.use.pdfstamper.addannotation")); 1108 } 1109 1110 void addDocumentField(PdfIndirectReference ref) { 1111 PdfDictionary catalog = reader.getCatalog(); 1112 PdfDictionary acroForm = (PdfDictionary)PdfReader.getPdfObject(catalog.get(PdfName.ACROFORM), catalog); 1113 if (acroForm == null) { 1114 acroForm = new PdfDictionary(); 1115 catalog.put(PdfName.ACROFORM, acroForm); 1116 markUsed(catalog); 1117 } 1118 PdfArray fields = (PdfArray)PdfReader.getPdfObject(acroForm.get(PdfName.FIELDS), acroForm); 1119 if (fields == null) { 1120 fields = new PdfArray(); 1121 acroForm.put(PdfName.FIELDS, fields); 1122 markUsed(acroForm); 1123 } 1124 if (!acroForm.contains(PdfName.DA)) { 1125 acroForm.put(PdfName.DA, new PdfString("/Helv 0 Tf 0 g ")); 1126 markUsed(acroForm); 1127 } 1128 fields.add(ref); 1129 markUsed(fields); 1130 } 1131 1132 void addFieldResources() throws IOException { 1133 if (fieldTemplates.isEmpty()) 1134 return; 1135 PdfDictionary catalog = reader.getCatalog(); 1136 PdfDictionary acroForm = (PdfDictionary)PdfReader.getPdfObject(catalog.get(PdfName.ACROFORM), catalog); 1137 if (acroForm == null) { 1138 acroForm = new PdfDictionary(); 1139 catalog.put(PdfName.ACROFORM, acroForm); 1140 markUsed(catalog); 1141 } 1142 PdfDictionary dr = (PdfDictionary)PdfReader.getPdfObject(acroForm.get(PdfName.DR), acroForm); 1143 if (dr == null) { 1144 dr = new PdfDictionary(); 1145 acroForm.put(PdfName.DR, dr); 1146 markUsed(acroForm); 1147 } 1148 markUsed(dr); 1149 for (PdfTemplate template: fieldTemplates) { 1150 PdfFormField.mergeResources(dr, (PdfDictionary)template.getResources(), this); 1151 } 1152 // if (dr.get(PdfName.ENCODING) == null) dr.put(PdfName.ENCODING, PdfName.WIN_ANSI_ENCODING); 1153 PdfDictionary fonts = dr.getAsDict(PdfName.FONT); 1154 if (fonts == null) { 1155 fonts = new PdfDictionary(); 1156 dr.put(PdfName.FONT, fonts); 1157 } 1158 if (!fonts.contains(PdfName.HELV)) { 1159 PdfDictionary dic = new PdfDictionary(PdfName.FONT); 1160 dic.put(PdfName.BASEFONT, PdfName.HELVETICA); 1161 dic.put(PdfName.ENCODING, PdfName.WIN_ANSI_ENCODING); 1162 dic.put(PdfName.NAME, PdfName.HELV); 1163 dic.put(PdfName.SUBTYPE, PdfName.TYPE1); 1164 fonts.put(PdfName.HELV, addToBody(dic).getIndirectReference()); 1165 } 1166 if (!fonts.contains(PdfName.ZADB)) { 1167 PdfDictionary dic = new PdfDictionary(PdfName.FONT); 1168 dic.put(PdfName.BASEFONT, PdfName.ZAPFDINGBATS); 1169 dic.put(PdfName.NAME, PdfName.ZADB); 1170 dic.put(PdfName.SUBTYPE, PdfName.TYPE1); 1171 fonts.put(PdfName.ZADB, addToBody(dic).getIndirectReference()); 1172 } 1173 if (acroForm.get(PdfName.DA) == null) { 1174 acroForm.put(PdfName.DA, new PdfString("/Helv 0 Tf 0 g ")); 1175 markUsed(acroForm); 1176 } 1177 } 1178 1179 void expandFields(PdfFormField field, ArrayList<PdfAnnotation> allAnnots) { 1180 allAnnots.add(field); 1181 ArrayList<PdfFormField> kids = field.getKids(); 1182 if (kids != null) { 1183 for (int k = 0; k < kids.size(); ++k) 1184 expandFields(kids.get(k), allAnnots); 1185 } 1186 } 1187 1188 void addAnnotation(PdfAnnotation annot, PdfDictionary pageN) { 1189 try { 1190 ArrayList<PdfAnnotation> allAnnots = new ArrayList<PdfAnnotation>(); 1191 if (annot.isForm()) { 1192 fieldsAdded = true; 1193 getAcroFields(); 1194 PdfFormField field = (PdfFormField)annot; 1195 if (field.getParent() != null) 1196 return; 1197 expandFields(field, allAnnots); 1198 } 1199 else 1200 allAnnots.add(annot); 1201 for (int k = 0; k < allAnnots.size(); ++k) { 1202 annot = allAnnots.get(k); 1203 if (annot.getPlaceInPage() > 0) 1204 pageN = reader.getPageN(annot.getPlaceInPage()); 1205 if (annot.isForm()) { 1206 if (!annot.isUsed()) { 1207 HashSet<PdfTemplate> templates = annot.getTemplates(); 1208 if (templates != null) 1209 fieldTemplates.addAll(templates); 1210 } 1211 PdfFormField field = (PdfFormField)annot; 1212 if (field.getParent() == null) 1213 addDocumentField(field.getIndirectReference()); 1214 } 1215 if (annot.isAnnotation()) { 1216 PdfObject pdfobj = PdfReader.getPdfObject(pageN.get(PdfName.ANNOTS), pageN); 1217 PdfArray annots = null; 1218 if (pdfobj == null || !pdfobj.isArray()) { 1219 annots = new PdfArray(); 1220 pageN.put(PdfName.ANNOTS, annots); 1221 markUsed(pageN); 1222 } 1223 else 1224 annots = (PdfArray)pdfobj; 1225 annots.add(annot.getIndirectReference()); 1226 markUsed(annots); 1227 if (!annot.isUsed()) { 1228 PdfRectangle rect = (PdfRectangle)annot.get(PdfName.RECT); 1229 if (rect != null && (rect.left() != 0 || rect.right() != 0 || rect.top() != 0 || rect.bottom() != 0)) { 1230 int rotation = reader.getPageRotation(pageN); 1231 Rectangle pageSize = reader.getPageSizeWithRotation(pageN); 1232 switch (rotation) { 1233 case 90: 1234 annot.put(PdfName.RECT, new PdfRectangle( 1235 pageSize.getTop() - rect.top(), 1236 rect.right(), 1237 pageSize.getTop() - rect.bottom(), 1238 rect.left())); 1239 break; 1240 case 180: 1241 annot.put(PdfName.RECT, new PdfRectangle( 1242 pageSize.getRight() - rect.left(), 1243 pageSize.getTop() - rect.bottom(), 1244 pageSize.getRight() - rect.right(), 1245 pageSize.getTop() - rect.top())); 1246 break; 1247 case 270: 1248 annot.put(PdfName.RECT, new PdfRectangle( 1249 rect.bottom(), 1250 pageSize.getRight() - rect.left(), 1251 rect.top(), 1252 pageSize.getRight() - rect.right())); 1253 break; 1254 } 1255 } 1256 } 1257 } 1258 if (!annot.isUsed()) { 1259 annot.setUsed(); 1260 addToBody(annot, annot.getIndirectReference()); 1261 } 1262 } 1263 } 1264 catch (IOException e) { 1265 throw new ExceptionConverter(e); 1266 } 1267 } 1268 1269 @Override 1270 void addAnnotation(PdfAnnotation annot, int page) { 1271 annot.setPage(page); 1272 addAnnotation(annot, reader.getPageN(page)); 1273 } 1274 1275 private void outlineTravel(PRIndirectReference outline) { 1276 while (outline != null) { 1277 PdfDictionary outlineR = (PdfDictionary)PdfReader.getPdfObjectRelease(outline); 1278 PRIndirectReference first = (PRIndirectReference)outlineR.get(PdfName.FIRST); 1279 if (first != null) { 1280 outlineTravel(first); 1281 } 1282 PdfReader.killIndirect(outlineR.get(PdfName.DEST)); 1283 PdfReader.killIndirect(outlineR.get(PdfName.A)); 1284 PdfReader.killIndirect(outline); 1285 outline = (PRIndirectReference)outlineR.get(PdfName.NEXT); 1286 } 1287 } 1288 1289 void deleteOutlines() { 1290 PdfDictionary catalog = reader.getCatalog(); 1291 PRIndirectReference outlines = (PRIndirectReference)catalog.get(PdfName.OUTLINES); 1292 if (outlines == null) 1293 return; 1294 outlineTravel(outlines); 1295 PdfReader.killIndirect(outlines); 1296 catalog.remove(PdfName.OUTLINES); 1297 markUsed(catalog); 1298 } 1299 1300 void setJavaScript() throws IOException { 1301 HashMap<String, PdfObject> djs = pdf.getDocumentLevelJS(); 1302 if (djs.isEmpty()) 1303 return; 1304 PdfDictionary catalog = reader.getCatalog(); 1305 PdfDictionary names = (PdfDictionary)PdfReader.getPdfObject(catalog.get(PdfName.NAMES), catalog); 1306 if (names == null) { 1307 names = new PdfDictionary(); 1308 catalog.put(PdfName.NAMES, names); 1309 markUsed(catalog); 1310 } 1311 markUsed(names); 1312 PdfDictionary tree = PdfNameTree.writeTree(djs, this); 1313 names.put(PdfName.JAVASCRIPT, addToBody(tree).getIndirectReference()); 1314 } 1315 1316 void addFileAttachments() throws IOException { 1317 HashMap<String, PdfObject> fs = pdf.getDocumentFileAttachment(); 1318 if (fs.isEmpty()) 1319 return; 1320 PdfDictionary catalog = reader.getCatalog(); 1321 PdfDictionary names = (PdfDictionary)PdfReader.getPdfObject(catalog.get(PdfName.NAMES), catalog); 1322 if (names == null) { 1323 names = new PdfDictionary(); 1324 catalog.put(PdfName.NAMES, names); 1325 markUsed(catalog); 1326 } 1327 markUsed(names); 1328 HashMap<String, PdfObject> old = PdfNameTree.readTree((PdfDictionary)PdfReader.getPdfObjectRelease(names.get(PdfName.EMBEDDEDFILES))); 1329 for (Map.Entry<String, PdfObject> entry: fs.entrySet()) { 1330 String name = entry.getKey(); 1331 int k = 0; 1332 StringBuilder nn = new StringBuilder(name); 1333 while (old.containsKey(nn.toString())) { 1334 ++k; 1335 nn.append(" ").append(k); 1336 } 1337 old.put(nn.toString(), entry.getValue()); 1338 } 1339 PdfDictionary tree = PdfNameTree.writeTree(old, this); 1340 // Remove old EmbeddedFiles object if preset 1341 PdfObject oldEmbeddedFiles = names.get(PdfName.EMBEDDEDFILES); 1342 if (oldEmbeddedFiles != null) { 1343 PdfReader.killIndirect(oldEmbeddedFiles); 1344 } 1345 1346 // Add new EmbeddedFiles object 1347 names.put(PdfName.EMBEDDEDFILES, addToBody(tree).getIndirectReference()); 1348 } 1349 1350 /** 1351 * Adds or replaces the Collection Dictionary in the Catalog. 1352 * @param collection the new collection dictionary. 1353 */ 1354 void makePackage( PdfCollection collection ) { 1355 PdfDictionary catalog = reader.getCatalog(); 1356 catalog.put( PdfName.COLLECTION, collection ); 1357 } 1358 1359 void setOutlines() throws IOException { 1360 if (newBookmarks == null) 1361 return; 1362 deleteOutlines(); 1363 if (newBookmarks.isEmpty()) 1364 return; 1365 PdfDictionary catalog = reader.getCatalog(); 1366 boolean namedAsNames = catalog.get(PdfName.DESTS) != null; 1367 writeOutlines(catalog, namedAsNames); 1368 markUsed(catalog); 1369 } 1370 1371 /** 1372 * Sets the viewer preferences. 1373 * @param preferences the viewer preferences 1374 * @see PdfWriter#setViewerPreferences(int) 1375 */ 1376 @Override 1377 public void setViewerPreferences(int preferences) { 1378 useVp = true; 1379 this.viewerPreferences.setViewerPreferences(preferences); 1380 } 1381 1382 /** Adds a viewer preference 1383 * @param key a key for a viewer preference 1384 * @param value the value for the viewer preference 1385 * @see PdfViewerPreferences#addViewerPreference 1386 */ 1387 @Override 1388 public void addViewerPreference(PdfName key, PdfObject value) { 1389 useVp = true; 1390 this.viewerPreferences.addViewerPreference(key, value); 1391 } 1392 1393 /** 1394 * Set the signature flags. 1395 * @param f the flags. This flags are ORed with current ones 1396 */ 1397 @Override 1398 public void setSigFlags(int f) { 1399 sigFlags |= f; 1400 } 1401 1402 /** Always throws an <code>UnsupportedOperationException</code>. 1403 * @param actionType ignore 1404 * @param action ignore 1405 * @throws PdfException ignore 1406 * @see PdfStamper#setPageAction(PdfName, PdfAction, int) 1407 */ 1408 @Override 1409 public void setPageAction(PdfName actionType, PdfAction action) throws PdfException { 1410 throw new UnsupportedOperationException(MessageLocalization.getComposedMessage("use.setpageaction.pdfname.actiontype.pdfaction.action.int.page")); 1411 } 1412 1413 /** 1414 * Sets the open and close page additional action. 1415 * @param actionType the action type. It can be <CODE>PdfWriter.PAGE_OPEN</CODE> 1416 * or <CODE>PdfWriter.PAGE_CLOSE</CODE> 1417 * @param action the action to perform 1418 * @param page the page where the action will be applied. The first page is 1 1419 * @throws PdfException if the action type is invalid 1420 */ 1421 void setPageAction(PdfName actionType, PdfAction action, int page) throws PdfException { 1422 if (!actionType.equals(PAGE_OPEN) && !actionType.equals(PAGE_CLOSE)) 1423 throw new PdfException(MessageLocalization.getComposedMessage("invalid.page.additional.action.type.1", actionType.toString())); 1424 PdfDictionary pg = reader.getPageN(page); 1425 PdfDictionary aa = (PdfDictionary)PdfReader.getPdfObject(pg.get(PdfName.AA), pg); 1426 if (aa == null) { 1427 aa = new PdfDictionary(); 1428 pg.put(PdfName.AA, aa); 1429 markUsed(pg); 1430 } 1431 aa.put(actionType, action); 1432 markUsed(aa); 1433 } 1434 1435 /** 1436 * Always throws an <code>UnsupportedOperationException</code>. 1437 * @param seconds ignore 1438 */ 1439 @Override 1440 public void setDuration(int seconds) { 1441 throw new UnsupportedOperationException(MessageLocalization.getComposedMessage("use.setpageaction.pdfname.actiontype.pdfaction.action.int.page")); 1442 } 1443 1444 /** 1445 * Always throws an <code>UnsupportedOperationException</code>. 1446 * @param transition ignore 1447 */ 1448 @Override 1449 public void setTransition(PdfTransition transition) { 1450 throw new UnsupportedOperationException(MessageLocalization.getComposedMessage("use.setpageaction.pdfname.actiontype.pdfaction.action.int.page")); 1451 } 1452 1453 /** 1454 * Sets the display duration for the page (for presentations) 1455 * @param seconds the number of seconds to display the page. A negative value removes the entry 1456 * @param page the page where the duration will be applied. The first page is 1 1457 */ 1458 void setDuration(int seconds, int page) { 1459 PdfDictionary pg = reader.getPageN(page); 1460 if (seconds < 0) 1461 pg.remove(PdfName.DUR); 1462 else 1463 pg.put(PdfName.DUR, new PdfNumber(seconds)); 1464 markUsed(pg); 1465 } 1466 1467 /** 1468 * Sets the transition for the page 1469 * @param transition the transition object. A <code>null</code> removes the transition 1470 * @param page the page where the transition will be applied. The first page is 1 1471 */ 1472 void setTransition(PdfTransition transition, int page) { 1473 PdfDictionary pg = reader.getPageN(page); 1474 if (transition == null) 1475 pg.remove(PdfName.TRANS); 1476 else 1477 pg.put(PdfName.TRANS, transition.getTransitionDictionary()); 1478 markUsed(pg); 1479 } 1480 1481 protected void markUsed(PdfObject obj) { 1482 if (append && obj != null) { 1483 PRIndirectReference ref = null; 1484 if (obj.type() == PdfObject.INDIRECT) 1485 ref = (PRIndirectReference)obj; 1486 else 1487 ref = obj.getIndRef(); 1488 if (ref != null) 1489 marked.put(ref.getNumber(), 1); 1490 } 1491 } 1492 1493 protected void markUsed(int num) { 1494 if (append) 1495 marked.put(num, 1); 1496 } 1497 1498 /** 1499 * Getter for property append. 1500 * @return Value of property append. 1501 */ 1502 boolean isAppend() { 1503 return append; 1504 } 1505 1506 /** Additional-actions defining the actions to be taken in 1507 * response to various trigger events affecting the document 1508 * as a whole. The actions types allowed are: <CODE>DOCUMENT_CLOSE</CODE>, 1509 * <CODE>WILL_SAVE</CODE>, <CODE>DID_SAVE</CODE>, <CODE>WILL_PRINT</CODE> 1510 * and <CODE>DID_PRINT</CODE>. 1511 * 1512 * @param actionType the action type 1513 * @param action the action to execute in response to the trigger 1514 * @throws PdfException on invalid action type 1515 */ 1516 @Override 1517 public void setAdditionalAction(PdfName actionType, PdfAction action) throws PdfException { 1518 if (!(actionType.equals(DOCUMENT_CLOSE) || 1519 actionType.equals(WILL_SAVE) || 1520 actionType.equals(DID_SAVE) || 1521 actionType.equals(WILL_PRINT) || 1522 actionType.equals(DID_PRINT))) { 1523 throw new PdfException(MessageLocalization.getComposedMessage("invalid.additional.action.type.1", actionType.toString())); 1524 } 1525 PdfDictionary aa = reader.getCatalog().getAsDict(PdfName.AA); 1526 if (aa == null) { 1527 if (action == null) 1528 return; 1529 aa = new PdfDictionary(); 1530 reader.getCatalog().put(PdfName.AA, aa); 1531 } 1532 markUsed(aa); 1533 if (action == null) 1534 aa.remove(actionType); 1535 else 1536 aa.put(actionType, action); 1537 } 1538 1539 /** 1540 * @see com.itextpdf.text.pdf.PdfWriter#setOpenAction(com.itextpdf.text.pdf.PdfAction) 1541 */ 1542 @Override 1543 public void setOpenAction(PdfAction action) { 1544 openAction = action; 1545 } 1546 1547 /** 1548 * @see com.itextpdf.text.pdf.PdfWriter#setOpenAction(java.lang.String) 1549 */ 1550 @Override 1551 public void setOpenAction(String name) { 1552 throw new UnsupportedOperationException(MessageLocalization.getComposedMessage("open.actions.by.name.are.not.supported")); 1553 } 1554 1555 /** 1556 * @see com.itextpdf.text.pdf.PdfWriter#setThumbnail(com.itextpdf.text.Image) 1557 */ 1558 @Override 1559 public void setThumbnail(com.itextpdf.text.Image image) { 1560 throw new UnsupportedOperationException(MessageLocalization.getComposedMessage("use.pdfstamper.setthumbnail")); 1561 } 1562 1563 void setThumbnail(Image image, int page) throws PdfException, DocumentException { 1564 PdfIndirectReference thumb = getImageReference(addDirectImageSimple(image)); 1565 reader.resetReleasePage(); 1566 PdfDictionary dic = reader.getPageN(page); 1567 dic.put(PdfName.THUMB, thumb); 1568 reader.resetReleasePage(); 1569 } 1570 1571 @Override 1572 public PdfContentByte getDirectContentUnder() { 1573 throw new UnsupportedOperationException(MessageLocalization.getComposedMessage("use.pdfstamper.getundercontent.or.pdfstamper.getovercontent")); 1574 } 1575 1576 @Override 1577 public PdfContentByte getDirectContent() { 1578 throw new UnsupportedOperationException(MessageLocalization.getComposedMessage("use.pdfstamper.getundercontent.or.pdfstamper.getovercontent")); 1579 } 1580 1581 /** 1582 * Reads the OCProperties dictionary from the catalog of the existing document 1583 * and fills the documentOCG, documentOCGorder and OCGRadioGroup variables in PdfWriter. 1584 * Note that the original OCProperties of the existing document can contain more information. 1585 * @since 2.1.2 1586 */ 1587 protected void readOCProperties() { 1588 if (!documentOCG.isEmpty()) { 1589 return; 1590 } 1591 PdfDictionary dict = reader.getCatalog().getAsDict(PdfName.OCPROPERTIES); 1592 if (dict == null) { 1593 return; 1594 } 1595 PdfArray ocgs = dict.getAsArray(PdfName.OCGS); 1596 PdfIndirectReference ref; 1597 PdfLayer layer; 1598 HashMap<String, PdfLayer> ocgmap = new HashMap<String, PdfLayer>(); 1599 for (Iterator<PdfObject> i = ocgs.listIterator(); i.hasNext(); ) { 1600 ref = (PdfIndirectReference)i.next(); 1601 layer = new PdfLayer(null); 1602 layer.setRef(ref); 1603 layer.setOnPanel(false); 1604 layer.merge((PdfDictionary)PdfReader.getPdfObject(ref)); 1605 ocgmap.put(ref.toString(), layer); 1606 } 1607 PdfDictionary d = dict.getAsDict(PdfName.D); 1608 PdfArray off = d.getAsArray(PdfName.OFF); 1609 if (off != null) { 1610 for (Iterator<PdfObject> i = off.listIterator(); i.hasNext(); ) { 1611 ref = (PdfIndirectReference)i.next(); 1612 layer = ocgmap.get(ref.toString()); 1613 layer.setOn(false); 1614 } 1615 } 1616 PdfArray order = d.getAsArray(PdfName.ORDER); 1617 if (order != null) { 1618 addOrder(null, order, ocgmap); 1619 } 1620 documentOCG.addAll(ocgmap.values()); 1621 OCGRadioGroup = d.getAsArray(PdfName.RBGROUPS); 1622 if (OCGRadioGroup == null) 1623 OCGRadioGroup = new PdfArray(); 1624 OCGLocked = d.getAsArray(PdfName.LOCKED); 1625 if (OCGLocked == null) 1626 OCGLocked = new PdfArray(); 1627 } 1628 1629 /** 1630 * Recursive method to reconstruct the documentOCGorder variable in the writer. 1631 * @param parent a parent PdfLayer (can be null) 1632 * @param arr an array possibly containing children for the parent PdfLayer 1633 * @param ocgmap a HashMap with indirect reference Strings as keys and PdfLayer objects as values. 1634 * @since 2.1.2 1635 */ 1636 private void addOrder(PdfLayer parent, PdfArray arr, Map<String, PdfLayer> ocgmap) { 1637 PdfObject obj; 1638 PdfLayer layer; 1639 for (int i = 0; i < arr.size(); i++) { 1640 obj = arr.getPdfObject(i); 1641 if (obj.isIndirect()) { 1642 layer = ocgmap.get(obj.toString()); 1643 layer.setOnPanel(true); 1644 registerLayer(layer); 1645 if (parent != null) { 1646 parent.addChild(layer); 1647 } 1648 if (arr.size() > i + 1 && arr.getPdfObject(i + 1).isArray()) { 1649 i++; 1650 addOrder(layer, (PdfArray)arr.getPdfObject(i), ocgmap); 1651 } 1652 } 1653 else if (obj.isArray()) { 1654 PdfArray sub = (PdfArray)obj; 1655 if (sub.isEmpty()) return; 1656 obj = sub.getPdfObject(0); 1657 if (obj.isString()) { 1658 layer = new PdfLayer(obj.toString()); 1659 layer.setOnPanel(true); 1660 registerLayer(layer); 1661 if (parent != null) { 1662 parent.addChild(layer); 1663 } 1664 PdfArray array = new PdfArray(); 1665 for (Iterator<PdfObject> j = sub.listIterator(); j.hasNext(); ) { 1666 array.add(j.next()); 1667 } 1668 addOrder(layer, array, ocgmap); 1669 } 1670 else { 1671 addOrder(parent, (PdfArray)obj, ocgmap); 1672 } 1673 } 1674 } 1675 } 1676 1677 /** 1678 * Gets the PdfLayer objects in an existing document as a Map 1679 * with the names/titles of the layers as keys. 1680 * @return a Map with all the PdfLayers in the document (and the name/title of the layer as key) 1681 * @since 2.1.2 1682 */ 1683 public Map<String, PdfLayer> getPdfLayers() { 1684 if (documentOCG.isEmpty()) { 1685 readOCProperties(); 1686 } 1687 HashMap<String, PdfLayer> map = new HashMap<String, PdfLayer>(); 1688 PdfLayer layer; 1689 String key; 1690 for (PdfOCG pdfOCG : documentOCG) { 1691 layer = (PdfLayer)pdfOCG; 1692 if (layer.getTitle() == null) { 1693 key = layer.getAsString(PdfName.NAME).toString(); 1694 } 1695 else { 1696 key = layer.getTitle(); 1697 } 1698 if (map.containsKey(key)) { 1699 int seq = 2; 1700 String tmp = key + "(" + seq + ")"; 1701 while (map.containsKey(tmp)) { 1702 seq++; 1703 tmp = key + "(" + seq + ")"; 1704 } 1705 key = tmp; 1706 } 1707 map.put(key, layer); 1708 } 1709 return map; 1710 } 1711 1712 static class PageStamp { 1713 1714 PdfDictionary pageN; 1715 StampContent under; 1716 StampContent over; 1717 PageResources pageResources; 1718 int replacePoint = 0; 1719 1720 PageStamp(PdfStamperImp stamper, PdfReader reader, PdfDictionary pageN) { 1721 this.pageN = pageN; 1722 pageResources = new PageResources(); 1723 PdfDictionary resources = pageN.getAsDict(PdfName.RESOURCES); 1724 pageResources.setOriginalResources(resources, stamper.namePtr); 1725 } 1726 } 1727}