1 49 50 package com.lowagie.text.pdf; 51 52 import java.io.ByteArrayInputStream ; 53 import java.io.ByteArrayOutputStream ; 54 import java.io.IOException ; 55 import java.util.ArrayList ; 56 import java.util.Collection ; 57 import java.util.EmptyStackException ; 58 import java.util.HashMap ; 59 import java.util.Iterator ; 60 61 import javax.xml.parsers.DocumentBuilder ; 62 import javax.xml.parsers.DocumentBuilderFactory ; 63 import javax.xml.parsers.ParserConfigurationException ; 64 65 import org.w3c.dom.Node ; 66 import org.xml.sax.SAXException ; 67 68 import com.lowagie.text.xml.XmlDomWriter; 69 70 74 public class XfaForm { 75 76 private Xml2SomTemplate templateSom; 77 private Xml2SomDatasets datasetsSom; 78 private AcroFieldsSearch acroFieldsSom; 79 private PdfReader reader; 80 private boolean xfaPresent; 81 private org.w3c.dom.Document domDocument; 82 private boolean changed; 83 private Node datasetsNode; 84 public static final String XFA_DATA_SCHEMA = "http://www.xfa.org/schema/xfa-data/1.0/"; 85 86 89 public XfaForm() { 90 } 91 92 100 public XfaForm(PdfReader reader) throws IOException , ParserConfigurationException , SAXException { 101 this.reader = reader; 102 PdfDictionary af = (PdfDictionary)PdfReader.getPdfObjectRelease(reader.getCatalog().get(PdfName.ACROFORM)); 103 if (af == null) { 104 xfaPresent = false; 105 return; 106 } 107 PdfObject xfa = PdfReader.getPdfObjectRelease(af.get(PdfName.XFA)); 108 if (xfa == null) { 109 xfaPresent = false; 110 return; 111 } 112 xfaPresent = true; 113 ByteArrayOutputStream bout = new ByteArrayOutputStream (); 114 if (xfa.isArray()) { 115 ArrayList ar = ((PdfArray)xfa).getArrayList(); 116 for (int k = 1; k < ar.size(); k += 2) { 117 PdfObject ob = PdfReader.getPdfObject((PdfObject)ar.get(k)); 118 if (ob instanceof PRStream) { 119 byte[] b = PdfReader.getStreamBytes((PRStream)ob); 120 bout.write(b); 121 } 122 } 123 } 124 else if (xfa instanceof PRStream) { 125 byte[] b = PdfReader.getStreamBytes((PRStream)xfa); 126 bout.write(b); 127 } 128 bout.close(); 129 DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance(); 130 fact.setNamespaceAware(true); 131 DocumentBuilder db = fact.newDocumentBuilder(); 132 domDocument = db.parse(new ByteArrayInputStream (bout.toByteArray())); 133 Node n = domDocument.getFirstChild(); 134 n = n.getFirstChild(); 135 while (n != null) { 136 if (n.getNodeType() == Node.ELEMENT_NODE) { 137 String s = n.getLocalName(); 138 if (s.equals("template")) { 139 templateSom = new Xml2SomTemplate(n); 140 } 141 else if (s.equals("datasets")) { 142 datasetsNode = n; 143 datasetsSom = new Xml2SomDatasets(n.getFirstChild()); 144 } 145 } 146 n = n.getNextSibling(); 147 } 148 } 149 150 157 public static void setXfa(byte[] xfaData, PdfReader reader, PdfWriter writer) throws IOException { 158 PdfDictionary af = (PdfDictionary)PdfReader.getPdfObjectRelease(reader.getCatalog().get(PdfName.ACROFORM)); 159 if (af == null) { 160 return; 161 } 162 reader.killXref(af.get(PdfName.XFA)); 163 PdfStream str = new PdfStream(xfaData); 164 str.flateCompress(); 165 PdfIndirectReference ref = writer.addToBody(str).getIndirectReference(); 166 af.put(PdfName.XFA, ref); 167 } 168 169 174 public void setXfa(PdfWriter writer) throws IOException { 175 setXfa(serializeDoc(domDocument), reader, writer); 176 } 177 178 184 public static byte[] serializeDoc(Node n) throws IOException { 185 XmlDomWriter xw = new XmlDomWriter(); 186 ByteArrayOutputStream fout = new ByteArrayOutputStream (); 187 xw.setOutput(fout, null); 188 xw.setCanonical(false); 189 xw.write(n); 190 fout.close(); 191 return fout.toByteArray(); 192 } 193 194 198 public boolean isXfaPresent() { 199 return xfaPresent; 200 } 201 202 206 public org.w3c.dom.Document getDomDocument() { 207 return domDocument; 208 } 209 210 211 218 public String findFieldName(String name, AcroFields af) { 219 HashMap items = af.getFields(); 220 if (items.containsKey(name)) 221 return name; 222 if (acroFieldsSom == null) { 223 acroFieldsSom = new AcroFieldsSearch(items.keySet()); 224 } 225 if (acroFieldsSom.getAcroShort2LongName().containsKey(name)) 226 return (String )acroFieldsSom.getAcroShort2LongName().get(name); 227 return acroFieldsSom.inverseSearchGlobal(Xml2Som.splitParts(name)); 228 } 229 230 236 public String findDatasetsName(String name) { 237 if (datasetsSom.getName2Node().containsKey(name)) 238 return name; 239 return datasetsSom.inverseSearchGlobal(Xml2Som.splitParts(name)); 240 } 241 242 248 public Node findDatasetsNode(String name) { 249 if (name == null) 250 return null; 251 name = findDatasetsName(name); 252 if (name == null) 253 return null; 254 return (Node )datasetsSom.getName2Node().get(name); 255 } 256 257 262 public static String getNodeText(Node n) { 263 if (n == null) 264 return ""; 265 return getNodeText(n, ""); 266 267 } 268 269 private static String getNodeText(Node n, String name) { 270 Node n2 = n.getFirstChild(); 271 while (n2 != null) { 272 if (n2.getNodeType() == Node.ELEMENT_NODE) { 273 name = getNodeText(n2, name); 274 } 275 else if (n2.getNodeType() == Node.TEXT_NODE) { 276 name += n2.getNodeValue(); 277 } 278 n2 = n2.getNextSibling(); 279 } 280 return name; 281 } 282 283 289 public void setNodeText(Node n, String text) { 290 if (n == null) 291 return; 292 Node nc = null; 293 while ((nc = n.getFirstChild()) != null) { 294 n.removeChild(nc); 295 } 296 if (n.getAttributes().getNamedItemNS(XFA_DATA_SCHEMA, "dataNode") != null) 297 n.getAttributes().removeNamedItemNS(XFA_DATA_SCHEMA, "dataNode"); 298 n.appendChild(domDocument.createTextNode(text)); 299 changed = true; 300 } 301 302 306 public void setXfaPresent(boolean xfaPresent) { 307 this.xfaPresent = xfaPresent; 308 } 309 310 314 public void setDomDocument(org.w3c.dom.Document domDocument) { 315 this.domDocument = domDocument; 316 } 317 318 322 public PdfReader getReader() { 323 return reader; 324 } 325 326 330 public void setReader(PdfReader reader) { 331 this.reader = reader; 332 } 333 334 338 public boolean isChanged() { 339 return changed; 340 } 341 342 346 public void setChanged(boolean changed) { 347 this.changed = changed; 348 } 349 350 354 public static class InverseStore { 355 protected ArrayList part = new ArrayList (); 356 protected ArrayList follow = new ArrayList (); 357 358 363 public String getDefaultName() { 364 InverseStore store = this; 365 while (true) { 366 Object obj = store.follow.get(0); 367 if (obj instanceof String ) 368 return (String )obj; 369 store = (InverseStore)obj; 370 } 371 } 372 373 381 public boolean isSimilar(String name) { 382 int idx = name.indexOf('['); 383 name = name.substring(0, idx + 1); 384 for (int k = 0; k < part.size(); ++k) { 385 if (((String )part.get(k)).startsWith(name)) 386 return true; 387 } 388 return false; 389 } 390 } 391 392 396 public static class Stack2 extends ArrayList { 397 private static final long serialVersionUID = -7451476576174095212L; 398 399 403 public Object peek() { 404 if (size() == 0) 405 throw new EmptyStackException (); 406 return get(size() - 1); 407 } 408 409 413 public Object pop() { 414 if (size() == 0) 415 throw new EmptyStackException (); 416 Object ret = get(size() - 1); 417 remove(size() - 1); 418 return ret; 419 } 420 421 426 public Object push(Object item) { 427 add(item); 428 return item; 429 } 430 431 435 public boolean empty() { 436 return size() == 0; 437 } 438 } 439 440 443 public static class Xml2Som { 444 447 protected ArrayList order; 448 451 protected HashMap name2Node; 452 455 protected HashMap inverseSearch; 456 459 protected Stack2 stack; 460 463 protected int anform; 464 465 470 public static String escapeSom(String s) { 471 int idx = s.indexOf('.'); 472 if (idx < 0) 473 return s; 474 StringBuffer sb = new StringBuffer (); 475 int last = 0; 476 while (idx >= 0) { 477 sb.append(s.substring(last, idx)); 478 sb.append('\\'); 479 last = idx; 480 idx = s.indexOf('.', idx + 1); 481 } 482 sb.append(s.substring(last)); 483 return sb.toString(); 484 } 485 486 491 public static String unescapeSom(String s) { 492 int idx = s.indexOf('\\'); 493 if (idx < 0) 494 return s; 495 StringBuffer sb = new StringBuffer (); 496 int last = 0; 497 while (idx >= 0) { 498 sb.append(s.substring(last, idx)); 499 last = idx + 1; 500 idx = s.indexOf('\\', idx + 1); 501 } 502 sb.append(s.substring(last)); 503 return sb.toString(); 504 } 505 506 511 protected String printStack() { 512 if (stack.empty()) 513 return ""; 514 StringBuffer s = new StringBuffer (); 515 for (int k = 0; k < stack.size(); ++k) 516 s.append('.').append((String )stack.get(k)); 517 return s.substring(1); 518 } 519 520 525 public static String getShortName(String s) { 526 int idx = s.indexOf(".#subform["); 527 if (idx < 0) 528 return s; 529 int last = 0; 530 StringBuffer sb = new StringBuffer (); 531 while (idx >= 0) { 532 sb.append(s.substring(last, idx)); 533 idx = s.indexOf("]", idx + 10); 534 if (idx < 0) 535 return sb.toString(); 536 last = idx + 1; 537 idx = s.indexOf(".#subform[", last); 538 } 539 sb.append(s.substring(last)); 540 return sb.toString(); 541 } 542 543 547 public void inverseSearchAdd(String unstack) { 548 inverseSearchAdd(inverseSearch, stack, unstack); 549 } 550 551 557 public static void inverseSearchAdd(HashMap inverseSearch, Stack2 stack, String unstack) { 558 String last = (String )stack.peek(); 559 InverseStore store = (InverseStore)inverseSearch.get(last); 560 if (store == null) { 561 store = new InverseStore(); 562 inverseSearch.put(last, store); 563 } 564 for (int k = stack.size() - 2; k >= 0; --k) { 565 last = (String )stack.get(k); 566 InverseStore store2; 567 int idx = store.part.indexOf(last); 568 if (idx < 0) { 569 store.part.add(last); 570 store2 = new InverseStore(); 571 store.follow.add(store2); 572 } 573 else 574 store2 = (InverseStore)store.follow.get(idx); 575 store = store2; 576 } 577 store.part.add(""); 578 store.follow.add(unstack); 579 } 580 581 586 public String inverseSearchGlobal(ArrayList parts) { 587 if (parts.isEmpty()) 588 return null; 589 InverseStore store = (InverseStore)inverseSearch.get(parts.get(parts.size() - 1)); 590 if (store == null) 591 return null; 592 for (int k = parts.size() - 2; k >= 0; --k) { 593 String part = (String )parts.get(k); 594 int idx = store.part.indexOf(part); 595 if (idx < 0) { 596 if (store.isSimilar(part)) 597 return null; 598 return store.getDefaultName(); 599 } 600 store = (InverseStore)store.follow.get(idx); 601 } 602 return store.getDefaultName(); 603 } 604 605 610 public static Stack2 splitParts(String name) { 611 while (name.startsWith(".")) 612 name = name.substring(1); 613 Stack2 parts = new Stack2(); 614 int last = 0; 615 int pos = 0; 616 String part; 617 while (true) { 618 pos = last; 619 while (true) { 620 pos = name.indexOf('.', pos); 621 if (pos < 0) 622 break; 623 if (name.charAt(pos - 1) == '\\') 624 ++pos; 625 else 626 break; 627 } 628 if (pos < 0) 629 break; 630 part = name.substring(last, pos); 631 if (!part.endsWith("]")) 632 part += "[0]"; 633 parts.add(part); 634 last = pos + 1; 635 } 636 part = name.substring(last); 637 if (!part.endsWith("]")) 638 part += "[0]"; 639 parts.add(part); 640 return parts; 641 } 642 643 647 public ArrayList getOrder() { 648 return order; 649 } 650 651 655 public void setOrder(ArrayList order) { 656 this.order = order; 657 } 658 659 663 public HashMap getName2Node() { 664 return name2Node; 665 } 666 667 671 public void setName2Node(HashMap name2Node) { 672 this.name2Node = name2Node; 673 } 674 675 679 public HashMap getInverseSearch() { 680 return inverseSearch; 681 } 682 683 687 public void setInverseSearch(HashMap inverseSearch) { 688 this.inverseSearch = inverseSearch; 689 } 690 } 691 692 695 public static class Xml2SomDatasets extends Xml2Som { 696 701 public Xml2SomDatasets(Node n) { 702 order = new ArrayList (); 703 name2Node = new HashMap (); 704 stack = new Stack2(); 705 anform = 0; 706 inverseSearch = new HashMap (); 707 processDatasetsInternal(n); 708 } 709 710 716 public Node insertNode(Node n, String shortName) { 717 Stack2 stack = splitParts(shortName); 718 org.w3c.dom.Document doc = n.getOwnerDocument(); 719 Node n2 = null; 720 n = n.getFirstChild(); 721 for (int k = 0; k < stack.size(); ++k) { 722 String part = (String )stack.get(k); 723 int idx = part.lastIndexOf('['); 724 String name = part.substring(0, idx); 725 idx = Integer.parseInt(part.substring(idx + 1, part.length() - 1)); 726 int found = -1; 727 for (n2 = n.getFirstChild(); n2 != null; n2 = n2.getNextSibling()) { 728 if (n2.getNodeType() == Node.ELEMENT_NODE) { 729 String s = escapeSom(n2.getLocalName()); 730 if (s.equals(name)) { 731 ++found; 732 if (found == idx) 733 break; 734 } 735 } 736 } 737 for (; found < idx; ++found) { 738 n2 = doc.createElementNS(null, name); 739 n2 = n.appendChild(n2); 740 Node attr = doc.createAttributeNS(XFA_DATA_SCHEMA, "dataNode"); 741 attr.setNodeValue("dataGroup"); 742 n2.getAttributes().setNamedItemNS(attr); 743 } 744 n = n2; 745 } 746 inverseSearchAdd(inverseSearch, stack, shortName); 747 name2Node.put(shortName, n2); 748 order.add(shortName); 749 return n2; 750 } 751 752 private static boolean hasChildren(Node n) { 753 Node dataNodeN = n.getAttributes().getNamedItemNS(XFA_DATA_SCHEMA, "dataNode"); 754 if (dataNodeN != null) { 755 String dataNode = dataNodeN.getNodeValue(); 756 if ("dataGroup".equals(dataNode)) 757 return true; 758 else if ("dataValue".equals(dataNode)) 759 return false; 760 } 761 if (!n.hasChildNodes()) 762 return false; 763 Node n2 = n.getFirstChild(); 764 while (n2 != null) { 765 if (n2.getNodeType() == Node.ELEMENT_NODE) { 766 return true; 767 } 768 n2 = n2.getNextSibling(); 769 } 770 return false; 771 } 772 773 private void processDatasetsInternal(Node n) { 774 HashMap ss = new HashMap (); 775 Node n2 = n.getFirstChild(); 776 while (n2 != null) { 777 if (n2.getNodeType() == Node.ELEMENT_NODE) { 778 String s = escapeSom(n2.getLocalName()); 779 Integer i = (Integer )ss.get(s); 780 if (i == null) 781 i = new Integer (0); 782 else 783 i = new Integer (i.intValue() + 1); 784 ss.put(s, i); 785 if (hasChildren(n2)) { 786 stack.push(s + "[" + i.toString() + "]"); 787 processDatasetsInternal(n2); 788 stack.pop(); 789 } 790 else { 791 stack.push(s + "[" + i.toString() + "]"); 792 String unstack = printStack(); 793 order.add(unstack); 794 inverseSearchAdd(unstack); 795 name2Node.put(unstack, n2); 796 stack.pop(); 797 } 798 } 799 n2 = n2.getNextSibling(); 800 } 801 } 802 } 803 804 807 public static class AcroFieldsSearch extends Xml2Som { 808 private HashMap acroShort2LongName; 809 810 814 public AcroFieldsSearch(Collection items) { 815 inverseSearch = new HashMap (); 816 acroShort2LongName = new HashMap (); 817 for (Iterator it = items.iterator(); it.hasNext();) { 818 String itemName = (String )it.next(); 819 String itemShort = getShortName(itemName); 820 acroShort2LongName.put(itemShort, itemName); 821 inverseSearchAdd(inverseSearch, splitParts(itemShort), itemName); 822 } 823 } 824 825 830 public HashMap getAcroShort2LongName() { 831 return acroShort2LongName; 832 } 833 834 839 public void setAcroShort2LongName(HashMap acroShort2LongName) { 840 this.acroShort2LongName = acroShort2LongName; 841 } 842 } 843 844 847 public static class Xml2SomTemplate extends Xml2Som { 848 private boolean dynamicForm; 849 private int templateLevel; 850 851 855 public Xml2SomTemplate(Node n) { 856 order = new ArrayList (); 857 name2Node = new HashMap (); 858 stack = new Stack2(); 859 anform = 0; 860 templateLevel = 0; 861 inverseSearch = new HashMap (); 862 processTemplate(n, null); 863 } 864 865 870 public String getFieldType(String s) { 871 Node n = (Node )name2Node.get(s); 872 if (n == null) 873 return null; 874 if (n.getLocalName().equals("exclGroup")) 875 return "exclGroup"; 876 Node ui = n.getFirstChild(); 877 while (ui != null) { 878 if (ui.getNodeType() == Node.ELEMENT_NODE && ui.getLocalName().equals("ui")) { 879 break; 880 } 881 ui = ui.getNextSibling(); 882 } 883 if (ui == null) 884 return null; 885 Node type = ui.getFirstChild(); 886 while (type != null) { 887 if (type.getNodeType() == Node.ELEMENT_NODE && !(type.getLocalName().equals("extras") && type.getLocalName().equals("picture"))) { 888 return type.getLocalName(); 889 } 890 type = type.getNextSibling(); 891 } 892 return null; 893 } 894 895 private void processTemplate(Node n, HashMap ff) { 896 if (ff == null) 897 ff = new HashMap (); 898 HashMap ss = new HashMap (); 899 Node n2 = n.getFirstChild(); 900 while (n2 != null) { 901 if (n2.getNodeType() == Node.ELEMENT_NODE) { 902 String s = n2.getLocalName(); 903 if (s.equals("subform")) { 904 Node name = n2.getAttributes().getNamedItem("name"); 905 String nn = "#subform"; 906 boolean annon = true; 907 if (name != null) { 908 nn = escapeSom(name.getNodeValue()); 909 annon = false; 910 } 911 Integer i; 912 if (annon) { 913 i = new Integer (anform); 914 ++anform; 915 } 916 else { 917 i = (Integer )ss.get(nn); 918 if (i == null) 919 i = new Integer (0); 920 else 921 i = new Integer (i.intValue() + 1); 922 ss.put(nn, i); 923 } 924 stack.push(nn + "[" + i.toString() + "]"); 925 ++templateLevel; 926 if (annon) 927 processTemplate(n2, ff); 928 else 929 processTemplate(n2, null); 930 --templateLevel; 931 stack.pop(); 932 } 933 else if (s.equals("field") || s.equals("exclGroup")) { 934 Node name = n2.getAttributes().getNamedItem("name"); 935 if (name != null) { 936 String nn = escapeSom(name.getNodeValue()); 937 Integer i = (Integer )ff.get(nn); 938 if (i == null) 939 i = new Integer (0); 940 else 941 i = new Integer (i.intValue() + 1); 942 ff.put(nn, i); 943 stack.push(nn + "[" + i.toString() + "]"); 944 String unstack = printStack(); 945 order.add(unstack); 946 inverseSearchAdd(unstack); 947 name2Node.put(unstack, n2); 948 stack.pop(); 949 } 950 } 951 else if (!dynamicForm && templateLevel > 0 && s.equals("occur")) { 952 int initial = 1; 953 int min = 1; 954 int max = 1; 955 Node a = n2.getAttributes().getNamedItem("initial"); 956 if (a != null) 957 try{initial = Integer.parseInt(a.getNodeValue().trim());}catch(Exception e){} 958 a = n2.getAttributes().getNamedItem("min"); 959 if (a != null) 960 try{min = Integer.parseInt(a.getNodeValue().trim());}catch(Exception e){} 961 a = n2.getAttributes().getNamedItem("max"); 962 if (a != null) 963 try{max = Integer.parseInt(a.getNodeValue().trim());}catch(Exception e){} 964 if (initial != min || min != max) 965 dynamicForm = true; 966 } 967 } 968 n2 = n2.getNextSibling(); 969 } 970 } 971 972 978 public boolean isDynamicForm() { 979 return dynamicForm; 980 } 981 982 986 public void setDynamicForm(boolean dynamicForm) { 987 this.dynamicForm = dynamicForm; 988 } 989 } 990 991 995 public Xml2SomTemplate getTemplateSom() { 996 return templateSom; 997 } 998 999 1003 public void setTemplateSom(Xml2SomTemplate templateSom) { 1004 this.templateSom = templateSom; 1005 } 1006 1007 1011 public Xml2SomDatasets getDatasetsSom() { 1012 return datasetsSom; 1013 } 1014 1015 1019 public void setDatasetsSom(Xml2SomDatasets datasetsSom) { 1020 this.datasetsSom = datasetsSom; 1021 } 1022 1023 1027 public AcroFieldsSearch getAcroFieldsSom() { 1028 return acroFieldsSom; 1029 } 1030 1031 1035 public void setAcroFieldsSom(AcroFieldsSearch acroFieldsSom) { 1036 this.acroFieldsSom = acroFieldsSom; 1037 } 1038 1039 1043 public Node getDatasetsNode() { 1044 return datasetsNode; 1045 } 1046} 1047 | Popular Tags |