1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 /* $Id: PDFExtensionHandler.java 1693610 2015-07-31 14:36:56Z ssteiner $ */ 19 20 package org.apache.fop.render.pdf.extensions; 21 22 import java.util.Stack; 23 24 import org.xml.sax.Attributes; 25 import org.xml.sax.SAXException; 26 import org.xml.sax.helpers.AttributesImpl; 27 import org.xml.sax.helpers.DefaultHandler; 28 29 import org.apache.commons.logging.Log; 30 import org.apache.commons.logging.LogFactory; 31 32 import org.apache.fop.util.ContentHandlerFactory; 33 import org.apache.fop.util.ContentHandlerFactory.ObjectBuiltListener; 34 35 /** 36 * ContentHandler (parser) for restoring PDF extension objects from XML. 37 */ 38 public class PDFExtensionHandler extends DefaultHandler implements ContentHandlerFactory.ObjectSource { 39 40 /** Logger instance */ 41 protected static final Log log = LogFactory.getLog(PDFExtensionHandler.class); 42 43 private PDFExtensionAttachment returnedObject; 44 private ObjectBuiltListener listener; 45 46 // PDFEmbeddedFileAttachment related state 47 private Attributes lastAttributes; 48 49 // PDFDictionaryAttachment related 50 private Stack<PDFCollectionExtension> collections = new Stack<PDFCollectionExtension>(); 51 private boolean captureContent; 52 private StringBuffer characters; 53 54 @Override startElement(String uri, String localName, String qName, Attributes attributes)55 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 56 if (PDFExtensionAttachment.CATEGORY.equals(uri)) { 57 if (localName.equals(PDFEmbeddedFileAttachment.ELEMENT)) { 58 lastAttributes = new AttributesImpl(attributes); 59 } else if (PDFDictionaryType.Action.elementName().equals(localName)) { 60 PDFActionExtension action = new PDFActionExtension(); 61 String id = attributes.getValue(PDFDictionaryElement.ATT_ID); 62 if (id != null) { 63 action.setProperty(PDFDictionaryExtension.PROPERTY_ID, id); 64 } 65 String type = attributes.getValue(PDFActionElement.ATT_TYPE); 66 if (type != null) { 67 action.setProperty(PDFActionExtension.PROPERTY_TYPE, type); 68 } 69 collections.push(action); 70 } else if (PDFObjectType.Array.elementName().equals(localName)) { 71 PDFArrayExtension array = new PDFArrayExtension(); 72 String key = attributes.getValue(PDFCollectionEntryElement.ATT_KEY); 73 if (key != null) { 74 array.setKey(key); 75 } 76 collections.push(array); 77 } else if (PDFDictionaryType.Catalog.elementName().equals(localName)) { 78 PDFCatalogExtension catalog = new PDFCatalogExtension(); 79 collections.push(catalog); 80 } else if (PDFDictionaryType.Dictionary.elementName().equals(localName)) { 81 PDFDictionaryExtension dictionary = new PDFDictionaryExtension(); 82 String key = attributes.getValue(PDFCollectionEntryElement.ATT_KEY); 83 if (key != null) { 84 dictionary.setKey(key); 85 } 86 collections.push(dictionary); 87 } else if (PDFDictionaryType.Layer.elementName().equals(localName)) { 88 PDFLayerExtension layer = new PDFLayerExtension(); 89 String id = attributes.getValue(PDFDictionaryElement.ATT_ID); 90 if (id != null) { 91 layer.setProperty(PDFDictionaryExtension.PROPERTY_ID, id); 92 } 93 collections.push(layer); 94 } else if (PDFDictionaryType.Navigator.elementName().equals(localName)) { 95 PDFNavigatorExtension navigator = new PDFNavigatorExtension(); 96 String id = attributes.getValue(PDFDictionaryElement.ATT_ID); 97 if (id != null) { 98 navigator.setProperty(PDFDictionaryExtension.PROPERTY_ID, id); 99 } 100 collections.push(navigator); 101 } else if (PDFDictionaryType.Page.elementName().equals(localName)) { 102 PDFPageExtension page = new PDFPageExtension(); 103 String pageNumbers = attributes.getValue(PDFPageElement.ATT_PAGE_NUMBERS); 104 if (pageNumbers != null) { 105 page.setProperty(PDFPageExtension.PROPERTY_PAGE_NUMBERS, pageNumbers); 106 } 107 collections.push(page); 108 } else if (PDFDictionaryType.Info.elementName().equals(localName)) { 109 PDFDocumentInformationExtension info = new PDFDocumentInformationExtension(); 110 collections.push(info); 111 } else if (PDFDictionaryType.VT.elementName().equals(localName)) { 112 PDFVTExtension dictionary = new PDFVTExtension(); 113 collections.push(dictionary); 114 } else if (PDFDictionaryType.PagePiece.elementName().equals(localName)) { 115 PDFPagePieceExtension dictionary = new PDFPagePieceExtension(); 116 collections.push(dictionary); 117 } else if (PDFObjectType.hasValueOfElementName(localName)) { 118 PDFCollectionEntryExtension entry; 119 if (PDFObjectType.Reference.elementName().equals(localName)) { 120 entry = new PDFReferenceExtension(); 121 } else { 122 entry = new PDFCollectionEntryExtension(PDFObjectType.valueOfElementName(localName)); 123 } 124 String key = attributes.getValue(PDFCollectionEntryElement.ATT_KEY); 125 if (key != null) { 126 entry.setKey(key); 127 } 128 if (entry instanceof PDFReferenceExtension) { 129 String refid = attributes.getValue(PDFReferenceElement.ATT_REFID); 130 if (refid != null) { 131 ((PDFReferenceExtension) entry).setReferenceId(refid); 132 } 133 } 134 if (!collections.empty()) { 135 PDFCollectionExtension collection = collections.peek(); 136 collection.addEntry(entry); 137 if (!(entry instanceof PDFReferenceExtension)) { 138 captureContent = true; 139 } 140 } 141 } else { 142 throw new SAXException("Unhandled element " + localName + " in namespace: " + uri); 143 } 144 } else { 145 log.warn("Unhandled element " + localName + " in namespace: " + uri); 146 } 147 } 148 149 @Override characters(char[] data, int start, int length)150 public void characters(char[] data, int start, int length) throws SAXException { 151 if (captureContent) { 152 if (characters == null) { 153 characters = new StringBuffer((length < 16) ? 16 : length); 154 } 155 characters.append(data, start, length); 156 } 157 } 158 159 @Override endElement(String uri, String localName, String qName)160 public void endElement(String uri, String localName, String qName) throws SAXException { 161 if (PDFExtensionAttachment.CATEGORY.equals(uri)) { 162 if (PDFEmbeddedFileAttachment.ELEMENT.equals(localName)) { 163 String name = lastAttributes.getValue("filename"); 164 String src = lastAttributes.getValue("src"); 165 String desc = lastAttributes.getValue("description"); 166 this.lastAttributes = null; 167 this.returnedObject = new PDFEmbeddedFileAttachment(name, src, desc); 168 } else if (PDFDictionaryType.hasValueOfElementName(localName)) { 169 if (!collections.empty() && (collections.peek() instanceof PDFDictionaryExtension)) { 170 PDFDictionaryExtension dictionary = (PDFDictionaryExtension) collections.pop(); 171 if (!collections.empty()) { 172 PDFCollectionExtension collectionOuter = collections.peek(); 173 collectionOuter.addEntry(dictionary); 174 } else if (dictionary.getDictionaryType() != PDFDictionaryType.Dictionary) { 175 this.returnedObject = new PDFDictionaryAttachment(dictionary); 176 } else { 177 throw new SAXException( 178 new IllegalStateException("generic dictionary not permitted at outer level")); 179 } 180 } else { 181 throw new SAXException(new IllegalStateException("collections stack is empty or not a dictionary")); 182 } 183 } else if (PDFObjectType.Array.elementName().equals(localName)) { 184 if (!collections.empty() && (collections.peek() instanceof PDFArrayExtension)) { 185 PDFArrayExtension array = (PDFArrayExtension) collections.pop(); 186 if (!collections.empty()) { 187 PDFCollectionExtension collectionOuter = collections.peek(); 188 collectionOuter.addEntry(array); 189 } else { 190 throw new SAXException(new IllegalStateException("array not permitted at outer level")); 191 } 192 } else { 193 throw new SAXException(new IllegalStateException("collections stack is empty or not an array")); 194 } 195 } else if (PDFObjectType.hasValueOfElementName(localName)) { 196 if (!collections.empty()) { 197 PDFCollectionExtension collection = collections.peek(); 198 PDFCollectionEntryExtension entry = collection.getLastEntry(); 199 if (entry != null) { 200 if (characters != null) { 201 entry.setValue(characters.toString()); 202 characters = null; 203 } 204 } else { 205 throw new SAXException(new IllegalStateException("no current entry")); 206 } 207 } else { 208 throw new SAXException(new IllegalStateException("entry not permitted at outer level")); 209 } 210 } 211 } 212 captureContent = false; 213 } 214 215 @Override endDocument()216 public void endDocument() throws SAXException { 217 if (listener != null) { 218 listener.notifyObjectBuilt(getObject()); 219 } 220 } 221 getObject()222 public Object getObject() { 223 return returnedObject; 224 } 225 setObjectBuiltListener(ObjectBuiltListener listener)226 public void setObjectBuiltListener(ObjectBuiltListener listener) { 227 this.listener = listener; 228 } 229 } 230