blob: dd8a946c2c0b1e010f6a470a23afedc9bff196b5 [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.Paint;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import org.apache.xmlgraphics.image.loader.ImageManager;
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
import org.apache.xmlgraphics.util.QName;
import org.apache.xmlgraphics.util.XMLizable;
import org.apache.fop.accessibility.StructureTreeEventHandler;
import org.apache.fop.fo.extensions.InternalElementMapping;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.render.DefaultRendererConfigurator;
import org.apache.fop.render.RenderingContext;
import org.apache.fop.render.intermediate.IFRendererConfig.IFRendererConfigParser;
import org.apache.fop.render.intermediate.IFStructureTreeBuilder.IFStructureTreeElement;
import org.apache.fop.render.intermediate.extensions.AbstractAction;
import org.apache.fop.render.intermediate.extensions.Bookmark;
import org.apache.fop.render.intermediate.extensions.BookmarkTree;
import org.apache.fop.render.intermediate.extensions.DocumentNavigationExtensionConstants;
import org.apache.fop.render.intermediate.extensions.Link;
import org.apache.fop.render.intermediate.extensions.NamedDestination;
import org.apache.fop.traits.BorderProps;
import org.apache.fop.traits.RuleStyle;
import org.apache.fop.util.ColorUtil;
import org.apache.fop.util.DOM2SAX;
import org.apache.fop.util.LanguageTags;
import org.apache.fop.util.XMLConstants;
import org.apache.fop.util.XMLUtil;
/**
* IFPainter implementation that serializes the intermediate format to XML.
*/
public class IFSerializer extends AbstractXMLWritingIFDocumentHandler
implements IFConstants, IFPainter, IFDocumentNavigationHandler {
/**
* Intermediate Format (IF) version, used to express an @version attribute
* in the root element of the IF document, the initial value of which
* is set to '2.0' to signify that something preceded it (but didn't
* happen to be marked as such), and that this version is not necessarily
* backwards compatible with the unmarked (<2.0) version.
*/
public static final String VERSION = "2.0";
private IFDocumentHandler mimicHandler;
private int pageSequenceIndex; // used for accessibility
/** Holds the intermediate format state */
private IFState state;
private String currentID = "";
private IFStructureTreeBuilder structureTreeBuilder;
public IFSerializer(IFContext context) {
super(context);
}
/** {@inheritDoc} */
@Override
protected String getMainNamespace() {
return NAMESPACE;
}
/** {@inheritDoc} */
public boolean supportsPagesOutOfOrder() {
return false;
//Theoretically supported but disabled to improve performance when
//rendering the IF to the final format later on
}
/** {@inheritDoc} */
public String getMimeType() {
return MIME_TYPE;
}
/** {@inheritDoc} */
public IFDocumentHandlerConfigurator getConfigurator() {
if (this.mimicHandler != null) {
return getMimickedDocumentHandler().getConfigurator();
} else {
return new DefaultRendererConfigurator(getUserAgent(), new IFRendererConfigParser());
}
}
/** {@inheritDoc} */
@Override
public IFDocumentNavigationHandler getDocumentNavigationHandler() {
return this;
}
/**
* Tells this serializer to mimic the given document handler (mostly applies to the font set
* that is used during layout).
* @param targetHandler the document handler to mimic
*/
public void mimicDocumentHandler(IFDocumentHandler targetHandler) {
this.mimicHandler = targetHandler;
}
/**
* Returns the document handler that is being mimicked by this serializer.
* @return the mimicked document handler or null if no such document handler has been set
*/
public IFDocumentHandler getMimickedDocumentHandler() {
return this.mimicHandler;
}
/** {@inheritDoc} */
public FontInfo getFontInfo() {
if (this.mimicHandler != null) {
return this.mimicHandler.getFontInfo();
} else {
return null;
}
}
/** {@inheritDoc} */
public void setFontInfo(FontInfo fontInfo) {
if (this.mimicHandler != null) {
this.mimicHandler.setFontInfo(fontInfo);
}
}
/** {@inheritDoc} */
public void setDefaultFontInfo(FontInfo fontInfo) {
if (this.mimicHandler != null) {
this.mimicHandler.setDefaultFontInfo(fontInfo);
}
}
@Override
public StructureTreeEventHandler getStructureTreeEventHandler() {
if (structureTreeBuilder == null) {
structureTreeBuilder = new IFStructureTreeBuilder();
}
return structureTreeBuilder;
}
/** {@inheritDoc} */
@Override
public void startDocument() throws IFException {
super.startDocument();
try {
handler.startDocument();
handler.startPrefixMapping("", NAMESPACE);
handler.startPrefixMapping(XLINK_PREFIX, XLINK_NAMESPACE);
handler.startPrefixMapping(DocumentNavigationExtensionConstants.PREFIX,
DocumentNavigationExtensionConstants.NAMESPACE);
handler.startPrefixMapping(InternalElementMapping.STANDARD_PREFIX,
InternalElementMapping.URI);
AttributesImpl atts = new AttributesImpl();
addAttribute(atts, "version", VERSION);
handler.startElement(EL_DOCUMENT, atts);
} catch (SAXException e) {
throw new IFException("SAX error in startDocument()", e);
}
}
@Override
public void setDocumentLocale(Locale locale) {
AttributesImpl atts = new AttributesImpl();
atts.addAttribute(XML_NAMESPACE, "lang", "xml:lang", XMLUtil.CDATA,
LanguageTags.toLanguageTag(locale));
try {
handler.startElement(EL_LOCALE, atts);
handler.endElement(EL_LOCALE);
} catch (SAXException e) {
throw new RuntimeException("Unable to create the " + EL_LOCALE + " element.", e);
}
}
/** {@inheritDoc} */
@Override
public void startDocumentHeader() throws IFException {
try {
handler.startElement(EL_HEADER);
} catch (SAXException e) {
throw new IFException("SAX error in startDocumentHeader()", e);
}
}
/** {@inheritDoc} */
@Override
public void endDocumentHeader() throws IFException {
try {
handler.endElement(EL_HEADER);
} catch (SAXException e) {
throw new IFException("SAX error in startDocumentHeader()", e);
}
}
/** {@inheritDoc} */
@Override
public void startDocumentTrailer() throws IFException {
try {
handler.startElement(EL_TRAILER);
} catch (SAXException e) {
throw new IFException("SAX error in startDocumentTrailer()", e);
}
}
/** {@inheritDoc} */
@Override
public void endDocumentTrailer() throws IFException {
try {
handler.endElement(EL_TRAILER);
} catch (SAXException e) {
throw new IFException("SAX error in endDocumentTrailer()", e);
}
}
/** {@inheritDoc} */
public void endDocument() throws IFException {
try {
handler.endElement(EL_DOCUMENT);
handler.endDocument();
finishDocumentNavigation();
} catch (SAXException e) {
throw new IFException("SAX error in endDocument()", e);
}
}
/** {@inheritDoc} */
public void startPageSequence(String id) throws IFException {
try {
AttributesImpl atts = new AttributesImpl();
if (id != null) {
atts.addAttribute(XML_NAMESPACE, "id", "xml:id", XMLUtil.CDATA, id);
}
Locale lang = getContext().getLanguage();
if (lang != null) {
atts.addAttribute(XML_NAMESPACE, "lang", "xml:lang", XMLUtil.CDATA,
LanguageTags.toLanguageTag(lang));
}
XMLUtil.addAttribute(atts, XMLConstants.XML_SPACE, "preserve");
addForeignAttributes(atts);
handler.startElement(EL_PAGE_SEQUENCE, atts);
if (this.getUserAgent().isAccessibilityEnabled()) {
assert (structureTreeBuilder != null);
structureTreeBuilder.replayEventsForPageSequence(handler, pageSequenceIndex++);
}
} catch (SAXException e) {
throw new IFException("SAX error in startPageSequence()", e);
}
}
/** {@inheritDoc} */
public void endPageSequence() throws IFException {
try {
handler.endElement(EL_PAGE_SEQUENCE);
} catch (SAXException e) {
throw new IFException("SAX error in endPageSequence()", e);
}
}
/** {@inheritDoc} */
public void startPage(int index, String name, String pageMasterName, Dimension size)
throws IFException {
try {
AttributesImpl atts = new AttributesImpl();
addAttribute(atts, "index", Integer.toString(index));
addAttribute(atts, "name", name);
if (pageMasterName != null) {
//fox:external-document doesn't have a page-master
addAttribute(atts, "page-master-name", pageMasterName);
}
addAttribute(atts, "width", Integer.toString(size.width));
addAttribute(atts, "height", Integer.toString(size.height));
addForeignAttributes(atts);
getContext().setPageIndex(index);
handler.startElement(EL_PAGE, atts);
} catch (SAXException e) {
throw new IFException("SAX error in startPage()", e);
}
}
/** {@inheritDoc} */
@Override
public void startPageHeader() throws IFException {
try {
handler.startElement(EL_PAGE_HEADER);
if (this.getUserAgent().isAccessibilityEnabled()) {
structureTreeBuilder.replayEventsForRetrievedMarkers(handler);
}
} catch (SAXException e) {
throw new IFException("SAX error in startPageHeader()", e);
}
}
/** {@inheritDoc} */
@Override
public void endPageHeader() throws IFException {
try {
handler.endElement(EL_PAGE_HEADER);
} catch (SAXException e) {
throw new IFException("SAX error in endPageHeader()", e);
}
}
/** {@inheritDoc} */
public IFPainter startPageContent() throws IFException {
try {
handler.startElement(EL_PAGE_CONTENT);
this.state = IFState.create();
return this;
} catch (SAXException e) {
throw new IFException("SAX error in startPageContent()", e);
}
}
/** {@inheritDoc} */
public void endPageContent() throws IFException {
try {
this.state = null;
currentID = "";
handler.endElement(EL_PAGE_CONTENT);
} catch (SAXException e) {
throw new IFException("SAX error in endPageContent()", e);
}
}
/** {@inheritDoc} */
@Override
public void startPageTrailer() throws IFException {
try {
handler.startElement(EL_PAGE_TRAILER);
} catch (SAXException e) {
throw new IFException("SAX error in startPageTrailer()", e);
}
}
/** {@inheritDoc} */
@Override
public void endPageTrailer() throws IFException {
try {
commitNavigation();
handler.endElement(EL_PAGE_TRAILER);
} catch (SAXException e) {
throw new IFException("SAX error in endPageTrailer()", e);
}
}
/** {@inheritDoc} */
public void endPage() throws IFException {
try {
handler.endElement(EL_PAGE);
getContext().setPageIndex(-1);
} catch (SAXException e) {
throw new IFException("SAX error in endPage()", e);
}
}
//---=== IFPainter ===---
/** {@inheritDoc} */
public void startViewport(AffineTransform transform, Dimension size, Rectangle clipRect)
throws IFException {
startViewport(IFUtil.toString(transform), size, clipRect);
}
/** {@inheritDoc} */
public void startViewport(AffineTransform[] transforms, Dimension size, Rectangle clipRect)
throws IFException {
startViewport(IFUtil.toString(transforms), size, clipRect);
}
private void startViewport(String transform, Dimension size, Rectangle clipRect)
throws IFException {
try {
AttributesImpl atts = new AttributesImpl();
if (transform != null && transform.length() > 0) {
addAttribute(atts, "transform", transform);
}
addAttribute(atts, "width", Integer.toString(size.width));
addAttribute(atts, "height", Integer.toString(size.height));
if (clipRect != null) {
addAttribute(atts, "clip-rect", IFUtil.toString(clipRect));
}
handler.startElement(EL_VIEWPORT, atts);
} catch (SAXException e) {
throw new IFException("SAX error in startViewport()", e);
}
}
/** {@inheritDoc} */
public void endViewport() throws IFException {
try {
handler.endElement(EL_VIEWPORT);
} catch (SAXException e) {
throw new IFException("SAX error in endViewport()", e);
}
}
/** {@inheritDoc} */
public void startGroup(AffineTransform[] transforms, String layer) throws IFException {
startGroup(IFUtil.toString(transforms), layer);
}
/** {@inheritDoc} */
public void startGroup(AffineTransform transform, String layer) throws IFException {
startGroup(IFUtil.toString(transform), layer);
}
private void startGroup(String transform, String layer) throws IFException {
try {
AttributesImpl atts = new AttributesImpl();
if (transform != null && transform.length() > 0) {
addAttribute(atts, "transform", transform);
}
if (layer != null && layer.length() > 0) {
addAttribute(atts, "layer", layer);
}
handler.startElement(EL_GROUP, atts);
} catch (SAXException e) {
throw new IFException("SAX error in startGroup()", e);
}
}
/** {@inheritDoc} */
public void endGroup() throws IFException {
try {
handler.endElement(EL_GROUP);
} catch (SAXException e) {
throw new IFException("SAX error in endGroup()", e);
}
}
/** {@inheritDoc} */
public void drawImage(String uri, Rectangle rect) throws IFException {
try {
addID();
AttributesImpl atts = new AttributesImpl();
addAttribute(atts, XLINK_HREF, uri);
addAttribute(atts, "x", Integer.toString(rect.x));
addAttribute(atts, "y", Integer.toString(rect.y));
addAttribute(atts, "width", Integer.toString(rect.width));
addAttribute(atts, "height", Integer.toString(rect.height));
addForeignAttributes(atts);
addStructureReference(atts);
handler.element(EL_IMAGE, atts);
} catch (SAXException e) {
throw new IFException("SAX error in startGroup()", e);
} finally {
ImageSessionContext session = getUserAgent().getImageSessionContext();
ImageManager imageManager = getUserAgent().getImageManager();
imageManager.closeImage(uri, session);
}
}
private void addForeignAttributes(AttributesImpl atts) throws SAXException {
Map foreignAttributes = getContext().getForeignAttributes();
if (!foreignAttributes.isEmpty()) {
for (Object o : foreignAttributes.entrySet()) {
Map.Entry entry = (Map.Entry) o;
addAttribute(atts, (QName) entry.getKey(), entry.getValue().toString());
}
}
}
/** {@inheritDoc} */
public void drawImage(Document doc, Rectangle rect) throws IFException {
try {
addID();
AttributesImpl atts = new AttributesImpl();
addAttribute(atts, "x", Integer.toString(rect.x));
addAttribute(atts, "y", Integer.toString(rect.y));
addAttribute(atts, "width", Integer.toString(rect.width));
addAttribute(atts, "height", Integer.toString(rect.height));
addForeignAttributes(atts);
addStructureReference(atts);
handler.startElement(EL_IMAGE, atts);
new DOM2SAX(handler).writeDocument(doc, true);
handler.endElement(EL_IMAGE);
} catch (SAXException e) {
throw new IFException("SAX error in startGroup()", e);
}
}
private static String toString(Paint paint) {
if (paint instanceof Color) {
return ColorUtil.colorToString((Color)paint);
} else {
throw new UnsupportedOperationException("Paint not supported: " + paint);
}
}
/** {@inheritDoc} */
public void clipRect(Rectangle rect) throws IFException {
try {
AttributesImpl atts = new AttributesImpl();
addAttribute(atts, "x", Integer.toString(rect.x));
addAttribute(atts, "y", Integer.toString(rect.y));
addAttribute(atts, "width", Integer.toString(rect.width));
addAttribute(atts, "height", Integer.toString(rect.height));
handler.element(EL_CLIP_RECT, atts);
} catch (SAXException e) {
throw new IFException("SAX error in clipRect()", e);
}
}
/** {@inheritDoc} */
public void clipBackground(Rectangle rect, BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) throws IFException {
try {
AttributesImpl atts = new AttributesImpl();
addAttribute(atts, "x", Integer.toString(rect.x));
addAttribute(atts, "y", Integer.toString(rect.y));
addAttribute(atts, "width", Integer.toString(rect.width));
addAttribute(atts, "height", Integer.toString(rect.height));
if (hasRoundedCorners(bpsBefore, bpsAfter, bpsStart, bpsEnd)) {
if (bpsBefore != null) {
addAttribute(atts, "top", bpsBefore.toString());
}
if (bpsAfter != null) {
addAttribute(atts, "bottom", bpsAfter.toString());
}
if (bpsStart != null) {
addAttribute(atts, "left", bpsStart.toString());
}
if (bpsEnd != null) {
addAttribute(atts, "right", bpsEnd.toString());
}
}
handler.element(EL_CLIP_RECT, atts);
} catch (SAXException e) {
throw new IFException("SAX error in clipRect()", e);
}
}
/** {@inheritDoc} */
public void fillRect(Rectangle rect, Paint fill) throws IFException {
if (fill == null) {
return;
}
try {
AttributesImpl atts = new AttributesImpl();
addAttribute(atts, "x", Integer.toString(rect.x));
addAttribute(atts, "y", Integer.toString(rect.y));
addAttribute(atts, "width", Integer.toString(rect.width));
addAttribute(atts, "height", Integer.toString(rect.height));
addAttribute(atts, "fill", toString(fill));
handler.element(EL_RECT, atts);
} catch (SAXException e) {
throw new IFException("SAX error in fillRect()", e);
}
}
//TODO create a class representing all borders should exist
//with query methods like this
private boolean hasRoundedCorners(BorderProps bpsBefore, BorderProps bpsAfter,
BorderProps bpsStart, BorderProps bpsEnd) {
boolean rtn = false;
if (bpsBefore != null && bpsBefore.getRadiusStart() > 0
&& bpsStart != null && bpsStart.getRadiusStart() > 0) {
rtn = true;
}
if (bpsBefore != null && bpsBefore.getRadiusEnd() > 0
&& bpsEnd != null && bpsEnd.getRadiusStart() > 0) {
rtn = true;
}
if (bpsEnd != null && bpsEnd.getRadiusEnd() > 0
&& bpsAfter != null && bpsAfter.getRadiusEnd() > 0) {
rtn = true;
}
if (bpsAfter != null && bpsAfter.getRadiusStart() > 0
&& bpsStart != null && bpsStart.getRadiusEnd() > 0) {
rtn = true;
}
return rtn;
}
/** {@inheritDoc} */
public void drawBorderRect(Rectangle rect, BorderProps top, BorderProps bottom,
BorderProps left, BorderProps right, Color innerBackgroundColor) throws IFException {
if (top == null && bottom == null && left == null && right == null) {
return;
}
try {
AttributesImpl atts = new AttributesImpl();
addAttribute(atts, "x", Integer.toString(rect.x));
addAttribute(atts, "y", Integer.toString(rect.y));
addAttribute(atts, "width", Integer.toString(rect.width));
addAttribute(atts, "height", Integer.toString(rect.height));
if (top != null) {
addAttribute(atts, "top", top.toString());
}
if (bottom != null) {
addAttribute(atts, "bottom", bottom.toString());
}
if (left != null) {
addAttribute(atts, "left", left.toString());
}
if (right != null) {
addAttribute(atts, "right", right.toString());
}
if (innerBackgroundColor != null) {
addAttribute(atts, "inner-background-color",
ColorUtil.colorToString(innerBackgroundColor));
}
handler.element(EL_BORDER_RECT, atts);
} catch (SAXException e) {
throw new IFException("SAX error in drawBorderRect()", e);
}
}
/** {@inheritDoc} */
public void drawLine(Point start, Point end, int width, Color color, RuleStyle style)
throws IFException {
try {
addID();
AttributesImpl atts = new AttributesImpl();
addAttribute(atts, "x1", Integer.toString(start.x));
addAttribute(atts, "y1", Integer.toString(start.y));
addAttribute(atts, "x2", Integer.toString(end.x));
addAttribute(atts, "y2", Integer.toString(end.y));
addAttribute(atts, "stroke-width", Integer.toString(width));
addAttribute(atts, "color", ColorUtil.colorToString(color));
addAttribute(atts, "style", style.getName());
handler.element(EL_LINE, atts);
} catch (SAXException e) {
throw new IFException("SAX error in drawLine()", e);
}
}
/** {@inheritDoc} */
public void drawText(int x, int y, int letterSpacing, int wordSpacing,
int[][] dp, String text) throws IFException {
drawText(x, y, letterSpacing, wordSpacing, dp, text, false);
}
/** {@inheritDoc} */
public void drawText(int x, int y, int letterSpacing, int wordSpacing,
int[][] dp, String text, boolean nextIsSpace) throws IFException {
try {
addID();
AttributesImpl atts = new AttributesImpl();
addAttribute(atts, "x", Integer.toString(x));
addAttribute(atts, "y", Integer.toString(y));
if (nextIsSpace) {
addAttribute(atts, "next-is-space", "true");
}
if (letterSpacing != 0) {
addAttribute(atts, "letter-spacing", Integer.toString(letterSpacing));
}
if (wordSpacing != 0) {
addAttribute(atts, "word-spacing", Integer.toString(wordSpacing));
}
if (dp != null) {
if (IFUtil.isDPIdentity(dp)) {
// don't add dx or dp attribute
} else if (IFUtil.isDPOnlyDX(dp)) {
// add dx attribute only
int[] dx = IFUtil.convertDPToDX(dp);
addAttribute(atts, "dx", IFUtil.toString(dx));
} else {
// add dp attribute only
addAttribute(atts, "dp", XMLUtil.encodePositionAdjustments(dp));
}
}
addStructureReference(atts);
if (getContext().isHyphenated()) {
addAttribute(atts, "hyphenated", "true");
}
handler.startElement(EL_TEXT, atts);
char[] chars = text.toCharArray();
handler.characters(chars, 0, chars.length);
handler.endElement(EL_TEXT);
} catch (SAXException e) {
throw new IFException("SAX error in setFont()", e);
}
}
/** {@inheritDoc} */
public void setFont(String family, String style, Integer weight, String variant, Integer size,
Color color) throws IFException {
try {
AttributesImpl atts = new AttributesImpl();
boolean changed;
if (family != null) {
changed = !family.equals(state.getFontFamily());
if (changed) {
state.setFontFamily(family);
addAttribute(atts, "family", family);
}
}
if (style != null) {
changed = !style.equals(state.getFontStyle());
if (changed) {
state.setFontStyle(style);
addAttribute(atts, "style", style);
}
}
if (weight != null) {
changed = (weight != state.getFontWeight());
if (changed) {
state.setFontWeight(weight);
addAttribute(atts, "weight", weight.toString());
}
}
if (variant != null) {
changed = !variant.equals(state.getFontVariant());
if (changed) {
state.setFontVariant(variant);
addAttribute(atts, "variant", variant);
}
}
if (size != null) {
changed = (size != state.getFontSize());
if (changed) {
state.setFontSize(size);
addAttribute(atts, "size", size.toString());
}
}
if (color != null) {
changed = !org.apache.xmlgraphics.java2d.color.ColorUtil.isSameColor(
color, state.getTextColor());
if (changed) {
state.setTextColor(color);
addAttribute(atts, "color", toString(color));
}
}
if (atts.getLength() > 0) {
handler.element(EL_FONT, atts);
}
} catch (SAXException e) {
throw new IFException("SAX error in setFont()", e);
}
}
/** {@inheritDoc} */
public void handleExtensionObject(Object extension) throws IFException {
if (extension instanceof XMLizable) {
try {
((XMLizable)extension).toSAX(this.handler);
} catch (SAXException e) {
throw new IFException("SAX error while handling extension object", e);
}
} else {
throw new UnsupportedOperationException(
"Extension must implement XMLizable: "
+ extension + " (" + extension.getClass().getName() + ")");
}
}
/**
* @return a new rendering context
* @throws IllegalStateException unless overridden
*/
protected RenderingContext createRenderingContext() throws IllegalStateException {
throw new IllegalStateException("Should never be called!");
}
private void addAttribute(AttributesImpl atts,
org.apache.xmlgraphics.util.QName attribute, String value) throws SAXException {
handler.startPrefixMapping(attribute.getPrefix(), attribute.getNamespaceURI());
XMLUtil.addAttribute(atts, attribute, value);
}
private void addAttribute(AttributesImpl atts, String localName, String value) {
XMLUtil.addAttribute(atts, localName, value);
}
private void addStructureReference(AttributesImpl atts) {
IFStructureTreeElement structureTreeElement
= (IFStructureTreeElement) getContext().getStructureTreeElement();
if (structureTreeElement != null) {
addStructRefAttribute(atts, structureTreeElement.getId());
}
}
private void addStructRefAttribute(AttributesImpl atts, String id) {
atts.addAttribute(InternalElementMapping.URI,
InternalElementMapping.STRUCT_REF,
InternalElementMapping.STANDARD_PREFIX + ":" + InternalElementMapping.STRUCT_REF,
XMLConstants.CDATA,
id);
}
private void addID() throws SAXException {
String id = getContext().getID();
if (!currentID.equals(id)) {
AttributesImpl atts = new AttributesImpl();
addAttribute(atts, "name", id);
handler.startElement(EL_ID, atts);
handler.endElement(EL_ID);
currentID = id;
}
}
private Map incompleteActions = new java.util.HashMap();
private List completeActions = new java.util.LinkedList();
private void noteAction(AbstractAction action) {
if (action == null) {
throw new NullPointerException("action must not be null");
}
if (!action.isComplete()) {
assert action.hasID();
incompleteActions.put(action.getID(), action);
}
}
/** {@inheritDoc} */
public void renderNamedDestination(NamedDestination destination) throws IFException {
noteAction(destination.getAction());
AttributesImpl atts = new AttributesImpl();
atts.addAttribute("", "name", "name", XMLConstants.CDATA, destination.getName());
try {
handler.startElement(DocumentNavigationExtensionConstants.NAMED_DESTINATION, atts);
serializeXMLizable(destination.getAction());
handler.endElement(DocumentNavigationExtensionConstants.NAMED_DESTINATION);
} catch (SAXException e) {
throw new IFException("SAX error serializing named destination", e);
}
}
/** {@inheritDoc} */
public void renderBookmarkTree(BookmarkTree tree) throws IFException {
AttributesImpl atts = new AttributesImpl();
try {
handler.startElement(DocumentNavigationExtensionConstants.BOOKMARK_TREE, atts);
for (Object o : tree.getBookmarks()) {
Bookmark b = (Bookmark) o;
if (b.getAction() != null) {
serializeBookmark(b);
}
}
handler.endElement(DocumentNavigationExtensionConstants.BOOKMARK_TREE);
} catch (SAXException e) {
throw new IFException("SAX error serializing bookmark tree", e);
}
}
private void serializeBookmark(Bookmark bookmark) throws SAXException, IFException {
noteAction(bookmark.getAction());
AttributesImpl atts = new AttributesImpl();
atts.addAttribute("", "title", "title", XMLUtil.CDATA, bookmark.getTitle());
atts.addAttribute("", "starting-state", "starting-state",
XMLUtil.CDATA, bookmark.isShown() ? "show" : "hide");
handler.startElement(DocumentNavigationExtensionConstants.BOOKMARK, atts);
serializeXMLizable(bookmark.getAction());
for (Object o : bookmark.getChildBookmarks()) {
Bookmark b = (Bookmark) o;
if (b.getAction() != null) {
serializeBookmark(b);
}
}
handler.endElement(DocumentNavigationExtensionConstants.BOOKMARK);
}
/** {@inheritDoc} */
public void renderLink(Link link) throws IFException {
noteAction(link.getAction());
AttributesImpl atts = new AttributesImpl();
atts.addAttribute("", "rect", "rect",
XMLConstants.CDATA, IFUtil.toString(link.getTargetRect()));
if (getUserAgent().isAccessibilityEnabled()) {
addStructRefAttribute(atts,
((IFStructureTreeElement) link.getAction().getStructureTreeElement()).getId());
}
try {
handler.startElement(DocumentNavigationExtensionConstants.LINK, atts);
serializeXMLizable(link.getAction());
handler.endElement(DocumentNavigationExtensionConstants.LINK);
} catch (SAXException e) {
throw new IFException("SAX error serializing link", e);
}
}
/** {@inheritDoc} */
public void addResolvedAction(AbstractAction action) throws IFException {
assert action.isComplete();
assert action.hasID();
AbstractAction noted = (AbstractAction)incompleteActions.remove(action.getID());
if (noted != null) {
completeActions.add(action);
} else {
//ignore as it was already complete when it was first used.
}
}
public int getPageIndex() {
return -1;
}
private void commitNavigation() throws IFException {
Iterator iter = this.completeActions.iterator();
while (iter.hasNext()) {
AbstractAction action = (AbstractAction)iter.next();
iter.remove();
serializeXMLizable(action);
}
assert this.completeActions.size() == 0;
}
private void finishDocumentNavigation() {
assert this.incompleteActions.size() == 0 : "Still holding incomplete actions!";
}
private void serializeXMLizable(XMLizable object) throws IFException {
try {
object.toSAX(handler);
} catch (SAXException e) {
throw new IFException("SAX error serializing object", e);
}
}
/** {@inheritDoc} */
public boolean isBackgroundRequired(BorderProps bpsTop, BorderProps bpsBottom,
BorderProps bpsLeft, BorderProps bpsRight) {
return true;
}
}