001/* 002 * $Id: PdfCopyFieldsImp.java 4784 2011-03-15 08:33:00Z blowagie $ 003 * 004 * This file is part of the iText (R) project. 005 * Copyright (c) 1998-2011 1T3XT BVBA 006 * Authors: Bruno Lowagie, Paulo Soares, et al. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU Affero General Public License version 3 010 * as published by the Free Software Foundation with the addition of the 011 * following permission added to Section 15 as permitted in Section 7(a): 012 * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY 1T3XT, 013 * 1T3XT DISCLAIMS THE WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. 014 * 015 * This program is distributed in the hope that it will be useful, but 016 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 017 * or FITNESS FOR A PARTICULAR PURPOSE. 018 * See the GNU Affero General Public License for more details. 019 * You should have received a copy of the GNU Affero General Public License 020 * along with this program; if not, see http://www.gnu.org/licenses or write to 021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 022 * Boston, MA, 02110-1301 USA, or download the license from the following URL: 023 * http://itextpdf.com/terms-of-use/ 024 * 025 * The interactive user interfaces in modified source and object code versions 026 * of this program must display Appropriate Legal Notices, as required under 027 * Section 5 of the GNU Affero General Public License. 028 * 029 * In accordance with Section 7(b) of the GNU Affero General Public License, 030 * a covered work must retain the producer line in every PDF that is created 031 * or manipulated using iText. 032 * 033 * You can be released from the requirements of the license by purchasing 034 * a commercial license. Buying such a license is mandatory as soon as you 035 * develop commercial activities involving the iText software without 036 * disclosing the source code of your own applications. 037 * These activities include: offering paid services to customers as an ASP, 038 * serving PDFs on the fly in a web application, shipping iText with a closed 039 * source product. 040 * 041 * For more information, please contact iText Software Corp. at this 042 * address: sales@itextpdf.com 043 */ 044package com.itextpdf.text.pdf; 045 046import java.io.IOException; 047import java.io.OutputStream; 048import java.util.ArrayList; 049import java.util.HashMap; 050import java.util.Iterator; 051import java.util.List; 052import java.util.Map; 053import java.util.StringTokenizer; 054 055import com.itextpdf.text.Document; 056import com.itextpdf.text.DocumentException; 057import com.itextpdf.text.ExceptionConverter; 058import com.itextpdf.text.error_messages.MessageLocalization; 059import com.itextpdf.text.exceptions.BadPasswordException; 060import com.itextpdf.text.pdf.AcroFields.Item; 061 062/** 063 * 064 * @author psoares 065 */ 066class PdfCopyFieldsImp extends PdfWriter { 067 068 private static final PdfName iTextTag = new PdfName("_iTextTag_"); 069 private static final Integer zero = Integer.valueOf(0); 070 ArrayList<PdfReader> readers = new ArrayList<PdfReader>(); 071 HashMap<PdfReader, IntHashtable> readers2intrefs = new HashMap<PdfReader, IntHashtable>(); 072 HashMap<PdfReader, IntHashtable> pages2intrefs = new HashMap<PdfReader, IntHashtable>(); 073 HashMap<PdfReader, IntHashtable> visited = new HashMap<PdfReader, IntHashtable>(); 074 ArrayList<AcroFields> fields = new ArrayList<AcroFields>(); 075 RandomAccessFileOrArray file; 076 HashMap<String, Object> fieldTree = new HashMap<String, Object>(); 077 ArrayList<PdfIndirectReference> pageRefs = new ArrayList<PdfIndirectReference>(); 078 ArrayList<PdfDictionary> pageDics = new ArrayList<PdfDictionary>(); 079 PdfDictionary resources = new PdfDictionary(); 080 PdfDictionary form; 081 boolean closing = false; 082 Document nd; 083 private HashMap<PdfArray, ArrayList<Integer>> tabOrder; 084 private ArrayList<String> calculationOrder = new ArrayList<String>(); 085 private ArrayList<Object> calculationOrderRefs; 086 private boolean hasSignature; 087 088 PdfCopyFieldsImp(OutputStream os) throws DocumentException { 089 this(os, '\0'); 090 } 091 092 PdfCopyFieldsImp(OutputStream os, char pdfVersion) throws DocumentException { 093 super(new PdfDocument(), os); 094 pdf.addWriter(this); 095 if (pdfVersion != 0) 096 super.setPdfVersion(pdfVersion); 097 nd = new Document(); 098 nd.addDocListener(pdf); 099 } 100 101 void addDocument(PdfReader reader, List<Integer> pagesToKeep) throws DocumentException, IOException { 102 if (!readers2intrefs.containsKey(reader) && reader.isTampered()) 103 throw new DocumentException(MessageLocalization.getComposedMessage("the.document.was.reused")); 104 reader = new PdfReader(reader); 105 reader.selectPages(pagesToKeep); 106 if (reader.getNumberOfPages() == 0) 107 return; 108 reader.setTampered(false); 109 addDocument(reader); 110 } 111 112 void addDocument(PdfReader reader) throws DocumentException, IOException { 113 if (!reader.isOpenedWithFullPermissions()) 114 throw new BadPasswordException(MessageLocalization.getComposedMessage("pdfreader.not.opened.with.owner.password")); 115 openDoc(); 116 if (readers2intrefs.containsKey(reader)) { 117 reader = new PdfReader(reader); 118 } 119 else { 120 if (reader.isTampered()) 121 throw new DocumentException(MessageLocalization.getComposedMessage("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 = dic.getAsString(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 /** 159 * @since 2.1.5; before 2.1.5 the method was private 160 */ 161 protected void updateCalculationOrder(PdfReader reader) { 162 PdfDictionary catalog = reader.getCatalog(); 163 PdfDictionary acro = catalog.getAsDict(PdfName.ACROFORM); 164 if (acro == null) 165 return; 166 PdfArray co = acro.getAsArray(PdfName.CO); 167 if (co == null || co.size() == 0) 168 return; 169 AcroFields af = reader.getAcroFields(); 170 for (int k = 0; k < co.size(); ++k) { 171 PdfObject obj = co.getPdfObject(k); 172 if (obj == null || !obj.isIndirect()) 173 continue; 174 String name = getCOName(reader, (PRIndirectReference)obj); 175 if (af.getFieldItem(name) == null) 176 continue; 177 name = "." + name; 178 if (calculationOrder.contains(name)) 179 continue; 180 calculationOrder.add(name); 181 } 182 } 183 184 void propagate(PdfObject obj, PdfIndirectReference refo, boolean restricted) throws IOException { 185 if (obj == null) 186 return; 187// if (refo != null) 188// addToBody(obj, refo); 189 if (obj instanceof PdfIndirectReference) 190 return; 191 switch (obj.type()) { 192 case PdfObject.DICTIONARY: 193 case PdfObject.STREAM: { 194 PdfDictionary dic = (PdfDictionary)obj; 195 for (PdfName key: dic.getKeys()) { 196 if (restricted && (key.equals(PdfName.PARENT) || key.equals(PdfName.KIDS))) 197 continue; 198 PdfObject ob = dic.get(key); 199 if (ob != null && ob.isIndirect()) { 200 PRIndirectReference ind = (PRIndirectReference)ob; 201 if (!setVisited(ind) && !isPage(ind)) { 202 PdfIndirectReference ref = getNewReference(ind); 203 propagate(PdfReader.getPdfObjectRelease(ind), ref, restricted); 204 } 205 } 206 else 207 propagate(ob, null, restricted); 208 } 209 break; 210 } 211 case PdfObject.ARRAY: { 212 //PdfArray arr = new PdfArray(); 213 for (Iterator<PdfObject> it = ((PdfArray)obj).listIterator(); it.hasNext();) { 214 PdfObject ob = 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(MessageLocalization.getComposedMessage("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<Integer> t = tabOrder.get(annots); 236 if (t == null) { 237 t = new ArrayList<Integer>(); 238 int size = annots.size() - 1; 239 for (int k = 0; k < size; ++k) { 240 t.add(zero); 241 } 242 t.add(Integer.valueOf(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 (t.get(k).intValue() <= v) { 250 t.add(k + 1, Integer.valueOf(v)); 251 annots.add(k + 1, ind); 252 size = -2; 253 break; 254 } 255 } 256 if (size != -2) { 257 t.add(0, Integer.valueOf(v)); 258 annots.add(0, ind); 259 } 260 } 261 } 262 263 @SuppressWarnings("unchecked") 264 protected PdfArray branchForm(HashMap<String, Object> level, PdfIndirectReference parent, String fname) throws IOException { 265 PdfArray arr = new PdfArray(); 266 for (Map.Entry<String, Object> entry: level.entrySet()) { 267 String name = 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<String, Object>)obj, ind, fname2)); 280 arr.add(ind); 281 addToBody(dic, ind); 282 } 283 else { 284 ArrayList<Object> list = (ArrayList<Object>)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 = pageDics.get(page - 1); 290 PdfArray annots = pageDic.getAsArray(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 = pageDics.get(page - 1); 304 PdfArray annots = pageDic.getAsArray(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<PdfArray, ArrayList<Integer>>(); 337 calculationOrderRefs = new ArrayList<Object>(calculationOrder); 338 form.put(PdfName.FIELDS, branchForm(fieldTree, null, "")); 339 if (hasSignature) 340 form.put(PdfName.SIGFLAGS, new PdfNumber(3)); 341 PdfArray co = new PdfArray(); 342 for (int k = 0; k < calculationOrderRefs.size(); ++k) { 343 Object obj = calculationOrderRefs.get(k); 344 if (obj instanceof PdfIndirectReference) 345 co.add((PdfIndirectReference)obj); 346 } 347 if (co.size() > 0) 348 form.put(PdfName.CO, co); 349 } 350 351 @Override 352 public void close() { 353 if (closing) { 354 super.close(); 355 return; 356 } 357 closing = true; 358 try { 359 closeIt(); 360 } 361 catch (Exception e) { 362 throw new ExceptionConverter(e); 363 } 364 } 365 366 /** 367 * Creates the new PDF by merging the fields and forms. 368 */ 369 protected void closeIt() throws IOException { 370 for (int k = 0; k < readers.size(); ++k) { 371 readers.get(k).removeFields(); 372 } 373 for (int r = 0; r < readers.size(); ++r) { 374 PdfReader reader = readers.get(r); 375 for (int page = 1; page <= reader.getNumberOfPages(); ++page) { 376 pageRefs.add(getNewReference(reader.getPageOrigRef(page))); 377 pageDics.add(reader.getPageN(page)); 378 } 379 } 380 mergeFields(); 381 createAcroForms(); 382 for (int r = 0; r < readers.size(); ++r) { 383 PdfReader reader = readers.get(r); 384 for (int page = 1; page <= reader.getNumberOfPages(); ++page) { 385 PdfDictionary dic = reader.getPageN(page); 386 PdfIndirectReference pageRef = getNewReference(reader.getPageOrigRef(page)); 387 PdfIndirectReference parent = root.addPageRef(pageRef); 388 dic.put(PdfName.PARENT, parent); 389 propagate(dic, pageRef, false); 390 } 391 } 392 for (Map.Entry<PdfReader, IntHashtable>entry: readers2intrefs.entrySet()) { 393 PdfReader reader = entry.getKey(); 394 try { 395 file = reader.getSafeFile(); 396 file.reOpen(); 397 IntHashtable t = entry.getValue(); 398 int keys[] = t.toOrderedKeys(); 399 for (int k = 0; k < keys.length; ++k) { 400 PRIndirectReference ref = new PRIndirectReference(reader, keys[k]); 401 addToBody(PdfReader.getPdfObjectRelease(ref), t.get(keys[k])); 402 } 403 } 404 finally { 405 try { 406 file.close(); 407 reader.close(); 408 } 409 catch (Exception e) { 410 // empty on purpose 411 } 412 } 413 } 414 pdf.close(); 415 } 416 417 void addPageOffsetToField(Map<String, AcroFields.Item> fd, int pageOffset) { 418 if (pageOffset == 0) 419 return; 420 for (AcroFields.Item item: fd.values()) { 421 for (int k = 0; k < item.size(); ++k) { 422 int p = item.getPage(k).intValue(); 423 item.forcePage(k, p + pageOffset); 424 } 425 } 426 } 427 428 void createWidgets(ArrayList<Object> list, AcroFields.Item item) { 429 for (int k = 0; k < item.size(); ++k) { 430 list.add(item.getPage(k)); 431 PdfDictionary merged = item.getMerged(k); 432 PdfObject dr = merged.get(PdfName.DR); 433 if (dr != null) 434 PdfFormField.mergeResources(resources, (PdfDictionary)PdfReader.getPdfObject(dr)); 435 PdfDictionary widget = new PdfDictionary(); 436 for (Object element : merged.getKeys()) { 437 PdfName key = (PdfName)element; 438 if (widgetKeys.containsKey(key)) 439 widget.put(key, merged.get(key)); 440 } 441 widget.put(iTextTag, new PdfNumber(item.getTabOrder(k).intValue() + 1)); 442 list.add(widget); 443 } 444 } 445 446 @SuppressWarnings("unchecked") 447 void mergeField(String name, AcroFields.Item item) { 448 HashMap<String, Object> map = fieldTree; 449 StringTokenizer tk = new StringTokenizer(name, "."); 450 if (!tk.hasMoreTokens()) 451 return; 452 while (true) { 453 String s = tk.nextToken(); 454 Object obj = map.get(s); 455 if (tk.hasMoreTokens()) { 456 if (obj == null) { 457 obj = new HashMap(); 458 map.put(s, obj); 459 map = (HashMap<String, Object>)obj; 460 continue; 461 } 462 else if (obj instanceof HashMap) 463 map = (HashMap<String, Object>)obj; 464 else 465 return; 466 } 467 else { 468 if (obj instanceof HashMap) 469 return; 470 PdfDictionary merged = item.getMerged(0); 471 if (obj == null) { 472 PdfDictionary field = new PdfDictionary(); 473 if (PdfName.SIG.equals(merged.get(PdfName.FT))) 474 hasSignature = true; 475 for (Object element : merged.getKeys()) { 476 PdfName key = (PdfName)element; 477 if (fieldKeys.containsKey(key)) 478 field.put(key, merged.get(key)); 479 } 480 ArrayList<Object> list = new ArrayList<Object>(); 481 list.add(field); 482 createWidgets(list, item); 483 map.put(s, list); 484 } 485 else { 486 ArrayList<Object> list = (ArrayList<Object>)obj; 487 PdfDictionary field = (PdfDictionary)list.get(0); 488 PdfName type1 = (PdfName)field.get(PdfName.FT); 489 PdfName type2 = (PdfName)merged.get(PdfName.FT); 490 if (type1 == null || !type1.equals(type2)) 491 return; 492 int flag1 = 0; 493 PdfObject f1 = field.get(PdfName.FF); 494 if (f1 != null && f1.isNumber()) 495 flag1 = ((PdfNumber)f1).intValue(); 496 int flag2 = 0; 497 PdfObject f2 = merged.get(PdfName.FF); 498 if (f2 != null && f2.isNumber()) 499 flag2 = ((PdfNumber)f2).intValue(); 500 if (type1.equals(PdfName.BTN)) { 501 if (((flag1 ^ flag2) & PdfFormField.FF_PUSHBUTTON) != 0) 502 return; 503 if ((flag1 & PdfFormField.FF_PUSHBUTTON) == 0 && ((flag1 ^ flag2) & PdfFormField.FF_RADIO) != 0) 504 return; 505 } 506 else if (type1.equals(PdfName.CH)) { 507 if (((flag1 ^ flag2) & PdfFormField.FF_COMBO) != 0) 508 return; 509 } 510 createWidgets(list, item); 511 } 512 return; 513 } 514 } 515 } 516 517 void mergeWithMaster(Map<String, Item> fd) { 518 for (Map.Entry<String, Item> entry: fd.entrySet()) { 519 String name = entry.getKey(); 520 mergeField(name, entry.getValue()); 521 } 522 } 523 524 void mergeFields() { 525 int pageOffset = 0; 526 for (int k = 0; k < fields.size(); ++k) { 527 Map<String, Item> fd = fields.get(k).getFields(); 528 addPageOffsetToField(fd, pageOffset); 529 mergeWithMaster(fd); 530 pageOffset += readers.get(k).getNumberOfPages(); 531 } 532 } 533 534 @Override 535 public PdfIndirectReference getPageReference(int page) { 536 return pageRefs.get(page - 1); 537 } 538 539 @Override 540 protected PdfDictionary getCatalog(PdfIndirectReference rootObj) { 541 try { 542 PdfDictionary cat = pdf.getCatalog(rootObj); 543 if (form != null) { 544 PdfIndirectReference ref = addToBody(form).getIndirectReference(); 545 cat.put(PdfName.ACROFORM, ref); 546 } 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 @Override 559 protected int getNewObjectNumber(PdfReader reader, int number, int generation) { 560 IntHashtable refs = readers2intrefs.get(reader); 561 int n = refs.get(number); 562 if (n == 0) { 563 n = getIndirectReferenceNumber(); 564 refs.put(number, n); 565 } 566 return n; 567 } 568 569 570 /** 571 * Sets a reference to "visited" in the copy process. 572 * @param ref the reference that needs to be set to "visited" 573 * @return true if the reference was set to visited 574 */ 575 protected boolean setVisited(PRIndirectReference ref) { 576 IntHashtable refs = visited.get(ref.getReader()); 577 if (refs != null) 578 return refs.put(ref.getNumber(), 1) != 0; 579 else 580 return false; 581 } 582 583 /** 584 * Checks if a reference has already been "visited" in the copy process. 585 * @param ref the reference that needs to be checked 586 * @return true if the reference was already visited 587 */ 588 protected boolean isVisited(PRIndirectReference ref) { 589 IntHashtable refs = visited.get(ref.getReader()); 590 if (refs != null) 591 return refs.containsKey(ref.getNumber()); 592 else 593 return false; 594 } 595 596 protected boolean isVisited(PdfReader reader, int number, int generation) { 597 IntHashtable refs = readers2intrefs.get(reader); 598 return refs.containsKey(number); 599 } 600 601 /** 602 * Checks if a reference refers to a page object. 603 * @param ref the reference that needs to be checked 604 * @return true is the reference refers to a page object. 605 */ 606 protected boolean isPage(PRIndirectReference ref) { 607 IntHashtable refs = pages2intrefs.get(ref.getReader()); 608 if (refs != null) 609 return refs.containsKey(ref.getNumber()); 610 else 611 return false; 612 } 613 614 @Override 615 RandomAccessFileOrArray getReaderFile(PdfReader reader) { 616 return file; 617 } 618 619 public void openDoc() { 620 if (!nd.isOpen()) 621 nd.open(); 622 } 623 624 protected static final HashMap<PdfName, Integer> widgetKeys = new HashMap<PdfName, Integer>(); 625 protected static final HashMap<PdfName, Integer> fieldKeys = new HashMap<PdfName, Integer>(); 626 static { 627 Integer one = Integer.valueOf(1); 628 widgetKeys.put(PdfName.SUBTYPE, one); 629 widgetKeys.put(PdfName.CONTENTS, one); 630 widgetKeys.put(PdfName.RECT, one); 631 widgetKeys.put(PdfName.NM, one); 632 widgetKeys.put(PdfName.M, one); 633 widgetKeys.put(PdfName.F, one); 634 widgetKeys.put(PdfName.BS, one); 635 widgetKeys.put(PdfName.BORDER, one); 636 widgetKeys.put(PdfName.AP, one); 637 widgetKeys.put(PdfName.AS, one); 638 widgetKeys.put(PdfName.C, one); 639 widgetKeys.put(PdfName.A, one); 640 widgetKeys.put(PdfName.STRUCTPARENT, one); 641 widgetKeys.put(PdfName.OC, one); 642 widgetKeys.put(PdfName.H, one); 643 widgetKeys.put(PdfName.MK, one); 644 widgetKeys.put(PdfName.DA, one); 645 widgetKeys.put(PdfName.Q, one); 646 widgetKeys.put(PdfName.P, one); 647 fieldKeys.put(PdfName.AA, one); 648 fieldKeys.put(PdfName.FT, one); 649 fieldKeys.put(PdfName.TU, one); 650 fieldKeys.put(PdfName.TM, one); 651 fieldKeys.put(PdfName.FF, one); 652 fieldKeys.put(PdfName.V, one); 653 fieldKeys.put(PdfName.DV, one); 654 fieldKeys.put(PdfName.DS, one); 655 fieldKeys.put(PdfName.RV, one); 656 fieldKeys.put(PdfName.OPT, one); 657 fieldKeys.put(PdfName.MAXLEN, one); 658 fieldKeys.put(PdfName.TI, one); 659 fieldKeys.put(PdfName.I, one); 660 fieldKeys.put(PdfName.LOCK, one); 661 fieldKeys.put(PdfName.SV, one); 662 } 663}