Package org.apache.fop.area

Source Code of org.apache.fop.area.AreaTreeParser$Handler$LeaderMaker

/*
* 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.geom.Rectangle2D;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.StringTokenizer;

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.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.apps.FOUserAgent;
import org.apache.fop.area.Trait.Background;
import org.apache.fop.area.inline.AbstractTextArea;
import org.apache.fop.area.inline.Character;
import org.apache.fop.area.inline.ForeignObject;
import org.apache.fop.area.inline.Image;
import org.apache.fop.area.inline.InlineBlockParent;
import org.apache.fop.area.inline.InlineParent;
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.Viewport;
import org.apache.fop.area.inline.WordArea;
import org.apache.fop.fo.Constants;
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.image.FopImage;
import org.apache.fop.image.ImageFactory;
import org.apache.fop.traits.BorderProps;
import org.apache.fop.util.ColorUtil;
import org.apache.fop.util.ContentHandlerFactory;
import org.apache.fop.util.ContentHandlerFactoryRegistry;
import org.apache.fop.util.DefaultErrorListener;
import org.apache.fop.util.QName;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
* 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 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.getFactory().getElementMappingRegistry();
        return new Handler(treeModel, userAgent, elementMappingRegistry);
    }
   
    private static class Handler extends DefaultHandler {
    
        private Map makers = new java.util.HashMap();
       
        private AreaTreeModel treeModel;
        private FOUserAgent userAgent;
        private ElementMappingRegistry elementMappingRegistry;
       
        private Attributes lastAttributes;
        private StringBuffer content = new StringBuffer();

        private PageViewport currentPageViewport;
        private Stack areaStack = new Stack();
        private boolean firstFlow;
        private boolean pendingStartPageSequence;
       
        private Stack delegateStack = new Stack();
        private ContentHandler delegate;
        private DOMImplementation domImplementation;
       
       
        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("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("char", new CharMaker());
            makers.put("leader", new LeaderMaker());
            makers.put("viewport", new ViewportMaker());
            makers.put("image", new ImageMaker());
            makers.put("foreignObject", new ForeignObjectMaker());
        }

        private static Rectangle2D parseRect(String rect) {
            StringTokenizer tokenizer = new StringTokenizer(rect, " ");
            return new Rectangle2D.Double(
                    Double.parseDouble(tokenizer.nextToken()),
                    Double.parseDouble(tokenizer.nextToken()),
                    Double.parseDouble(tokenizer.nextToken()),
                    Double.parseDouble(tokenizer.nextToken()));
        }
       
        private Area findAreaType(Class clazz) {
            if (areaStack.size() > 0) {
                int pos = areaStack.size() - 1;
                Object obj = null;
                while (pos >= 0 && !(clazz.isInstance(obj = areaStack.get(pos)))) {
                    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 Viewport getCurrentViewport() {
            return (Viewport)findAreaType(Viewport.class);
        }
       
        /** @see org.xml.sax.helpers.DefaultHandler */
        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 {
                lastAttributes = attributes;
                boolean handled = true;
                if ("".equals(uri)) {
                    Maker maker = (Maker)makers.get(localName);
                    if (maker != null) {
                        maker.startElement(attributes);
                    } else if ("extension-attachments".equals(localName)) {
                        //TODO implement me
                    } else {
                        handled = false;
                    }
                } else {
                    ContentHandlerFactoryRegistry registry
                            = userAgent.getFactory().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);
                    }
                }
            }
        }
       
        /** @see org.xml.sax.helpers.DefaultHandler */
        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 = (Maker)makers.get(localName);
                    if (maker != null) {
                        maker.endElement();
                    }
                } else {
                    //log.debug("Ignoring " + localName + " in namespace: " + uri);
                }
                content.setLength(0); //Reset text buffer (see characters())
            }
        }
       
        // ============== Maker classes for the area tree objects =============
       
        private static interface Maker {
            void startElement(Attributes attributes) throws SAXException;
            void endElement();
        }
       
        private abstract class AbstractMaker implements Maker {

            public void startElement(Attributes attributes) throws SAXException {
                //nop
            }

            public void endElement() {
                //nop
            }
        }
       
        private class AreaTreeMaker extends AbstractMaker {
            //no overrides
        }
       
        private class PageSequenceMaker extends AbstractMaker {

            public void startElement(Attributes attributes) {
                pendingStartPageSequence = true;
                //treeModel.startPageSequence(null); Done after title or on the first viewport
            }
        }
       
        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();
                treeModel.startPageSequence(line);
                pendingStartPageSequence = false;
            }
           
           
        }
       
        private class PageViewportMaker extends AbstractMaker {

            public void startElement(Attributes attributes) {
                if (pendingStartPageSequence) {
                    treeModel.startPageSequence(null);
                    pendingStartPageSequence = false;
                }
                if (currentPageViewport != null) {
                    throw new IllegalStateException("currentPageViewport must be null");
                }
                Rectangle2D viewArea = parseRect(attributes.getValue("bounds"));
                int pageNumber = getAttributeAsInteger(attributes, "nr", -1);
                String key = attributes.getValue("key");
                String pageNumberString = attributes.getValue("formatted-nr");
                String pageMaster = attributes.getValue("simple-page-master-name");
                boolean blank = getAttributeAsBoolean(attributes, "blank", false);
                currentPageViewport = new PageViewport(viewArea,
                        pageNumber, pageNumberString,
                        pageMaster, blank);
                transferForeignObjects(attributes, currentPageViewport);
                currentPageViewport.setKey(key);
            }

        }
       
        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 = parseRect(attributes.getValue("rect"));
                rv = new RegionViewport(viewArea);
                transferForeignObjects(attributes, rv);
                rv.setClip(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, Constants.FO_REGION_BEFORE);
            }
           
            public void endElement() {
                assertObjectOfClass(areaStack.pop(), RegionReference.class);
            }           
        }

        private class RegionAfterMaker extends AbstractMaker {

            public void startElement(Attributes attributes) {
                pushNewRegionReference(attributes, Constants.FO_REGION_AFTER);
            }
           
            public void endElement() {
                assertObjectOfClass(areaStack.pop(), RegionReference.class);
            }           
        }

        private class RegionStartMaker extends AbstractMaker {

            public void startElement(Attributes attributes) {
                pushNewRegionReference(attributes, Constants.FO_REGION_START);
            }
           
            public void endElement() {
                assertObjectOfClass(areaStack.pop(), RegionReference.class);
            }           
        }
       
        private class RegionEndMaker extends AbstractMaker {

            public void startElement(Attributes attributes) {
                pushNewRegionReference(attributes, Constants.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 = getAttributeAsInteger(attributes, "columnCount", 1);
                int columnGap = getAttributeAsInteger(attributes, "columnGap", 0);
                RegionViewport rv = getCurrentRegionViewport();
                body = new BodyRegion(Constants.FO_REGION_BODY,
                        regionName, rv, columnCount, columnGap);
                transferForeignObjects(attributes, body);
                body.setCTM(getAttributeAsCTM(attributes, "ctm"));
                setAreaAttributes(attributes, body);
                rv.setRegionReference(body);
                currentPageViewport.getPage().setRegionViewport(
                        Constants.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 = getAttributeAsInteger(attributes, "ipd", 0);
                int columnCount = getAttributeAsInteger(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);
                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 = getAttributeAsBoolean(attributes,
                        "is-viewport-area", false);
                Block block;
                if (isViewport) {
                    BlockViewport bv = new BlockViewport();
                    bv.setClip(getAttributeAsBoolean(attributes, "clipped", false));
                    bv.setCTM(getAttributeAsCTM(attributes, "ctm"));
                    if (bv.getPositioning() != BlockViewport.RELATIVE) {
                        bv.setXOffset(
                                getAttributeAsInteger(attributes, "left-position", 0));
                        bv.setYOffset(
                                getAttributeAsInteger(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(getAttributeAsInteger(attributes, "left-offset", 0));
                }
                if (attributes.getValue("top-offset") != null) {
                    block.setYOffset(getAttributeAsInteger(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();
                //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);
            }           
        }

        private class InlineParentMaker extends AbstractMaker {

            public void startElement(Attributes attributes) {
                InlineParent ip = new InlineParent();
                transferForeignObjects(attributes, ip);
                ip.setOffset(getAttributeAsInteger(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.setOffset(getAttributeAsInteger(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(getAttributeAsInteger(attributes, "baseline", 0));
                text.setOffset(getAttributeAsInteger(attributes, "offset", 0));
                text.setTextLetterSpaceAdjust(getAttributeAsInteger(attributes,
                        "tlsadjust", 0));
                text.setTextWordSpaceAdjust(getAttributeAsInteger(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 {

            private int[] toIntArray(String s) {
                if (s == null || s.length() == 0) {
                    return null;
                }
                StringTokenizer tokenizer = new StringTokenizer(s, " ");
                List values = new java.util.ArrayList();
                while (tokenizer.hasMoreTokens()) {
                    values.add(new Integer(tokenizer.nextToken()));
                }
                int[] res = new int[values.size()];
                for (int i = 0, c = res.length; i < c; i++) {
                    res[i] = ((Integer)values.get(i)).intValue();
                }
                return res;
            }
           
            public void endElement() {
                int offset = getAttributeAsInteger(lastAttributes, "offset", 0);
                int[] letterAdjust = toIntArray(lastAttributes.getValue("letter-adjust"));
                String txt = content.toString();
                WordArea word = new WordArea(txt, offset, letterAdjust);
                AbstractTextArea text = getCurrentText();
                word.setParentArea(text);
                text.addChildArea(word);
            }           
        }

        private class SpaceMaker extends AbstractMaker {

            public void endElement() {
                int offset = getAttributeAsInteger(lastAttributes, "offset", 0);
                String txt = content.toString();
                //TODO the isAdjustable parameter is currently not used/implemented
                if (txt.length() > 0) {
                    boolean adjustable = getAttributeAsBoolean(lastAttributes, "adj", true);
                    SpaceArea space = new SpaceArea(txt.charAt(0), offset, 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.setOffset(offset);
                    Area parent = (Area)areaStack.peek();
                    parent.addChildArea(space);
                }
            }           
        }

        private class CharMaker extends AbstractMaker {

            public void endElement() {
                String txt = content.toString();
                Character ch = new Character(txt.charAt(0));
                transferForeignObjects(lastAttributes, ch);
                setAreaAttributes(lastAttributes, ch);
                setTraits(lastAttributes, ch, SUBSET_COMMON);
                setTraits(lastAttributes, ch, SUBSET_BOX);
                setTraits(lastAttributes, ch, SUBSET_COLOR);
                setTraits(lastAttributes, ch, SUBSET_FONT);
                ch.setOffset(getAttributeAsInteger(lastAttributes, "offset", 0));
                ch.setBaselineOffset(getAttributeAsInteger(lastAttributes, "baseline", 0));
                Area parent = (Area)areaStack.peek();
                parent.addChildArea(ch);
            }           
        }

        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.setOffset(getAttributeAsInteger(attributes, "offset", 0));
                String ruleStyle = attributes.getValue("ruleStyle");
                if (ruleStyle != null) {
                    leader.setRuleStyle(ruleStyle);
                }
                leader.setRuleThickness(
                        getAttributeAsInteger(attributes, "ruleThickness", 0));
                Area parent = (Area)areaStack.peek();
                parent.addChildArea(leader);
            }
           
            public void endElement() {
            }           
        }

        private class ViewportMaker extends AbstractMaker {

            public void startElement(Attributes attributes) {
                Viewport viewport = new Viewport(null);
                transferForeignObjects(attributes, viewport);
                setAreaAttributes(attributes, viewport);
                setTraits(attributes, viewport, SUBSET_COMMON);
                setTraits(attributes, viewport, SUBSET_BOX);
                setTraits(attributes, viewport, SUBSET_COLOR);
                viewport.setContentPosition(getAttributeAsRectangle2D(attributes, "pos"));
                viewport.setClip(getAttributeAsBoolean(attributes, "clip", false));
                viewport.setOffset(getAttributeAsInteger(attributes, "offset", 0));
                Area parent = (Area)areaStack.peek();
                parent.addChildArea(viewport);
                areaStack.push(viewport);
            }
           
            public void endElement() {
                assertObjectOfClass(areaStack.pop(), Viewport.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 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);
            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 {
                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")));
        }
       
        private static final Object[] SUBSET_COMMON = new Object[] {
            Trait.PROD_ID};
        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 void setTraits(Attributes attributes, Area area, Object[] traitSubset) {
            for (int i = 0, c = traitSubset.length; i < c; i++) {
                Object trait = 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, new Integer(value));
                    } else if (cl == Boolean.class) {
                        area.addTrait(trait, Boolean.valueOf(value));
                    } else if (cl == String.class) {
                        area.addTrait(trait, 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 == 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 url = attributes.getValue("bkg-img");
                        if (url != null) {
                            bkg.setURL(url);
                           
                            ImageFactory fact = userAgent.getFactory().getImageFactory();
                            FopImage img = fact.getImage(url, userAgent);
                            if (img == null) {
                                log.error("Background image not available: " + url);
                            } else {
                                // load dimensions
                                if (!img.load(FopImage.DIMENSIONS)) {
                                    log.error("Cannot read background image dimensions: "
                                            + url);
                                }
                            }
                            bkg.setFopImage(img);

                            String repeat = attributes.getValue("bkg-repeat");
                            if (repeat != null) {
                                bkg.setRepeat(repeat);
                            }
                            bkg.setHoriz(getAttributeAsInteger(attributes,
                                    "bkg-horz-offset", 0));
                            bkg.setVertical(getAttributeAsInteger(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 == Trait.FONT) {
                        String fontName = attributes.getValue("font-name");
                        if (fontName != null) {
                            String fontStyle = attributes.getValue("font-style");
                            int fontWeight = getAttributeAsInteger(
                                    attributes, "font-weight", Font.NORMAL);
                            area.addTrait(trait,
                                    FontInfo.createFontKey(fontName, fontStyle, fontWeight));
                        }
                    }
                }
            }
        }
       
        private boolean getAttributeAsBoolean(Attributes attributes, String name,
                boolean defaultValue) {
            String s = attributes.getValue(name);
            if (s == null) {
                return defaultValue;
            } else {
                return Boolean.valueOf(s).booleanValue();
            }
        }

        private int getAttributeAsInteger(Attributes attributes, String name,
                int defaultValue) {
            String s = attributes.getValue(name);
            if (s == null) {
                return defaultValue;
            } else {
                return Integer.parseInt(s);
            }
        }

        private CTM getAttributeAsCTM(Attributes attributes, String name) {
            String s = attributes.getValue(name).trim();
            if (s.startsWith("[") && s.endsWith("]")) {
                s = s.substring(1, s.length() - 1);
                StringTokenizer tokenizer = new StringTokenizer(s, " ");
                double[] values = new double[] {
                        Double.parseDouble(tokenizer.nextToken()),
                        Double.parseDouble(tokenizer.nextToken()),
                        Double.parseDouble(tokenizer.nextToken()),
                        Double.parseDouble(tokenizer.nextToken()),
                        Double.parseDouble(tokenizer.nextToken()),
                        Double.parseDouble(tokenizer.nextToken())};
                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 Rectangle2D getAttributeAsRectangle2D(Attributes attributes, String name) {
            String s = attributes.getValue(name).trim();
            StringTokenizer tokenizer = new StringTokenizer(s, " ");
            double[] values = new double[] {
                    Double.parseDouble(tokenizer.nextToken()),
                    Double.parseDouble(tokenizer.nextToken()),
                    Double.parseDouble(tokenizer.nextToken()),
                    Double.parseDouble(tokenizer.nextToken())};
            return new Rectangle2D.Double(values[0], values[1], values[2], values[3]);
        }

        private 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 ("http://www.w3.org/2000/xmlns/".equals(ns)) {
                        continue;
                    }
                    QName qname = new QName(ns, atts.getQName(i));
                    ato.setForeignAttribute(qname, atts.getValue(i));
                }
            }
        }
       
        /** @see org.xml.sax.ContentHandler#characters(char[], int, int) */
        public void characters(char[] ch, int start, int length) throws SAXException {
            if (delegate != null) {
                delegate.characters(ch, start, length);
            } else {
                content.append(ch, start, length);
            }
        }

    }
   
}
TOP

Related Classes of org.apache.fop.area.AreaTreeParser$Handler$LeaderMaker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.