| /* |
| * 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.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.InlineBlock; |
| 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.Direction; |
| 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. |
| */ |
| public class AreaTreeParser { |
| |
| /** 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. |
| * @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. |
| * @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("inlineblock", new InlineBlockMaker()); |
| 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} */ |
| public void setDocumentLocator(Locator locator) { |
| this.locator = locator; |
| } |
| |
| private Locator getLocator() { |
| return this.locator; |
| } |
| |
| /** {@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} */ |
| 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); |
| |
| String blockDirection = attributes.getValue("block-progression-direction"); |
| if (blockDirection != null) { |
| span.addTrait(Trait.BLOCK_PROGRESSION_DIRECTION, Direction.valueOf(blockDirection)); |
| } |
| |
| String inlineDirection = attributes.getValue("inline-progression-direction"); |
| if (inlineDirection != null) { |
| span.addTrait(Trait.INLINE_PROGRESSION_DIRECTION, Direction.valueOf(inlineDirection)); |
| } |
| |
| 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 InlineBlockMaker extends AbstractMaker { |
| |
| public void startElement(Attributes attributes) { |
| |
| Block block = new Block(); |
| |
| 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)); |
| } |
| 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(); |
| InlineBlock inlineBlock = new InlineBlock(block); |
| parent.addChildArea(inlineBlock); |
| areaStack.push(inlineBlock); |
| } |
| |
| public void endElement() { |
| assertObjectOfClass(areaStack.pop(), InlineBlock.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. |
| * @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} */ |
| 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()); |
| } |
| } |
| } |
| } |
| } |