1 49 package com.lowagie.text.pdf; 50 51 import java.io.IOException ; 52 import java.io.OutputStream ; 53 import java.util.ArrayList ; 54 import java.util.HashMap ; 55 import java.util.Iterator ; 56 import java.util.List ; 57 import java.util.Map ; 58 import java.util.StringTokenizer ; 59 60 import com.lowagie.text.Document; 61 import com.lowagie.text.DocumentException; 62 import com.lowagie.text.ExceptionConverter; 63 64 68 class PdfCopyFieldsImp extends PdfWriter { 69 70 private static final PdfName iTextTag = new PdfName("_iTextTag_"); 71 private static final Integer zero = new Integer (0); 72 ArrayList readers = new ArrayList (); 73 HashMap readers2intrefs = new HashMap (); 74 HashMap pages2intrefs = new HashMap (); 75 HashMap visited = new HashMap (); 76 ArrayList fields = new ArrayList (); 77 RandomAccessFileOrArray file; 78 HashMap fieldTree = new HashMap (); 79 ArrayList pageRefs = new ArrayList (); 80 ArrayList pageDics = new ArrayList (); 81 PdfDictionary resources = new PdfDictionary(); 82 PdfDictionary form; 83 protected List newBookmarks; 84 boolean closing = false; 85 Document nd; 86 private HashMap tabOrder; 87 private ArrayList calculationOrder = new ArrayList (); 88 private ArrayList calculationOrderRefs; 89 90 PdfCopyFieldsImp(OutputStream os) throws DocumentException { 91 this(os, '\0'); 92 } 93 94 PdfCopyFieldsImp(OutputStream os, char pdfVersion) throws DocumentException { 95 super(new PdfDocument(), os); 96 pdf.addWriter(this); 97 if (pdfVersion != 0) 98 super.setPdfVersion(pdfVersion); 99 nd = new Document(); 100 nd.addDocListener(pdf); 101 } 102 103 void addDocument(PdfReader reader, List pagesToKeep) throws DocumentException { 104 if (!readers2intrefs.containsKey(reader) && reader.isTampered()) 105 throw new DocumentException("The document was reused."); 106 reader = new PdfReader(reader); 107 reader.selectPages(pagesToKeep); 108 if (reader.getNumberOfPages() == 0) 109 return; 110 reader.setTampered(false); 111 addDocument(reader); 112 } 113 114 void addDocument(PdfReader reader) throws DocumentException { 115 openDoc(); 116 if (readers2intrefs.containsKey(reader)) { 117 reader = new PdfReader(reader); 118 } 119 else { 120 if (reader.isTampered()) 121 throw new DocumentException("The document was reused."); 122 reader.consolidateNamedDestinations(); 123 reader.setTampered(true); 124 } 125 reader.shuffleSubsetNames(); 126 readers2intrefs.put(reader, new IntHashtable()); 127 readers.add(reader); 128 int len = reader.getNumberOfPages(); 129 IntHashtable refs = new IntHashtable(); 130 for (int p = 1; p <= len; ++p) { 131 refs.put(reader.getPageOrigRef(p).getNumber(), 1); 132 reader.releasePage(p); 133 } 134 pages2intrefs.put(reader, refs); 135 visited.put(reader, new IntHashtable()); 136 fields.add(reader.getAcroFields()); 137 updateCalculationOrder(reader); 138 } 139 140 private static String getCOName(PdfReader reader, PRIndirectReference ref) { 141 String name = ""; 142 while (ref != null) { 143 PdfObject obj = PdfReader.getPdfObject(ref); 144 if (obj == null || obj.type() != PdfObject.DICTIONARY) 145 break; 146 PdfDictionary dic = (PdfDictionary)obj; 147 PdfString t = (PdfString)PdfReader.getPdfObject(dic.get(PdfName.T)); 148 if (t != null) { 149 name = t.toUnicodeString()+ "." + name; 150 } 151 ref = (PRIndirectReference)dic.get(PdfName.PARENT); 152 } 153 if (name.endsWith(".")) 154 name = name.substring(0, name.length() - 1); 155 return name; 156 } 157 158 private void updateCalculationOrder(PdfReader reader) { 159 PdfDictionary catalog = reader.getCatalog(); 160 PdfDictionary acro = (PdfDictionary)PdfReader.getPdfObject(catalog.get(PdfName.ACROFORM)); 161 if (acro == null) 162 return; 163 PdfArray co = (PdfArray)PdfReader.getPdfObject(acro.get(PdfName.CO)); 164 if (co == null || co.size() == 0) 165 return; 166 AcroFields af = reader.getAcroFields(); 167 ArrayList coa = co.getArrayList(); 168 for (int k = 0; k < coa.size(); ++k) { 169 PdfObject obj = (PdfObject)coa.get(k); 170 if (obj == null || !obj.isIndirect()) 171 continue; 172 String name = getCOName(reader, (PRIndirectReference)obj); 173 if (af.getFieldItem(name) == null) 174 continue; 175 name = "." + name; 176 if (calculationOrder.contains(name)) 177 continue; 178 calculationOrder.add(name); 179 } 180 } 181 182 void propagate(PdfObject obj, PdfIndirectReference refo, boolean restricted) throws IOException { 183 if (obj == null) 184 return; 185 if (obj instanceof PdfIndirectReference) 188 return; 189 switch (obj.type()) { 190 case PdfObject.DICTIONARY: 191 case PdfObject.STREAM: { 192 PdfDictionary dic = (PdfDictionary)obj; 193 for (Iterator it = dic.getKeys().iterator(); it.hasNext();) { 194 PdfName key = (PdfName)it.next(); 195 if (restricted && (key.equals(PdfName.PARENT) || key.equals(PdfName.KIDS))) 196 continue; 197 PdfObject ob = dic.get(key); 198 if (ob != null && ob.isIndirect()) { 199 PRIndirectReference ind = (PRIndirectReference)ob; 200 if (!setVisited(ind) && !isPage(ind)) { 201 PdfIndirectReference ref = getNewReference(ind); 202 propagate(PdfReader.getPdfObjectRelease(ind), ref, restricted); 203 } 204 } 205 else 206 propagate(ob, null, restricted); 207 } 208 break; 209 } 210 case PdfObject.ARRAY: { 211 ArrayList list = ((PdfArray)obj).getArrayList(); 212 for (Iterator it = list.iterator(); it.hasNext();) { 214 PdfObject ob = (PdfObject)it.next(); 215 if (ob != null && ob.isIndirect()) { 216 PRIndirectReference ind = (PRIndirectReference)ob; 217 if (!isVisited(ind) && !isPage(ind)) { 218 PdfIndirectReference ref = getNewReference(ind); 219 propagate(PdfReader.getPdfObjectRelease(ind), ref, restricted); 220 } 221 } 222 else 223 propagate(ob, null, restricted); 224 } 225 break; 226 } 227 case PdfObject.INDIRECT: { 228 throw new RuntimeException ("Reference pointing to reference."); 229 } 230 } 231 } 232 233 private void adjustTabOrder(PdfArray annots, PdfIndirectReference ind, PdfNumber nn) { 234 int v = nn.intValue(); 235 ArrayList t = (ArrayList )tabOrder.get(annots); 236 if (t == null) { 237 t = new ArrayList (); 238 int size = annots.size() - 1; 239 for (int k = 0; k < size; ++k) { 240 t.add(zero); 241 } 242 t.add(new Integer (v)); 243 tabOrder.put(annots, t); 244 annots.add(ind); 245 } 246 else { 247 int size = t.size() - 1; 248 for (int k = size; k >= 0; --k) { 249 if (((Integer )t.get(k)).intValue() <= v) { 250 t.add(k + 1, new Integer (v)); 251 annots.getArrayList().add(k + 1, ind); 252 size = -2; 253 break; 254 } 255 } 256 if (size != -2) { 257 t.add(0, new Integer (v)); 258 annots.getArrayList().add(0, ind); 259 } 260 } 261 } 262 263 protected PdfArray branchForm(HashMap level, PdfIndirectReference parent, String fname) throws IOException { 264 PdfArray arr = new PdfArray(); 265 for (Iterator it = level.entrySet().iterator(); it.hasNext();) { 266 Map.Entry entry = (Map.Entry ) it.next(); 267 String name = (String ) entry.getKey(); 268 Object obj = entry.getValue(); 269 PdfIndirectReference ind = getPdfIndirectReference(); 270 PdfDictionary dic = new PdfDictionary(); 271 if (parent != null) 272 dic.put(PdfName.PARENT, parent); 273 dic.put(PdfName.T, new PdfString(name, PdfObject.TEXT_UNICODE)); 274 String fname2 = fname + "." + name; 275 int coidx = calculationOrder.indexOf(fname2); 276 if (coidx >= 0) 277 calculationOrderRefs.set(coidx, ind); 278 if (obj instanceof HashMap ) { 279 dic.put(PdfName.KIDS, branchForm((HashMap )obj, ind, fname2)); 280 arr.add(ind); 281 addToBody(dic, ind); 282 } 283 else { 284 ArrayList list = (ArrayList )obj; 285 dic.mergeDifferent((PdfDictionary)list.get(0)); 286 if (list.size() == 3) { 287 dic.mergeDifferent((PdfDictionary)list.get(2)); 288 int page = ((Integer )list.get(1)).intValue(); 289 PdfDictionary pageDic = (PdfDictionary)pageDics.get(page - 1); 290 PdfArray annots = (PdfArray)PdfReader.getPdfObject(pageDic.get(PdfName.ANNOTS)); 291 if (annots == null) { 292 annots = new PdfArray(); 293 pageDic.put(PdfName.ANNOTS, annots); 294 } 295 PdfNumber nn = (PdfNumber)dic.get(iTextTag); 296 dic.remove(iTextTag); 297 adjustTabOrder(annots, ind, nn); 298 } 299 else { 300 PdfArray kids = new PdfArray(); 301 for (int k = 1; k < list.size(); k += 2) { 302 int page = ((Integer )list.get(k)).intValue(); 303 PdfDictionary pageDic = (PdfDictionary)pageDics.get(page - 1); 304 PdfArray annots = (PdfArray)PdfReader.getPdfObject(pageDic.get(PdfName.ANNOTS)); 305 if (annots == null) { 306 annots = new PdfArray(); 307 pageDic.put(PdfName.ANNOTS, annots); 308 } 309 PdfDictionary widget = new PdfDictionary(); 310 widget.merge((PdfDictionary)list.get(k + 1)); 311 widget.put(PdfName.PARENT, ind); 312 PdfNumber nn = (PdfNumber)widget.get(iTextTag); 313 widget.remove(iTextTag); 314 PdfIndirectReference wref = addToBody(widget).getIndirectReference(); 315 adjustTabOrder(annots, wref, nn); 316 kids.add(wref); 317 propagate(widget, null, false); 318 } 319 dic.put(PdfName.KIDS, kids); 320 } 321 arr.add(ind); 322 addToBody(dic, ind); 323 propagate(dic, null, false); 324 } 325 } 326 return arr; 327 } 328 329 protected void createAcroForms() throws IOException { 330 if (fieldTree.isEmpty()) 331 return; 332 form = new PdfDictionary(); 333 form.put(PdfName.DR, resources); 334 propagate(resources, null, false); 335 form.put(PdfName.DA, new PdfString("/Helv 0 Tf 0 g ")); 336 tabOrder = new HashMap (); 337 calculationOrderRefs = new ArrayList (calculationOrder); 338 form.put(PdfName.FIELDS, branchForm(fieldTree, null, "")); 339 PdfArray co = new PdfArray(); 340 for (int k = 0; k < calculationOrderRefs.size(); ++k) { 341 Object obj = calculationOrderRefs.get(k); 342 if (obj instanceof PdfIndirectReference) 343 co.add((PdfIndirectReference)obj); 344 } 345 if (co.size() > 0) 346 form.put(PdfName.CO, co); 347 } 348 349 public void close() { 350 if (closing) { 351 super.close(); 352 return; 353 } 354 closing = true; 355 try { 356 closeIt(); 357 } 358 catch (Exception e) { 359 throw new ExceptionConverter(e); 360 } 361 } 362 363 protected void closeIt() throws IOException { 364 for (int k = 0; k < readers.size(); ++k) { 365 ((PdfReader)readers.get(k)).removeFields(); 366 } 367 for (int r = 0; r < readers.size(); ++r) { 368 PdfReader reader = (PdfReader)readers.get(r); 369 for (int page = 1; page <= reader.getNumberOfPages(); ++page) { 370 pageRefs.add(getNewReference(reader.getPageOrigRef(page))); 371 pageDics.add(reader.getPageN(page)); 372 } 373 } 374 mergeFields(); 375 createAcroForms(); 376 for (int r = 0; r < readers.size(); ++r) { 377 PdfReader reader = (PdfReader)readers.get(r); 378 for (int page = 1; page <= reader.getNumberOfPages(); ++page) { 379 PdfDictionary dic = reader.getPageN(page); 380 PdfIndirectReference pageRef = getNewReference(reader.getPageOrigRef(page)); 381 PdfIndirectReference parent = root.addPageRef(pageRef); 382 dic.put(PdfName.PARENT, parent); 383 propagate(dic, pageRef, false); 384 } 385 } 386 for (Iterator it = readers2intrefs.entrySet().iterator(); it.hasNext();) { 387 Map.Entry entry = (Map.Entry ) it.next(); 388 PdfReader reader = (PdfReader) entry.getKey(); 389 try { 390 file = reader.getSafeFile(); 391 file.reOpen(); 392 IntHashtable t = (IntHashtable) entry.getValue(); 393 int keys[] = t.toOrderedKeys(); 394 for (int k = 0; k < keys.length; ++k) { 395 PRIndirectReference ref = new PRIndirectReference(reader, keys[k]); 396 addToBody(PdfReader.getPdfObjectRelease(ref), t.get(keys[k])); 397 } 398 } 399 finally { 400 try { 401 file.close(); 402 reader.close(); 403 } 404 catch (Exception e) { 405 } 407 } 408 } 409 pdf.close(); 410 } 411 412 void addPageOffsetToField(HashMap fd, int pageOffset) { 413 if (pageOffset == 0) 414 return; 415 for (Iterator it = fd.values().iterator(); it.hasNext();) { 416 ArrayList page = ((AcroFields.Item)it.next()).page; 417 for (int k = 0; k < page.size(); ++k) 418 page.set(k, new Integer (((Integer )page.get(k)).intValue() + pageOffset)); 419 } 420 } 421 422 void createWidgets(ArrayList list, AcroFields.Item item) { 423 for (int k = 0; k < item.merged.size(); ++k) { 424 list.add(item.page.get(k)); 425 PdfDictionary merged = (PdfDictionary)item.merged.get(k); 426 PdfObject dr = merged.get(PdfName.DR); 427 if (dr != null) 428 PdfFormField.mergeResources(resources, (PdfDictionary)PdfReader.getPdfObject(dr)); 429 PdfDictionary widget = new PdfDictionary(); 430 for (Iterator it = merged.getKeys().iterator(); it.hasNext();) { 431 PdfName key = (PdfName)it.next(); 432 if (widgetKeys.containsKey(key)) 433 widget.put(key, merged.get(key)); 434 } 435 widget.put(iTextTag, new PdfNumber(((Integer )item.tabOrder.get(k)).intValue() + 1)); 436 list.add(widget); 437 } 438 } 439 440 void mergeField(String name, AcroFields.Item item) { 441 HashMap map = fieldTree; 442 StringTokenizer tk = new StringTokenizer (name, "."); 443 if (!tk.hasMoreTokens()) 444 return; 445 while (true) { 446 String s = tk.nextToken(); 447 Object obj = map.get(s); 448 if (tk.hasMoreTokens()) { 449 if (obj == null) { 450 obj = new HashMap (); 451 map.put(s, obj); 452 map = (HashMap )obj; 453 continue; 454 } 455 else if (obj instanceof HashMap ) 456 map = (HashMap )obj; 457 else 458 return; 459 } 460 else { 461 if (obj instanceof HashMap ) 462 return; 463 PdfDictionary merged = (PdfDictionary)item.merged.get(0); 464 if (obj == null) { 465 PdfDictionary field = new PdfDictionary(); 466 for (Iterator it = merged.getKeys().iterator(); it.hasNext();) { 467 PdfName key = (PdfName)it.next(); 468 if (fieldKeys.containsKey(key)) 469 field.put(key, merged.get(key)); 470 } 471 ArrayList list = new ArrayList (); 472 list.add(field); 473 createWidgets(list, item); 474 map.put(s, list); 475 } 476 else { 477 ArrayList list = (ArrayList )obj; 478 PdfDictionary field = (PdfDictionary)list.get(0); 479 PdfName type1 = (PdfName)field.get(PdfName.FT); 480 PdfName type2 = (PdfName)merged.get(PdfName.FT); 481 if (type1 == null || !type1.equals(type2)) 482 return; 483 int flag1 = 0; 484 PdfObject f1 = field.get(PdfName.FF); 485 if (f1 != null && f1.isNumber()) 486 flag1 = ((PdfNumber)f1).intValue(); 487 int flag2 = 0; 488 PdfObject f2 = merged.get(PdfName.FF); 489 if (f2 != null && f2.isNumber()) 490 flag2 = ((PdfNumber)f2).intValue(); 491 if (type1.equals(PdfName.BTN)) { 492 if (((flag1 ^ flag2) & PdfFormField.FF_PUSHBUTTON) != 0) 493 return; 494 if ((flag1 & PdfFormField.FF_PUSHBUTTON) == 0 && ((flag1 ^ flag2) & PdfFormField.FF_RADIO) != 0) 495 return; 496 } 497 else if (type1.equals(PdfName.CH)) { 498 if (((flag1 ^ flag2) & PdfFormField.FF_COMBO) != 0) 499 return; 500 } 501 createWidgets(list, item); 502 } 503 return; 504 } 505 } 506 } 507 508 void mergeWithMaster(HashMap fd) { 509 for (Iterator it = fd.entrySet().iterator(); it.hasNext();) { 510 Map.Entry entry = (Map.Entry ) it.next(); 511 String name = (String ) entry.getKey(); 512 mergeField(name, (AcroFields.Item) entry.getValue()); 513 } 514 } 515 516 void mergeFields() { 517 int pageOffset = 0; 518 for (int k = 0; k < fields.size(); ++k) { 519 HashMap fd = ((AcroFields)fields.get(k)).getFields(); 520 addPageOffsetToField(fd, pageOffset); 521 mergeWithMaster(fd); 522 pageOffset += ((PdfReader)readers.get(k)).getNumberOfPages(); 523 } 524 } 525 526 public PdfIndirectReference getPageReference(int page) { 527 return (PdfIndirectReference)pageRefs.get(page - 1); 528 } 529 530 protected PdfDictionary getCatalog(PdfIndirectReference rootObj) { 531 try { 532 PdfDictionary cat = pdf.getCatalog(rootObj); 533 if (form != null) { 534 PdfIndirectReference ref = addToBody(form).getIndirectReference(); 535 cat.put(PdfName.ACROFORM, ref); 536 } 537 if (newBookmarks == null || newBookmarks.isEmpty()) 538 return cat; 539 PdfDictionary top = new PdfDictionary(); 540 PdfIndirectReference topRef = getPdfIndirectReference(); 541 Object kids[] = SimpleBookmark.iterateOutlines(this, topRef, newBookmarks, false); 542 top.put(PdfName.FIRST, (PdfIndirectReference)kids[0]); 543 top.put(PdfName.LAST, (PdfIndirectReference)kids[1]); 544 top.put(PdfName.COUNT, new PdfNumber(((Integer )kids[2]).intValue())); 545 addToBody(top, topRef); 546 cat.put(PdfName.OUTLINES, topRef); 547 return cat; 548 } 549 catch (IOException e) { 550 throw new ExceptionConverter(e); 551 } 552 } 553 554 protected PdfIndirectReference getNewReference(PRIndirectReference ref) { 555 return new PdfIndirectReference(0, getNewObjectNumber(ref.getReader(), ref.getNumber(), 0)); 556 } 557 558 protected int getNewObjectNumber(PdfReader reader, int number, int generation) { 559 IntHashtable refs = (IntHashtable)readers2intrefs.get(reader); 560 int n = refs.get(number); 561 if (n == 0) { 562 n = getIndirectReferenceNumber(); 563 refs.put(number, n); 564 } 565 return n; 566 } 567 568 protected boolean isVisited(PdfReader reader, int number, int generation) { 569 IntHashtable refs = (IntHashtable)readers2intrefs.get(reader); 570 return refs.containsKey(number); 571 } 572 573 protected boolean isVisited(PRIndirectReference ref) { 574 IntHashtable refs = (IntHashtable)visited.get(ref.getReader()); 575 return refs.containsKey(ref.getNumber()); 576 } 577 578 protected boolean setVisited(PRIndirectReference ref) { 579 IntHashtable refs = (IntHashtable)visited.get(ref.getReader()); 580 return (refs.put(ref.getNumber(), 1) != 0); 581 } 582 583 protected boolean isPage(PRIndirectReference ref) { 584 IntHashtable refs = (IntHashtable)pages2intrefs.get(ref.getReader()); 585 return refs.containsKey(ref.getNumber()); 586 } 587 588 RandomAccessFileOrArray getReaderFile(PdfReader reader) { 589 return file; 590 } 591 592 597 public void setOutlines(List outlines) { 598 newBookmarks = outlines; 599 } 600 601 public void openDoc() { 602 if (!nd.isOpen()) 603 nd.open(); 604 } 605 606 protected static final HashMap widgetKeys = new HashMap (); 607 protected static final HashMap fieldKeys = new HashMap (); 608 static { 609 Integer one = new Integer (1); 610 widgetKeys.put(PdfName.SUBTYPE, one); 611 widgetKeys.put(PdfName.CONTENTS, one); 612 widgetKeys.put(PdfName.RECT, one); 613 widgetKeys.put(PdfName.NM, one); 614 widgetKeys.put(PdfName.M, one); 615 widgetKeys.put(PdfName.F, one); 616 widgetKeys.put(PdfName.BS, one); 617 widgetKeys.put(PdfName.BORDER, one); 618 widgetKeys.put(PdfName.AP, one); 619 widgetKeys.put(PdfName.AS, one); 620 widgetKeys.put(PdfName.C, one); 621 widgetKeys.put(PdfName.A, one); 622 widgetKeys.put(PdfName.STRUCTPARENT, one); 623 widgetKeys.put(PdfName.OC, one); 624 widgetKeys.put(PdfName.H, one); 625 widgetKeys.put(PdfName.MK, one); 626 widgetKeys.put(PdfName.DA, one); 627 widgetKeys.put(PdfName.Q, one); 628 fieldKeys.put(PdfName.AA, one); 629 fieldKeys.put(PdfName.FT, one); 630 fieldKeys.put(PdfName.TU, one); 631 fieldKeys.put(PdfName.TM, one); 632 fieldKeys.put(PdfName.FF, one); 633 fieldKeys.put(PdfName.V, one); 634 fieldKeys.put(PdfName.DV, one); 635 fieldKeys.put(PdfName.DS, one); 636 fieldKeys.put(PdfName.RV, one); 637 fieldKeys.put(PdfName.OPT, one); 638 fieldKeys.put(PdfName.MAXLEN, one); 639 fieldKeys.put(PdfName.TI, one); 640 fieldKeys.put(PdfName.I, one); 641 fieldKeys.put(PdfName.LOCK, one); 642 fieldKeys.put(PdfName.SV, one); 643 } 644 } 645 | Popular Tags |