/*
 * 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: AreaTreeParser.java 1790795 2017-04-10 10:03:35Z cbowditch $ */

package org.apache.fop.area;

import java.awt.Color;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.CharBuffer;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;

import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
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.image.loader.ImageException;
import org.apache.xmlgraphics.image.loader.ImageInfo;
import org.apache.xmlgraphics.image.loader.ImageManager;
import org.apache.xmlgraphics.image.loader.ImageSessionContext;
import org.apache.xmlgraphics.util.QName;

import org.apache.fop.ResourceEventProducer;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.area.Trait.Background;
import org.apache.fop.area.Trait.InternalLink;
import org.apache.fop.area.inline.AbstractTextArea;
import org.apache.fop.area.inline.Container;
import org.apache.fop.area.inline.ForeignObject;
import org.apache.fop.area.inline.Image;
import org.apache.fop.area.inline.InlineArea;
import org.apache.fop.area.inline.InlineBlockParent;
import org.apache.fop.area.inline.InlineParent;
import org.apache.fop.area.inline.InlineViewport;
import org.apache.fop.area.inline.Leader;
import org.apache.fop.area.inline.Space;
import org.apache.fop.area.inline.SpaceArea;
import org.apache.fop.area.inline.TextArea;
import org.apache.fop.area.inline.WordArea;
import org.apache.fop.fo.ElementMappingRegistry;
import org.apache.fop.fo.expr.PropertyException;
import org.apache.fop.fo.extensions.ExtensionAttachment;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.traits.BorderProps;
import org.apache.fop.traits.Visibility;
import org.apache.fop.util.ColorUtil;
import org.apache.fop.util.ContentHandlerFactory;
import org.apache.fop.util.ContentHandlerFactoryRegistry;
import org.apache.fop.util.ConversionUtils;
import org.apache.fop.util.DefaultErrorListener;
import org.apache.fop.util.LanguageTags;
import org.apache.fop.util.XMLConstants;
import org.apache.fop.util.XMLUtil;

import static org.apache.fop.fo.Constants.FO_REGION_AFTER;
import static org.apache.fop.fo.Constants.FO_REGION_BEFORE;
import static org.apache.fop.fo.Constants.FO_REGION_BODY;
import static org.apache.fop.fo.Constants.FO_REGION_END;
import static org.apache.fop.fo.Constants.FO_REGION_START;

This is a parser for the area tree XML (intermediate format) which is used to reread an area tree (or part of it) into memory again for rendering to the final output format.
/** * This is a parser for the area tree XML (intermediate format) which is used to reread an area * tree (or part of it) into memory again for rendering to the final output format. */
public class AreaTreeParser {
Logger instance
/** Logger instance */
protected static final Log log = LogFactory.getLog(AreaTreeParser.class); private static SAXTransformerFactory tFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
Parses an intermediate file (area tree XML) into an AreaTreeModel instance by adding pages to it.
Params:
  • src – the Source instance pointing to the intermediate file
  • treeModel – the AreaTreeModel that the parsed pages are added to
  • userAgent – the user agent
Throws:
/** * Parses an intermediate file (area tree XML) into an AreaTreeModel instance by adding * pages to it. * @param src the Source instance pointing to the intermediate file * @param treeModel the AreaTreeModel that the parsed pages are added to * @param userAgent the user agent * @throws TransformerException if an error occurs while parsing the area tree XML */
public void parse(Source src, AreaTreeModel treeModel, FOUserAgent userAgent) throws TransformerException { Transformer transformer = tFactory.newTransformer(); transformer.setErrorListener(new DefaultErrorListener(log)); SAXResult res = new SAXResult(getContentHandler(treeModel, userAgent)); transformer.transform(src, res); }
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.
Params:
  • treeModel – the AreaTreeModel that the parsed pages are added to
  • userAgent – the user agent
Returns:the ContentHandler instance to receive the SAX stream from the area tree XML
/** * 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 treeModel the AreaTreeModel that the parsed pages are added to * @param userAgent the user agent * @return the ContentHandler instance to receive the SAX stream from the area tree XML */
public ContentHandler getContentHandler(AreaTreeModel treeModel, FOUserAgent userAgent) { ElementMappingRegistry elementMappingRegistry = userAgent.getElementMappingRegistry(); return new Handler(treeModel, userAgent, elementMappingRegistry); } private static class Handler extends DefaultHandler { private Map<String, AbstractMaker> makers = new java.util.HashMap<String, AbstractMaker>(); private AreaTreeModel treeModel; private FOUserAgent userAgent; private ElementMappingRegistry elementMappingRegistry; private Attributes lastAttributes; private CharBuffer content = CharBuffer.allocate(64); private boolean ignoreCharacters = true; private PageViewport currentPageViewport; private Map<String, PageViewport> pageViewportsByKey = new java.util.HashMap<String, PageViewport>(); // set of "ID firsts" that have already been assigned to a PV: private Set<String> idFirstsAssigned = new java.util.HashSet<String>(); private Stack<Object> areaStack = new Stack<Object>(); private boolean firstFlow; private Stack<String> delegateStack = new Stack<String>(); private ContentHandler delegate; private DOMImplementation domImplementation; private Locator locator; public Handler(AreaTreeModel treeModel, FOUserAgent userAgent, ElementMappingRegistry elementMappingRegistry) { this.treeModel = treeModel; this.userAgent = userAgent; this.elementMappingRegistry = elementMappingRegistry; makers.put("areaTree", new AreaTreeMaker()); makers.put("page", new PageMaker()); makers.put("pageSequence", new PageSequenceMaker()); makers.put("title", new TitleMaker()); makers.put("pageViewport", new PageViewportMaker()); makers.put("regionViewport", new RegionViewportMaker()); makers.put("regionBefore", new RegionBeforeMaker()); makers.put("regionAfter", new RegionAfterMaker()); makers.put("regionStart", new RegionStartMaker()); makers.put("regionEnd", new RegionEndMaker()); makers.put("regionBody", new RegionBodyMaker()); makers.put("flow", new FlowMaker()); makers.put("mainReference", new MainReferenceMaker()); makers.put("span", new SpanMaker()); makers.put("footnote", new FootnoteMaker()); makers.put("beforeFloat", new BeforeFloatMaker()); makers.put("block", new BlockMaker()); makers.put("lineArea", new LineAreaMaker()); makers.put("inline", new InlineMaker()); makers.put("inlineparent", new InlineParentMaker()); makers.put("inlineblockparent", new InlineBlockParentMaker()); makers.put("text", new TextMaker()); makers.put("word", new WordMaker()); makers.put("space", new SpaceMaker()); makers.put("leader", new LeaderMaker()); makers.put("viewport", new InlineViewportMaker()); makers.put("container", new ContainerMaker()); makers.put("image", new ImageMaker()); makers.put("foreignObject", new ForeignObjectMaker()); makers.put("bookmarkTree", new BookmarkTreeMaker()); makers.put("bookmark", new BookmarkMaker()); makers.put("destination", new DestinationMaker()); } private Area findAreaType(Class clazz) { if (areaStack.size() > 0) { int pos = areaStack.size() - 1; Object obj = null; while (pos >= 0) { obj = areaStack.get(pos); if (clazz.isInstance(obj)) { break; } else { pos--; } } if (pos >= 0) { return (Area)obj; } } return null; } private RegionViewport getCurrentRegionViewport() { return (RegionViewport)findAreaType(RegionViewport.class); } private BodyRegion getCurrentBodyRegion() { return (BodyRegion)findAreaType(BodyRegion.class); } private BlockParent getCurrentBlockParent() { return (BlockParent)findAreaType(BlockParent.class); } private AbstractTextArea getCurrentText() { return (AbstractTextArea)findAreaType(AbstractTextArea.class); } private InlineViewport getCurrentViewport() { return (InlineViewport)findAreaType(InlineViewport.class); }
{@inheritDoc}
/** {@inheritDoc} */
public void setDocumentLocator(Locator locator) { this.locator = locator; } private Locator getLocator() { return this.locator; }
{@inheritDoc}
/** {@inheritDoc} */
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (delegate != null) { delegateStack.push(qName); delegate.startElement(uri, localName, qName, attributes); } else if (domImplementation != null) { //domImplementation is set so we need to start a new DOM building sub-process TransformerHandler handler; try { handler = tFactory.newTransformerHandler(); } catch (TransformerConfigurationException e) { throw new SAXException("Error creating a new TransformerHandler", e); } Document doc = domImplementation.createDocument(uri, qName, null); //It's easier to work with an empty document, so remove the root element doc.removeChild(doc.getDocumentElement()); handler.setResult(new DOMResult(doc)); Area parent = (Area)areaStack.peek(); ((ForeignObject)parent).setDocument(doc); //activate delegate for nested foreign document domImplementation = null; //Not needed anymore now this.delegate = handler; delegateStack.push(qName); delegate.startDocument(); delegate.startElement(uri, localName, qName, attributes); } else { boolean handled = true; if ("".equals(uri)) { if (localName.equals("structureTree")) { /* The area tree parser no longer supports the structure tree. */ delegate = new DefaultHandler(); delegateStack.push(qName); delegate.startDocument(); delegate.startElement(uri, localName, qName, attributes); } else { handled = startAreaTreeElement(localName, attributes); } } else { ContentHandlerFactoryRegistry registry = userAgent.getContentHandlerFactoryRegistry(); ContentHandlerFactory factory = registry.getFactory(uri); if (factory != null) { delegate = factory.createContentHandler(); delegateStack.push(qName); delegate.startDocument(); delegate.startElement(uri, localName, qName, attributes); } else { handled = false; } } 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 boolean startAreaTreeElement(String localName, Attributes attributes) throws SAXException { lastAttributes = new AttributesImpl(attributes); Maker maker = makers.get(localName); content.clear(); ignoreCharacters = true; if (maker != null) { ignoreCharacters = maker.ignoreCharacters(); maker.startElement(attributes); } else if ("extension-attachments".equals(localName)) { //TODO implement me } else { return false; } return true; }
{@inheritDoc}
/** {@inheritDoc} */
public void endElement(String uri, String localName, String qName) throws SAXException { if (delegate != null) { delegate.endElement(uri, localName, qName); delegateStack.pop(); if (delegateStack.size() == 0) { delegate.endDocument(); if (delegate instanceof ContentHandlerFactory.ObjectSource) { Object obj = ((ContentHandlerFactory.ObjectSource)delegate).getObject(); handleExternallyGeneratedObject(obj); } delegate = null; //Sub-document is processed, return to normal processing } } else { if ("".equals(uri)) { Maker maker = makers.get(localName); if (maker != null) { maker.endElement(); content.clear(); } ignoreCharacters = true; } else { //log.debug("Ignoring " + localName + " in namespace: " + uri); } } } // ============== Maker classes for the area tree objects ============= private interface Maker { void startElement(Attributes attributes) throws SAXException; void endElement(); boolean ignoreCharacters(); } private abstract class AbstractMaker implements Maker { public void startElement(Attributes attributes) throws SAXException { //nop } public void endElement() { //nop } public boolean ignoreCharacters() { return true; } } private class AreaTreeMaker extends AbstractMaker { public void startElement(Attributes attributes) { // In case the Handler is reused: idFirstsAssigned.clear(); } } private class PageSequenceMaker extends AbstractMaker { public void startElement(Attributes attributes) { PageSequence pageSequence = new PageSequence(null); String lang = attributes.getValue(javax.xml.XMLConstants.XML_NS_URI, "lang"); if (lang != null) { pageSequence.setLocale(LanguageTags.toLocale(lang)); } transferForeignObjects(attributes, pageSequence); areaStack.push(pageSequence); } } private class TitleMaker extends AbstractMaker { public void startElement(Attributes attributes) { LineArea line = new LineArea(); transferForeignObjects(attributes, line); areaStack.push(line); } public void endElement() { LineArea line = (LineArea)areaStack.pop(); PageSequence pageSequence = (PageSequence)areaStack.peek(); pageSequence.setTitle(line); } } private class PageViewportMaker extends AbstractMaker { public void startElement(Attributes attributes) { if (!areaStack.isEmpty()) { PageSequence pageSequence = (PageSequence)areaStack.peek(); treeModel.startPageSequence(pageSequence); areaStack.pop(); } if (currentPageViewport != null) { throw new IllegalStateException("currentPageViewport must be null"); } Rectangle viewArea = XMLUtil.getAttributeAsRectangle(attributes, "bounds"); int pageNumber = XMLUtil.getAttributeAsInt(attributes, "nr", -1); String key = attributes.getValue("key"); String pageNumberString = attributes.getValue("formatted-nr"); String pageMaster = attributes.getValue("simple-page-master-name"); boolean blank = XMLUtil.getAttributeAsBoolean(attributes, "blank", false); currentPageViewport = new PageViewport(viewArea, pageNumber, pageNumberString, pageMaster, blank); transferForeignObjects(attributes, currentPageViewport); currentPageViewport.setKey(key); pageViewportsByKey.put(key, currentPageViewport); } } private class PageMaker extends AbstractMaker { public void startElement(Attributes attributes) { Page p = new Page(); currentPageViewport.setPage(p); } public void endElement() { treeModel.addPage(currentPageViewport); currentPageViewport = null; } } private class RegionViewportMaker extends AbstractMaker { public void startElement(Attributes attributes) { RegionViewport rv = getCurrentRegionViewport(); if (rv != null) { throw new IllegalStateException("Current RegionViewport must be null"); } Rectangle2D viewArea = XMLUtil.getAttributeAsRectangle2D(attributes, "rect"); rv = new RegionViewport(viewArea); transferForeignObjects(attributes, rv); rv.setClip(XMLUtil.getAttributeAsBoolean(attributes, "clipped", false)); setAreaAttributes(attributes, rv); setTraits(attributes, rv, SUBSET_COMMON); setTraits(attributes, rv, SUBSET_BOX); setTraits(attributes, rv, SUBSET_COLOR); areaStack.push(rv); } public void endElement() { assertObjectOfClass(areaStack.pop(), RegionViewport.class); } } private class RegionBeforeMaker extends AbstractMaker { public void startElement(Attributes attributes) { pushNewRegionReference(attributes, FO_REGION_BEFORE); } public void endElement() { assertObjectOfClass(areaStack.pop(), RegionReference.class); } } private class RegionAfterMaker extends AbstractMaker { public void startElement(Attributes attributes) { pushNewRegionReference(attributes, FO_REGION_AFTER); } public void endElement() { assertObjectOfClass(areaStack.pop(), RegionReference.class); } } private class RegionStartMaker extends AbstractMaker { public void startElement(Attributes attributes) { pushNewRegionReference(attributes, FO_REGION_START); } public void endElement() { assertObjectOfClass(areaStack.pop(), RegionReference.class); } } private class RegionEndMaker extends AbstractMaker { public void startElement(Attributes attributes) { pushNewRegionReference(attributes, FO_REGION_END); } public void endElement() { assertObjectOfClass(areaStack.pop(), RegionReference.class); } } private class RegionBodyMaker extends AbstractMaker { public void startElement(Attributes attributes) { BodyRegion body = getCurrentBodyRegion(); if (body != null) { throw new IllegalStateException("Current BodyRegion must be null"); } String regionName = attributes.getValue("name"); int columnCount = XMLUtil.getAttributeAsInt(attributes, "columnCount", 1); int columnGap = XMLUtil.getAttributeAsInt(attributes, "columnGap", 0); RegionViewport rv = getCurrentRegionViewport(); body = new BodyRegion(FO_REGION_BODY, regionName, rv, columnCount, columnGap); transferForeignObjects(attributes, body); body.setCTM(getAttributeAsCTM(attributes, "ctm")); setAreaAttributes(attributes, body); setTraits(attributes, body, SUBSET_BORDER_PADDING); rv.setRegionReference(body); currentPageViewport.getPage().setRegionViewport(FO_REGION_BODY, rv); areaStack.push(body); } public void endElement() { assertObjectOfClass(areaStack.pop(), BodyRegion.class); } } private class FlowMaker extends AbstractMaker { public void startElement(Attributes attributes) { BodyRegion body = getCurrentBodyRegion(); if (!firstFlow) { body.getMainReference().getCurrentSpan().moveToNextFlow(); } else { firstFlow = false; } NormalFlow flow = body.getMainReference().getCurrentSpan().getCurrentFlow(); transferForeignObjects(attributes, flow); setAreaAttributes(attributes, flow); areaStack.push(flow); } public void endElement() { assertObjectOfClass(areaStack.pop(), NormalFlow.class); } } private class MainReferenceMaker extends AbstractMaker { public void startElement(Attributes attributes) { //mainReference is created by the BodyRegion MainReference mr = getCurrentBodyRegion().getMainReference(); transferForeignObjects(attributes, mr); setAreaAttributes(attributes, mr); } } private class SpanMaker extends AbstractMaker { public void startElement(Attributes attributes) { int ipd = XMLUtil.getAttributeAsInt(attributes, "ipd", 0); int columnCount = XMLUtil.getAttributeAsInt(attributes, "columnCount", 1); BodyRegion body = getCurrentBodyRegion(); Span span = new Span(columnCount, body.getColumnGap(), ipd); transferForeignObjects(attributes, span); setAreaAttributes(attributes, span); body.getMainReference().getSpans().add(span); firstFlow = true; } } private class FootnoteMaker extends AbstractMaker { public void startElement(Attributes attributes) { Footnote fn = getCurrentBodyRegion().getFootnote(); transferForeignObjects(attributes, fn); fn.setTop(XMLUtil.getAttributeAsInt(attributes, "top-offset", 0)); areaStack.push(fn); } public void endElement() { assertObjectOfClass(areaStack.pop(), Footnote.class); } } private class BeforeFloatMaker extends AbstractMaker { public void startElement(Attributes attributes) { BeforeFloat bf = getCurrentBodyRegion().getBeforeFloat(); transferForeignObjects(attributes, bf); areaStack.push(bf); } public void endElement() { assertObjectOfClass(areaStack.pop(), BeforeFloat.class); } } private class BlockMaker extends AbstractMaker { public void startElement(Attributes attributes) { boolean isViewport = XMLUtil.getAttributeAsBoolean(attributes, "is-viewport-area", false); Block block; if (isViewport) { BlockViewport bv = new BlockViewport(); bv.setClip(XMLUtil.getAttributeAsBoolean(attributes, "clipped", false)); bv.setCTM(getAttributeAsCTM(attributes, "ctm")); if (bv.getPositioning() != BlockViewport.RELATIVE) { bv.setXOffset( XMLUtil.getAttributeAsInt(attributes, "left-position", 0)); bv.setYOffset( XMLUtil.getAttributeAsInt(attributes, "top-position", 0)); } block = bv; } else { block = new Block(); } String positioning = attributes.getValue("positioning"); if ("absolute".equalsIgnoreCase(positioning)) { block.setPositioning(Block.ABSOLUTE); } else if ("fixed".equalsIgnoreCase(positioning)) { block.setPositioning(Block.FIXED); } else if ("relative".equalsIgnoreCase(positioning)) { block.setPositioning(Block.RELATIVE); } else { block.setPositioning(Block.STACK); } if (attributes.getValue("left-offset") != null) { block.setXOffset(XMLUtil.getAttributeAsInt(attributes, "left-offset", 0)); } if (attributes.getValue("top-offset") != null) { block.setYOffset(XMLUtil.getAttributeAsInt(attributes, "top-offset", 0)); } if (attributes.getValue("visibility") != null) { block.addTrait(Trait.VISIBILITY, Visibility.valueOf(attributes.getValue("visibility"))); } transferForeignObjects(attributes, block); setAreaAttributes(attributes, block); setTraits(attributes, block, SUBSET_COMMON); setTraits(attributes, block, SUBSET_BOX); setTraits(attributes, block, SUBSET_COLOR); Area parent = (Area)areaStack.peek(); //BlockParent parent = getCurrentBlockParent(); parent.addChildArea(block); areaStack.push(block); } public void endElement() { assertObjectOfClass(areaStack.pop(), Block.class); } } private class LineAreaMaker extends AbstractMaker { public void startElement(Attributes attributes) { LineArea line = new LineArea(); setAreaAttributes(attributes, line); setTraits(attributes, line, SUBSET_COMMON); setTraits(attributes, line, SUBSET_BOX); setTraits(attributes, line, SUBSET_COLOR); BlockParent parent = getCurrentBlockParent(); parent.addChildArea(line); areaStack.push(line); } public void endElement() { assertObjectOfClass(areaStack.pop(), LineArea.class); } } // Maker for "generic" inline areas private class InlineMaker extends AbstractMaker { public void startElement(Attributes attributes) { InlineArea inl = new InlineArea(); transferForeignObjects(attributes, inl); inl.setBlockProgressionOffset(XMLUtil.getAttributeAsInt(attributes, "offset", 0)); setAreaAttributes(attributes, inl); setTraits(attributes, inl, SUBSET_COMMON); setTraits(attributes, inl, SUBSET_BOX); setTraits(attributes, inl, SUBSET_COLOR); Area parent = (Area)areaStack.peek(); parent.addChildArea(inl); areaStack.push(inl); } public void endElement() { assertObjectOfClass(areaStack.pop(), InlineArea.class); } } private class InlineParentMaker extends AbstractMaker { public void startElement(Attributes attributes) { InlineParent ip = new InlineParent(); transferForeignObjects(attributes, ip); ip.setBlockProgressionOffset(XMLUtil.getAttributeAsInt(attributes, "offset", 0)); setAreaAttributes(attributes, ip); setTraits(attributes, ip, SUBSET_COMMON); setTraits(attributes, ip, SUBSET_BOX); setTraits(attributes, ip, SUBSET_COLOR); setTraits(attributes, ip, SUBSET_LINK); Area parent = (Area)areaStack.peek(); parent.addChildArea(ip); areaStack.push(ip); } public void endElement() { assertObjectOfClass(areaStack.pop(), InlineParent.class); } } private class InlineBlockParentMaker extends AbstractMaker { public void startElement(Attributes attributes) { InlineBlockParent ibp = new InlineBlockParent(); transferForeignObjects(attributes, ibp); ibp.setBlockProgressionOffset(XMLUtil.getAttributeAsInt(attributes, "offset", 0)); setAreaAttributes(attributes, ibp); setTraits(attributes, ibp, SUBSET_COMMON); setTraits(attributes, ibp, SUBSET_BOX); setTraits(attributes, ibp, SUBSET_COLOR); Area parent = (Area)areaStack.peek(); parent.addChildArea(ibp); areaStack.push(ibp); } public void endElement() { assertObjectOfClass(areaStack.pop(), InlineBlockParent.class); } } private class TextMaker extends AbstractMaker { public void startElement(Attributes attributes) { if (getCurrentText() != null) { throw new IllegalStateException("Current Text must be null"); } TextArea text = new TextArea(); setAreaAttributes(attributes, text); setTraits(attributes, text, SUBSET_COMMON); setTraits(attributes, text, SUBSET_BOX); setTraits(attributes, text, SUBSET_COLOR); setTraits(attributes, text, SUBSET_FONT); text.setBaselineOffset(XMLUtil.getAttributeAsInt(attributes, "baseline", 0)); text.setBlockProgressionOffset(XMLUtil.getAttributeAsInt(attributes, "offset", 0)); text.setTextLetterSpaceAdjust(XMLUtil.getAttributeAsInt(attributes, "tlsadjust", 0)); text.setTextWordSpaceAdjust(XMLUtil.getAttributeAsInt(attributes, "twsadjust", 0)); Area parent = (Area)areaStack.peek(); parent.addChildArea(text); areaStack.push(text); } public void endElement() { assertObjectOfClass(areaStack.pop(), TextArea.class); } } private class WordMaker extends AbstractMaker { public void endElement() { int offset = XMLUtil.getAttributeAsInt(lastAttributes, "offset", 0); int[] letterAdjust = ConversionUtils.toIntArray( lastAttributes.getValue("letter-adjust"), "\\s"); int level = XMLUtil.getAttributeAsInt(lastAttributes, "level", -1); boolean reversed = XMLUtil.getAttributeAsBoolean(lastAttributes, "reversed", false); int[][] gposAdjustments = XMLUtil.getAttributeAsPositionAdjustments(lastAttributes, "position-adjust"); content.flip(); WordArea word = new WordArea( offset, level, content.toString().trim(), letterAdjust, null, gposAdjustments, reversed); AbstractTextArea text = getCurrentText(); word.setParentArea(text); text.addChildArea(word); } public boolean ignoreCharacters() { return false; } } private class SpaceMaker extends AbstractMaker { public void endElement() { int offset = XMLUtil.getAttributeAsInt(lastAttributes, "offset", 0); //TODO the isAdjustable parameter is currently not used/implemented if (content.position() > 0) { content.flip(); boolean adjustable = XMLUtil.getAttributeAsBoolean(lastAttributes, "adj", true); int level = XMLUtil.getAttributeAsInt(lastAttributes, "level", -1); SpaceArea space = new SpaceArea(offset, level, content.charAt(0), adjustable); AbstractTextArea text = getCurrentText(); space.setParentArea(text); text.addChildArea(space); } else { Space space = new Space(); setAreaAttributes(lastAttributes, space); setTraits(lastAttributes, space, SUBSET_COMMON); setTraits(lastAttributes, space, SUBSET_BOX); setTraits(lastAttributes, space, SUBSET_COLOR); space.setBlockProgressionOffset(offset); Area parent = (Area)areaStack.peek(); parent.addChildArea(space); } } public boolean ignoreCharacters() { return false; } } private class LeaderMaker extends AbstractMaker { public void startElement(Attributes attributes) { Leader leader = new Leader(); transferForeignObjects(attributes, leader); setAreaAttributes(attributes, leader); setTraits(attributes, leader, SUBSET_COMMON); setTraits(attributes, leader, SUBSET_BOX); setTraits(attributes, leader, SUBSET_COLOR); setTraits(attributes, leader, SUBSET_FONT); leader.setBlockProgressionOffset( XMLUtil.getAttributeAsInt(attributes, "offset", 0)); String ruleStyle = attributes.getValue("ruleStyle"); if (ruleStyle != null) { leader.setRuleStyle(ruleStyle); } leader.setRuleThickness( XMLUtil.getAttributeAsInt(attributes, "ruleThickness", 0)); Area parent = (Area)areaStack.peek(); parent.addChildArea(leader); } } private class ContainerMaker extends AbstractMaker { public void startElement(Attributes attributes) { Container container = new Container(); transferForeignObjects(attributes, container); InlineViewport parent = (InlineViewport) areaStack.peek(); parent.setContent(container); areaStack.push(container); } public void endElement() { assertObjectOfClass(areaStack.pop(), Container.class); } } private class InlineViewportMaker extends AbstractMaker { public void startElement(Attributes attributes) { int level = XMLUtil.getAttributeAsInt(attributes, "level", -1); InlineViewport viewport = new InlineViewport(null, level); transferForeignObjects(attributes, viewport); setAreaAttributes(attributes, viewport); setTraits(attributes, viewport, SUBSET_COMMON); setTraits(attributes, viewport, SUBSET_BOX); setTraits(attributes, viewport, SUBSET_COLOR); viewport.setContentPosition(XMLUtil.getAttributeAsRectangle2D(attributes, "pos")); viewport.setClip(XMLUtil.getAttributeAsBoolean(attributes, "clip", false)); viewport.setBlockProgressionOffset( XMLUtil.getAttributeAsInt(attributes, "offset", 0)); Area parent = (Area)areaStack.peek(); parent.addChildArea(viewport); areaStack.push(viewport); } public void endElement() { assertObjectOfClass(areaStack.pop(), InlineViewport.class); } } private class ImageMaker extends AbstractMaker { public void startElement(Attributes attributes) { String url = attributes.getValue("url"); Image image = new Image(url); transferForeignObjects(attributes, image); setAreaAttributes(attributes, image); setTraits(attributes, image, SUBSET_COMMON); getCurrentViewport().setContent(image); } } private class ForeignObjectMaker extends AbstractMaker { public void startElement(Attributes attributes) throws SAXException { String ns = attributes.getValue("ns"); domImplementation = elementMappingRegistry.getDOMImplementationForNamespace(ns); if (domImplementation == null) { throw new SAXException("No DOMImplementation could be" + " identified to handle namespace: " + ns); } ForeignObject foreign = new ForeignObject(ns); transferForeignObjects(attributes, foreign); setAreaAttributes(attributes, foreign); setTraits(attributes, foreign, SUBSET_COMMON); getCurrentViewport().setContent(foreign); areaStack.push(foreign); } public void endElement() { assertObjectOfClass(areaStack.pop(), ForeignObject.class); } } private class BookmarkTreeMaker extends AbstractMaker { public void startElement(Attributes attributes) { BookmarkData bm = new BookmarkData(); areaStack.push(bm); } public void endElement() { Object tos = areaStack.pop(); assertObjectOfClass(tos, BookmarkData.class); treeModel.handleOffDocumentItem((BookmarkData) tos); // as long as the bookmark tree comes after the last PageViewport in the // area tree XML, we don't have to worry about resolved/unresolved. The // only resolution needed is the mapping of the pvKey to the PV instance. } } private class BookmarkMaker extends AbstractMaker { public void startElement(Attributes attributes) { String title = attributes.getValue("title"); boolean showChildren = XMLUtil.getAttributeAsBoolean( attributes, "show-children", false); String[] linkdata = InternalLink.parseXMLAttribute(attributes.getValue("internal-link")); PageViewport pv = pageViewportsByKey.get(linkdata[0]); BookmarkData bm = new BookmarkData(title, showChildren, pv, linkdata[1]); Object tos = areaStack.peek(); if (tos instanceof BookmarkData) { BookmarkData parent = (BookmarkData) tos; parent.addSubData(bm); } areaStack.push(bm); } public void endElement() { assertObjectOfClass(areaStack.pop(), BookmarkData.class); } } private class DestinationMaker extends AbstractMaker { public void startElement(Attributes attributes) { String[] linkdata = InternalLink.parseXMLAttribute(lastAttributes.getValue("internal-link")); PageViewport pv = pageViewportsByKey.get(linkdata[0]); DestinationData dest = new DestinationData(linkdata[1]); List<PageViewport> pages = new java.util.ArrayList<PageViewport>(); pages.add(pv); dest.resolveIDRef(linkdata[1], pages); areaStack.push(dest); } public void endElement() { Object tos = areaStack.pop(); assertObjectOfClass(tos, DestinationData.class); treeModel.handleOffDocumentItem((DestinationData) tos); } } // ==================================================================== private void pushNewRegionReference(Attributes attributes, int side) { String regionName = attributes.getValue("name"); RegionViewport rv = getCurrentRegionViewport(); RegionReference reg = new RegionReference(side, regionName, rv); transferForeignObjects(attributes, reg); reg.setCTM(getAttributeAsCTM(attributes, "ctm")); setAreaAttributes(attributes, reg); setTraits(attributes, reg, SUBSET_BORDER_PADDING); rv.setRegionReference(reg); currentPageViewport.getPage().setRegionViewport( side, rv); areaStack.push(reg); } private void assertObjectOfClass(Object obj, Class clazz) { if (!clazz.isInstance(obj)) { throw new IllegalStateException("Object is not an instance of " + clazz.getName() + " but of " + obj.getClass().getName()); } }
Handles objects created by "sub-parsers" that implement the ObjectSource interface. An example of object handled here are ExtensionAttachments.
Params:
  • obj – the Object to be handled.
/** * 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. */
protected void handleExternallyGeneratedObject(Object obj) { if (areaStack.size() == 0 && obj instanceof ExtensionAttachment) { ExtensionAttachment attachment = (ExtensionAttachment)obj; if (this.currentPageViewport == null) { this.treeModel.handleOffDocumentItem( new OffDocumentExtensionAttachment(attachment)); } else { this.currentPageViewport.addExtensionAttachment(attachment); } } else { Object o = areaStack.peek(); if (o instanceof AreaTreeObject && obj instanceof ExtensionAttachment) { AreaTreeObject ato = (AreaTreeObject)o; ExtensionAttachment attachment = (ExtensionAttachment)obj; ato.addExtensionAttachment(attachment); } else { log.warn("Don't know how to handle externally generated object: " + obj); } } } private void setAreaAttributes(Attributes attributes, Area area) { area.setIPD(Integer.parseInt(attributes.getValue("ipd"))); area.setBPD(Integer.parseInt(attributes.getValue("bpd"))); area.setBidiLevel(XMLUtil.getAttributeAsInt(attributes, "level", -1)); } private static final Object[] SUBSET_COMMON = new Object[] { Trait.PROD_ID, Trait.LAYER}; private static final Object[] SUBSET_LINK = new Object[] { Trait.INTERNAL_LINK, Trait.EXTERNAL_LINK}; private static final Object[] SUBSET_COLOR = new Object[] { Trait.BACKGROUND, Trait.COLOR}; private static final Object[] SUBSET_FONT = new Object[] { Trait.FONT, Trait.FONT_SIZE, Trait.BLINK, Trait.OVERLINE, Trait.OVERLINE_COLOR, Trait.LINETHROUGH, Trait.LINETHROUGH_COLOR, Trait.UNDERLINE, Trait.UNDERLINE_COLOR}; private static final Object[] SUBSET_BOX = new Object[] { Trait.BORDER_BEFORE, Trait.BORDER_AFTER, Trait.BORDER_START, Trait.BORDER_END, Trait.SPACE_BEFORE, Trait.SPACE_AFTER, Trait.SPACE_START, Trait.SPACE_END, Trait.PADDING_BEFORE, Trait.PADDING_AFTER, Trait.PADDING_START, Trait.PADDING_END, Trait.START_INDENT, Trait.END_INDENT, Trait.IS_REFERENCE_AREA, Trait.IS_VIEWPORT_AREA}; private static final Object[] SUBSET_BORDER_PADDING = new Object[] { Trait.BORDER_BEFORE, Trait.BORDER_AFTER, Trait.BORDER_START, Trait.BORDER_END, Trait.PADDING_BEFORE, Trait.PADDING_AFTER, Trait.PADDING_START, Trait.PADDING_END}; private void setTraits(Attributes attributes, Area area, Object[] traitSubset) { for (int i = traitSubset.length; --i >= 0;) { Integer trait = (Integer) traitSubset[i]; String traitName = Trait.getTraitName(trait); String value = attributes.getValue(traitName); if (value != null) { Class cl = Trait.getTraitClass(trait); if (cl == Integer.class) { area.addTrait(trait, Integer.valueOf(value)); } else if (cl == Boolean.class) { area.addTrait(trait, Boolean.valueOf(value)); } else if (cl == String.class) { area.addTrait(trait, value); if (Trait.PROD_ID.equals(trait) && !idFirstsAssigned.contains(value) && currentPageViewport != null) { currentPageViewport.setFirstWithID(value); idFirstsAssigned.add(value); } } else if (cl == Color.class) { try { area.addTrait(trait, ColorUtil.parseColorString(this.userAgent, value)); } catch (PropertyException e) { throw new IllegalArgumentException(e.getMessage()); } } else if (cl == InternalLink.class) { area.addTrait(trait, new InternalLink(value)); } else if (cl == Trait.ExternalLink.class) { area.addTrait(trait, Trait.ExternalLink.makeFromTraitValue(value)); } else if (cl == Background.class) { Background bkg = new Background(); try { Color col = ColorUtil.parseColorString( this.userAgent, attributes.getValue("bkg-color")); bkg.setColor(col); } catch (PropertyException e) { throw new IllegalArgumentException(e.getMessage()); } String uri = attributes.getValue("bkg-img"); if (uri != null) { bkg.setURL(uri); try { ImageManager manager = userAgent.getImageManager(); ImageSessionContext sessionContext = userAgent.getImageSessionContext(); ImageInfo info = manager.getImageInfo(uri, sessionContext); bkg.setImageInfo(info); } catch (ImageException e) { ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get( this.userAgent.getEventBroadcaster()); eventProducer.imageError(this, uri, e, getLocator()); } catch (FileNotFoundException fnfe) { ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get( this.userAgent.getEventBroadcaster()); eventProducer.imageNotFound(this, uri, fnfe, getLocator()); } catch (IOException ioe) { ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get( this.userAgent.getEventBroadcaster()); eventProducer.imageIOError(this, uri, ioe, getLocator()); } String repeat = attributes.getValue("bkg-repeat"); if (repeat != null) { bkg.setRepeat(repeat); } bkg.setHoriz(XMLUtil.getAttributeAsInt(attributes, "bkg-horz-offset", 0)); bkg.setVertical(XMLUtil.getAttributeAsInt(attributes, "bkg-vert-offset", 0)); } area.addTrait(trait, bkg); } else if (cl == BorderProps.class) { area.addTrait(trait, BorderProps.valueOf(this.userAgent, value)); } } else { if (Trait.FONT.equals(trait)) { String fontName = attributes.getValue("font-name"); if (fontName != null) { String fontStyle = attributes.getValue("font-style"); int fontWeight = XMLUtil.getAttributeAsInt( attributes, "font-weight", Font.WEIGHT_NORMAL); area.addTrait(trait, FontInfo.createFontKey(fontName, fontStyle, fontWeight)); } } } } } private static CTM getAttributeAsCTM(Attributes attributes, String name) { String s = attributes.getValue(name).trim(); if (s.startsWith("[") && s.endsWith("]")) { s = s.substring(1, s.length() - 1); double[] values = ConversionUtils.toDoubleArray(s, "\\s"); if (values.length != 6) { throw new IllegalArgumentException("CTM must consist of 6 double values!"); } return new CTM(values[0], values[1], values[2], values[3], values[4], values[5]); } else { throw new IllegalArgumentException("CTM must be surrounded by square brackets!"); } } private static void transferForeignObjects(Attributes atts, AreaTreeObject ato) { for (int i = 0, c = atts.getLength(); i < c; i++) { String ns = atts.getURI(i); if (ns.length() > 0) { if (XMLConstants.XMLNS_NAMESPACE_URI.equals(ns)) { continue; } QName qname = new QName(ns, atts.getQName(i)); ato.setForeignAttribute(qname, atts.getValue(i)); } } }
{@inheritDoc}
/** {@inheritDoc} */
public void characters(char[] ch, int start, int length) throws SAXException { if (delegate != null) { delegate.characters(ch, start, length); } else if (!ignoreCharacters) { int maxLength = this.content.capacity() - this.content.position(); if (maxLength < length) { // allocate a larger buffer and transfer content CharBuffer newContent = CharBuffer.allocate(this.content.position() + length); this.content.flip(); newContent.put(this.content); this.content = newContent; } // make sure the full capacity is used this.content.limit(this.content.capacity()); // add characters to the buffer this.content.put(ch, start, length); // decrease the limit, if necessary if (this.content.position() < this.content.limit()) { this.content.limit(this.content.position()); } } } } }