blob: a75e61cf3639a0680068ebff2d85b277507c1766 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* $Id$ */
package org.apache.fop.render.intermediate;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.SAXTransformerFactory;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.DefaultHandler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xmlgraphics.util.QName;
import org.apache.fop.accessibility.AccessibilityEventProducer;
import org.apache.fop.accessibility.StructureTreeElement;
import org.apache.fop.accessibility.StructureTreeEventHandler;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.fo.ElementMapping;
import org.apache.fop.fo.ElementMappingRegistry;
import org.apache.fop.fo.expr.PropertyException;
import org.apache.fop.fo.extensions.InternalElementMapping;
import org.apache.fop.render.intermediate.extensions.DocumentNavigationExtensionConstants;
import org.apache.fop.render.intermediate.extensions.DocumentNavigationHandler;
import org.apache.fop.render.intermediate.extensions.GoToXYAction;
import org.apache.fop.traits.BorderProps;
import org.apache.fop.traits.RuleStyle;
import org.apache.fop.util.ColorUtil;
import org.apache.fop.util.ContentHandlerFactory;
import org.apache.fop.util.ContentHandlerFactoryRegistry;
import org.apache.fop.util.DOMBuilderContentHandlerFactory;
import org.apache.fop.util.DefaultErrorListener;
import org.apache.fop.util.LanguageTags;
import org.apache.fop.util.XMLUtil;
/**
* This is a parser for the intermediate format XML which converts the intermediate file into
* {@link IFPainter} events.
*/
public class IFParser implements IFConstants {
/** Logger instance */
protected static final Log log = LogFactory.getLog(IFParser.class);
private static SAXTransformerFactory tFactory
= (SAXTransformerFactory)SAXTransformerFactory.newInstance();
private static Set<String> handledNamespaces = new java.util.HashSet<String>();
static {
handledNamespaces.add(XMLNS_NAMESPACE_URI);
handledNamespaces.add(XML_NAMESPACE);
handledNamespaces.add(NAMESPACE);
handledNamespaces.add(XLINK_NAMESPACE);
}
/**
* Parses an intermediate file and paints it.
* @param src the Source instance pointing to the intermediate file
* @param documentHandler the intermediate format document handler used to process the IF events
* @param userAgent the user agent
* @throws TransformerException if an error occurs while parsing the area tree XML
* @throws IFException if an IF-related error occurs inside the target document handler
*/
public void parse(Source src, IFDocumentHandler documentHandler, FOUserAgent userAgent)
throws TransformerException, IFException {
try {
Transformer transformer = tFactory.newTransformer();
transformer.setErrorListener(new DefaultErrorListener(log));
SAXResult res = new SAXResult(getContentHandler(documentHandler, userAgent));
transformer.transform(src, res);
} catch (TransformerException te) {
Throwable cause = te.getCause();
//Unpack original IFException if applicable
if (cause instanceof SAXException) {
SAXException se = (SAXException) cause;
cause = se.getCause();
if (cause instanceof IFException) {
throw (IFException) cause;
}
} else if (cause instanceof IFException) {
throw (IFException) cause;
}
throw te;
}
}
/**
* Creates a new ContentHandler instance that you can send the area tree XML to. The parsed
* pages are added to the AreaTreeModel instance you pass in as a parameter.
* @param documentHandler the intermediate format document handler used to process the IF events
* @param userAgent the user agent
* @return the ContentHandler instance to receive the SAX stream from the area tree XML
*/
public ContentHandler getContentHandler(IFDocumentHandler documentHandler,
FOUserAgent userAgent) {
ElementMappingRegistry elementMappingRegistry
= userAgent.getElementMappingRegistry();
return new Handler(documentHandler, userAgent, elementMappingRegistry);
}
private static class Handler extends DefaultHandler {
private Map<String, ElementHandler> elementHandlers = new HashMap<String, ElementHandler>();
private IFDocumentHandler documentHandler;
private IFPainter painter;
private FOUserAgent userAgent;
private ElementMappingRegistry elementMappingRegistry;
private Attributes lastAttributes;
private StringBuffer content = new StringBuffer();
private boolean ignoreCharacters = true;
//private Stack delegateStack = new Stack();
private int delegateDepth;
private ContentHandler delegate;
private boolean inForeignObject;
private Document foreignObject;
private ContentHandler navParser;
private StructureTreeHandler structureTreeHandler;
private Attributes pageSequenceAttributes;
private Map<String, StructureTreeElement> structureTreeElements
= new HashMap<String, StructureTreeElement>();
private Map<String, GoToXYAction> unresolvedIds = new HashMap<>();
private class StructureTreeHandler extends DefaultHandler {
protected final StructureTreeEventHandler structureTreeEventHandler;
StructureTreeHandler(StructureTreeEventHandler structureTreeEventHandler) {
this.structureTreeEventHandler = structureTreeEventHandler;
}
void startStructureTree(String type) {
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
if (!"structure-tree".equals(localName)) {
if (localName.equals("marked-content")) {
localName = "#PCDATA";
}
StructureTreeElement parent = getStructureTreeElement(attributes);
String structID = attributes.getValue(InternalElementMapping.URI,
InternalElementMapping.STRUCT_ID);
if (structID == null) {
structureTreeEventHandler.startNode(localName, attributes, parent);
} else if (localName.equals("external-graphic")
|| localName.equals("instream-foreign-object")) {
StructureTreeElement structureTreeElement
= structureTreeEventHandler.startImageNode(localName, attributes, parent);
structureTreeElements.put(structID, structureTreeElement);
} else {
StructureTreeElement structureTreeElement = structureTreeEventHandler
.startReferencedNode(localName, attributes, parent);
structureTreeElements.put(structID, structureTreeElement);
}
}
}
@Override
public void endElement(String uri, String localName, String arqNameg2)
throws SAXException {
if (!"structure-tree".equals(localName)) {
structureTreeEventHandler.endNode(localName);
}
}
}
private class MainStructureTreeHandler extends StructureTreeHandler {
private final Locale pageSequenceLanguage;
MainStructureTreeHandler(StructureTreeEventHandler structureTreeEventHandler,
Locale pageSequenceLanguage) throws SAXException {
super(structureTreeEventHandler);
this.pageSequenceLanguage = pageSequenceLanguage;
}
@Override
void startStructureTree(String type) {
structureTreeEventHandler.startPageSequence(pageSequenceLanguage, type);
}
public void endDocument() throws SAXException {
startIFElement(EL_PAGE_SEQUENCE, pageSequenceAttributes);
pageSequenceAttributes = null;
}
}
public Handler(IFDocumentHandler documentHandler, FOUserAgent userAgent,
ElementMappingRegistry elementMappingRegistry) {
this.documentHandler = documentHandler;
this.userAgent = userAgent;
this.elementMappingRegistry = elementMappingRegistry;
elementHandlers.put(EL_DOCUMENT, new DocumentHandler());
elementHandlers.put(EL_HEADER, new DocumentHeaderHandler());
elementHandlers.put(EL_LOCALE, new LocaleHandler());
elementHandlers.put(EL_TRAILER, new DocumentTrailerHandler());
elementHandlers.put(EL_PAGE_SEQUENCE, new PageSequenceHandler());
elementHandlers.put(EL_PAGE, new PageHandler());
elementHandlers.put(EL_PAGE_HEADER, new PageHeaderHandler());
elementHandlers.put(EL_PAGE_CONTENT, new PageContentHandler());
elementHandlers.put(EL_PAGE_TRAILER, new PageTrailerHandler());
//Page content
elementHandlers.put(EL_VIEWPORT, new ViewportHandler());
elementHandlers.put(EL_GROUP, new GroupHandler());
elementHandlers.put(EL_ID, new IDHandler());
elementHandlers.put(EL_FONT, new FontHandler());
elementHandlers.put(EL_TEXT, new TextHandler());
elementHandlers.put(EL_CLIP_RECT, new ClipRectHandler());
elementHandlers.put(EL_RECT, new RectHandler());
elementHandlers.put(EL_LINE, new LineHandler());
elementHandlers.put(EL_BORDER_RECT, new BorderRectHandler());
elementHandlers.put(EL_IMAGE, new ImageHandler());
}
private void establishForeignAttributes(Map<QName, String> foreignAttributes) {
documentHandler.getContext().setForeignAttributes(foreignAttributes);
}
private void resetForeignAttributes() {
documentHandler.getContext().resetForeignAttributes();
}
/** {@inheritDoc} */
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException {
if (delegate != null) {
delegateDepth++;
delegate.startElement(uri, localName, qName, attributes);
} else {
boolean handled = true;
if (NAMESPACE.equals(uri)) {
if (localName.equals(EL_PAGE_SEQUENCE) && userAgent.isAccessibilityEnabled()) {
pageSequenceAttributes = new AttributesImpl(attributes);
Locale language = getLanguage(attributes);
structureTreeHandler = new MainStructureTreeHandler(
userAgent.getStructureTreeEventHandler(), language);
} else if (localName.equals(EL_STRUCTURE_TREE)) {
if (userAgent.isAccessibilityEnabled()) {
String type = attributes.getValue("type");
structureTreeHandler.startStructureTree(type);
delegate = structureTreeHandler;
} else {
/* Delegate to a handler that does nothing */
delegate = new DefaultHandler();
}
delegateDepth++;
delegate.startDocument();
delegate.startElement(uri, localName, qName, attributes);
} else {
if (pageSequenceAttributes != null) {
/*
* This means that no structure-element tag was
* found in the XML, otherwise a
* StructureTreeBuilderWrapper object would have
* been created, which would have reset the
* pageSequenceAttributes field.
*/
AccessibilityEventProducer.Provider
.get(userAgent.getEventBroadcaster())
.noStructureTreeInXML(this);
}
handled = startIFElement(localName, attributes);
if (EL_ID.equals(localName) && documentHandler.getDocumentNavigationHandler() != null) {
if (this.navParser == null) {
this.navParser = new DocumentNavigationHandler(
documentHandler.getDocumentNavigationHandler(),
structureTreeElements, unresolvedIds);
}
delegate = this.navParser;
delegateDepth++;
delegate.startDocument();
delegate.startElement(uri, localName, qName, attributes);
}
}
} else if (DocumentNavigationExtensionConstants.NAMESPACE.equals(uri)) {
if (this.navParser == null) {
this.navParser = new DocumentNavigationHandler(
this.documentHandler.getDocumentNavigationHandler(),
structureTreeElements, unresolvedIds);
}
delegate = this.navParser;
delegateDepth++;
delegate.startDocument();
delegate.startElement(uri, localName, qName, attributes);
} else {
ContentHandlerFactoryRegistry registry
= userAgent.getContentHandlerFactoryRegistry();
ContentHandlerFactory factory = registry.getFactory(uri);
if (factory == null) {
DOMImplementation domImplementation
= elementMappingRegistry.getDOMImplementationForNamespace(uri);
if (domImplementation == null) {
domImplementation = ElementMapping.getDefaultDOMImplementation();
/*
throw new SAXException("No DOMImplementation could be"
+ " identified to handle namespace: " + uri);
*/
}
factory = new DOMBuilderContentHandlerFactory(uri, domImplementation);
}
delegate = factory.createContentHandler();
delegateDepth++;
delegate.startDocument();
delegate.startElement(uri, localName, qName, attributes);
}
if (!handled) {
if (uri == null || uri.length() == 0) {
throw new SAXException("Unhandled element " + localName
+ " in namespace: " + uri);
} else {
log.warn("Unhandled element " + localName
+ " in namespace: " + uri);
}
}
}
}
private static Locale getLanguage(Attributes attributes) {
String xmllang = attributes.getValue(XML_NAMESPACE, "lang");
return (xmllang == null) ? null : LanguageTags.toLocale(xmllang);
}
private boolean startIFElement(String localName, Attributes attributes)
throws SAXException {
lastAttributes = new AttributesImpl(attributes);
ElementHandler elementHandler = elementHandlers.get(localName);
content.setLength(0);
ignoreCharacters = true;
if (elementHandler != null) {
ignoreCharacters = elementHandler.ignoreCharacters();
try {
elementHandler.startElement(attributes);
} catch (IFException ife) {
handleIFException(ife);
}
return true;
} else {
return false;
}
}
private void handleIFException(IFException ife) throws SAXException {
Throwable cause = ife.getCause();
if (cause instanceof SAXException) {
//unwrap
throw (SAXException) cause;
} else {
//wrap
throw new SAXException(ife);
}
}
/** {@inheritDoc} */
public void endElement(String uri, String localName, String qName) throws SAXException {
if (delegate != null) {
delegate.endElement(uri, localName, qName);
delegateDepth--;
if (delegateDepth == 0) {
delegate.endDocument();
if (delegate instanceof ContentHandlerFactory.ObjectSource) {
Object obj = ((ContentHandlerFactory.ObjectSource)delegate).getObject();
if (inForeignObject) {
this.foreignObject = (Document)obj;
} else {
handleExternallyGeneratedObject(obj);
}
}
delegate = null; //Sub-document is processed, return to normal processing
}
} else {
if (NAMESPACE.equals(uri)) {
ElementHandler elementHandler = elementHandlers.get(localName);
if (elementHandler != null) {
try {
if (elementHandler instanceof DocumentHandler) {
addUnresolvedIds();
}
elementHandler.endElement();
} catch (IFException ife) {
handleIFException(ife);
}
content.setLength(0);
}
ignoreCharacters = true;
} else {
if (log.isTraceEnabled()) {
log.trace("Ignoring " + localName + " in namespace: " + uri);
}
}
}
}
private void addUnresolvedIds() throws IFException {
for (GoToXYAction action : unresolvedIds.values()) {
if (action != null) {
documentHandler.getDocumentNavigationHandler().addResolvedAction(action);
}
}
}
// ============== Element handlers for the intermediate format =============
private interface ElementHandler {
void startElement(Attributes attributes) throws IFException, SAXException;
void endElement() throws IFException;
boolean ignoreCharacters();
}
private abstract class AbstractElementHandler implements ElementHandler {
public void startElement(Attributes attributes) throws IFException, SAXException {
//nop
}
public void endElement() throws IFException {
//nop
}
public boolean ignoreCharacters() {
return true;
}
}
private class DocumentHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
documentHandler.startDocument();
}
public void endElement() throws IFException {
documentHandler.endDocument();
}
}
private class DocumentHeaderHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
documentHandler.startDocumentHeader();
}
public void endElement() throws IFException {
documentHandler.endDocumentHeader();
}
}
private class LocaleHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
documentHandler.setDocumentLocale(getLanguage(attributes));
}
}
private class DocumentTrailerHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
documentHandler.startDocumentTrailer();
}
public void endElement() throws IFException {
documentHandler.endDocumentTrailer();
}
}
private class PageSequenceHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
String id = attributes.getValue("id");
Locale language = getLanguage(attributes);
if (language != null) {
documentHandler.getContext().setLanguage(language);
}
Map<QName, String> foreignAttributes = getForeignAttributes(lastAttributes);
establishForeignAttributes(foreignAttributes);
documentHandler.startPageSequence(id);
resetForeignAttributes();
}
public void endElement() throws IFException {
documentHandler.endPageSequence();
documentHandler.getContext().setLanguage(null);
}
}
private class PageHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
int index = Integer.parseInt(attributes.getValue("index"));
String name = attributes.getValue("name");
String pageMasterName = attributes.getValue("page-master-name");
int width = Integer.parseInt(attributes.getValue("width"));
int height = Integer.parseInt(attributes.getValue("height"));
Map<QName, String> foreignAttributes = getForeignAttributes(lastAttributes);
establishForeignAttributes(foreignAttributes);
documentHandler.startPage(index, name, pageMasterName,
new Dimension(width, height));
documentHandler.getContext().setPageNumber(index + 1);
resetForeignAttributes();
}
public void endElement() throws IFException {
documentHandler.endPage();
}
}
private class PageHeaderHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
documentHandler.startPageHeader();
structureTreeHandler = new StructureTreeHandler(userAgent.getStructureTreeEventHandler());
}
public void endElement() throws IFException {
documentHandler.endPageHeader();
}
}
private class PageContentHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
painter = documentHandler.startPageContent();
}
public void endElement() throws IFException {
painter = null;
documentHandler.getContext().setID("");
documentHandler.endPageContent();
}
}
private class PageTrailerHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
documentHandler.startPageTrailer();
}
public void endElement() throws IFException {
documentHandler.endPageTrailer();
}
}
private class ViewportHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
String transform = attributes.getValue("transform");
AffineTransform[] transforms
= AffineTransformArrayParser.createAffineTransform(transform);
int width = Integer.parseInt(attributes.getValue("width"));
int height = Integer.parseInt(attributes.getValue("height"));
Rectangle clipRect = XMLUtil.getAttributeAsRectangle(attributes, "clip-rect");
painter.startViewport(transforms, new Dimension(width, height), clipRect);
documentHandler.getContext().setRegionType(attributes.getValue("region-type"));
}
public void endElement() throws IFException {
painter.endViewport();
}
}
private class GroupHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
String transform = attributes.getValue("transform");
AffineTransform[] transforms
= AffineTransformArrayParser.createAffineTransform(transform);
String layer = attributes.getValue("layer");
painter.startGroup(transforms, layer);
}
public void endElement() throws IFException {
painter.endGroup();
}
}
private class IDHandler extends AbstractElementHandler {
@Override
public void startElement(Attributes attributes) throws IFException, SAXException {
String id = attributes.getValue("name");
documentHandler.getContext().setID(id);
}
}
private class FontHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
String family = attributes.getValue("family");
String style = attributes.getValue("style");
Integer weight = XMLUtil.getAttributeAsInteger(attributes, "weight");
String variant = attributes.getValue("variant");
Integer size = XMLUtil.getAttributeAsInteger(attributes, "size");
Color color;
try {
color = getAttributeAsColor(attributes, "color");
} catch (PropertyException pe) {
throw new IFException("Error parsing the color attribute", pe);
}
painter.setFont(family, style, weight, variant, size, color);
}
}
private class TextHandler extends AbstractElementHandler {
public void endElement() throws IFException {
int x = Integer.parseInt(lastAttributes.getValue("x"));
int y = Integer.parseInt(lastAttributes.getValue("y"));
String s = lastAttributes.getValue("letter-spacing");
int letterSpacing = (s != null ? Integer.parseInt(s) : 0);
s = lastAttributes.getValue("word-spacing");
int wordSpacing = (s != null ? Integer.parseInt(s) : 0);
int[] dx = XMLUtil.getAttributeAsIntArray(lastAttributes, "dx");
int[][] dp = XMLUtil.getAttributeAsPositionAdjustments(lastAttributes, "dp");
// if only DX present, then convert DX to DP; otherwise use only DP,
// effectively ignoring DX
if ((dp == null) && (dx != null)) {
dp = IFUtil.convertDXToDP(dx);
}
establishStructureTreeElement(lastAttributes);
boolean isHyphenated = Boolean.valueOf(lastAttributes.getValue("hyphenated"));
if (isHyphenated) {
documentHandler.getContext().setHyphenated(isHyphenated);
}
boolean nextIsSpace = Boolean.valueOf(lastAttributes.getValue("next-is-space"));
painter.drawText(x, y, letterSpacing, wordSpacing, dp, content.toString(), nextIsSpace);
documentHandler.getContext().setHyphenated(false);
resetStructureTreeElement();
}
public boolean ignoreCharacters() {
return false;
}
}
private class ClipRectHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
int x = Integer.parseInt(attributes.getValue("x"));
int y = Integer.parseInt(attributes.getValue("y"));
int width = Integer.parseInt(attributes.getValue("width"));
int height = Integer.parseInt(attributes.getValue("height"));
BorderProps[] borders = new BorderProps[4];
for (int i = 0; i < 4; i++) {
String b = attributes.getValue(SIDES[i]);
if (b != null) {
borders[i] = BorderProps.valueOf(userAgent, b);
}
}
if (!(borders[0] == null && borders[1] == null
&& borders[2] == null && borders[3] == null)) {
painter.clipBackground(new Rectangle(x, y, width, height),
borders[0], borders[1], borders[2], borders[3]);
}
painter.clipRect(new Rectangle(x, y, width, height));
}
}
private class RectHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
int x = Integer.parseInt(attributes.getValue("x"));
int y = Integer.parseInt(attributes.getValue("y"));
int width = Integer.parseInt(attributes.getValue("width"));
int height = Integer.parseInt(attributes.getValue("height"));
Color fillColor;
try {
fillColor = getAttributeAsColor(attributes, "fill");
} catch (PropertyException pe) {
throw new IFException("Error parsing the fill attribute", pe);
}
Rectangle rectangularArea = new Rectangle(x, y, width, height);
//TODO should rect be overloaded to include rounded corners
// or should we introduce a new IF element?
BorderProps[] borders = new BorderProps[4];
for (int i = 0; i < 4; i++) {
String b = attributes.getValue(SIDES[i]);
if (b != null) {
borders[i] = BorderProps.valueOf(userAgent, b);
}
}
if (!(borders[0] == null && borders[1] == null
&& borders[2] == null && borders[3] == null)) {
painter.clipBackground(rectangularArea,
borders[0], borders[1], borders[2], borders[3]);
}
painter.fillRect(rectangularArea , fillColor);
}
}
private class LineHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
int x1 = Integer.parseInt(attributes.getValue("x1"));
int y1 = Integer.parseInt(attributes.getValue("y1"));
int x2 = Integer.parseInt(attributes.getValue("x2"));
int y2 = Integer.parseInt(attributes.getValue("y2"));
int width = Integer.parseInt(attributes.getValue("stroke-width"));
Color color;
try {
color = getAttributeAsColor(attributes, "color");
} catch (PropertyException pe) {
throw new IFException("Error parsing the fill attribute", pe);
}
RuleStyle style = RuleStyle.valueOf(attributes.getValue("style"));
painter.drawLine(new Point(x1, y1), new Point(x2, y2), width, color, style);
}
}
private static final String[] SIDES = new String[] {"top", "bottom", "left", "right"};
private class BorderRectHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
int x = Integer.parseInt(attributes.getValue("x"));
int y = Integer.parseInt(attributes.getValue("y"));
int width = Integer.parseInt(attributes.getValue("width"));
int height = Integer.parseInt(attributes.getValue("height"));
BorderProps[] borders = new BorderProps[4];
for (int i = 0; i < 4; i++) {
String b = attributes.getValue(SIDES[i]);
if (b != null) {
borders[i] = BorderProps.valueOf(userAgent, b);
}
}
Color backgroundColor;
try {
backgroundColor = getAttributeAsColor(attributes, "inner-background-color");
} catch (PropertyException pe) {
throw new IFException("Error parsing the color attribute", pe);
}
painter.drawBorderRect(new Rectangle(x, y, width, height),
borders[0], borders[1], borders[2], borders[3], backgroundColor);
}
}
private class ImageHandler extends AbstractElementHandler {
public void startElement(Attributes attributes) throws IFException {
inForeignObject = true;
}
public void endElement() throws IFException {
int x = Integer.parseInt(lastAttributes.getValue("x"));
int y = Integer.parseInt(lastAttributes.getValue("y"));
int width = Integer.parseInt(lastAttributes.getValue("width"));
int height = Integer.parseInt(lastAttributes.getValue("height"));
Map<QName, String> foreignAttributes = getForeignAttributes(lastAttributes);
establishForeignAttributes(foreignAttributes);
establishStructureTreeElement(lastAttributes);
if (foreignObject != null) {
painter.drawImage(foreignObject,
new Rectangle(x, y, width, height));
foreignObject = null;
} else {
String uri = lastAttributes.getValue(
XLINK_HREF.getNamespaceURI(), XLINK_HREF.getLocalName());
if (uri == null) {
throw new IFException("xlink:href is missing on image", null);
}
painter.drawImage(uri, new Rectangle(x, y, width, height));
}
resetStructureTreeElement();
resetForeignAttributes();
inForeignObject = false;
}
public boolean ignoreCharacters() {
return false;
}
}
// ====================================================================
/**
* Handles objects created by "sub-parsers" that implement the ObjectSource interface.
* An example of object handled here are ExtensionAttachments.
* @param obj the Object to be handled.
* @throws SAXException if an error occurs while handling the extension object
*/
protected void handleExternallyGeneratedObject(Object obj) throws SAXException {
try {
documentHandler.handleExtensionObject(obj);
} catch (IFException ife) {
handleIFException(ife);
}
}
private Color getAttributeAsColor(Attributes attributes, String name)
throws PropertyException {
String s = attributes.getValue(name);
if (s == null) {
return null;
} else {
return ColorUtil.parseColorString(userAgent, s);
}
}
private static Map<QName, String> getForeignAttributes(Attributes atts) {
Map<QName, String> foreignAttributes = null;
for (int i = 0, c = atts.getLength(); i < c; i++) {
String ns = atts.getURI(i);
if (ns.length() > 0) {
if (handledNamespaces.contains(ns)) {
continue;
}
if (foreignAttributes == null) {
foreignAttributes = new java.util.HashMap<QName, String>();
}
QName qname = new QName(ns, atts.getQName(i));
foreignAttributes.put(qname, atts.getValue(i));
}
}
return foreignAttributes;
}
private void establishStructureTreeElement(Attributes attributes) {
StructureTreeElement element = getStructureTreeElement(attributes);
if (element != null) {
documentHandler.getContext().setStructureTreeElement(element);
}
}
private StructureTreeElement getStructureTreeElement(Attributes attributes) {
String structRef = attributes.getValue(InternalElementMapping.URI, InternalElementMapping.STRUCT_REF);
if (structRef != null && structRef.length() > 0) {
assert structureTreeElements.containsKey(structRef);
return structureTreeElements.get(structRef);
} else {
return null;
}
}
private void resetStructureTreeElement() {
documentHandler.getContext().resetStructureTreeElement();
}
/** {@inheritDoc} */
public void characters(char[] ch, int start, int length) throws SAXException {
if (delegate != null) {
delegate.characters(ch, start, length);
} else if (!ignoreCharacters) {
this.content.append(ch, start, length);
}
}
}
}