/* * Copyright 1999-2004 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$ */ package org.apache.fop.tools; // Java import java.awt.geom.Rectangle2D; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import java.util.StringTokenizer; // JAXP import javax.xml.parsers.DocumentBuilderFactory; // DOM import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Element; /* org.w3c.dom.Document is not imported to avoid conflict with org.apache.fop.control.Document */ // Batik import org.apache.batik.dom.svg.SVGDOMImplementation; // FOP import org.apache.fop.area.Area; import org.apache.fop.area.AreaTree; import org.apache.fop.area.AreaTreeModel; import org.apache.fop.area.BeforeFloat; import org.apache.fop.area.Block; import org.apache.fop.area.BodyRegion; import org.apache.fop.area.CTM; import org.apache.fop.area.Flow; import org.apache.fop.area.Footnote; import org.apache.fop.area.LineArea; import org.apache.fop.area.MainReference; import org.apache.fop.area.Page; 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.StorePagesModel; import org.apache.fop.area.Title; import org.apache.fop.area.Trait; import org.apache.fop.area.inline.Character; 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.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.apps.Document; import org.apache.fop.fonts.Font; import org.apache.fop.render.Renderer; import org.apache.fop.render.pdf.PDFRenderer; import org.apache.fop.render.svg.SVGRenderer; import org.apache.fop.render.xml.XMLRenderer; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.fo.Constants; import org.apache.fop.fo.pagination.Region; import org.apache.fop.fonts.FontMetrics; // Avalon import org.apache.avalon.framework.logger.ConsoleLogger; import org.apache.avalon.framework.logger.AbstractLogEnabled; /** * Area tree tester. * The purpose of this class is to create and render an area tree * 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 extends AbstractLogEnabled { /** * Main method * @param args command line arguments */ public static void main(String[] args) { AreaTreeBuilder atb = new AreaTreeBuilder(); atb.enableLogging(new ConsoleLogger(ConsoleLogger.LEVEL_DEBUG)); atb.runTests(args[0], args[1], args[2]); System.exit(0); } /** * Run the tests. * @param in input filename * @param type output format * @param out output filename */ protected void runTests(String in, String type, String out) { getLogger().debug("Starting tests"); runTest(in, type, out); getLogger().debug("Finished"); } /** * Run a test. * @param in input filename * @param type output format * @param out output filename */ protected void runTest(String in, String type, String out) { Renderer rend = null; if ("xml".equals(type)) { rend = new XMLRenderer(); } else if ("pdf".equals(type)) { rend = new PDFRenderer(); } else if ("svg".equals(type)) { rend = new SVGRenderer(); } setupLogger(rend); Document fi = new Document(null); rend.setupFontInfo(fi); FOUserAgent ua = new FOUserAgent(); setupLogger(ua); rend.setUserAgent(ua); StorePagesModel sm = AreaTree.createStorePagesModel(); TreeLoader tl = new TreeLoader(fi); setupLogger(tl); tl.setTreeModel(sm); try { InputStream is = new java.io.BufferedInputStream(new java.io.FileInputStream(in)); tl.buildAreaTree(is); renderAreaTree(sm, rend, out); } catch (IOException e) { getLogger().error("error reading file" + e.getMessage(), e); } } /** * Renders an area tree to a target format using a renderer. * @param sm area tree pages * @param rend renderer to use for output * @param out target filename */ protected void renderAreaTree(StorePagesModel sm, Renderer rend, String out) { try { OutputStream os = new java.io.BufferedOutputStream(new java.io.FileOutputStream(out)); rend.startRenderer(os); int count = 0; int seqc = sm.getPageSequenceCount(); while (count < seqc) { Title title = sm.getTitle(count); rend.startPageSequence(title); int pagec = sm.getPageCount(count); int c = 0; 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"))); page.savePage(tempstream); tempstream.close(); File temp = new File("temp.ser"); getLogger().debug("page serialized to: " + temp.length()); temp = null; ObjectInputStream in = new ObjectInputStream( new BufferedInputStream( new FileInputStream("temp.ser"))); page.loadPage(in); in.close();*/ rend.renderPage(page); } count++; } rend.stopRenderer(); os.close(); } catch (Exception e) { getLogger().error("error rendering output", e); } } } // this loads an area tree from an xml file // the xml format is the same as the xml renderer output class TreeLoader extends AbstractLogEnabled { private AreaTree areaTree; private AreaTreeModel model; private Document fontInfo; private Font currentFontState; TreeLoader(Document fi) { fontInfo = fi; } public void setTreeModel(AreaTreeModel mo) { model = mo; } public void buildAreaTree(InputStream is) { org.w3c.dom.Document doc = null; try { DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance(); fact.setNamespaceAware(true); doc = fact.newDocumentBuilder().parse(is); } catch (Exception e) { e.printStackTrace(); } Element root = null; root = doc.getDocumentElement(); areaTree = new AreaTree(fontInfo); areaTree.setTreeModel(model); readAreaTree(root); } public void readAreaTree(Element root) { NodeList childs = root.getChildNodes(); for (int i = 0; i < childs.getLength(); i++) { Node obj = childs.item(i); if (obj.getNodeName().equals("pageSequence")) { readPageSequence((Element) obj); } } } public void readPageSequence(Element root) { Title title = null; boolean started = false; NodeList childs = root.getChildNodes(); for (int i = 0; i < childs.getLength(); i++) { Node obj = childs.item(i); if (obj.getNodeName().equals("title")) { if (started) { // problem } else { title = readTitle((Element) obj); model.startPageSequence(title); started = true; } } else if (obj.getNodeName().equals("pageViewport")) { if (!started) { model.startPageSequence(null); started = true; } PageViewport viewport = readPageViewport((Element) obj); areaTree.addPage(viewport); } } } public Title readTitle(Element root) { Title title = new Title(); List childs = getInlineAreas(root); for (int i = 0; i < childs.size(); i++) { InlineArea obj = (InlineArea) childs.get(i); title.addInlineArea(obj); } return title; } public PageViewport readPageViewport(Element root) { Rectangle2D bounds = getRectangle(root, "bounds"); PageViewport viewport = null; NodeList childs = root.getChildNodes(); for (int i = 0; i < childs.getLength(); i++) { Node obj = childs.item(i); if (obj.getNodeName().equals("page")) { Page page = readPage((Element) obj); viewport = new PageViewport(page, bounds); } } return viewport; } public Page readPage(Element root) { //String bounds = root.getAttribute("bounds"); Page page = new Page(); NodeList childs = root.getChildNodes(); for (int i = 0; i < childs.getLength(); i++) { Node obj = childs.item(i); if (obj.getNodeName().equals("regionViewport")) { readRegionViewport(page, (Element) obj); } } return page; } Rectangle2D getRectangle(Element root, String attr) { String rect = root.getAttribute(attr); StringTokenizer st = new StringTokenizer(rect, " "); int x = 0, y = 0, w = 0, h = 0; if (st.hasMoreTokens()) { String tok = st.nextToken(); x = Integer.parseInt(tok); } if (st.hasMoreTokens()) { String tok = st.nextToken(); y = Integer.parseInt(tok); } if (st.hasMoreTokens()) { String tok = st.nextToken(); w = Integer.parseInt(tok); } if (st.hasMoreTokens()) { String tok = st.nextToken(); h = Integer.parseInt(tok); } Rectangle2D r2d = new Rectangle2D.Float(x, y, w, h); return r2d; } public RegionViewport readRegionViewport(Page page, Element root) { RegionViewport reg = new RegionViewport(getRectangle(root, "rect")); NodeList childs = root.getChildNodes(); for (int i = 0; i < childs.getLength(); i++) { Node obj = childs.item(i); if (obj.getNodeName().equals("regionBefore")) { reg.setRegion(readRegion((Element) obj, Region.BEFORE_CODE)); page.setRegionViewport(Region.BEFORE_CODE, reg); } else if (obj.getNodeName().equals("regionStart")) { reg.setRegion(readRegion((Element) obj, Region.START_CODE)); page.setRegionViewport(Region.START_CODE, reg); } else if (obj.getNodeName().equals("regionBody")) { reg.setRegion(readRegion((Element) obj, Region.BODY_CODE)); page.setRegionViewport(Region.BODY_CODE, reg); } else if (obj.getNodeName().equals("regionEnd")) { reg.setRegion(readRegion((Element) obj, Region.END_CODE)); page.setRegionViewport(Region.END_CODE, reg); } else if (obj.getNodeName().equals("regionAfter")) { reg.setRegion(readRegion((Element) obj, Region.AFTER_CODE)); page.setRegionViewport(Region.AFTER_CODE, reg); } } return reg; } public RegionReference readRegion(Element root, int type) { RegionReference reg; if (type == Region.BODY_CODE) { BodyRegion br = new BodyRegion(); NodeList childs = root.getChildNodes(); for (int i = 0; i < childs.getLength(); i++) { Node obj = childs.item(i); if (obj.getNodeName().equals("beforeFloat")) { BeforeFloat bf = readBeforeFloat((Element) obj); br.setBeforeFloat(bf); } else if (obj.getNodeName().equals("mainReference")) { MainReference mr = readMainReference((Element) obj); br.setMainReference(mr); } else if (obj.getNodeName().equals("footnote")) { Footnote foot = readFootnote((Element) obj); br.setFootnote(foot); } } reg = br; } else { reg = new RegionReference(type); List blocks = getBlocks(root); for (int i = 0; i < blocks.size(); i++) { Block obj = (Block) blocks.get(i); reg.addBlock(obj); } } reg.setCTM(new CTM()); return reg; } public BeforeFloat readBeforeFloat(Element root) { BeforeFloat bf = new BeforeFloat(); List blocks = getBlocks(root); for (int i = 0; i < blocks.size(); i++) { Block obj = (Block) blocks.get(i); bf.addBlock(obj); } return bf; } public MainReference readMainReference(Element root) { MainReference mr = new MainReference(); List spans = getSpans(root); for (int i = 0; i < spans.size(); i++) { Span obj = (Span) spans.get(i); mr.addSpan(obj); } return mr; } List getSpans(Element root) { List list = new java.util.ArrayList(); NodeList childs = root.getChildNodes(); for (int i = 0; i < childs.getLength(); i++) { Node obj = childs.item(i); if (obj.getNodeName().equals("span")) { List flows = getFlows((Element) obj); Span span = new Span(flows.size()); for (int j = 0; j < flows.size(); j++) { Flow flow = (Flow) flows.get(j); span.addFlow(flow); } list.add(span); } } return list; } List getFlows(Element root) { List list = new java.util.ArrayList(); NodeList childs = root.getChildNodes(); for (int i = 0; i < childs.getLength(); i++) { Node obj = childs.item(i); if (obj.getNodeName().equals("flow")) { Flow flow = new Flow(); List blocks = getBlocks((Element) obj); for (int j = 0; j < blocks.size(); j++) { Block block = (Block) blocks.get(j); flow.addBlock(block); } list.add(flow); } } return list; } public Footnote readFootnote(Element root) { Footnote foot = new Footnote(); List blocks = getBlocks(root); for (int i = 0; i < blocks.size(); i++) { Block obj = (Block) blocks.get(i); foot.addBlock(obj); } return foot; } List getBlocks(Element root) { List list = new java.util.ArrayList(); NodeList childs = root.getChildNodes(); for (int i = 0; i < childs.getLength(); i++) { Node obj = childs.item(i); if (obj.getNodeName().equals("block")) { Block block = new Block(); addTraits((Element)obj, block); addBlockChildren(block, (Element) obj); list.add(block); } } return list; } protected void addBlockChildren(Block block, Element root) { NodeList childs = root.getChildNodes(); int type = -1; for (int i = 0; i < childs.getLength(); i++) { Node obj = childs.item(i); if (obj.getNodeName().equals("block")) { if (type == 2) { // error } Block b = new Block(); addBlockChildren(b, (Element) obj); block.addBlock(b); type = 1; } else if (obj.getNodeName().equals("lineArea")) { if (type == 1) { // error } LineArea line = new LineArea(); addTraits((Element) obj, line); String height = ((Element) obj).getAttribute("height"); int h = Integer.parseInt(height); line.setHeight(h); List inlines = getInlineAreas((Element) obj); for (int j = 0; j < inlines.size(); j++) { InlineArea inline = (InlineArea) inlines.get(j); line.addInlineArea(inline); } block.addLineArea(line); type = 2; } } } // children of element are inline areas List getInlineAreas(Element root) { List list = new java.util.ArrayList(); NodeList childs = root.getChildNodes(); for (int i = 0; i < childs.getLength(); i++) { Node obj = childs.item(i); if (obj.getNodeName().equals("char")) { Character ch = new Character(getString((Element) obj).charAt(0)); addTraits((Element) obj, ch); String fname = fontInfo.fontLookup("sans-serif", "normal", Font.NORMAL); FontMetrics metrics = fontInfo.getMetricsFor(fname); currentFontState = new Font(fname, metrics, 12000); ch.setWidth(currentFontState.getWidth(ch.getChar())); ch.setOffset(currentFontState.getCapHeight()); list.add(ch); } else if (obj.getNodeName().equals("space")) { Space space = new Space(); String width = ((Element) obj).getAttribute("width"); int w = Integer.parseInt(width); space.setWidth(w); list.add(space); } else if (obj.getNodeName().equals("viewport")) { Viewport viewport = getViewport((Element) obj); if (viewport != null) { list.add(viewport); } } else if (obj.getNodeName().equals("leader")) { Leader leader = getLeader((Element) obj); if (leader != null) { list.add(leader); } } else if (obj.getNodeName().equals("word")) { String fname = fontInfo.fontLookup("sans-serif", "normal", Font.NORMAL); FontMetrics metrics = fontInfo.getMetricsFor(fname); currentFontState = new Font(fname, metrics, 12000); TextArea text = getText((Element) obj); text.addTrait(Trait.FONT_NAME, fname); text.addTrait(Trait.FONT_SIZE, new Integer(12000)); if (text != null) { list.add(text); } } else { } } return list; } Viewport getViewport(Element root) { Area child = null; NodeList childs = root.getChildNodes(); for (int i = 0; i < childs.getLength(); i++) { Node obj = childs.item(i); if (obj.getNodeName().equals("container")) { child = getContainer((Element) obj); } else if (obj.getNodeName().equals("foreignObject")) { child = getForeignObject((Element) obj); } else if (obj.getNodeName().equals("image")) { child = getImage((Element) obj); } } if (child == null) { 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; } Container getContainer(Element root) { Container cont = new Container(); List blocks = getBlocks(root); for (int i = 0; i < blocks.size(); i++) { Block obj = (Block) blocks.get(i); cont.addBlock(obj); } return cont; } ForeignObject getForeignObject(Element root) { org.w3c.dom.Document doc; String svgNS = SVGDOMImplementation.SVG_NAMESPACE_URI; NodeList childs = root.getChildNodes(); for (int i = 0; i < childs.getLength(); i++) { Node obj = childs.item(i); if (obj instanceof Element) { //getLogger().debug(obj.getNodeName()); Element rootEle = (Element) obj; String space = rootEle.getAttribute("xmlns"); if (svgNS.equals(space)) { try { 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); return fo; } catch (Exception e) { e.printStackTrace(); } } else { try { 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); return fo; } catch (Exception e) { e.printStackTrace(); } } } } return null; } Image getImage(Element root) { String url = root.getAttribute("url"); Image image = new Image(url); return image; } Leader getLeader(Element root) { Leader leader = new Leader(); String rs = root.getAttribute("ruleStyle"); if ("solid".equals(rs)) { leader.setRuleStyle(Constants.RuleStyle.SOLID); } else if ("dotted".equals(rs)) { leader.setRuleStyle(Constants.RuleStyle.DOTTED); } else if ("dashed".equals(rs)) { leader.setRuleStyle(Constants.RuleStyle.DASHED); } else if ("double".equals(rs)) { leader.setRuleStyle(Constants.RuleStyle.DOUBLE); } else if ("groove".equals(rs)) { leader.setRuleStyle(Constants.RuleStyle.GROOVE); } else if ("ridge".equals(rs)) { leader.setRuleStyle(Constants.RuleStyle.RIDGE); } 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()); addTraits(root, leader); return leader; } TextArea getText(Element root) { String str = getString(root); TextArea text = new TextArea(); text.setTextArea(str); addTraits(root, text); int width = 0; for (int count = 0; count < str.length(); count++) { width += currentFontState.getWidth(str.charAt(count)); } text.setWidth(width); text.setOffset(currentFontState.getCapHeight()); return text; } public void addTraits(Element ele, Area area) { String str = ele.getAttribute("props"); StringTokenizer st = new StringTokenizer(str, ";"); while (st.hasMoreTokens()) { String tok = st.nextToken(); int index = tok.indexOf(":"); String id = tok.substring(0, index); Object traitCode = Trait.getTraitCode(id); if (traitCode != null) { area.addTrait(traitCode, Trait.makeTraitValue(traitCode, tok.substring(index + 1))); } else { getLogger().error("Unknown trait: " + id); } } } public List getRanges(Element ele) { List list = new java.util.ArrayList(); String str = ele.getAttribute("ranges"); StringTokenizer st = new StringTokenizer(str, ";"); while (st.hasMoreTokens()) { /*String tok =*/ st.nextToken(); } return list; } public String getString(Element ele) { String str = ""; NodeList childs = ele.getChildNodes(); if (childs.getLength() == 0) { return null; } for (int i = 0; i < childs.getLength(); i++) { Node obj = childs.item(i); str = str + obj.getNodeValue(); } return str; } }