From 4cf83fe1e5dab96e2e348877b0e43d8abdc6c0d9 Mon Sep 17 00:00:00 2001 From: Keiron Liddle Date: Fri, 2 Nov 2001 07:45:18 +0000 Subject: [PATCH] svg renderer now basically works, pages, text, leader, svg some other misc updates to areas git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@194534 13f79535-47bb-0310-9956-ffa450edef68 --- src/org/apache/fop/apps/Driver.java | 4 + src/org/apache/fop/area/Footnote.java | 3 +- src/org/apache/fop/area/Property.java | 10 + src/org/apache/fop/area/inline/Container.java | 9 +- .../apache/fop/area/inline/InlineArea.java | 8 + src/org/apache/fop/area/inline/Word.java | 8 + .../apache/fop/datatypes/IDReferences.java | 2 - src/org/apache/fop/fo/FOTreeBuilder.java | 19 +- src/org/apache/fop/fo/FOUserAgent.java | 2 +- .../fop/fo/pagination/PageSequence.java | 6 +- .../fop/messaging/DefaultMessageListener.java | 4 - .../apache/fop/messaging/MessageEvent.java | 3 - .../apache/fop/render/AbstractRenderer.java | 43 ++- .../apache/fop/render/svg/SVGRenderer.java | 291 +++++++++++++++++- .../apache/fop/render/xml/XMLRenderer.java | 6 +- src/org/apache/fop/svg/SVGElementMapping.java | 140 +++++---- src/org/apache/fop/tools/AreaTreeBuilder.java | 63 +++- 17 files changed, 498 insertions(+), 123 deletions(-) diff --git a/src/org/apache/fop/apps/Driver.java b/src/org/apache/fop/apps/Driver.java index e885fafb5..7cb5579c5 100644 --- a/src/org/apache/fop/apps/Driver.java +++ b/src/org/apache/fop/apps/Driver.java @@ -566,6 +566,10 @@ class Service { public static synchronized Enumeration providers(Class cls) { ClassLoader cl = cls.getClassLoader(); + // null if loaded by bootstrap class loader + if(cl == null) { + cl = ClassLoader.getSystemClassLoader(); + } String serviceFile = "META-INF/services/" + cls.getName(); // System.out.println("File: " + serviceFile); diff --git a/src/org/apache/fop/area/Footnote.java b/src/org/apache/fop/area/Footnote.java index dcd62ecdf..d36a769bc 100644 --- a/src/org/apache/fop/area/Footnote.java +++ b/src/org/apache/fop/area/Footnote.java @@ -7,11 +7,12 @@ package org.apache.fop.area; +import java.io.Serializable; import java.util.List; import java.util.ArrayList; // may combine with before float into a conditional area -public class Footnote { +public class Footnote implements Serializable { Block separator = null; // footnote has an optional separator diff --git a/src/org/apache/fop/area/Property.java b/src/org/apache/fop/area/Property.java index 3b9a836e0..4c6d5914c 100644 --- a/src/org/apache/fop/area/Property.java +++ b/src/org/apache/fop/area/Property.java @@ -7,6 +7,8 @@ package org.apache.fop.area; +import org.apache.fop.datatypes.ColorType; + import java.io.Serializable; // properties should be serialized by the holder @@ -26,8 +28,16 @@ public class Property implements Serializable { public static final int LINETHROUGH = 12; public static final int OFFSET = 13; public static final int SHADOW = 14; + public int propType; public Object data; + public static class Background { + ColorType color; + String url; + int repeat; + int horiz; + int vertical; + } } diff --git a/src/org/apache/fop/area/inline/Container.java b/src/org/apache/fop/area/inline/Container.java index 76c987e21..daf347fff 100644 --- a/src/org/apache/fop/area/inline/Container.java +++ b/src/org/apache/fop/area/inline/Container.java @@ -14,10 +14,13 @@ import org.apache.fop.render.Renderer; import java.util.List; import java.util.ArrayList; +// this is an inline area that can have blocks as children public class Container extends Area { ArrayList blocks = new ArrayList(); + int width; - // this is an inline area that can have blocks as children + public Container() { + } public void render(Renderer renderer) { renderer.renderContainer(this); @@ -31,4 +34,8 @@ public class Container extends Area { return blocks; } + public int getWidth() { + return width; + } + } diff --git a/src/org/apache/fop/area/inline/InlineArea.java b/src/org/apache/fop/area/inline/InlineArea.java index aa229316f..d688ae038 100644 --- a/src/org/apache/fop/area/inline/InlineArea.java +++ b/src/org/apache/fop/area/inline/InlineArea.java @@ -45,6 +45,14 @@ public class InlineArea extends Area { return width; } + public void setOffset(int v) { + verticalPosition = v; + } + + public int getOffset() { + return verticalPosition; + } + public void addProperty(Property prop) { if (props == null) { props = new ArrayList(); diff --git a/src/org/apache/fop/area/inline/Word.java b/src/org/apache/fop/area/inline/Word.java index 070af40ff..51b009b28 100644 --- a/src/org/apache/fop/area/inline/Word.java +++ b/src/org/apache/fop/area/inline/Word.java @@ -17,4 +17,12 @@ public class Word extends InlineArea { public void render(Renderer renderer) { renderer.renderWord(this); } + + public void setWord(String w) { + word = w; + } + + public String getWord() { + return word; + } } diff --git a/src/org/apache/fop/datatypes/IDReferences.java b/src/org/apache/fop/datatypes/IDReferences.java index 3701c6662..8f157cc81 100644 --- a/src/org/apache/fop/datatypes/IDReferences.java +++ b/src/org/apache/fop/datatypes/IDReferences.java @@ -91,7 +91,6 @@ public class IDReferences { * Creates id entry that hasn't been validated * * @param id The id to create - * @exception FOPException */ public void createUnvalidatedID(String id) { if (id != null &&!id.equals("")) { @@ -178,7 +177,6 @@ public class IDReferences { * Removes id from IDReferences * * @param id The id to remove - * @exception FOPException */ public void removeID(String id) { idReferences.remove(id); diff --git a/src/org/apache/fop/fo/FOTreeBuilder.java b/src/org/apache/fop/fo/FOTreeBuilder.java index 4e4cefafe..e4ac25758 100644 --- a/src/org/apache/fop/fo/FOTreeBuilder.java +++ b/src/org/apache/fop/fo/FOTreeBuilder.java @@ -27,7 +27,7 @@ import org.xml.sax.Attributes; // Java import java.util.HashMap; import java.util.Stack; -import java.util.Vector; +import java.util.ArrayList; import java.io.IOException; /** @@ -49,7 +49,7 @@ public class FOTreeBuilder extends DefaultHandler implements TreeBuilder { */ protected HashMap fobjTable = new HashMap(); - protected Vector namespaces = new Vector(); + protected ArrayList namespaces = new ArrayList(); /** * class that builds a property list for each formatting object @@ -101,7 +101,7 @@ public class FOTreeBuilder extends DefaultHandler implements TreeBuilder { */ public void addMapping(String namespaceURI, HashMap table) { this.fobjTable.put(namespaceURI, table); - this.namespaces.addElement(namespaceURI.intern()); + this.namespaces.add(namespaceURI.intern()); } /** @@ -212,8 +212,6 @@ public class FOTreeBuilder extends DefaultHandler implements TreeBuilder { /* the maker for the formatting object started */ FObj.Maker fobjMaker = null; - // String fullName = mapName(rawName); - //String fullName = uri + "^" + localName; HashMap table = (HashMap)fobjTable.get(uri); if(table != null) { fobjMaker = (FObj.Maker)table.get(localName); @@ -275,17 +273,6 @@ public class FOTreeBuilder extends DefaultHandler implements TreeBuilder { currentFObj = fobj; } - /** - * format this formatting object tree - * - * @param areaTree the area tree to format into - */ -/* public void format(AreaTree areaTree) throws FOPException { - log.info("formatting FOs into areas"); - this.bufferManager.readComplete(); - ((Root)this.rootFObj).format(areaTree); - } -*/ public void reset() { currentFObj = null; rootFObj = null; diff --git a/src/org/apache/fop/fo/FOUserAgent.java b/src/org/apache/fop/fo/FOUserAgent.java index 05ae93456..d89f308e4 100644 --- a/src/org/apache/fop/fo/FOUserAgent.java +++ b/src/org/apache/fop/fo/FOUserAgent.java @@ -73,7 +73,7 @@ public class FOUserAgent { handler.handleXML(ctx, doc, namespace); } catch (Throwable t) { // could not handle document - //t.printStackTrace(); + t.printStackTrace(); } } else { // no handler found for document diff --git a/src/org/apache/fop/fo/pagination/PageSequence.java b/src/org/apache/fop/fo/pagination/PageSequence.java index 290f19454..5b957d8a8 100644 --- a/src/org/apache/fop/fo/pagination/PageSequence.java +++ b/src/org/apache/fop/fo/pagination/PageSequence.java @@ -28,10 +28,10 @@ import org.apache.fop.apps.FOPException; // Java import java.util.*; - /** - * This provides pagination of flows onto pages. Much of the logic for paginating - * flows is contained in this class. The main entry point is the format method. + * This provides pagination of flows onto pages. Much of the + * logic for paginating flows is contained in this class. + * The main entry point is the format method. */ public class PageSequence extends FObj { // diff --git a/src/org/apache/fop/messaging/DefaultMessageListener.java b/src/org/apache/fop/messaging/DefaultMessageListener.java index 55e6e70f4..081bd6032 100644 --- a/src/org/apache/fop/messaging/DefaultMessageListener.java +++ b/src/org/apache/fop/messaging/DefaultMessageListener.java @@ -7,14 +7,10 @@ package org.apache.fop.messaging; - /** * A trivial implementation of a MessageListener * For further explanation - * @see MessageListener */ - - public class DefaultMessageListener implements MessageListener { /** diff --git a/src/org/apache/fop/messaging/MessageEvent.java b/src/org/apache/fop/messaging/MessageEvent.java index ac4c6f683..92a742aaa 100644 --- a/src/org/apache/fop/messaging/MessageEvent.java +++ b/src/org/apache/fop/messaging/MessageEvent.java @@ -13,10 +13,7 @@ import java.util.EventObject; * a container for the text and the type of a message * MessageEvents are created by MessageHandler and can be received by any * MessageListener, which is added to MessageHandler; - * @see org.apache.fop.MessageListener MessageListener - * */ - public class MessageEvent extends EventObject { public static final int LOG = 0; public static final int ERROR = 1; diff --git a/src/org/apache/fop/render/AbstractRenderer.java b/src/org/apache/fop/render/AbstractRenderer.java index a76a32f5e..d2bebb77c 100644 --- a/src/org/apache/fop/render/AbstractRenderer.java +++ b/src/org/apache/fop/render/AbstractRenderer.java @@ -13,12 +13,14 @@ import org.apache.fop.apps.FOPException; import org.apache.fop.area.*; import org.apache.fop.area.Span; import org.apache.fop.area.inline.*; +import org.apache.fop.area.inline.Character; import org.apache.fop.area.inline.Space; import org.apache.fop.fo.FOUserAgent; import org.apache.log.Logger; // Java +import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.OutputStream; import java.util.HashMap; @@ -56,6 +58,29 @@ public abstract class AbstractRenderer implements Renderer { options = opt; } + /** + * Utility method to convert a page sequence title to a string. + * Some renderers may only be able to use a string title. + * A title is a sequence of inline areas that this method + * attempts to convert to an equivalent string. + */ + public String convertTitleToString(Title title) { + String str = ""; + List children = title.getInlineAreas(); + + for (int count = 0; count < children.size(); count++) { + InlineArea inline = (InlineArea) children.get(count); + if (inline instanceof Character) { + str += ((Character) inline).getChar(); + } else if (inline instanceof Word) { + str += ((Word) inline).getWord(); + } else { + str += " "; + } + } + return str.trim(); + } + public void startPageSequence(Title seqTitle) { } @@ -87,6 +112,11 @@ public abstract class AbstractRenderer implements Renderer { // a position from where the region is placed protected void renderRegionViewport(RegionViewport port) { if (port != null) { + Rectangle2D view = port.getViewArea(); + currentBPPosition = (int) view.getY(); + currentIPPosition = (int) view.getX(); + currentBlockIPPosition = currentIPPosition; + Region region = port.getRegion(); if (region.getRegionClass() == Region.BODY) { renderBodyRegion((BodyRegion) region); @@ -185,7 +215,9 @@ public abstract class AbstractRenderer implements Renderer { // of the line, each inline object is offset from there for (int count = 0; count < children.size(); count++) { LineArea line = (LineArea) children.get(count); + currentBlockIPPosition = currentIPPosition; renderLineArea(line); + currentBPPosition += line.getHeight(); } } @@ -213,21 +245,30 @@ public abstract class AbstractRenderer implements Renderer { } else if (content instanceof ForeignObject) { renderForeignObject((ForeignObject) content); } + currentBlockIPPosition += viewport.getWidth(); } public void renderImage(Image image) { } public void renderContainer(Container cont) { + int saveIP = currentIPPosition; + currentIPPosition = currentBlockIPPosition; + int saveBlockIP = currentBlockIPPosition; + int saveBP = currentBPPosition; + List blocks = cont.getBlocks(); renderBlocks(blocks); + currentIPPosition = saveIP; + currentBlockIPPosition = saveBlockIP; + currentBPPosition = saveBP; } public void renderForeignObject(ForeignObject fo) { } - public void renderCharacter(org.apache.fop.area.inline.Character ch) { + public void renderCharacter(Character ch) { currentBlockIPPosition += ch.getWidth(); } diff --git a/src/org/apache/fop/render/svg/SVGRenderer.java b/src/org/apache/fop/render/svg/SVGRenderer.java index 46458f420..b47a832fd 100644 --- a/src/org/apache/fop/render/svg/SVGRenderer.java +++ b/src/org/apache/fop/render/svg/SVGRenderer.java @@ -7,13 +7,16 @@ package org.apache.fop.render.svg; -import org.apache.fop.layout.*; -import org.apache.fop.layout.inline.*; +import org.apache.fop.apps.FOPException; +import org.apache.fop.area.*; +import org.apache.fop.area.inline.*; import org.apache.fop.datatypes.IDReferences; import org.apache.fop.datatypes.ColorType; import org.apache.fop.image.*; import org.apache.fop.svg.SVGArea; import org.apache.fop.svg.SVGUtilities; +import org.apache.fop.layout.FontInfo; +import org.apache.fop.fo.FOUserAgent; import org.w3c.dom.Node; import org.w3c.dom.ProcessingInstruction; @@ -22,6 +25,7 @@ import org.w3c.dom.svg.SVGDocument; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.DOMImplementation; +import org.w3c.dom.Text; import org.apache.batik.dom.svg.SVGDOMImplementation; import org.apache.batik.dom.svg.SVGOMElement; @@ -30,12 +34,13 @@ import org.apache.batik.transcoder.svg2svg.SVGTranscoder; import org.apache.batik.transcoder.TranscoderInput; import org.apache.batik.transcoder.TranscoderOutput; import org.apache.batik.transcoder.TranscoderException; +import org.apache.batik.dom.util.DOMUtilities; import java.awt.Color; import java.awt.Image; import java.awt.image.BufferedImage; import java.awt.geom.Rectangle2D; -import java.util.Hashtable; +import java.util.HashMap; import java.net.URL; import java.net.MalformedURLException; import java.io.OutputStream; @@ -44,23 +49,40 @@ import java.io.OutputStreamWriter; import javax.swing.ImageIcon; import org.apache.fop.render.AbstractRenderer; +import org.apache.fop.render.XMLHandler; +import org.apache.fop.render.RendererContext; -public class SVGRenderer extends AbstractRenderer { +public class SVGRenderer extends AbstractRenderer implements XMLHandler { + public static final String mimeType = "image/svg+xml"; static final String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI; Document svgDocument; Element svgRoot; Element currentPageG = null; Element lastLink = null; + String lastViewbox = null; + + Element docDefs = null; + Element pageDefs = null; + Element pagesGroup = null; + + // first sequence title + Title docTitle = null; + + RendererContext context; + + OutputStream ostream; float totalWidth = 0; float totalHeight = 0; + float sequenceWidth = 0; + float sequenceHeight = 0; - protected int pageWidth = 0; - protected int pageHeight = 0; + protected float pageWidth = 0; + protected float pageHeight = 0; protected int pageNumber = 0; - protected Hashtable fontNames = new Hashtable(); - protected Hashtable fontStyles = new Hashtable(); + protected HashMap fontNames = new HashMap(); + protected HashMap fontStyles = new HashMap(); protected Color saveColor = null; protected IDReferences idReferences = null; @@ -83,26 +105,267 @@ public class SVGRenderer extends AbstractRenderer { protected float currentBlue = 0; public SVGRenderer() { + context = new RendererContext(mimeType); + } + + public void setUserAgent(FOUserAgent agent) { + super.setUserAgent(agent); + userAgent.setDefaultXMLHandler(mimeType, this); + userAgent.addXMLHandler(mimeType, svgNS, this); } public void setupFontInfo(FontInfo fontInfo) { // create a temp Image to test font metrics on BufferedImage fontImage = - new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); - org.apache.fop.render.awt.FontSetup.setup(fontInfo, fontImage.createGraphics()); + new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); + org.apache.fop.render.awt.FontSetup.setup(fontInfo, + fontImage.createGraphics()); } public void setProducer(String producer) { } public void startRenderer(OutputStream outputStream) - throws IOException {} + throws IOException { + ostream = outputStream; + DOMImplementation impl = + SVGDOMImplementation.getDOMImplementation(); + svgDocument = impl.createDocument(svgNS, "svg", null); + ProcessingInstruction pi = + svgDocument.createProcessingInstruction("xml", " version=\"1.0\" encoding=\"ISO-8859-1\""); + svgRoot = svgDocument.getDocumentElement(); + svgDocument.insertBefore(pi, svgRoot); + + docDefs = svgDocument.createElementNS(svgNS, "defs"); + svgRoot.appendChild(docDefs); + + pagesGroup = svgDocument.createElementNS(svgNS, "g"); + pageDefs = svgDocument.createElementNS(svgNS, "defs"); + pagesGroup.appendChild(pageDefs); + svgRoot.appendChild(pagesGroup); + + } /** * */ - public void stopRenderer() - throws IOException - { + public void stopRenderer() throws IOException { + totalWidth += sequenceWidth; + if (sequenceHeight > totalHeight) { + totalHeight = sequenceHeight; + } + + svgRoot.setAttributeNS(null, "width", "" + (totalWidth + 1)); + svgRoot.setAttributeNS(null, "height", "" + (totalHeight + 1)); + //svgRoot.setAttributeNS(null, "viewBox", "0 0 " + pageWidth + " " + pageHeight); + SVGTranscoder svgT = new SVGTranscoder(); + TranscoderInput input = new TranscoderInput(svgDocument); + TranscoderOutput output = + new TranscoderOutput(new OutputStreamWriter(ostream)); + try { + svgT.transcode(input, output); + } catch (TranscoderException e) { + log.error("could not write svg file :" + e.getMessage(), e); + } + ostream.flush(); + ostream = null; + + svgDocument = null; + svgRoot = null; + currentPageG = null; + lastLink = null; + + totalWidth = 0; + totalHeight = 0; + + pageNumber = 0; + } + + public void startPageSequence(Title seqTitle) { + totalWidth += sequenceWidth; + if (sequenceHeight > totalHeight) { + totalHeight = sequenceHeight; + } + sequenceWidth = 0; + sequenceHeight = 0; + if (seqTitle != null && docTitle == null) { + // convert first title to a string and set for svg document title + docTitle = seqTitle; + String str = convertTitleToString(seqTitle); + Element svgTitle = svgDocument.createElementNS(svgNS, "title"); + Text strNode = svgDocument.createTextNode(str); + svgTitle.appendChild(strNode); + svgRoot.insertBefore(svgTitle, svgRoot.getFirstChild()); + } + } + + public void renderPage(PageViewport page) throws IOException, + FOPException { + float lastWidth = pageWidth; + float lastHeight = pageHeight; + + Rectangle2D area = page.getViewArea(); + pageWidth = (float) area.getWidth() / 1000f; + pageHeight = (float) area.getHeight() / 1000f; + + // if there is a link from the last page + if (lastLink != null) { + lastLink.setAttributeNS(null, "xlink:href", + "#svgView(viewBox(" + totalWidth + ", "+ + sequenceHeight + ", " + pageWidth + ", " + + pageHeight + "))"); + pagesGroup.appendChild(lastLink); + } + + currentPageG = svgDocument.createElementNS(svgNS, "svg"); + currentPageG.setAttributeNS(null, "viewbox", + "0 0 " + (int) pageWidth + " " + (int) pageHeight); + currentPageG.setAttributeNS(null, "width", + "" + ((int) pageWidth + 1)); + currentPageG.setAttributeNS(null, "height", + "" + ((int) pageHeight + 1)); + currentPageG.setAttributeNS(null, "id", "Page-" + pageNumber); + currentPageG.setAttributeNS(null, "style", "font-family:sanserif;font-size:12"); + pageDefs.appendChild(currentPageG); + + if (pageWidth > sequenceWidth) { + sequenceWidth = pageWidth; + } + sequenceHeight += pageHeight; + + Element border = + SVGUtilities.createRect(svgDocument, 0, 0, pageWidth, + pageHeight); + border.setAttributeNS(null, "style", "fill:none;stroke:black"); + currentPageG.appendChild(border); + + // render the page contents + super.renderPage(page); + + Element use = svgDocument.createElementNS(svgNS, "use"); + use.setAttributeNS(null, "xlink:href", "#Page-" + pageNumber); + use.setAttributeNS(null, "x", "" + totalWidth); + use.setAttributeNS(null, "y", "" + (sequenceHeight - pageHeight)); + pagesGroup.appendChild(use); + + Element lastPageLink = svgDocument.createElementNS(svgNS, "a"); + if (lastLink != null) { + lastPageLink.setAttributeNS(null, "xlink:href", lastViewbox); + } else { + lastPageLink.setAttributeNS(null, "xlink:href", + "#svgView(viewBox(" + totalWidth + ", " + + (sequenceHeight - pageHeight) + ", " + pageWidth + + ", " + pageHeight + "))"); + } + pagesGroup.appendChild(lastPageLink); + + // setup a link to the next page, only added when the + // next page is rendered + Element rect = SVGUtilities.createRect(svgDocument, totalWidth, + (sequenceHeight - pageHeight), pageWidth / 2, pageHeight); + rect.setAttributeNS(null, "style", "fill:blue;visibility:hidden"); + lastPageLink.appendChild(rect); + + lastLink = svgDocument.createElementNS(svgNS, "a"); + rect = SVGUtilities.createRect(svgDocument, + totalWidth + pageWidth / 2, + (sequenceHeight - pageHeight), pageWidth / 2, pageHeight); + rect.setAttributeNS(null, "style", "fill:blue;visibility:hidden"); + lastLink.appendChild(rect); + + lastViewbox = "#svgView(viewBox(" + totalWidth + ", " + + (sequenceHeight - pageHeight) + ", " + pageWidth + + ", " + pageHeight + "))"; + + pageNumber++; + + } + + public void renderForeignObject(ForeignObject fo) { + Document doc = fo.getDocument(); + String ns = fo.getNameSpace(); + userAgent.renderXML(context, doc, ns); + } + + public void handleXML(RendererContext context, Document doc, + String ns) throws Exception { + if (svgNS.equals(ns)) { + if (!(doc instanceof SVGDocument)) { + DOMImplementation impl = + SVGDOMImplementation.getDOMImplementation(); + doc = DOMUtilities.deepCloneDocument(doc, impl); + } + SVGSVGElement svg = ((SVGDocument) doc).getRootElement(); + Element view = svgDocument.createElementNS(svgNS, "svg"); + Node newsvg = svgDocument.importNode(svg, true); + //view.setAttributeNS(null, "viewBox", "0 0 "); + view.setAttributeNS(null, "x", + "" + currentBlockIPPosition / 1000f); + view.setAttributeNS(null, "y", "" + currentBPPosition / 1000f); + + // this fixes a problem where the xmlns is repeated sometimes + Element ele = (Element) newsvg; + ele.setAttributeNS(XMLSupport.XMLNS_NAMESPACE_URI, "xmlns", + svgNS); + if (ele.hasAttributeNS(null, "xmlns")) { + ele.removeAttributeNS(null, "xmlns"); + } + + view.appendChild(newsvg); + currentPageG.appendChild(view); + } + } + + public void renderLeader(Leader area) { + String style = "stroke:black;stroke-width:" + + (area.getRuleThickness() / 1000) + ";"; + switch (area.getRuleStyle()) { + case Leader.DOTTED: + style += "stroke-dasharray:1,1"; + break; + case Leader.DASHED: + style += "stroke-dasharray:5,1"; + break; + case Leader.SOLID: + break; + case Leader.DOUBLE: + break; + case Leader.GROOVE: + break; + case Leader.RIDGE: + break; + } + Element line = SVGUtilities.createLine(svgDocument, + currentBlockIPPosition / 1000, + (currentBPPosition + area.getOffset() - + area.getRuleThickness() / 2) / 1000, + (currentBlockIPPosition + area.getWidth()) / 1000, + (currentBPPosition + area.getOffset() - + area.getRuleThickness() / 2) / 1000); + line.setAttributeNS(null, "style", style); + currentPageG.appendChild(line); + + super.renderLeader(area); + } + + public void renderWord(Word word) { + Element text = SVGUtilities.createText(svgDocument, + currentBlockIPPosition / 1000, + (currentBPPosition + word.getOffset()) / 1000, + word.getWord()); + currentPageG.appendChild(text); + + super.renderWord(word); + } + + public void renderCharacter(org.apache.fop.area.inline.Character ch) { + Element text = SVGUtilities.createText(svgDocument, + currentBlockIPPosition / 1000, + (currentBPPosition + ch.getOffset()) / 1000, + "" + ch.getChar()); + currentPageG.appendChild(text); + + super.renderCharacter(ch); } } + diff --git a/src/org/apache/fop/render/xml/XMLRenderer.java b/src/org/apache/fop/render/xml/XMLRenderer.java index 33fa2ed9a..e0a35fd36 100644 --- a/src/org/apache/fop/render/xml/XMLRenderer.java +++ b/src/org/apache/fop/render/xml/XMLRenderer.java @@ -37,6 +37,10 @@ import org.w3c.dom.Document; /** * 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 { public static final String mimeType = "text/xml"; @@ -353,7 +357,7 @@ public class XMLRenderer extends AbstractRenderer { style = "ridge"; break; } - writeElement(""); super.renderLeader(area); } diff --git a/src/org/apache/fop/svg/SVGElementMapping.java b/src/org/apache/fop/svg/SVGElementMapping.java index 012cd9b89..dc7df3dfb 100644 --- a/src/org/apache/fop/svg/SVGElementMapping.java +++ b/src/org/apache/fop/svg/SVGElementMapping.java @@ -17,78 +17,84 @@ import org.apache.fop.fo.ElementMapping; import org.apache.fop.apps.Driver; import org.apache.batik.util.XMLResourceDescriptor; +import org.apache.batik.dom.svg.SVGDOMImplementation; public class SVGElementMapping implements ElementMapping { - private static HashMap foObjs = null; - - public synchronized void addToBuilder(TreeBuilder builder) { + private static HashMap foObjs = null; - if(foObjs == null) { - // this sets the parser that will be used - // by default (SVGBrokenLinkProvider) - // normally the user agent value is used - XMLResourceDescriptor.setXMLParserClassName(Driver.getParserClassName()); - - foObjs = new HashMap(); - foObjs.put("svg", SVGElement.maker()); - foObjs.put("rect", SVGObj.maker("rect")); - foObjs.put("line", SVGObj.maker("line")); - foObjs.put("text", SVGObj.maker("text")); - - foObjs.put("desc", SVGObj.maker("desc")); - foObjs.put("title", SVGObj.maker("title")); - foObjs.put("circle", SVGObj.maker("circle")); - foObjs.put("ellipse", SVGObj.maker("ellipse")); - foObjs.put("g", SVGObj.maker("g")); - foObjs.put("polyline", SVGObj.maker("polyline")); - foObjs.put("polygon", SVGObj.maker("polygon")); - foObjs.put("defs", SVGObj.maker("defs")); - foObjs.put("path", SVGObj.maker("path")); - foObjs.put("use", SVGObj.maker("use")); - foObjs.put("tspan", SVGObj.maker("tspan")); - foObjs.put("tref", SVGObj.maker("tref")); - foObjs.put("image", SVGObj.maker("image")); - foObjs.put("style", SVGObj.maker("style")); - - foObjs.put("textPath", SVGObj.maker("textPath")); - foObjs.put("clipPath", SVGObj.maker("clipPath")); - foObjs.put("mask", SVGObj.maker("mask")); - foObjs.put("linearGradient", SVGObj.maker("linearGradient")); - foObjs.put("radialGradient", SVGObj.maker("radialGradient")); - foObjs.put("stop", SVGObj.maker("stop")); - foObjs.put("a", SVGObj.maker("a")); - foObjs.put("switch", SVGObj.maker("switch")); - foObjs.put("symbol", SVGObj.maker("symbol")); - - foObjs.put("pattern", SVGObj.maker("pattern")); - - foObjs.put("marker", SVGObj.maker("marker")); - foObjs.put("animate", SVGObj.maker("animate")); - foObjs.put("altGlyph", SVGObj.maker("altGlyph")); - foObjs.put("font", SVGObj.maker("font")); - foObjs.put("glyph", SVGObj.maker("glyph")); - foObjs.put("missing-glyph", SVGObj.maker("missing-glyph")); - foObjs.put("hkern", SVGObj.maker("hkern")); - foObjs.put("vkern", SVGObj.maker("vkern")); - foObjs.put("set", SVGObj.maker("set")); - foObjs.put("animateMotion", SVGObj.maker("animateMotion")); - foObjs.put("animateColor", SVGObj.maker("animateColor")); - foObjs.put("animateTransform", SVGObj.maker("animateTransform")); - foObjs.put("cursor", SVGObj.maker("cursor")); - foObjs.put("filter", SVGObj.maker("filter")); - - foObjs.put("feFlood", SVGObj.maker("feFlood")); - foObjs.put("feGaussianBlur", SVGObj.maker("feGaussianBlur")); - foObjs.put("feOffset", SVGObj.maker("feOffset")); - foObjs.put("feMerge", SVGObj.maker("feMerge")); - foObjs.put("feMergeNode", SVGObj.maker("feMergeNode")); + public synchronized void addToBuilder(TreeBuilder builder) { + try { + if (foObjs == null) { + // this sets the parser that will be used + // by default (SVGBrokenLinkProvider) + // normally the user agent value is used + XMLResourceDescriptor.setXMLParserClassName( + Driver.getParserClassName()); + + foObjs = new HashMap(); + foObjs.put("svg", SVGElement.maker()); + foObjs.put("rect", SVGObj.maker("rect")); + foObjs.put("line", SVGObj.maker("line")); + foObjs.put("text", SVGObj.maker("text")); + + foObjs.put("desc", SVGObj.maker("desc")); + foObjs.put("title", SVGObj.maker("title")); + foObjs.put("circle", SVGObj.maker("circle")); + foObjs.put("ellipse", SVGObj.maker("ellipse")); + foObjs.put("g", SVGObj.maker("g")); + foObjs.put("polyline", SVGObj.maker("polyline")); + foObjs.put("polygon", SVGObj.maker("polygon")); + foObjs.put("defs", SVGObj.maker("defs")); + foObjs.put("path", SVGObj.maker("path")); + foObjs.put("use", SVGObj.maker("use")); + foObjs.put("tspan", SVGObj.maker("tspan")); + foObjs.put("tref", SVGObj.maker("tref")); + foObjs.put("image", SVGObj.maker("image")); + foObjs.put("style", SVGObj.maker("style")); + + foObjs.put("textPath", SVGObj.maker("textPath")); + foObjs.put("clipPath", SVGObj.maker("clipPath")); + foObjs.put("mask", SVGObj.maker("mask")); + foObjs.put("linearGradient", SVGObj.maker("linearGradient")); + foObjs.put("radialGradient", SVGObj.maker("radialGradient")); + foObjs.put("stop", SVGObj.maker("stop")); + foObjs.put("a", SVGObj.maker("a")); + foObjs.put("switch", SVGObj.maker("switch")); + foObjs.put("symbol", SVGObj.maker("symbol")); + + foObjs.put("pattern", SVGObj.maker("pattern")); + + foObjs.put("marker", SVGObj.maker("marker")); + foObjs.put("animate", SVGObj.maker("animate")); + foObjs.put("altGlyph", SVGObj.maker("altGlyph")); + foObjs.put("font", SVGObj.maker("font")); + foObjs.put("glyph", SVGObj.maker("glyph")); + foObjs.put("missing-glyph", SVGObj.maker("missing-glyph")); + foObjs.put("hkern", SVGObj.maker("hkern")); + foObjs.put("vkern", SVGObj.maker("vkern")); + foObjs.put("set", SVGObj.maker("set")); + foObjs.put("animateMotion", SVGObj.maker("animateMotion")); + foObjs.put("animateColor", SVGObj.maker("animateColor")); + foObjs.put("animateTransform", SVGObj.maker("animateTransform")); + foObjs.put("cursor", SVGObj.maker("cursor")); + foObjs.put("filter", SVGObj.maker("filter")); + + foObjs.put("feFlood", SVGObj.maker("feFlood")); + foObjs.put("feGaussianBlur", SVGObj.maker("feGaussianBlur")); + foObjs.put("feOffset", SVGObj.maker("feOffset")); + foObjs.put("feMerge", SVGObj.maker("feMerge")); + foObjs.put("feMergeNode", SVGObj.maker("feMergeNode")); + } + + String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI; + builder.addMapping(svgNS, foObjs); + + builder.addPropertyListBuilder(svgNS, + new DirectPropertyListBuilder()); + } catch (Throwable t) { + // if the classes are not available } - - String uri = "http://www.w3.org/2000/svg"; - builder.addMapping(uri, foObjs); - - builder.addPropertyListBuilder(uri, new DirectPropertyListBuilder()); } } diff --git a/src/org/apache/fop/tools/AreaTreeBuilder.java b/src/org/apache/fop/tools/AreaTreeBuilder.java index e77713da8..63fac5c91 100644 --- a/src/org/apache/fop/tools/AreaTreeBuilder.java +++ b/src/org/apache/fop/tools/AreaTreeBuilder.java @@ -17,6 +17,7 @@ import org.apache.fop.render.pdf.*; import org.apache.fop.render.svg.*; import org.apache.fop.render.xml.*; import org.apache.fop.layout.FontInfo; +import org.apache.fop.layout.FontState; import org.apache.fop.fo.FOUserAgent; import org.apache.log.*; @@ -30,6 +31,8 @@ import java.util.*; import java.awt.geom.Rectangle2D; import java.util.StringTokenizer; +import javax.xml.parsers.DocumentBuilderFactory; + import org.w3c.dom.*; import org.apache.batik.dom.svg.SVGDOMImplementation; @@ -41,12 +44,14 @@ import org.apache.batik.dom.util.DOMUtilities; * for the purpose of testing the area tree and rendering. * This covers the set of possible properties that can be set * on the area tree for rendering. + * As this is not for general purpose there is no attempt to handle + * invalid area tree xml. + * * Tests: different renderers, saving and loading pages with serialization * out of order rendering */ public class AreaTreeBuilder { private Logger log; - //String baseName = "temp"; /** */ @@ -129,6 +134,7 @@ public class AreaTreeBuilder { while (c < pagec) { PageViewport page = sm.getPage(count, c); c++; + // save the page to a stream for testing ObjectOutputStream tempstream = new ObjectOutputStream( new BufferedOutputStream( new FileOutputStream("temp.ser"))); @@ -142,6 +148,7 @@ public class AreaTreeBuilder { new FileInputStream("temp.ser"))); page.loadPage(in); in.close(); + rend.renderPage(page); } count++; @@ -163,6 +170,7 @@ class TreeLoader { AreaTree areaTree; AreaTree.AreaTreeModel model; FontInfo fontInfo; + FontState currentFontState; TreeLoader(FontInfo fi) { fontInfo = fi; @@ -175,8 +183,10 @@ class TreeLoader { public void buildAreaTree(InputStream is) { Document doc = null; try { - doc = javax.xml.parsers.DocumentBuilderFactory.newInstance(). - newDocumentBuilder().parse(is); + DocumentBuilderFactory fact = + DocumentBuilderFactory.newInstance(); + fact.setNamespaceAware(true); + doc = fact.newDocumentBuilder().parse(is); } catch (Exception e) { e.printStackTrace(); } @@ -475,6 +485,16 @@ class TreeLoader { Character ch = new Character(getString((Element) obj).charAt(0)); addProperties((Element) obj, ch); + try { + currentFontState = + new FontState(fontInfo, "sans-serif", "normal", + "normal", 12000, 0); + } catch (FOPException e) { + + } + + ch.setWidth(currentFontState.width(ch.getChar())); + ch.setOffset(currentFontState.getCapHeight()); list.add(ch); } else if (obj.getNodeName().equals("space")) { Space space = new Space(); @@ -520,6 +540,11 @@ class TreeLoader { return null; } Viewport viewport = new Viewport(child); + String str = root.getAttribute("width"); + if (str != null && !"".equals(str)) { + int width = Integer.parseInt(str); + viewport.setWidth(width); + } return viewport; } @@ -544,15 +569,19 @@ class TreeLoader { //System.out.println(obj.getNodeName()); Element rootEle = (Element) obj; String space = rootEle.getAttribute("xmlns"); - if (space.equals(svgNS)) { + if (svgNS.equals(space)) { try { - doc = javax.xml.parsers.DocumentBuilderFactory.newInstance(). - newDocumentBuilder().newDocument(); + DocumentBuilderFactory fact = + DocumentBuilderFactory.newInstance(); + fact.setNamespaceAware(true); + + doc = fact. newDocumentBuilder().newDocument(); Node node = doc.importNode(obj, true); doc.appendChild(node); DOMImplementation impl = SVGDOMImplementation.getDOMImplementation(); // due to namespace problem attributes are not cloned + // serializing causes an npe //doc = DOMUtilities.deepCloneDocument(doc, impl); ForeignObject fo = new ForeignObject(doc, svgNS); @@ -562,8 +591,10 @@ class TreeLoader { } } else { try { - doc = javax.xml.parsers.DocumentBuilderFactory.newInstance(). - newDocumentBuilder().newDocument(); + DocumentBuilderFactory fact = + DocumentBuilderFactory.newInstance(); + fact.setNamespaceAware(true); + doc = fact. newDocumentBuilder().newDocument(); Node node = doc.importNode(obj, true); doc.appendChild(node); ForeignObject fo = new ForeignObject(doc, space); @@ -602,14 +633,28 @@ class TreeLoader { String rt = root.getAttribute("ruleThickness"); int thick = Integer.parseInt(rt); leader.setRuleThickness(thick); + rt = root.getAttribute("width"); + if (rt != null && !"".equals(rt)) { + thick = Integer.parseInt(rt); + leader.setWidth(thick); + } + leader.setOffset(currentFontState.getCapHeight()); addProperties(root, leader); return leader; } Word getWord(Element root) { - String url = root.getAttribute("url"); + String str = getString(root); Word word = new Word(); + word.setWord(str); addProperties(root, word); + int width = 0; + for (int count = 0; count < str.length(); count++) { + width += currentFontState.width(str.charAt(count)); + } + word.setWidth(width); + word.setOffset(currentFontState.getCapHeight()); + return word; } -- 2.39.5