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