| /* |
| * Copyright 1999-2005 The Apache Software Foundation. |
| * |
| * Licensed 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: XMLRenderer.java,v 1.20 2004/04/25 04:45:28 gmazza Exp $ */ |
| |
| package org.apache.fop.render.xml; |
| |
| // Java |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Iterator; |
| import java.awt.geom.Rectangle2D; |
| |
| import javax.xml.transform.TransformerConfigurationException; |
| import javax.xml.transform.sax.SAXTransformerFactory; |
| import javax.xml.transform.sax.TransformerHandler; |
| import javax.xml.transform.stream.StreamResult; |
| |
| import org.w3c.dom.Document; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.helpers.AttributesImpl; |
| |
| // FOP |
| import org.apache.fop.render.AbstractRenderer; |
| import org.apache.fop.render.RendererContext; |
| import org.apache.fop.render.XMLHandler; |
| import org.apache.fop.apps.FOUserAgent; |
| import org.apache.fop.apps.FOPException; |
| import org.apache.fop.area.Area; |
| import org.apache.fop.area.BeforeFloat; |
| import org.apache.fop.area.Block; |
| import org.apache.fop.area.BlockViewport; |
| import org.apache.fop.area.BodyRegion; |
| import org.apache.fop.area.NormalFlow; |
| import org.apache.fop.area.Footnote; |
| import org.apache.fop.area.LineArea; |
| import org.apache.fop.area.MainReference; |
| import org.apache.fop.area.PageViewport; |
| import org.apache.fop.area.RegionReference; |
| import org.apache.fop.area.RegionViewport; |
| import org.apache.fop.area.Span; |
| import org.apache.fop.area.Trait; |
| 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.InlineParent; |
| import org.apache.fop.area.inline.Leader; |
| import org.apache.fop.area.inline.Space; |
| import org.apache.fop.area.inline.Viewport; |
| import org.apache.fop.area.inline.TextArea; |
| import org.apache.fop.fonts.FontSetup; |
| import org.apache.fop.fonts.FontInfo; |
| |
| /** |
| * Renderer that renders areas to XML for debugging purposes. |
| * This creates an xml that contains the information of the area |
| * tree. It does not output any state or derived information. |
| * The output can be used to build a new area tree (@see AreaTreeBuilder) |
| * which can be rendered to any renderer. |
| */ |
| public class XMLRenderer extends AbstractRenderer { |
| |
| /** XML MIME type */ |
| public static final String XML_MIME_TYPE = "application/x-fop-areatree"; |
| |
| /** Main namespace in use. */ |
| public static final String NS = ""; |
| |
| /** CDATA type */ |
| public static final String CDATA = "CDATA"; |
| |
| /** An empty Attributes object used when no attributes are needed. */ |
| public static final Attributes EMPTY_ATTS = new AttributesImpl(); |
| |
| private boolean startedSequence = false; |
| private RendererContext context; |
| |
| /** TransformerHandler that the generated XML is written to */ |
| protected TransformerHandler handler; |
| |
| /** AttributesImpl instance that can be used during XML generation. */ |
| protected AttributesImpl atts = new AttributesImpl(); |
| |
| /** The OutputStream to write the generated XML to. */ |
| protected OutputStream out; |
| |
| /** |
| * Creates a new XML renderer. |
| */ |
| public XMLRenderer() { |
| context = new RendererContext(XML_MIME_TYPE); |
| } |
| |
| /** |
| * @see org.apache.fop.render.Renderer#setUserAgent(FOUserAgent) |
| */ |
| public void setUserAgent(FOUserAgent agent) { |
| super.setUserAgent(agent); |
| |
| // |
| //userAgent.addExtensionHandler(); |
| XMLHandler handler = new XMLXMLHandler(); |
| setDefaultXMLHandler(userAgent, XML_MIME_TYPE, handler); |
| String svg = "http://www.w3.org/2000/svg"; |
| addXMLHandler(userAgent, XML_MIME_TYPE, svg, handler); |
| } |
| |
| /** |
| * Sets an outside TransformerHandler to use instead of the default one |
| * create in this class in startRenderer(). |
| * @param handler Overriding TransformerHandler |
| */ |
| public void setTransformerHandler(TransformerHandler handler) { |
| this.handler = handler; |
| } |
| |
| /** |
| * set up the font info |
| * |
| * @param fontInfo the font info object to set up |
| */ |
| public void setupFontInfo(FontInfo fontInfo) { |
| FontSetup.setup(fontInfo, null); |
| } |
| |
| private boolean isCoarseXml() { |
| return ((Boolean) |
| userAgent.getRendererOptions().get("fineDetail")).booleanValue(); |
| } |
| |
| /** |
| * Handles SAXExceptions. |
| * @param saxe the SAXException to handle |
| */ |
| protected void handleSAXException(SAXException saxe) { |
| throw new RuntimeException(saxe.getMessage()); |
| } |
| |
| /** |
| * Writes a comment to the generated XML. |
| * @param comment the comment |
| */ |
| protected void comment(String comment) { |
| try { |
| handler.comment(comment.toCharArray(), 0, comment.length()); |
| } catch (SAXException saxe) { |
| handleSAXException(saxe); |
| } |
| } |
| |
| /** |
| * Starts a new element (without attributes). |
| * @param tagName tag name of the element |
| */ |
| protected void startElement(String tagName) { |
| startElement(tagName, EMPTY_ATTS); |
| } |
| |
| /** |
| * Starts a new element. |
| * @param tagName tag name of the element |
| * @param atts attributes to add |
| */ |
| protected void startElement(String tagName, Attributes atts) { |
| try { |
| handler.startElement(NS, tagName, tagName, atts); |
| } catch (SAXException saxe) { |
| handleSAXException(saxe); |
| } |
| } |
| |
| /** |
| * Ends an element. |
| * @param tagName tag name of the element |
| */ |
| protected void endElement(String tagName) { |
| try { |
| handler.endElement(NS, tagName, tagName); |
| } catch (SAXException saxe) { |
| handleSAXException(saxe); |
| } |
| } |
| |
| /** |
| * Sends plain text to the XML |
| * @param text the text |
| */ |
| protected void characters(String text) { |
| try { |
| char[] ca = text.toCharArray(); |
| handler.characters(ca, 0, ca.length); |
| } catch (SAXException saxe) { |
| handleSAXException(saxe); |
| } |
| } |
| |
| /** |
| * Adds a new attribute to the protected member variable "atts". |
| * @param name name of the attribute |
| * @param value value of the attribute |
| */ |
| protected void addAttribute(String name, String value) { |
| atts.addAttribute(NS, name, name, CDATA, value); |
| } |
| |
| /** |
| * Adds a new attribute to the protected member variable "atts". |
| * @param name name of the attribute |
| * @param value value of the attribute |
| */ |
| protected void addAttribute(String name, int value) { |
| addAttribute(name, Integer.toString(value)); |
| } |
| |
| /** |
| * Adds a new attribute to the protected member variable "atts". |
| * @param name name of the attribute |
| * @param rect a Rectangle2D to format and use as attribute value |
| */ |
| protected void addAttribute(String name, Rectangle2D rect) { |
| addAttribute(name, createString(rect)); |
| } |
| |
| /** |
| * Adds the general Area attributes. |
| * @param area Area to extract attributes from |
| */ |
| protected void addAreaAttributes(Area area) { |
| addAttribute("ipd", area.getIPD()); |
| addAttribute("bpd", area.getBPD()); |
| if (area.getIPD() != 0) { |
| addAttribute("ipda", area.getAllocIPD()); |
| } |
| if (area.getBPD() != 0) { |
| addAttribute("bpda", area.getAllocBPD()); |
| } |
| addAttribute("bap", area.getBorderAndPaddingWidthStart() + " " |
| + area.getBorderAndPaddingWidthEnd() + " " |
| + area.getBorderAndPaddingWidthBefore() + " " |
| + area.getBorderAndPaddingWidthAfter()); |
| } |
| |
| /** |
| * Adds attributes from traits of an Area. |
| * @param area Area to extract traits from |
| */ |
| protected void addTraitAttributes(Area area) { |
| Map traitMap = area.getTraits(); |
| if (traitMap != null) { |
| Iterator iter = traitMap.entrySet().iterator(); |
| while (iter.hasNext()) { |
| Map.Entry traitEntry = (Map.Entry) iter.next(); |
| String name = Trait.getTraitName(traitEntry.getKey()); |
| if ("break-before".equals(name) || "break-after".equals(name)) { |
| continue; |
| } |
| String value = traitEntry.getValue().toString(); |
| addAttribute(name, value); |
| } |
| } |
| } |
| |
| private String createString(Rectangle2D rect) { |
| return "" + (int) rect.getX() + " " + (int) rect.getY() + " " |
| + (int) rect.getWidth() + " " + (int) rect.getHeight(); |
| } |
| |
| /** |
| * @see org.apache.fop.render.Renderer#startRenderer(OutputStream) |
| */ |
| public void startRenderer(OutputStream outputStream) |
| throws IOException { |
| getLogger().debug("Rendering areas to Area Tree XML"); |
| |
| if (this.handler == null) { |
| SAXTransformerFactory factory |
| = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); |
| try { |
| this.handler = factory.newTransformerHandler(); |
| StreamResult res = new StreamResult(outputStream); |
| handler.setResult(res); |
| } catch (TransformerConfigurationException tce) { |
| throw new RuntimeException(tce.getMessage()); |
| } |
| |
| this.out = outputStream; |
| } |
| |
| try { |
| handler.startDocument(); |
| } catch (SAXException saxe) { |
| handleSAXException(saxe); |
| } |
| comment("Produced by " |
| + (userAgent.getProducer() != null ? userAgent.getProducer() : "")); |
| startElement("areaTree"); |
| } |
| |
| /** |
| * @see org.apache.fop.render.Renderer#stopRenderer() |
| */ |
| public void stopRenderer() throws IOException { |
| if (startedSequence) { |
| endElement("pageSequence"); |
| } |
| endElement("areaTree"); |
| try { |
| handler.endDocument(); |
| } catch (SAXException saxe) { |
| handleSAXException(saxe); |
| } |
| if (this.out != null) { |
| this.out.flush(); |
| } |
| getLogger().debug("Written out Area Tree XML"); |
| } |
| |
| /** |
| * @see org.apache.fop.render.Renderer#renderPage(PageViewport) |
| */ |
| public void renderPage(PageViewport page) throws IOException, FOPException { |
| atts.clear(); |
| addAttribute("bounds", page.getViewArea()); |
| addAttribute("nr", page.getPageNumberString()); |
| startElement("pageViewport", atts); |
| startElement("page"); |
| super.renderPage(page); |
| endElement("page"); |
| endElement("pageViewport"); |
| } |
| |
| /** |
| * @see org.apache.fop.render.Renderer#startPageSequence(Title) |
| */ |
| public void startPageSequence(LineArea seqTitle) { |
| if (startedSequence) { |
| endElement("pageSequence"); |
| } |
| startedSequence = true; |
| startElement("pageSequence"); |
| if (seqTitle != null) { |
| startElement("title"); |
| List children = seqTitle.getInlineAreas(); |
| |
| for (int count = 0; count < children.size(); count++) { |
| InlineArea inline = (InlineArea) children.get(count); |
| renderInlineArea(inline); |
| } |
| |
| endElement("title"); |
| } |
| } |
| |
| /** |
| * @see org.apache.fop.render.AbstractRenderer#renderRegionViewport(RegionViewport) |
| */ |
| protected void renderRegionViewport(RegionViewport port) { |
| if (port != null) { |
| atts.clear(); |
| addAreaAttributes(port); |
| addTraitAttributes(port); |
| addAttribute("rect", port.getViewArea()); |
| startElement("regionViewport", atts); |
| RegionReference region = port.getRegionReference(); |
| atts.clear(); |
| addAreaAttributes(region); |
| addTraitAttributes(region); |
| if (region.getRegionClass() == FO_REGION_BEFORE) { |
| startElement("regionBefore", atts); |
| renderRegion(region); |
| endElement("regionBefore"); |
| } else if (region.getRegionClass() == FO_REGION_START) { |
| startElement("regionStart", atts); |
| renderRegion(region); |
| endElement("regionStart"); |
| } else if (region.getRegionClass() == FO_REGION_BODY) { |
| startElement("regionBody", atts); |
| renderBodyRegion((BodyRegion) region); |
| endElement("regionBody"); |
| } else if (region.getRegionClass() == FO_REGION_END) { |
| startElement("regionEnd", atts); |
| renderRegion(region); |
| endElement("regionEnd"); |
| } else if (region.getRegionClass() == FO_REGION_AFTER) { |
| startElement("regionAfter", atts); |
| renderRegion(region); |
| endElement("regionAfter"); |
| } |
| endElement("regionViewport"); |
| } |
| } |
| |
| /** |
| * @see org.apache.fop.render.AbstractRenderer#renderBeforeFloat(BeforeFloat) |
| */ |
| protected void renderBeforeFloat(BeforeFloat bf) { |
| startElement("beforeFloat"); |
| super.renderBeforeFloat(bf); |
| endElement("beforeFloat"); |
| } |
| |
| /** |
| * @see org.apache.fop.render.AbstractRenderer#renderFootnote(Footnote) |
| */ |
| protected void renderFootnote(Footnote footnote) { |
| startElement("footnote"); |
| super.renderFootnote(footnote); |
| endElement("footnote"); |
| } |
| |
| /** |
| * @see org.apache.fop.render.AbstractRenderer#renderMainReference(MainReference) |
| */ |
| protected void renderMainReference(MainReference mr) { |
| atts.clear(); |
| addAreaAttributes(mr); |
| addTraitAttributes(mr); |
| addAttribute("columnGap", mr.getColumnGap()); |
| addAttribute("width", mr.getWidth()); |
| startElement("mainReference", atts); |
| |
| Span span = null; |
| List spans = mr.getSpans(); |
| for (int count = 0; count < spans.size(); count++) { |
| span = (Span) spans.get(count); |
| atts.clear(); |
| addAreaAttributes(span); |
| addTraitAttributes(span); |
| startElement("span", atts); |
| for (int c = 0; c < span.getColumnCount(); c++) { |
| NormalFlow flow = (NormalFlow) span.getNormalFlow(c); |
| |
| renderFlow(flow); |
| } |
| endElement("span"); |
| } |
| endElement("mainReference"); |
| } |
| |
| /** |
| * @see org.apache.fop.render.AbstractRenderer#renderFlow(Flow) |
| */ |
| protected void renderFlow(NormalFlow flow) { |
| // the normal flow reference area contains stacked blocks |
| atts.clear(); |
| addAreaAttributes(flow); |
| addTraitAttributes(flow); |
| startElement("flow", atts); |
| super.renderFlow(flow); |
| endElement("flow"); |
| } |
| |
| /** |
| * @see org.apache.fop.render.AbstractRenderer#renderBlock(Block) |
| */ |
| protected void renderBlock(Block block) { |
| atts.clear(); |
| addAreaAttributes(block); |
| addTraitAttributes(block); |
| if (block instanceof BlockViewport) { |
| BlockViewport bvp = (BlockViewport)block; |
| boolean abspos = false; |
| if (bvp.getPositioning() == Block.ABSOLUTE) { |
| addAttribute("positioning", "absolute"); |
| abspos = true; |
| } else if (bvp.getPositioning() == Block.FIXED) { |
| addAttribute("positioning", "fixed"); |
| abspos = true; |
| } |
| if (abspos) { |
| addAttribute("left-position", bvp.getXOffset()); |
| addAttribute("top-position", bvp.getYOffset()); |
| } |
| addAttribute("ctm", bvp.getCTM().toString()); |
| if (bvp.getClip()) { |
| addAttribute("clipped", "true"); |
| } |
| } else { |
| if (block.getPositioning() == Block.RELATIVE) { |
| addAttribute("positioning", "relative"); |
| } |
| if (block.getXOffset() != 0) { |
| addAttribute("left-offset", block.getXOffset()); |
| } |
| if (block.getYOffset() != 0) { |
| addAttribute("top-offset", block.getYOffset()); |
| } |
| } |
| startElement("block", atts); |
| super.renderBlock(block); |
| endElement("block"); |
| } |
| |
| /** |
| * @see org.apache.fop.render.AbstractRenderer#renderLineArea(LineArea) |
| */ |
| protected void renderLineArea(LineArea line) { |
| atts.clear(); |
| addAreaAttributes(line); |
| addTraitAttributes(line); |
| startElement("lineArea", atts); |
| super.renderLineArea(line); |
| endElement("lineArea"); |
| } |
| |
| /** |
| * @see org.apache.fop.render.Renderer#renderViewport(Viewport) |
| */ |
| protected void renderViewport(Viewport viewport) { |
| atts.clear(); |
| addAreaAttributes(viewport); |
| startElement("viewport", atts); |
| super.renderViewport(viewport); |
| endElement("viewport"); |
| } |
| |
| /** |
| * Renders an image |
| * @param image the image |
| */ |
| public void renderImage(Image image) { |
| atts.clear(); |
| addAreaAttributes(image); |
| addAttribute("url", image.getURL()); |
| startElement("image", atts); |
| endElement("image"); |
| } |
| |
| /** |
| * @see org.apache.fop.render.Renderer#renderContainer(Container) |
| */ |
| public void renderContainer(Container cont) { |
| startElement("container"); |
| super.renderContainer(cont); |
| endElement("container"); |
| } |
| |
| /** |
| * Renders an fo:foreing-object. |
| * @param fo the foreign object |
| */ |
| public void renderForeignObject(ForeignObject fo, Rectangle2D pos) { |
| atts.clear(); |
| addAreaAttributes(fo); |
| startElement("foreignObject", atts); |
| Document doc = fo.getDocument(); |
| String ns = fo.getNameSpace(); |
| context.setProperty(XMLXMLHandler.HANDLER, handler); |
| renderXML(userAgent, context, doc, ns); |
| endElement("foreignObject"); |
| } |
| |
| /** |
| * @see org.apache.fop.render.Renderer#renderCharacter(Character) |
| */ |
| protected void renderCharacter(org.apache.fop.area.inline.Character ch) { |
| atts.clear(); |
| addTraitAttributes(ch); |
| startElement("char", atts); |
| characters(ch.getChar()); |
| endElement("char"); |
| } |
| |
| /** |
| * @see org.apache.fop.render.Renderer#renderInlineSpace(Space) |
| */ |
| protected void renderInlineSpace(Space space) { |
| atts.clear(); |
| addAreaAttributes(space); |
| startElement("space", atts); |
| endElement("space"); |
| } |
| |
| /** |
| * @see org.apache.fop.render.Renderer#renderText(TextArea) |
| */ |
| protected void renderText(TextArea text) { |
| atts.clear(); |
| if (text.getTextWordSpaceAdjust() != 0) { |
| addAttribute("twsadjust", text.getTextWordSpaceAdjust()); |
| } |
| if (text.getTextLetterSpaceAdjust() != 0) { |
| addAttribute("tlsadjust", text.getTextLetterSpaceAdjust()); |
| } |
| addTraitAttributes(text); |
| startElement("text", atts); |
| characters(text.getTextArea()); |
| endElement("text"); |
| super.renderText(text); |
| } |
| |
| /** |
| * @see org.apache.fop.render.Renderer#renderInlineParent(InlineParent) |
| */ |
| protected void renderInlineParent(InlineParent ip) { |
| atts.clear(); |
| addTraitAttributes(ip); |
| startElement("inlineparent", atts); |
| super.renderInlineParent(ip); |
| endElement("inlineparent"); |
| } |
| |
| /** |
| * @see org.apache.fop.render.Renderer#renderLeader(Leader) |
| */ |
| protected void renderLeader(Leader area) { |
| String style = "solid"; |
| switch (area.getRuleStyle()) { |
| case EN_DOTTED: |
| style = "dotted"; |
| break; |
| case EN_DASHED: |
| style = "dashed"; |
| break; |
| case EN_SOLID: |
| break; |
| case EN_DOUBLE: |
| style = "double"; |
| break; |
| case EN_GROOVE: |
| style = "groove"; |
| break; |
| case EN_RIDGE: |
| style = "ridge"; |
| break; |
| default: |
| style = "--NYI--"; |
| } |
| atts.clear(); |
| addAreaAttributes(area); |
| addAttribute("ruleStyle", style); |
| addAttribute("ruleThickness", area.getRuleThickness()); |
| startElement("leader", atts); |
| endElement("leader"); |
| super.renderLeader(area); |
| } |
| |
| /** @see org.apache.fop.render.AbstractRenderer */ |
| public String getMimeType() { |
| return XML_MIME_TYPE; |
| } |
| |
| } |
| |