From c7d04323247a0d75f773c6fbe00cc1d68844a2e5 Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Tue, 17 Jan 2006 11:07:10 +0000 Subject: [PATCH] New feature: "Intermediate format" (IF). The IF is basically the XML dialect written by the area tree renderer (XMLRenderer). A new parser for this format allows reparsing a serialized and possibly modified area tree and rendering it to the final target format. More details on the Wiki at http://wiki.apache.org/xmlgraphics-fop/AreaTreeIntermediateXml. No advanced features have been implemented, yet, only the basic functionality. The whole change should be fully backwards-compatible WRT the outer FOP API except maybe for FOTreeBuilder.addElementMapping(), and the area tree XML which got small changes. The area tree has been cleaned up. The serializability has been restored. The CachedRenderPagesModel works again and can, in certain situations, decrease the maximum amount of memory held at one point in time. Some adjustments were necessary in the area tree to help the work of the AreaTreeParser. The AreaTreeParser is new and is responsible for parsing area tree XML files and adding pages to a RenderPagesModel instance. It is SAX-based and should be pretty efficient. XMLUnit (http://xmlunit.sourceforge.net, BSD license) is a new dependency for the test code. It is used to verify the correctness of the intermediate format code. It doesn't have to be installed for the build to run through, though. ElementMapping got a new method getDOMImplementation() which provides the DOMImplementation used to handle a subdocument of a particular namespace. For example, SVG uses Batik's SVG DOM. The AreaTreeParser needs that to properly recreate foreign objects because it can't use the mechanism of the FO tree. The default implementation returns null. The ElementMapping instances are no longer maintained by the FOTreeBuilder, but by the newly created ElementMappingRegistry class. It is expected that the instance of this class is moved from the FOTreeBuilder and the AreaTreeParser's Handler class to the "environment class" once it is created to cut down on the startup time for each processed document. The XMLRenderer has been slightly modified to improve the serialization/deserialization qualities of the area tree XML format. The XMLRenderer can now mimic another renderer (see mimicRenderer(Renderer)) in order to use its font setup. That way it is made certain that the reparsed area tree will render to the final target format exactly as expected. Fixed a bug in the XMLHandlerRegistry which did not always return the right XMLHandler for every situation. Added a DefaultErrorListener to the util package. I've had problems with Xalan-J swallowing exceptions with its default ErrorListener, so I added a simple one for convenience and use in AreaTreeParser. Example code for working with the AreaTreeParser can be found in examples/embedding. Documentation will follow. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@369753 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 54 +- .../embedding/intermediate/ExampleConcat.java | 214 ++++++ .../fop/mathml/MathMLElementMapping.java | 10 +- .../apache/fop/plan/PlanElementMapping.java | 12 +- .../org/apache/fop/area/AreaTreeParser.java | 676 ++++++++++++++++++ src/java/org/apache/fop/area/BeforeFloat.java | 3 +- src/java/org/apache/fop/area/Block.java | 12 +- src/java/org/apache/fop/area/BlockParent.java | 22 +- src/java/org/apache/fop/area/Footnote.java | 27 +- src/java/org/apache/fop/area/LineArea.java | 11 +- src/java/org/apache/fop/area/Page.java | 4 +- .../org/apache/fop/area/PageViewport.java | 18 + .../org/apache/fop/area/RegionReference.java | 9 +- .../org/apache/fop/area/RenderPagesModel.java | 9 +- src/java/org/apache/fop/area/Trait.java | 121 +++- .../apache/fop/area/inline/ForeignObject.java | 20 +- .../apache/fop/area/inline/InlineArea.java | 2 +- .../apache/fop/area/inline/InlineParent.java | 17 +- .../org/apache/fop/area/inline/Leader.java | 39 +- .../org/apache/fop/area/inline/Viewport.java | 12 +- .../org/apache/fop/fo/ElementMapping.java | 43 +- .../apache/fop/fo/ElementMappingRegistry.java | 175 +++++ src/java/org/apache/fop/fo/FOTreeBuilder.java | 117 +-- .../svg/BatikExtensionElementMapping.java | 15 +- .../fo/extensions/svg/SVGElementMapping.java | 24 +- src/java/org/apache/fop/fonts/LazyFont.java | 1 + .../apache/fop/render/XMLHandlerRegistry.java | 12 +- .../apache/fop/render/xml/XMLRenderer.java | 139 ++-- .../org/apache/fop/traits/BorderProps.java | 93 ++- .../apache/fop/util/DefaultErrorListener.java | 64 ++ status.xml | 6 + .../org/apache/fop/UtilityCodeTestSuite.java | 6 +- .../intermediate/AreaTreeParserTestCase.java | 241 +++++++ .../IntermediateFormatTestSuite.java | 66 ++ .../layoutengine/LayoutEngineTestSuite.java | 34 +- .../fop/traits/BorderPropsTestCase.java | 53 ++ .../apache/fop/traits/TraitColorTestCase.java | 75 ++ .../external-graphic_border_padding.xml | 4 +- .../external-graphic_display-align.xml | 10 +- .../external-graphic_svg.xml | 8 +- ...instream-foreign-object_border_padding.xml | 4 +- .../instream-foreign-object_display-align.xml | 10 +- 42 files changed, 2200 insertions(+), 292 deletions(-) create mode 100644 examples/embedding/java/embedding/intermediate/ExampleConcat.java create mode 100644 src/java/org/apache/fop/area/AreaTreeParser.java create mode 100644 src/java/org/apache/fop/fo/ElementMappingRegistry.java create mode 100644 src/java/org/apache/fop/util/DefaultErrorListener.java create mode 100644 test/java/org/apache/fop/intermediate/AreaTreeParserTestCase.java create mode 100644 test/java/org/apache/fop/intermediate/IntermediateFormatTestSuite.java create mode 100644 test/java/org/apache/fop/traits/BorderPropsTestCase.java create mode 100644 test/java/org/apache/fop/traits/TraitColorTestCase.java diff --git a/build.xml b/build.xml index d15aef2e6..b82fd72cb 100644 --- a/build.xml +++ b/build.xml @@ -1,6 +1,6 @@ - + + + + + + + + + + @@ -802,7 +827,30 @@ list of possible build targets. - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/embedding/java/embedding/intermediate/ExampleConcat.java b/examples/embedding/java/embedding/intermediate/ExampleConcat.java new file mode 100644 index 000000000..845d3bfbf --- /dev/null +++ b/examples/embedding/java/embedding/intermediate/ExampleConcat.java @@ -0,0 +1,214 @@ +/* + * Copyright 2006 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: ExampleDOM2PDF.java 332791 2005-11-12 15:58:07Z jeremias $ */ + +package embedding.intermediate; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; + +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.stream.StreamSource; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.Fop; +import org.apache.fop.apps.MimeConstants; +import org.apache.fop.area.AreaTreeModel; +import org.apache.fop.area.AreaTreeParser; +import org.apache.fop.area.RenderPagesModel; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.render.Renderer; +import org.apache.fop.render.xml.XMLRenderer; +import org.xml.sax.SAXException; + +import embedding.ExampleObj2XML; +import embedding.model.ProjectMember; +import embedding.model.ProjectTeam; + +/** + * Example for the intermediate format that demonstrates the concatenation of two documents + * renderered to the intermediate format. A single PDF file is generated from the two intermediate + * files. + */ +public class ExampleConcat { + + /** + * Creates a sample ProjectTeam instance for this demo. + * @return ProjectTeam the newly created ProjectTeam instance + */ + public static ProjectTeam createAnotherProjectTeam() { + ProjectTeam team = new ProjectTeam(); + team.setProjectName("The Dynamic Duo"); + team.addMember(new ProjectMember( + "Batman", "lead", "batman@heroes.org")); + team.addMember(new ProjectMember( + "Robin", "aid", "robin@heroes.org")); + return team; + } + + /** + * Converts an XSL-FO document to an intermediate file. + * @param src the source file + * @param xslt the stylesheet file + * @param intermediate the target intermediate file (area tree XML) + * @throws IOException In case of an I/O problem + * @throws FOPException In case of a FOP problem + * @throws TransformerException In case of a XSL transformation problem + */ + public void convertToIntermediate(Source src, Source xslt, File intermediate) + throws IOException, FOPException, TransformerException { + + //Create a user agent + FOUserAgent userAgent = new FOUserAgent(); + + //Create an instance of the target renderer so the XMLRenderer can use its font setup + Renderer targetRenderer = userAgent.getRendererFactory().createRenderer( + userAgent, MimeConstants.MIME_PDF); + + //Create the XMLRenderer to create the intermediate format (area tree XML) + XMLRenderer xmlRenderer = new XMLRenderer(); + xmlRenderer.setUserAgent(userAgent); + + //Tell the XMLRenderer to mimic the target renderer + xmlRenderer.mimicRenderer(targetRenderer); + + //Make sure the prepared XMLRenderer is used + userAgent.setRendererOverride(xmlRenderer); + + // Construct fop (the MIME type here is unimportant due to the override on the user agent) + Fop fop = new Fop(MimeConstants.MIME_FOP_AREA_TREE, userAgent); + + // Setup output + OutputStream out = new java.io.FileOutputStream(intermediate); + out = new java.io.BufferedOutputStream(out); + try { + fop.setOutputStream(out); + + // Setup XSLT + TransformerFactory factory = TransformerFactory.newInstance(); + Transformer transformer; + if (xslt != null) { + transformer = factory.newTransformer(xslt); + } else { + transformer = factory.newTransformer(); + } + + // Resulting SAX events (the generated FO) must be piped through to FOP + Result res = new SAXResult(fop.getDefaultHandler()); + + // Start XSLT transformation and FOP processing + transformer.transform(src, res); + } finally { + out.close(); + } + } + + /** + * Concatenates an array of intermediate files to a single PDF file. + * @param files the array of intermediate files (area tree XML) + * @param pdffile the target PDF file + * @throws IOException In case of an I/O problem + * @throws TransformerException In case of a XSL transformation problem + * @throws SAXException In case of an XML-related problem + */ + public void concatToPDF(File[] files, File pdffile) + throws IOException, TransformerException, SAXException { + // Setup output + OutputStream out = new java.io.FileOutputStream(pdffile); + out = new java.io.BufferedOutputStream(out); + try { + //Setup fonts and user agent + FontInfo fontInfo = new FontInfo(); + FOUserAgent userAgent = new FOUserAgent(); + + //Construct the AreaTreeModel that will received the individual pages + AreaTreeModel treeModel = new RenderPagesModel(userAgent, + MimeConstants.MIME_PDF, fontInfo, out); + + //Iterate over all intermediate files + AreaTreeParser parser = new AreaTreeParser(); + for (int i = 0; i < files.length; i++) { + Source src = new StreamSource(files[i]); + parser.parse(src, treeModel, userAgent); + } + + //Signal the end of the processing. The renderer can finalize the target document. + treeModel.endDocument(); + } finally { + out.close(); + } + } + + /** + * Main method. + * @param args command-line arguments + */ + public static void main(String[] args) { + try { + System.out.println("FOP ExampleConcat\n"); + + //Setup directories + File baseDir = new File("."); + File outDir = new File(baseDir, "out"); + outDir.mkdirs(); + + //Setup output file + File xsltfile = new File(baseDir, "xml/xslt/projectteam2fo.xsl"); + File[] files = new File[] { + new File(outDir, "team1.at.xml"), + new File(outDir, "team2.at.xml")}; + File pdffile = new File(outDir, "ResultConcat.pdf"); + for (int i = 0; i < files.length; i++) { + System.out.println("Intermediate file " + (i + 1) + ": " + + files[i].getCanonicalPath()); + } + System.out.println("PDF Output File: " + pdffile.getCanonicalPath()); + System.out.println(); + + + ProjectTeam team1 = ExampleObj2XML.createSampleProjectTeam(); + ProjectTeam team2 = createAnotherProjectTeam(); + + ExampleConcat app = new ExampleConcat(); + + //Create intermediate files + app.convertToIntermediate( + team1.getSourceForProjectTeam(), + new StreamSource(xsltfile), files[0]); + app.convertToIntermediate( + team2.getSourceForProjectTeam(), + new StreamSource(xsltfile), files[1]); + + //Concatenate the individual intermediate files to one document + app.concatToPDF(files, pdffile); + + System.out.println("Success!"); + + } catch (Exception e) { + e.printStackTrace(System.err); + System.exit(-1); + } + } + +} diff --git a/examples/mathml/src/org/apache/fop/mathml/MathMLElementMapping.java b/examples/mathml/src/org/apache/fop/mathml/MathMLElementMapping.java index 144d970be..4f0d21d2e 100644 --- a/examples/mathml/src/org/apache/fop/mathml/MathMLElementMapping.java +++ b/examples/mathml/src/org/apache/fop/mathml/MathMLElementMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Copyright 1999-2004,2006 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. @@ -22,6 +22,7 @@ import org.apache.fop.fo.FONode; import org.apache.fop.fo.ElementMapping; import org.apache.fop.image.analyser.XMLReader; import org.apache.fop.image.FopImage; +import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import java.util.HashMap; @@ -37,10 +38,17 @@ public class MathMLElementMapping extends ElementMapping { /** MathML Namespace */ public static final String NAMESPACE = "http://www.w3.org/1998/Math/MathML"; + /** Main constructor. */ public MathMLElementMapping() { this.namespaceURI = NAMESPACE; } + /** @see org.apache.fop.fo.ElementMapping#getDOMImplementation() */ + public DOMImplementation getDOMImplementation() { + return getDefaultDOMImplementation(); + } + + /** @see org.apache.fop.fo.ElementMapping#initialize() */ protected void initialize() { if (foObjs == null) { foObjs = new HashMap(); diff --git a/examples/plan/src/org/apache/fop/plan/PlanElementMapping.java b/examples/plan/src/org/apache/fop/plan/PlanElementMapping.java index 13d65a3f7..94c6127e5 100644 --- a/examples/plan/src/org/apache/fop/plan/PlanElementMapping.java +++ b/examples/plan/src/org/apache/fop/plan/PlanElementMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Copyright 1999-2004,2006 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. @@ -22,10 +22,9 @@ import org.apache.fop.fo.FONode; import org.apache.fop.fo.ElementMapping; import org.apache.fop.image.analyser.XMLReader; import org.apache.fop.image.FopImage; +import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; -import java.util.HashMap; - /** * This class provides the element mapping for FOP. */ @@ -34,10 +33,17 @@ public class PlanElementMapping extends ElementMapping { /** Plan Namespace */ public static final String NAMESPACE = "http://xmlgraphics.apache.org/fop/plan"; + /** Main constructor. */ public PlanElementMapping() { this.namespaceURI = NAMESPACE; } + /** @see org.apache.fop.fo.ElementMapping#getDOMImplementation() */ + public DOMImplementation getDOMImplementation() { + return getDefaultDOMImplementation(); + } + + /** @see org.apache.fop.fo.ElementMapping#initialize() */ protected void initialize() { if (foObjs == null) { foObjs = new java.util.HashMap(); diff --git a/src/java/org/apache/fop/area/AreaTreeParser.java b/src/java/org/apache/fop/area/AreaTreeParser.java new file mode 100644 index 000000000..32e2bce1e --- /dev/null +++ b/src/java/org/apache/fop/area/AreaTreeParser.java @@ -0,0 +1,676 @@ +/* + * Copyright 2006 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.area; + +import java.awt.geom.Rectangle2D; +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.Trait.Color; +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.fonts.Font; +import org.apache.fop.fonts.FontTriplet; +import org.apache.fop.image.FopImage; +import org.apache.fop.image.ImageFactory; +import org.apache.fop.traits.BorderProps; +import org.apache.fop.util.DefaultErrorListener; + +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) { + //TODO Retrieve this instance from the environment class once it has been created. + ElementMappingRegistry elementMappingRegistry = new ElementMappingRegistry(userAgent); + return new Handler(treeModel, userAgent, elementMappingRegistry); + } + + private static class Handler extends DefaultHandler { + + 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 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; + } + + 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) { + 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 + this.delegate = handler; + delegateStack.push(qName); + delegate.startDocument(); + delegate.startElement(uri, localName, qName, attributes); + } else { + lastAttributes = attributes; + boolean handled = true; + if ("".equals(uri)) { + if ("areaTree".equals(localName)) { + //nop + } else if ("pageSequence".equals(localName)) { + treeModel.startPageSequence(null); + } else if ("pageViewport".equals(localName)) { + if (currentPageViewport != null) { + throw new IllegalStateException("currentPageViewport must be null"); + } + Rectangle2D viewArea = parseRect(attributes.getValue("bounds")); + currentPageViewport = new PageViewport(viewArea, attributes.getValue("nr")); + } else if ("page".equals(localName)) { + Page p = new Page(); + currentPageViewport.setPage(p); + } else if ("regionViewport".equals(localName)) { + RegionViewport rv = getCurrentRegionViewport(); + if (rv != null) { + throw new IllegalStateException("Current RegionViewport must be null"); + } + Rectangle2D viewArea = parseRect(attributes.getValue("rect")); + rv = new RegionViewport(viewArea); + rv.setClip(getAttributeAsBoolean(attributes, "clipped", false)); + setAreaAttributes(attributes, rv); + setTraits(attributes, rv); + areaStack.push(rv); + } else if ("regionBefore".equals(localName)) { + pushNewRegionReference(attributes, Constants.FO_REGION_BEFORE); + } else if ("regionAfter".equals(localName)) { + pushNewRegionReference(attributes, Constants.FO_REGION_AFTER); + } else if ("regionStart".equals(localName)) { + pushNewRegionReference(attributes, Constants.FO_REGION_START); + } else if ("regionEnd".equals(localName)) { + pushNewRegionReference(attributes, Constants.FO_REGION_END); + } else if ("regionBody".equals(localName)) { + 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); + body.setCTM(getAttributeAsCTM(attributes, "ctm")); + setAreaAttributes(attributes, body); + rv.setRegionReference(body); + currentPageViewport.getPage().setRegionViewport( + Constants.FO_REGION_BODY, rv); + areaStack.push(body); + } else if ("mainReference".equals(localName)) { + //mainReference is created by the BodyRegion + setAreaAttributes(attributes, getCurrentBodyRegion().getMainReference()); + } else if ("span".equals(localName)) { + int ipd = getAttributeAsInteger(attributes, "ipd", 0); + int columnCount = getAttributeAsInteger(attributes, "columnCount", 1); + BodyRegion body = getCurrentBodyRegion(); + Span span = new Span(columnCount, + body.getColumnGap(), ipd); + setAreaAttributes(attributes, span); + body.getMainReference().getSpans().add(span); + firstFlow = true; + } else if ("flow".equals(localName)) { + BodyRegion body = getCurrentBodyRegion(); + if (!firstFlow) { + body.getMainReference().getCurrentSpan().moveToNextFlow(); + } else { + firstFlow = false; + } + NormalFlow flow = body.getMainReference().getCurrentSpan().getCurrentFlow(); + setAreaAttributes(attributes, flow); + areaStack.push(flow); + } else if ("footnote".equals(localName)) { + areaStack.push(getCurrentBodyRegion().getFootnote()); + } else if ("beforeFloat".equals(localName)) { + areaStack.push(getCurrentBodyRegion().getBeforeFloat()); + } else if ("block".equals(localName)) { + 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)); + } + setAreaAttributes(attributes, block); + setTraits(attributes, block); + Area parent = (Area)areaStack.peek(); + //BlockParent parent = getCurrentBlockParent(); + parent.addChildArea(block); + areaStack.push(block); + } else if ("lineArea".equals(localName)) { + LineArea line = new LineArea(); + setAreaAttributes(attributes, line); + setTraits(attributes, line); + BlockParent parent = getCurrentBlockParent(); + parent.addChildArea(line); + areaStack.push(line); + } else if ("inlineparent".equals(localName)) { + InlineParent ip = new InlineParent(); + ip.setOffset(getAttributeAsInteger(attributes, "offset", 0)); + setAreaAttributes(attributes, ip); + setTraits(attributes, ip); + Area parent = (Area)areaStack.peek(); + parent.addChildArea(ip); + areaStack.push(ip); + } else if ("inlineblockparent".equals(localName)) { + InlineBlockParent ibp = new InlineBlockParent(); + ibp.setOffset(getAttributeAsInteger(attributes, "offset", 0)); + setAreaAttributes(attributes, ibp); + setTraits(attributes, ibp); + Area parent = (Area)areaStack.peek(); + parent.addChildArea(ibp); + areaStack.push(ibp); + } else if ("text".equals(localName)) { + if (getCurrentText() != null) { + throw new IllegalStateException("Current Text must be null"); + } + TextArea text = new TextArea(); + setAreaAttributes(attributes, text); + setTraits(attributes, text); + text.setBaselineOffset(getAttributeAsInteger(attributes, "baseline", 0)); + text.setTextLetterSpaceAdjust(getAttributeAsInteger(attributes, + "tlsadjust", 0)); + text.setTextWordSpaceAdjust(getAttributeAsInteger(attributes, + "twsadjust", 0)); + Area parent = (Area)areaStack.peek(); + parent.addChildArea(text); + areaStack.push(text); + } else if ("word".equals(localName)) { + //handled in endElement + } else if ("space".equals(localName)) { + //handled in endElement + } else if ("char".equals(localName)) { + //handled in endElement + } else if ("leader".equals(localName)) { + Leader leader = new Leader(); + setAreaAttributes(attributes, leader); + setTraits(attributes, leader); + 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); + } else if ("viewport".equals(localName)) { + Viewport viewport = new Viewport(null); + setAreaAttributes(attributes, viewport); + setTraits(attributes, viewport); + 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); + } else if ("image".equals(localName)) { + String url = attributes.getValue("url"); + Image image = new Image(url); + setAreaAttributes(attributes, image); + setTraits(attributes, image); + getCurrentViewport().setContent(image); + } else if ("foreignObject".equals(localName)) { + String ns = attributes.getValue("ns"); + this.domImplementation + = elementMappingRegistry.getDOMImplementationForNamespace(ns); + if (this.domImplementation == null) { + throw new SAXException("No DOMImplementation could be" + + " identified to handle namespace: " + ns); + } + ForeignObject foreign = new ForeignObject(ns); + setAreaAttributes(attributes, foreign); + setTraits(attributes, foreign); + getCurrentViewport().setContent(foreign); + areaStack.push(foreign); + } else { + handled = false; + } + } else { + handled = false; + } + if (!handled) { + throw new SAXException("Unhandled element " + localName + + " in namespace: " + uri); + } + } + } + + private void pushNewRegionReference(Attributes attributes, int side) { + String regionName = attributes.getValue("name"); + RegionViewport rv = getCurrentRegionViewport(); + RegionReference reg = new RegionReference(side, + regionName, rv); + 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()); + } + } + + /** @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(); + delegate = null; + domImplementation = null; + } + } else { + if ("".equals(uri)) { + if ("pageSequence".equals(localName)) { + //end page-sequence + } else if ("page".equals(localName)) { + treeModel.addPage(currentPageViewport); + currentPageViewport = null; + } else if ("pageViewport".equals(localName)) { + //nop + } else if ("regionViewport".equals(localName)) { + assertObjectOfClass(areaStack.pop(), RegionViewport.class); + } else if ("regionBefore".equals(localName)) { + assertObjectOfClass(areaStack.pop(), RegionReference.class); + } else if ("regionAfter".equals(localName)) { + assertObjectOfClass(areaStack.pop(), RegionReference.class); + } else if ("regionStart".equals(localName)) { + assertObjectOfClass(areaStack.pop(), RegionReference.class); + } else if ("regionEnd".equals(localName)) { + assertObjectOfClass(areaStack.pop(), RegionReference.class); + } else if ("regionBody".equals(localName)) { + assertObjectOfClass(areaStack.pop(), BodyRegion.class); + } else if ("flow".equals(localName)) { + assertObjectOfClass(areaStack.pop(), NormalFlow.class); + } else if ("footnote".equals(localName)) { + assertObjectOfClass(areaStack.pop(), Footnote.class); + } else if ("beforeFloat".equals(localName)) { + assertObjectOfClass(areaStack.pop(), BeforeFloat.class); + } else if ("block".equals(localName)) { + assertObjectOfClass(areaStack.pop(), Block.class); + } else if ("lineArea".equals(localName)) { + assertObjectOfClass(areaStack.pop(), LineArea.class); + } else if ("inlineparent".equals(localName)) { + assertObjectOfClass(areaStack.pop(), InlineParent.class); + } else if ("inlineblockparent".equals(localName)) { + assertObjectOfClass(areaStack.pop(), InlineBlockParent.class); + } else if ("text".equals(localName)) { + assertObjectOfClass(areaStack.pop(), TextArea.class); + } else if ("word".equals(localName)) { + int offset = getAttributeAsInteger(lastAttributes, "offset", 0); + String txt = content.toString(); + WordArea word = new WordArea(txt, offset); + AbstractTextArea text = getCurrentText(); + word.setParentArea(text); + text.addChildArea(word); + } else if ("space".equals(localName)) { + int offset = getAttributeAsInteger(lastAttributes, "offset", 0); + String txt = content.toString(); + //TODO the isAdjustable parameter is currently not used/implemented + if (txt.length() > 0) { + SpaceArea space = new SpaceArea(txt.charAt(0), offset, false); + AbstractTextArea text = getCurrentText(); + space.setParentArea(text); + text.addChildArea(space); + } else { + Space space = new Space(); + setAreaAttributes(lastAttributes, space); + setTraits(lastAttributes, space); + space.setOffset(getAttributeAsInteger(lastAttributes, "offset", 0)); + Area parent = (Area)areaStack.peek(); + parent.addChildArea(space); + } + } else if ("char".equals(localName)) { + String txt = content.toString(); + Character ch = new Character(txt.charAt(0)); + setAreaAttributes(lastAttributes, ch); + setTraits(lastAttributes, ch); + ch.setOffset(getAttributeAsInteger(lastAttributes, "offset", 0)); + ch.setBaselineOffset(getAttributeAsInteger(lastAttributes, "baseline", 0)); + Area parent = (Area)areaStack.peek(); + parent.addChildArea(ch); + } else if ("viewport".equals(localName)) { + assertObjectOfClass(areaStack.pop(), Viewport.class); + } else if ("foreignObject".equals(localName)) { + assertObjectOfClass(areaStack.pop(), ForeignObject.class); + } + } else { + //log.debug("Ignoring " + localName + " in namespace: " + uri); + } + content.setLength(0); //Reset text buffer (see characters()) + } + } + + private void setAreaAttributes(Attributes attributes, Area area) { + area.setIPD(Integer.parseInt(attributes.getValue("ipd"))); + area.setBPD(Integer.parseInt(attributes.getValue("bpd"))); + } + + private void setTraits(Attributes attributes, Area area) { + for (int i = 0, c = Trait.TRAIT_LIST.length; i < c; i++) { + Object trait = Trait.TRAIT_LIST[i]; + Class cl = Trait.getTraitClass(trait); + if (cl == Boolean.class) { + String value = attributes.getValue(Trait.getTraitName(trait)); + if (value != null) { + area.addTrait(trait, Boolean.valueOf(value)); + } + } else if (cl == Integer.class) { + String value = attributes.getValue(Trait.getTraitName(trait)); + if (value != null) { + area.addTrait(trait, new Integer(value)); + } + } else if (cl == String.class) { + String value = attributes.getValue(Trait.getTraitName(trait)); + if (value != null) { + area.addTrait(trait, value); + } + } else if (cl == FontTriplet.class) { + 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, + new FontTriplet(fontName, fontStyle, fontWeight)); + } + } else if (cl == Color.class) { + String value = attributes.getValue(Trait.getTraitName(trait)); + if (value != null) { + area.addTrait(trait, Color.valueOf(value)); + } + } else if (cl == Background.class) { + String value = attributes.getValue(Trait.getTraitName(trait)); + if (value != null) { + Background bkg = new Background(); + Color col = Color.valueOf(attributes.getValue("bkg-color")); + if (col != null) { + bkg.setColor(col); + } + String url = attributes.getValue("bkg-img"); + if (url != null) { + bkg.setURL(url); + + ImageFactory fact = ImageFactory.getInstance(); + 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) { + String value = attributes.getValue(Trait.getTraitName(trait)); + if (value != null) { + area.addTrait(trait, BorderProps.valueOf(value)); + } + } + } + } + + 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]); + } + + /** @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); + } + } + + } + +} diff --git a/src/java/org/apache/fop/area/BeforeFloat.java b/src/java/org/apache/fop/area/BeforeFloat.java index 2db35a635..4ebbaf296 100644 --- a/src/java/org/apache/fop/area/BeforeFloat.java +++ b/src/java/org/apache/fop/area/BeforeFloat.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2005 The Apache Software Foundation. + * Copyright 1999-2006 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. @@ -61,6 +61,7 @@ public class BeforeFloat extends BlockParent { return h; } + /** @see org.apache.fop.area.BlockParent#isEmpty() */ public boolean isEmpty() { return true; // before floats are not yet implemented } diff --git a/src/java/org/apache/fop/area/Block.java b/src/java/org/apache/fop/area/Block.java index 700334e77..f51bb7efc 100644 --- a/src/java/org/apache/fop/area/Block.java +++ b/src/java/org/apache/fop/area/Block.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2005 The Apache Software Foundation. + * Copyright 1999-2006 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. @@ -77,13 +77,10 @@ public class Block extends BlockParent { * @param autoHeight increase the height of the block. */ public void addBlock(Block block, boolean autoHeight) { - if (children == null) { - children = new ArrayList(); - } if (autoHeight) { bpd += block.getAllocBPD(); } - children.add(block); + addChildArea(block); } /** @@ -92,11 +89,8 @@ public class Block extends BlockParent { * @param line the line area to add */ public void addLineArea(LineArea line) { - if (children == null) { - children = new ArrayList(); - } bpd += line.getAllocBPD(); - children.add(line); + addChildArea(line); } /** diff --git a/src/java/org/apache/fop/area/BlockParent.java b/src/java/org/apache/fop/area/BlockParent.java index 63b35ca24..fe6650dbc 100644 --- a/src/java/org/apache/fop/area/BlockParent.java +++ b/src/java/org/apache/fop/area/BlockParent.java @@ -52,16 +52,21 @@ public class BlockParent extends Area { // orientation if reference area private int orientation = ORIENT_0; + /** @see org.apache.fop.area.Area#addChildArea(org.apache.fop.area.Area) */ + public void addChildArea(Area childArea) { + if (children == null) { + children = new ArrayList(); + } + children.add(childArea); + } + /** * Add the block area to this block parent. * * @param block the child block area to add */ public void addBlock(Block block) { - if (children == null) { - children = new ArrayList(); - } - children.add(block); + addChildArea(block); } /** @@ -73,6 +78,15 @@ public class BlockParent extends Area { return children; } + /** + * Check whether there are child areas. + * + * @return the result. + */ + public boolean isEmpty() { + return children == null || children.size() == 0; + } + /** * Set the X offset of this block parent area. * diff --git a/src/java/org/apache/fop/area/Footnote.java b/src/java/org/apache/fop/area/Footnote.java index 735dfaf25..afa12afaf 100644 --- a/src/java/org/apache/fop/area/Footnote.java +++ b/src/java/org/apache/fop/area/Footnote.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2005 The Apache Software Foundation. + * Copyright 1999-2006 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. @@ -18,9 +18,6 @@ package org.apache.fop.area; -import java.util.ArrayList; -import java.util.List; - // may combine with before float into a conditional area /** @@ -80,29 +77,9 @@ public class Footnote extends BlockParent { * @param child the block area. */ public void addBlock(Block child) { - if (children == null) { - children = new ArrayList(); - } + addChildArea(child); this.setBPD(this.getBPD() + child.getBPD()); - children.add(child); - } - - /** - * Get all child areas. - * - * @return the list of child areas. Maybe null. - */ - public List getChildAreas() { - return children; } - /** - * Check whether there are child areas. - * - * @return the result. - */ - public boolean isEmpty() { - return children == null || children.size() == 0; - } } diff --git a/src/java/org/apache/fop/area/LineArea.java b/src/java/org/apache/fop/area/LineArea.java index 15d22548f..e19e71374 100644 --- a/src/java/org/apache/fop/area/LineArea.java +++ b/src/java/org/apache/fop/area/LineArea.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2005 The Apache Software Foundation. + * Copyright 1999-2006 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. @@ -57,13 +57,13 @@ public class LineArea extends Area { private LineAdjustingInfo adjustingInfo = null; - private int stacking = LR; + //private int stacking = LR; // contains inline areas // has start indent and length, dominant baseline, height private int startIndent; // this is the offset for the dominant baseline - private int baseLine; + //private int baseLine; // this class can contain the dominant char styling info // this means that many renderers can optimise a bit @@ -82,6 +82,8 @@ public class LineArea extends Area { * a new LineAdjustingInfo object is created * @param alignment alignment of this line * @param diff difference between content width and line width + * @param stretch the available stretch for any adjustments + * @param shrink the available shrink for any adjustments */ public LineArea(int alignment, int diff, int stretch, int shrink) { @@ -185,7 +187,8 @@ public class LineArea extends Area { break; case Constants.EN_JUSTIFY: // compute variation factor - adjustingInfo.variationFactor *= (float) (adjustingInfo.difference - ipdVariation) / adjustingInfo.difference; + adjustingInfo.variationFactor *= (float) (adjustingInfo.difference - ipdVariation) + / adjustingInfo.difference; adjustingInfo.difference -= ipdVariation; // if the LineArea has already been added to the area tree, // call finalize(); otherwise, wait for the LineLM to call it diff --git a/src/java/org/apache/fop/area/Page.java b/src/java/org/apache/fop/area/Page.java index cc920b993..a0ad2e877 100644 --- a/src/java/org/apache/fop/area/Page.java +++ b/src/java/org/apache/fop/area/Page.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2005 The Apache Software Foundation. + * Copyright 1999-2006 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. @@ -187,7 +187,7 @@ public class Page implements Serializable, Cloneable { * @param areaclass the area class of the region to set * @param port the region viewport to set */ - private void setRegionViewport(int areaclass, RegionViewport port) { + public void setRegionViewport(int areaclass, RegionViewport port) { if (areaclass == Constants.FO_REGION_BEFORE) { regionBefore = port; } else if (areaclass == Constants.FO_REGION_START) { diff --git a/src/java/org/apache/fop/area/PageViewport.java b/src/java/org/apache/fop/area/PageViewport.java index f1daaa2f1..b516b3446 100644 --- a/src/java/org/apache/fop/area/PageViewport.java +++ b/src/java/org/apache/fop/area/PageViewport.java @@ -107,6 +107,16 @@ public class PageViewport implements Resolvable, Cloneable { this.viewArea = (Rectangle2D)original.viewArea.clone(); } + /** + * Constructor used by the area tree parser. + * @param viewArea the view area + * @param pageStr String representation of the page number + */ + public PageViewport(Rectangle2D viewArea, String pageStr) { + this.viewArea = viewArea; + this.pageNumberString = pageStr; + } + /** * Get the view area rectangle of this viewport. * @return the rectangle for this viewport @@ -122,6 +132,14 @@ public class PageViewport implements Resolvable, Cloneable { public Page getPage() { return page; } + + /** + * Sets the page object for this PageViewport. + * @param page the page + */ + public void setPage(Page page) { + this.page = page; + } /** * Get the page number of this page. diff --git a/src/java/org/apache/fop/area/RegionReference.java b/src/java/org/apache/fop/area/RegionReference.java index b643db4dc..eb85fc4b8 100644 --- a/src/java/org/apache/fop/area/RegionReference.java +++ b/src/java/org/apache/fop/area/RegionReference.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2005 The Apache Software Foundation. + * Copyright 1999-2006 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. @@ -67,6 +67,11 @@ public class RegionReference extends Area implements Cloneable { regionViewport = parent; } + /** @see org.apache.fop.area.Area#addChildArea(org.apache.fop.area.Area) */ + public void addChildArea(Area child) { + blocks.add(child); + } + /** * Set the Coordinate Transformation Matrix which transforms content * coordinates in this region reference area which are specified in @@ -125,7 +130,7 @@ public class RegionReference extends Area implements Cloneable { * @param block the block area to add */ public void addBlock(Block block) { - blocks.add(block); + addChildArea(block); } /** diff --git a/src/java/org/apache/fop/area/RenderPagesModel.java b/src/java/org/apache/fop/area/RenderPagesModel.java index 3f44ee736..f4ae85fe7 100644 --- a/src/java/org/apache/fop/area/RenderPagesModel.java +++ b/src/java/org/apache/fop/area/RenderPagesModel.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2005 The Apache Software Foundation. + * Copyright 1999-2006 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. @@ -113,8 +113,11 @@ public class RenderPagesModel extends AreaTreeModel { try { renderer.renderPage(page); } catch (Exception e) { - // use error handler to handle this FOP or IO Exception - log.error(e); + //TODO use error handler to handle this FOP or IO Exception or propagate exception + String err = "Error while rendering page " + page.getPageNumberString(); + log.error(err, e); + throw new IllegalStateException("Fatal error occurred. Cannot continue. " + + e.getClass().getName() + ": " + err); } page.clear(); } else { diff --git a/src/java/org/apache/fop/area/Trait.java b/src/java/org/apache/fop/area/Trait.java index 0dd1c5681..a58d527a9 100644 --- a/src/java/org/apache/fop/area/Trait.java +++ b/src/java/org/apache/fop/area/Trait.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2005 The Apache Software Foundation. + * Copyright 1999-2006 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. @@ -202,6 +202,9 @@ public class Trait implements Serializable { public static final Integer LINETHROUGH_COLOR = new Integer(36); private static final Map TRAIT_INFO = new HashMap(); + + /** The list of simple traits in order to avoid iterators */ + public static final Object[] TRAIT_LIST; private static class TraitInfo { private String name; @@ -232,19 +235,19 @@ public class Trait implements Serializable { new TraitInfo("font", FontTriplet.class)); TRAIT_INFO.put(FONT_SIZE, new TraitInfo("font-size", Integer.class)); - TRAIT_INFO.put(COLOR, new TraitInfo("color", String.class)); + TRAIT_INFO.put(COLOR, new TraitInfo("color", Color.class)); TRAIT_INFO.put(PROD_ID, new TraitInfo("prod-id", String.class)); TRAIT_INFO.put(BACKGROUND, new TraitInfo("background", Background.class)); TRAIT_INFO.put(UNDERLINE, new TraitInfo("underline-score", Boolean.class)); - TRAIT_INFO.put(UNDERLINE_COLOR, new TraitInfo("underline-score-color", String.class)); + TRAIT_INFO.put(UNDERLINE_COLOR, new TraitInfo("underline-score-color", Color.class)); TRAIT_INFO.put(OVERLINE, new TraitInfo("overline-score", Boolean.class)); - TRAIT_INFO.put(OVERLINE_COLOR, new TraitInfo("overline-score-color", String.class)); + TRAIT_INFO.put(OVERLINE_COLOR, new TraitInfo("overline-score-color", Color.class)); TRAIT_INFO.put(LINETHROUGH, new TraitInfo("through-score", Boolean.class)); - TRAIT_INFO.put(LINETHROUGH_COLOR, new TraitInfo("through-score-color", String.class)); + TRAIT_INFO.put(LINETHROUGH_COLOR, new TraitInfo("through-score-color", Color.class)); TRAIT_INFO.put(BLINK, new TraitInfo("blink", Boolean.class)); TRAIT_INFO.put(OFFSET, new TraitInfo("offset", Integer.class)); @@ -285,6 +288,8 @@ public class Trait implements Serializable { new TraitInfo("is-reference-area", Boolean.class)); TRAIT_INFO.put(IS_VIEWPORT_AREA, new TraitInfo("is-viewport-area", Boolean.class)); + + TRAIT_LIST = TRAIT_INFO.keySet().toArray(); } /** @@ -326,7 +331,7 @@ public class Trait implements Serializable { * @param oTraitCode the trait code to lookup * @return the class type for the trait */ - private static Class getTraitClass(Object oTraitCode) { + public static Class getTraitClass(Object oTraitCode) { TraitInfo ti = (TraitInfo) TRAIT_INFO.get(oTraitCode); return (ti != null ? ti.getClazz() : null); } @@ -496,6 +501,51 @@ public class Trait implements Serializable { } } + /** @see java.lang.Object#hashCode() */ + public int hashCode() { + return toString().hashCode(); + } + + /** @see java.lang.Object#equals(java.lang.Object) */ + public boolean equals(Object obj) { + if (obj == null) { + return false; + } else if (obj == this) { + return true; + } else { + if (obj instanceof ColorType) { + ColorType other = (ColorType)obj; + return getRed() == other.getRed() + && getGreen() == other.getGreen() + && getBlue() == other.getBlue() + && getAlpha() == other.getAlpha(); + } + } + return false; + } + + /** + * Returns a Color represtation of a string of the format "#RRGGBB". + * @param s the string + * @return the Color value + */ + public static Color valueOf(String s) { + if (s == null) { + return null; + } + if (!s.startsWith("#")) { + throw new IllegalArgumentException("Color must start with '#'"); + } + int r = Integer.parseInt(s.substring(1, 3), 16); + int g = Integer.parseInt(s.substring(3, 5), 16); + int b = Integer.parseInt(s.substring(5, 7), 16); + int a = 255; + if (s.length() > 7) { + a = Integer.parseInt(s.substring(7, 9), 16); + } + return new Color(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f); + } + /** @see java.lang.Object#toString() */ public String toString() { StringBuffer sbuf = new StringBuffer(8); @@ -515,6 +565,13 @@ public class Trait implements Serializable { sbuf.append('0'); } sbuf.append(s); + if (alpha != 1) { + s = Integer.toHexString((int)(alpha * 255.0)); + if (s.length() == 1) { + sbuf.append('0'); + } + sbuf.append(s); + } return sbuf.toString(); } @@ -616,6 +673,14 @@ public class Trait implements Serializable { this.repeat = repeat; } + /** + * Sets the image repetition behaviour for images. + * @param repeat The image repetition behaviour to set + */ + public void setRepeat(String repeat) { + setRepeat(getConstantForRepeat(repeat)); + } + /** * Sets the URL to the background image. * @param url The URL to set @@ -640,7 +705,31 @@ public class Trait implements Serializable { this.vertical = vertical; } - /** + private String getRepeatString() { + switch (getRepeat()) { + case Constants.EN_REPEAT: return "repeat"; + case Constants.EN_REPEATX: return "repeat-x"; + case Constants.EN_REPEATY: return "repeat-y"; + case Constants.EN_NOREPEAT: return "no-repeat"; + default: throw new IllegalStateException("Illegal repeat style: " + getRepeat()); + } + } + + private static int getConstantForRepeat(String repeat) { + if ("repeat".equalsIgnoreCase(repeat)) { + return Constants.EN_REPEAT; + } else if ("repeat-x".equalsIgnoreCase(repeat)) { + return Constants.EN_REPEATX; + } else if ("repeat-y".equalsIgnoreCase(repeat)) { + return Constants.EN_REPEATY; + } else if ("no-repeat".equalsIgnoreCase(repeat)) { + return Constants.EN_NOREPEAT; + } else { + throw new IllegalStateException("Illegal repeat style: " + repeat); + } + } + + /** * Return the string for debugging. * @see java.lang.Object#toString() */ @@ -649,23 +738,7 @@ public class Trait implements Serializable { sb.append("color=").append(color); if (url != null) { sb.append(",url=").append(url); - sb.append(",repeat="); - switch (repeat) { - case Constants.EN_REPEAT: - sb.append("repeat"); - break; - case Constants.EN_REPEATX: - sb.append("repeat-x"); - break; - case Constants.EN_REPEATY: - sb.append("repeat-y"); - break; - case Constants.EN_NOREPEAT: - sb.append("no-repeat"); - break; - default: - sb.append("ILLEGAL!"); - } + sb.append(",repeat=").append(getRepeatString()); sb.append(",horiz=").append(horiz); sb.append(",vertical=").append(vertical); } diff --git a/src/java/org/apache/fop/area/inline/ForeignObject.java b/src/java/org/apache/fop/area/inline/ForeignObject.java index 7bf24963d..8a47be6e1 100644 --- a/src/java/org/apache/fop/area/inline/ForeignObject.java +++ b/src/java/org/apache/fop/area/inline/ForeignObject.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Copyright 1999-2004,2006 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. @@ -29,6 +29,7 @@ import org.w3c.dom.Document; * This holds an xml document and the associated namespace. */ public class ForeignObject extends Area { + private Document doc; private String namespace; @@ -43,6 +44,23 @@ public class ForeignObject extends Area { namespace = ns; } + /** + * Create a new empty foreign object for which the DOM Document will be set later. + * + * @param ns the namespace of the document + */ + public ForeignObject(String ns) { + namespace = ns; + } + + /** + * Sets the DOM document for this foreign object. + * @param document the DOM document + */ + public void setDocument(Document document) { + this.doc = document; + } + /** * Get the document for this foreign object. * diff --git a/src/java/org/apache/fop/area/inline/InlineArea.java b/src/java/org/apache/fop/area/inline/InlineArea.java index 2f326823b..d4e0f47cf 100644 --- a/src/java/org/apache/fop/area/inline/InlineArea.java +++ b/src/java/org/apache/fop/area/inline/InlineArea.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2005 The Apache Software Foundation. + * Copyright 1999-2006 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. diff --git a/src/java/org/apache/fop/area/inline/InlineParent.java b/src/java/org/apache/fop/area/inline/InlineParent.java index 6444da8c4..122c8b2c9 100644 --- a/src/java/org/apache/fop/area/inline/InlineParent.java +++ b/src/java/org/apache/fop/area/inline/InlineParent.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2005 The Apache Software Foundation. + * Copyright 1999-2006 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. @@ -33,11 +33,9 @@ public class InlineParent extends InlineArea { */ protected List inlines = new ArrayList(); - /** - * An inline parent is a reference area somay have clipping - */ - protected boolean clip = false; - + /** Controls whether the IPD is automatically adjusted based on the area's children. */ + protected transient boolean autoSize; + /** * Create a new inline parent to add areas to. */ @@ -50,9 +48,14 @@ public class InlineParent extends InlineArea { * @param childArea the child area to add */ public void addChildArea(Area childArea) { + if (inlines.size() == 0) { + autoSize = (getIPD() == 0); + } if (childArea instanceof InlineArea) { inlines.add(childArea); - increaseIPD(((InlineArea) childArea).getAllocIPD()); + if (autoSize) { + increaseIPD(((InlineArea) childArea).getAllocIPD()); + } } } diff --git a/src/java/org/apache/fop/area/inline/Leader.java b/src/java/org/apache/fop/area/inline/Leader.java index f683b5af0..e68469b01 100644 --- a/src/java/org/apache/fop/area/inline/Leader.java +++ b/src/java/org/apache/fop/area/inline/Leader.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Copyright 1999-2004,2006 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. @@ -49,6 +49,28 @@ public class Leader extends InlineArea { ruleStyle = style; } + /** + * Set the rule style of this leader area. + * @param style the rule style for the leader area (XSL enum values) + */ + public void setRuleStyle(String style) { + if ("dotted".equalsIgnoreCase(style)) { + setRuleStyle(Constants.EN_DOTTED); + } else if ("dashed".equalsIgnoreCase(style)) { + setRuleStyle(Constants.EN_DASHED); + } else if ("solid".equalsIgnoreCase(style)) { + setRuleStyle(Constants.EN_SOLID); + } else if ("double".equalsIgnoreCase(style)) { + setRuleStyle(Constants.EN_DOUBLE); + } else if ("groove".equalsIgnoreCase(style)) { + setRuleStyle(Constants.EN_GROOVE); + } else if ("ridge".equalsIgnoreCase(style)) { + setRuleStyle(Constants.EN_RIDGE); + } else if ("none".equalsIgnoreCase(style)) { + setRuleStyle(Constants.EN_NONE); + } + } + /** * Set the rule thickness of the rule in miilipoints. * @@ -67,6 +89,21 @@ public class Leader extends InlineArea { return ruleStyle; } + /** @return the rule style as string */ + public String getRuleStyleAsString() { + switch (getRuleStyle()) { + case Constants.EN_DOTTED: return "dotted"; + case Constants.EN_DASHED: return "dashed"; + case Constants.EN_SOLID: return "solid"; + case Constants.EN_DOUBLE: return "double"; + case Constants.EN_GROOVE: return "groove"; + case Constants.EN_RIDGE: return "ridge"; + case Constants.EN_NONE: return "none"; + default: + throw new IllegalStateException("Unsupported rule style: " + getRuleStyle()); + } + } + /** * Get the rule thickness of the rule in miilipoints. * diff --git a/src/java/org/apache/fop/area/inline/Viewport.java b/src/java/org/apache/fop/area/inline/Viewport.java index c9c0b5b10..e8035a904 100644 --- a/src/java/org/apache/fop/area/inline/Viewport.java +++ b/src/java/org/apache/fop/area/inline/Viewport.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Copyright 1999-2004,2006 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. @@ -35,7 +35,7 @@ public class Viewport extends InlineArea { private Area content; // clipping for the viewport private boolean clip = false; - // position of the cild area relative to this area + // position of the child area relative to this area private Rectangle2D contentPosition; /** @@ -83,6 +83,14 @@ public class Viewport extends InlineArea { return contentPosition; } + /** + * Sets the content area. + * @param content the content area + */ + public void setContent(Area content) { + this.content = content; + } + /** * Get the content area for this viewport. * diff --git a/src/java/org/apache/fop/fo/ElementMapping.java b/src/java/org/apache/fop/fo/ElementMapping.java index 3501194ab..092a411f0 100644 --- a/src/java/org/apache/fop/fo/ElementMapping.java +++ b/src/java/org/apache/fop/fo/ElementMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Copyright 1999-2004,2006 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. @@ -20,6 +20,11 @@ package org.apache.fop.fo; import java.util.HashMap; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.w3c.dom.DOMImplementation; + /** * Abstract base class for Element Mappings (including FO Element Mappings) * which provide the framework of valid elements and attibutes for a given @@ -55,14 +60,50 @@ public abstract class ElementMapping { public String getNamespaceURI() { return namespaceURI; } + + /** + * Returns the DOMImplementation used by this ElementMapping. The value returned may be null + * for cases where no DOM is used to represent the element tree (XSL-FO, for example). This + * method is used by the intermediate format to instantiate the right kind of DOM document + * for foreign objects. For example, SVG handled through Apache Batik has to use a special + * DOMImplementation. + * @return the DOMImplementation used by this ElementMapping, may be null + */ + public DOMImplementation getDOMImplementation() { + return null; //For namespaces not used in foreign objects + } + /** + * @return the default DOMImplementation when no specialized DOM is necessary. + */ + protected DOMImplementation getDefaultDOMImplementation() { + DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance(); + fact.setNamespaceAware(true); + fact.setValidating(false); + try { + return fact.newDocumentBuilder().getDOMImplementation(); + } catch (ParserConfigurationException e) { + throw new RuntimeException( + "Cannot return default DOM implementation: " + e.getMessage()); + } + } + /** * Initializes the set of maker objects associated with this ElementMapping */ protected abstract void initialize(); + /** + * Base class for all Makers. It is responsible to return the right kind of FONode for a + * particular element. + */ public static class Maker { + /** + * Creates a new FONode (or rather a specialized subclass of it). + * @param parent the parent FONode + * @return the newly created FONode instance + */ public FONode make(FONode parent) { return null; } diff --git a/src/java/org/apache/fop/fo/ElementMappingRegistry.java b/src/java/org/apache/fop/fo/ElementMappingRegistry.java new file mode 100644 index 000000000..35224e7ae --- /dev/null +++ b/src/java/org/apache/fop/fo/ElementMappingRegistry.java @@ -0,0 +1,175 @@ +/* + * Copyright 2006 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.fo; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.fo.ElementMapping.Maker; +import org.apache.fop.util.Service; +import org.w3c.dom.DOMImplementation; +import org.xml.sax.Locator; + +/** + * This class keeps track of all configured ElementMapping implementations which are responsible + * for properly handling all kinds of different XML namespaces. + */ +public class ElementMappingRegistry { + + /** logging instance */ + protected Log log = LogFactory.getLog(ElementMappingRegistry.class); + + /** + * Table mapping element names to the makers of objects + * representing formatting objects. + */ + protected Map fobjTable = new java.util.HashMap(); + + /** + * Map of mapped namespaces and their associated ElementMapping instances. + */ + protected Map namespaces = new java.util.HashMap(); + + /** + * Main constructor. Adds all default element mapping as well as detects ElementMapping + * through the Service discovery. + * @param userAgent the user agent + */ + public ElementMappingRegistry(FOUserAgent userAgent) { + // Add standard element mappings + setupDefaultMappings(); + + // add additional ElementMappings defined within FOUserAgent + List addlEMs = userAgent.getAdditionalElementMappings(); + + if (addlEMs != null) { + for (int i = 0; i < addlEMs.size(); i++) { + addElementMapping((ElementMapping) addlEMs.get(i)); + } + } + } + + /** + * Sets all the element and property list mappings to their default values. + */ + private void setupDefaultMappings() { + addElementMapping("org.apache.fop.fo.FOElementMapping"); + addElementMapping("org.apache.fop.fo.extensions.svg.SVGElementMapping"); + addElementMapping("org.apache.fop.fo.extensions.svg.BatikExtensionElementMapping"); + addElementMapping("org.apache.fop.fo.extensions.ExtensionElementMapping"); + addElementMapping("org.apache.fop.render.ps.extensions.PSExtensionElementMapping"); + + // add mappings from available services + Iterator providers = Service.providers(ElementMapping.class); + if (providers != null) { + while (providers.hasNext()) { + String str = (String)providers.next(); + try { + addElementMapping(str); + } catch (IllegalArgumentException e) { + log.warn("Error while adding element mapping", e); + } + + } + } + } + + /** + * Add the element mapping with the given class name. + * @param mappingClassName the class name representing the element mapping. + * @throws IllegalArgumentException if there was not such element mapping. + */ + public void addElementMapping(String mappingClassName) + throws IllegalArgumentException { + + try { + ElementMapping mapping + = (ElementMapping)Class.forName(mappingClassName).newInstance(); + addElementMapping(mapping); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException("Could not find " + + mappingClassName); + } catch (InstantiationException e) { + throw new IllegalArgumentException("Could not instantiate " + + mappingClassName); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException("Could not access " + + mappingClassName); + } catch (ClassCastException e) { + throw new IllegalArgumentException(mappingClassName + + " is not an ElementMapping"); + } + } + + private void addElementMapping(ElementMapping mapping) { + this.fobjTable.put(mapping.getNamespaceURI(), mapping.getTable()); + this.namespaces.put(mapping.getNamespaceURI().intern(), mapping); + } + + /** + * Finds the Maker used to create node objects of a particular type + * @param namespaceURI URI for the namespace of the element + * @param localName name of the Element + * @param locator the Locator instance for context information + * @return the ElementMapping.Maker that can create an FO object for this element + * @throws FOPException if a Maker could not be found for a bound namespace. + */ + public Maker findFOMaker(String namespaceURI, String localName, Locator locator) + throws FOPException { + Map table = (Map)fobjTable.get(namespaceURI); + Maker fobjMaker = null; + if (table != null) { + fobjMaker = (ElementMapping.Maker)table.get(localName); + // try default + if (fobjMaker == null) { + fobjMaker = (ElementMapping.Maker)table.get(ElementMapping.DEFAULT); + } + } + + if (fobjMaker == null) { + if (namespaces.containsKey(namespaceURI.intern())) { + throw new FOPException(FONode.errorText(locator) + + "No element mapping definition found for " + + FONode.getNodeString(namespaceURI, localName), locator); + } else { + log.warn("Unknown formatting object " + namespaceURI + "^" + localName); + fobjMaker = new UnknownXMLObj.Maker(namespaceURI); + } + } + return fobjMaker; + } + + /** + * Tries to determine the DOMImplementation that is used to handled a particular namespace. + * The method may return null for namespaces that don't result in a DOM. It is mostly used + * in namespaces occurring in foreign objects. + * @param namespaceURI the namespace URI + * @return the handling DOMImplementation, or null if not applicable + */ + public DOMImplementation getDOMImplementationForNamespace(String namespaceURI) { + ElementMapping mapping = (ElementMapping)this.namespaces.get(namespaceURI); + return mapping.getDOMImplementation(); + } + +} diff --git a/src/java/org/apache/fop/fo/FOTreeBuilder.java b/src/java/org/apache/fop/fo/FOTreeBuilder.java index cc87fcca9..c0aa87a44 100644 --- a/src/java/org/apache/fop/fo/FOTreeBuilder.java +++ b/src/java/org/apache/fop/fo/FOTreeBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2005 The Apache Software Foundation. + * Copyright 1999-2006 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. @@ -19,10 +19,6 @@ package org.apache.fop.fo; import java.io.OutputStream; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -30,7 +26,6 @@ import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.FormattingResults; import org.apache.fop.area.AreaTreeHandler; -import org.apache.fop.util.Service; import org.apache.fop.fo.ElementMapping.Maker; import org.apache.fop.fo.pagination.Root; import org.apache.fop.image.ImageFactory; @@ -48,22 +43,12 @@ import org.xml.sax.helpers.DefaultHandler; */ public class FOTreeBuilder extends DefaultHandler { - /** - * Table mapping element names to the makers of objects - * representing formatting objects. - */ - protected Map fobjTable = new java.util.HashMap(); - - /** - * logging instance - */ + /** logging instance */ protected Log log = LogFactory.getLog(FOTreeBuilder.class); - /** - * Set of mapped namespaces. - */ - protected Set namespaces = new java.util.HashSet(); - + /** The registry for ElementMapping instances */ + protected ElementMappingRegistry elementMappingRegistry; + /** * The root of the formatting object tree */ @@ -113,75 +98,7 @@ public class FOTreeBuilder extends DefaultHandler { } }); - // Add standard element mappings - setupDefaultMappings(); - - // add additional ElementMappings defined within FOUserAgent - List addlEMs = foUserAgent.getAdditionalElementMappings(); - - if (addlEMs != null) { - for (int i = 0; i < addlEMs.size(); i++) { - addElementMapping((ElementMapping) addlEMs.get(i)); - } - } - } - - /** - * Sets all the element and property list mappings to their default values. - * - */ - private void setupDefaultMappings() { - addElementMapping("org.apache.fop.fo.FOElementMapping"); - addElementMapping("org.apache.fop.fo.extensions.svg.SVGElementMapping"); - addElementMapping("org.apache.fop.fo.extensions.svg.BatikExtensionElementMapping"); - addElementMapping("org.apache.fop.fo.extensions.ExtensionElementMapping"); - addElementMapping("org.apache.fop.render.ps.extensions.PSExtensionElementMapping"); - - // add mappings from available services - Iterator providers = Service.providers(ElementMapping.class); - if (providers != null) { - while (providers.hasNext()) { - String str = (String)providers.next(); - try { - addElementMapping(str); - } catch (IllegalArgumentException e) { - log.warn("Error while adding element mapping", e); - } - - } - } - } - - /** - * Add the element mapping with the given class name. - * @param mappingClassName the class name representing the element mapping. - * @throws IllegalArgumentException if there was not such element mapping. - */ - public void addElementMapping(String mappingClassName) - throws IllegalArgumentException { - - try { - ElementMapping mapping - = (ElementMapping)Class.forName(mappingClassName).newInstance(); - addElementMapping(mapping); - } catch (ClassNotFoundException e) { - throw new IllegalArgumentException("Could not find " - + mappingClassName); - } catch (InstantiationException e) { - throw new IllegalArgumentException("Could not instantiate " - + mappingClassName); - } catch (IllegalAccessException e) { - throw new IllegalArgumentException("Could not access " - + mappingClassName); - } catch (ClassCastException e) { - throw new IllegalArgumentException(mappingClassName - + " is not an ElementMapping"); - } - } - - private void addElementMapping(ElementMapping mapping) { - this.fobjTable.put(mapping.getNamespaceURI(), mapping.getTable()); - this.namespaces.add(mapping.getNamespaceURI().intern()); + this.elementMappingRegistry = new ElementMappingRegistry(foUserAgent); } /** @@ -340,27 +257,7 @@ public class FOTreeBuilder extends DefaultHandler { * @throws FOPException if a Maker could not be found for a bound namespace. */ private Maker findFOMaker(String namespaceURI, String localName) throws FOPException { - Map table = (Map)fobjTable.get(namespaceURI); - Maker fobjMaker = null; - if (table != null) { - fobjMaker = (ElementMapping.Maker)table.get(localName); - // try default - if (fobjMaker == null) { - fobjMaker = (ElementMapping.Maker)table.get(ElementMapping.DEFAULT); - } - } - - if (fobjMaker == null) { - if (namespaces.contains(namespaceURI.intern())) { - throw new FOPException(FONode.errorText(locator) - + "No element mapping definition found for " - + FONode.getNodeString(namespaceURI, localName), locator); - } else { - log.warn("Unknown formatting object " + namespaceURI + "^" + localName); - fobjMaker = new UnknownXMLObj.Maker(namespaceURI); - } - } - return fobjMaker; + return elementMappingRegistry.findFOMaker(namespaceURI, localName, locator); } /** @see org.xml.sax.ErrorHandler#warning(org.xml.sax.SAXParseException) */ diff --git a/src/java/org/apache/fop/fo/extensions/svg/BatikExtensionElementMapping.java b/src/java/org/apache/fop/fo/extensions/svg/BatikExtensionElementMapping.java index 7b34d6c01..0fce5b8dd 100644 --- a/src/java/org/apache/fop/fo/extensions/svg/BatikExtensionElementMapping.java +++ b/src/java/org/apache/fop/fo/extensions/svg/BatikExtensionElementMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Copyright 1999-2004,2006 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. @@ -24,19 +24,29 @@ import javax.xml.parsers.SAXParserFactory; import org.apache.batik.util.XMLResourceDescriptor; import org.apache.fop.fo.ElementMapping; import org.apache.fop.fo.FONode; +import org.w3c.dom.DOMImplementation; /** * This Element Mapping is for Batik SVG Extension elements * of the http://xml.apache.org/batik/ext namespace. */ public class BatikExtensionElementMapping extends ElementMapping { - public static String URI = "http://xml.apache.org/batik/ext"; + + /** Namespace URI for Batik extension elements */ + public static final String URI = "http://xml.apache.org/batik/ext"; + private boolean batikAvail = true; + /** Main constructor. */ public BatikExtensionElementMapping() { namespaceURI = URI; } + /** @see org.apache.fop.fo.ElementMapping#getDOMImplementation() */ + public DOMImplementation getDOMImplementation() { + return null; //no DOMImplementation necessary here + } + /** * Returns the fully qualified classname of an XML parser for * Batik classes that apparently need it (error messages, perhaps) @@ -83,4 +93,5 @@ public class BatikExtensionElementMapping extends ElementMapping { return new SVGElement(parent); } } + } diff --git a/src/java/org/apache/fop/fo/extensions/svg/SVGElementMapping.java b/src/java/org/apache/fop/fo/extensions/svg/SVGElementMapping.java index 6b477bf46..78fab53f8 100644 --- a/src/java/org/apache/fop/fo/extensions/svg/SVGElementMapping.java +++ b/src/java/org/apache/fop/fo/extensions/svg/SVGElementMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Copyright 1999-2004,2006 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. @@ -26,6 +26,7 @@ import org.apache.fop.fo.ElementMapping; import org.apache.batik.util.XMLResourceDescriptor; import org.apache.batik.dom.svg.SVGDOMImplementation; +import org.w3c.dom.DOMImplementation; /** * Setup the SVG element mapping. @@ -33,19 +34,28 @@ import org.apache.batik.dom.svg.SVGDOMImplementation; * that create the SVG Document. */ public class SVGElementMapping extends ElementMapping { - public static String URI = SVGDOMImplementation.SVG_NAMESPACE_URI; - private boolean batik = true; + + /** the SVG namespace */ + public static final String URI = SVGDOMImplementation.SVG_NAMESPACE_URI; + + private boolean batikAvailable = true; + /** Main constructor. */ public SVGElementMapping() { namespaceURI = URI; } + /** @see org.apache.fop.fo.ElementMapping#getDOMImplementation() */ + public DOMImplementation getDOMImplementation() { + return SVGDOMImplementation.getDOMImplementation(); + } + /** * Returns the fully qualified classname of an XML parser for * Batik classes that apparently need it (error messages, perhaps) * @return an XML parser classname */ - private final String getAParserClassName() { + private String getAParserClassName() { try { SAXParserFactory factory = SAXParserFactory.newInstance(); return factory.newSAXParser().getXMLReader().getClass().getName(); @@ -54,8 +64,9 @@ public class SVGElementMapping extends ElementMapping { } } + /** @see org.apache.fop.fo.ElementMapping#initialize() */ protected void initialize() { - if (foObjs == null && batik == true) { + if (foObjs == null && batikAvailable) { // this sets the parser that will be used // by default (SVGBrokenLinkProvider) // normally the user agent value is used @@ -69,7 +80,7 @@ public class SVGElementMapping extends ElementMapping { } catch (Throwable t) { // if the classes are not available // the DISPLAY is not checked - batik = false; + batikAvailable = false; } } } @@ -85,4 +96,5 @@ public class SVGElementMapping extends ElementMapping { return new SVGElement(parent); } } + } diff --git a/src/java/org/apache/fop/fonts/LazyFont.java b/src/java/org/apache/fop/fonts/LazyFont.java index 867158e6b..f8d22f3bb 100644 --- a/src/java/org/apache/fop/fonts/LazyFont.java +++ b/src/java/org/apache/fop/fonts/LazyFont.java @@ -165,6 +165,7 @@ public class LazyFont extends Typeface implements FontDescriptor { * @see org.apache.fop.fonts.Typeface#isMultiByte() */ public boolean isMultiByte() { + load(true); return realFont.isMultiByte(); } diff --git a/src/java/org/apache/fop/render/XMLHandlerRegistry.java b/src/java/org/apache/fop/render/XMLHandlerRegistry.java index e033c2d4e..1603f74ec 100644 --- a/src/java/org/apache/fop/render/XMLHandlerRegistry.java +++ b/src/java/org/apache/fop/render/XMLHandlerRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2005 The Apache Software Foundation. + * Copyright 2005-2006 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. @@ -117,9 +117,16 @@ public class XMLHandlerRegistry { XMLHandler handler; List lst = (List)handlers.get(ns); - if (lst == null) { + handler = getXMLHandler(renderer, lst); + if (handler == null) { lst = (List)handlers.get(XMLHandler.HANDLE_ALL); + handler = getXMLHandler(renderer, lst); } + return handler; + } + + private XMLHandler getXMLHandler(Renderer renderer, List lst) { + XMLHandler handler; if (lst != null) { for (int i = 0, c = lst.size(); i < c; i++) { //TODO Maybe add priorities later @@ -132,7 +139,6 @@ public class XMLHandlerRegistry { return null; //No handler found } - /** * Discovers XMLHandler implementations through the classpath and dynamically * registers them. diff --git a/src/java/org/apache/fop/render/xml/XMLRenderer.java b/src/java/org/apache/fop/render/xml/XMLRenderer.java index 2f7da2a08..1b9afc566 100644 --- a/src/java/org/apache/fop/render/xml/XMLRenderer.java +++ b/src/java/org/apache/fop/render/xml/XMLRenderer.java @@ -40,6 +40,7 @@ import org.xml.sax.helpers.AttributesImpl; // FOP import org.apache.fop.render.PrintRenderer; +import org.apache.fop.render.Renderer; import org.apache.fop.render.RendererContext; import org.apache.fop.render.XMLHandler; import org.apache.fop.apps.FOUserAgent; @@ -60,6 +61,7 @@ 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.Trait.Background; import org.apache.fop.area.inline.Container; import org.apache.fop.area.inline.ForeignObject; import org.apache.fop.area.inline.Image; @@ -72,6 +74,8 @@ import org.apache.fop.area.inline.Viewport; import org.apache.fop.area.inline.TextArea; import org.apache.fop.area.inline.SpaceArea; import org.apache.fop.area.inline.WordArea; +import org.apache.fop.fo.Constants; +import org.apache.fop.fonts.FontInfo; import org.apache.fop.fonts.FontSetup; import org.apache.fop.fonts.FontTriplet; @@ -99,6 +103,9 @@ public class XMLRenderer extends PrintRenderer { private boolean startedSequence = false; private RendererContext context; + /** If not null, the XMLRenderer will mimic another renderer by using its font setup. */ + protected Renderer mimic; + /** TransformerHandler that the generated XML is written to */ protected TransformerHandler handler; @@ -137,10 +144,26 @@ public class XMLRenderer extends PrintRenderer { public void setUserAgent(FOUserAgent agent) { super.setUserAgent(agent); - // - //userAgent.addExtensionHandler(); - XMLHandler handler = new XMLXMLHandler(); - userAgent.getXMLHandlerRegistry().addXMLHandler(handler); + XMLHandler xmlHandler = new XMLXMLHandler(); + userAgent.getXMLHandlerRegistry().addXMLHandler(xmlHandler); + } + + /** + * Call this method to make the XMLRenderer mimic a different renderer by using its font + * setup. This is useful when working with the intermediate format parser. + * @param renderer the renderer to mimic + */ + public void mimicRenderer(Renderer renderer) { + this.mimic = renderer; + } + + /** @see org.apache.fop.render.PrintRenderer#setupFontInfo(org.apache.fop.fonts.FontInfo) */ + public void setupFontInfo(FontInfo inFontInfo) { + if (mimic != null) { + mimic.setupFontInfo(inFontInfo); + } else { + super.setupFontInfo(inFontInfo); + } } /** @@ -280,6 +303,7 @@ public class XMLRenderer extends PrintRenderer { while (iter.hasNext()) { Map.Entry traitEntry = (Map.Entry) iter.next(); String name = Trait.getTraitName(traitEntry.getKey()); + Class clazz = Trait.getTraitClass(traitEntry.getKey()); if ("break-before".equals(name) || "break-after".equals(name)) { continue; } @@ -289,6 +313,38 @@ public class XMLRenderer extends PrintRenderer { addAttribute("font-name", triplet.getName()); addAttribute("font-style", triplet.getStyle()); addAttribute("font-weight", triplet.getWeight()); + } else if (clazz.equals(Background.class)) { + Background bkg = (Background)value; + //TODO Remove the following line (makes changes in the test checks necessary) + addAttribute(name, bkg.toString()); + if (bkg.getColor() != null) { + addAttribute("bkg-color", bkg.getColor().toString()); + } + if (bkg.getURL() != null) { + addAttribute("bkg-img", bkg.getURL()); + String repString; + int repeat = bkg.getRepeat(); + switch (repeat) { + case Constants.EN_REPEAT: + repString = "repeat"; + break; + case Constants.EN_REPEATX: + repString = "repeat-x"; + break; + case Constants.EN_REPEATY: + repString = "repeat-y"; + break; + case Constants.EN_NOREPEAT: + repString = "no-repeat"; + break; + default: + throw new IllegalStateException( + "Illegal value for repeat encountered: " + repeat); + } + addAttribute("bkg-repeat", repString); + addAttribute("bkg-horz-offset", bkg.getHoriz()); + addAttribute("bkg-vert-offset", bkg.getVertical()); + } } else { addAttribute(name, value.toString()); } @@ -404,6 +460,7 @@ public class XMLRenderer extends PrintRenderer { atts.clear(); addAreaAttributes(region); addTraitAttributes(region); + addAttribute("name", region.getRegionName()); addAttribute("ctm", region.getCTM().toString()); if (region.getRegionClass() == FO_REGION_BEFORE) { startElement("regionBefore", atts); @@ -414,8 +471,13 @@ public class XMLRenderer extends PrintRenderer { renderRegion(region); endElement("regionStart"); } else if (region.getRegionClass() == FO_REGION_BODY) { + BodyRegion body = (BodyRegion)region; + if (body.getColumnCount() != 1) { + addAttribute("columnGap", body.getColumnGap()); + addAttribute("columnCount", body.getColumnCount()); + } startElement("regionBody", atts); - renderBodyRegion((BodyRegion) region); + renderBodyRegion(body); endElement("regionBody"); } else if (region.getRegionClass() == FO_REGION_END) { startElement("regionEnd", atts); @@ -465,7 +527,9 @@ public class XMLRenderer extends PrintRenderer { atts.clear(); addAreaAttributes(mr); addTraitAttributes(mr); - addAttribute("columnGap", mr.getColumnGap()); + if (mr.getColumnCount() != 1) { + addAttribute("columnGap", mr.getColumnGap()); + } startElement("mainReference", atts); Span span = null; @@ -473,6 +537,9 @@ public class XMLRenderer extends PrintRenderer { for (int count = 0; count < spans.size(); count++) { span = (Span) spans.get(count); atts.clear(); + if (span.getColumnCount() != 1) { + addAttribute("columnCount", span.getColumnCount()); + } addAreaAttributes(span); addTraitAttributes(span); startElement("span", atts); @@ -506,14 +573,12 @@ public class XMLRenderer extends PrintRenderer { atts.clear(); addAreaAttributes(block); addTraitAttributes(block); + int positioning = block.getPositioning(); 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"); + if (bvp.getPositioning() == Block.ABSOLUTE + || bvp.getPositioning() == Block.FIXED) { abspos = true; } if (abspos) { @@ -525,9 +590,6 @@ public class XMLRenderer extends PrintRenderer { addAttribute("clipped", "true"); } } else { - if (block.getPositioning() == Block.RELATIVE) { - addAttribute("positioning", "relative"); - } if (block.getXOffset() != 0) { addAttribute("left-offset", block.getXOffset()); } @@ -535,6 +597,18 @@ public class XMLRenderer extends PrintRenderer { addAttribute("top-offset", block.getYOffset()); } } + switch (positioning) { + case Block.RELATIVE: + addAttribute("positioning", "relative"); + break; + case Block.ABSOLUTE: + addAttribute("positioning", "absolute"); + break; + case Block.FIXED: + addAttribute("positioning", "fixed"); + break; + default: //nop + } startElement("block", atts); super.renderBlock(block); endElement("block"); @@ -563,6 +637,10 @@ public class XMLRenderer extends PrintRenderer { addAreaAttributes(viewport); addTraitAttributes(viewport); addAttribute("offset", viewport.getOffset()); + addAttribute("pos", viewport.getContentPosition()); + if (viewport.getClip()) { + addAttribute("clip", Boolean.toString(viewport.getClip())); + } startElement("viewport", atts); super.renderViewport(viewport); endElement("viewport"); @@ -576,7 +654,7 @@ public class XMLRenderer extends PrintRenderer { addAreaAttributes(image); addTraitAttributes(image); addAttribute("url", image.getURL()); - addAttribute("pos", pos); + //addAttribute("pos", pos); startElement("image", atts); endElement("image"); } @@ -600,10 +678,10 @@ public class XMLRenderer extends PrintRenderer { atts.clear(); addAreaAttributes(fo); addTraitAttributes(fo); - addAttribute("pos", pos); + String ns = fo.getNameSpace(); + addAttribute("ns", ns); startElement("foreignObject", atts); Document doc = fo.getDocument(); - String ns = fo.getNameSpace(); context.setProperty(XMLXMLHandler.HANDLER, handler); renderXML(context, doc, ns); endElement("foreignObject"); @@ -709,36 +787,11 @@ public class XMLRenderer extends PrintRenderer { * @see org.apache.fop.render.AbstractRenderer#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; - case EN_NONE: - style = "none"; - break; - default: - style = "--NYI--"; - } atts.clear(); addAreaAttributes(area); addTraitAttributes(area); addAttribute("offset", area.getOffset()); - addAttribute("ruleStyle", style); + addAttribute("ruleStyle", area.getRuleStyleAsString()); addAttribute("ruleThickness", area.getRuleThickness()); startElement("leader", atts); endElement("leader"); diff --git a/src/java/org/apache/fop/traits/BorderProps.java b/src/java/org/apache/fop/traits/BorderProps.java index 8de33920f..32722cc8a 100644 --- a/src/java/org/apache/fop/traits/BorderProps.java +++ b/src/java/org/apache/fop/traits/BorderProps.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2005 The Apache Software Foundation. + * Copyright 1999-2006 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. @@ -21,8 +21,10 @@ package org.apache.fop.traits; import org.apache.fop.area.Trait; import org.apache.fop.datatypes.ColorType; import org.apache.fop.fo.Constants; +import org.apache.fop.fonts.FontTriplet; import java.io.Serializable; +import java.util.StringTokenizer; /** * Border properties. @@ -60,6 +62,17 @@ public class BorderProps implements Serializable { this.mode = mode; } + /** + * Constructs a new BorderProps instance. + * @param style border style (one of the XSL enum values for border style) + * @param width border width + * @param color border color + * @param mode border mode ((one of SEPARATE, COLLAPSE_INNER and COLLAPSE_OUTER) + */ + public BorderProps(String style, int width, ColorType color, int mode) { + this(getConstantForStyle(style), width, color, mode); + } + /** * @param bp the border properties or null * @return the effective width of the clipped part of the border @@ -88,6 +101,83 @@ public class BorderProps implements Serializable { } } + private static int getConstantForStyle(String style) { + if ("none".equalsIgnoreCase(style)) { + return Constants.EN_NONE; + } else if ("hidden".equalsIgnoreCase(style)) { + return Constants.EN_HIDDEN; + } else if ("dotted".equalsIgnoreCase(style)) { + return Constants.EN_DOTTED; + } else if ("dashed".equalsIgnoreCase(style)) { + return Constants.EN_DASHED; + } else if ("solid".equalsIgnoreCase(style)) { + return Constants.EN_SOLID; + } else if ("double".equalsIgnoreCase(style)) { + return Constants.EN_DOUBLE; + } else if ("groove".equalsIgnoreCase(style)) { + return Constants.EN_GROOVE; + } else if ("ridge".equalsIgnoreCase(style)) { + return Constants.EN_RIDGE; + } else if ("inset".equalsIgnoreCase(style)) { + return Constants.EN_INSET; + } else if ("outset".equalsIgnoreCase(style)) { + return Constants.EN_OUTSET; + } else { + throw new IllegalStateException("Illegal border style: " + style); + } + } + + /** @see java.lang.Object#hashCode() */ + public int hashCode() { + return toString().hashCode(); + } + + /** @see java.lang.Object#equals(java.lang.Object) */ + public boolean equals(Object obj) { + if (obj == null) { + return false; + } else if (obj == this) { + return true; + } else { + if (obj instanceof BorderProps) { + BorderProps other = (BorderProps)obj; + return (style == other.style) + && color.equals(other.color) + && width == other.width + && mode == other.mode; + } + } + return false; + } + + /** + * Returns a BorderProps represtation of a string of the format as written by + * BorderProps.toString(). + * @param s the string + * @return a BorderProps instance + */ + public static BorderProps valueOf(String s) { + if (s.startsWith("(") && s.endsWith(")")) { + s = s.substring(1, s.length() - 1); + StringTokenizer st = new StringTokenizer(s, ","); + String style = st.nextToken(); + String color = st.nextToken(); + int width = Integer.parseInt(st.nextToken()); + int mode = SEPARATE; + if (st.hasMoreTokens()) { + String ms = st.nextToken(); + if ("collapse-inner".equalsIgnoreCase(ms)) { + mode = COLLAPSE_INNER; + } else if ("collapse-outer".equalsIgnoreCase(ms)) { + mode = COLLAPSE_OUTER; + } + } + return new BorderProps(style, width, Trait.Color.valueOf(color), mode); + } else { + throw new IllegalArgumentException("BorderProps must be surrounded by parentheses"); + } + } + /** @see java.lang.Object#toString() */ public String toString() { StringBuffer sbuf = new StringBuffer(); @@ -108,4 +198,5 @@ public class BorderProps implements Serializable { sbuf.append(')'); return sbuf.toString(); } + } diff --git a/src/java/org/apache/fop/util/DefaultErrorListener.java b/src/java/org/apache/fop/util/DefaultErrorListener.java new file mode 100644 index 000000000..5e1a9cc37 --- /dev/null +++ b/src/java/org/apache/fop/util/DefaultErrorListener.java @@ -0,0 +1,64 @@ +/* + * Copyright 2006 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.util; + +import javax.xml.transform.ErrorListener; +import javax.xml.transform.TransformerException; + +import org.apache.commons.logging.Log; + +/** + * Standard ErrorListener implementation for in-FOP use. Some Xalan-J versions don't properly + * re-throw exceptions. + */ +public class DefaultErrorListener implements ErrorListener { + + private Log log; + + /** + * Main constructor + * @param log the log instance to send log events to + */ + public DefaultErrorListener(Log log) { + this.log = log; + } + + /** + * @see javax.xml.transform.ErrorListener#warning(javax.xml.transform.TransformerException) + */ + public void warning(TransformerException exc) { + log.warn(exc.toString()); + } + + /** + * @see javax.xml.transform.ErrorListener#error(javax.xml.transform.TransformerException) + */ + public void error(TransformerException exc) throws TransformerException { + throw exc; + } + + /** + * @see javax.xml.transform.ErrorListener#fatalError(javax.xml.transform.TransformerException) + */ + public void fatalError(TransformerException exc) + throws TransformerException { + throw exc; + } + +} \ No newline at end of file diff --git a/status.xml b/status.xml index db6b9e6fc..37d891f97 100644 --- a/status.xml +++ b/status.xml @@ -27,6 +27,12 @@ + + New feature: "Intermediate format" (IF). The IF is basically the XML dialect + written by the area tree renderer (XMLRenderer). A new parser for this format + allows reparsing a serialized and possibly modified area tree and rendering it + to the final target format. + Bugfix: Floating point numbers were not properly formatted in the PDF renderer which could lead to error messages while opening a PDF in Acrobat Reader. diff --git a/test/java/org/apache/fop/UtilityCodeTestSuite.java b/test/java/org/apache/fop/UtilityCodeTestSuite.java index 719ffa655..86b086c53 100644 --- a/test/java/org/apache/fop/UtilityCodeTestSuite.java +++ b/test/java/org/apache/fop/UtilityCodeTestSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2005 The Apache Software Foundation. + * Copyright 1999-2006 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. @@ -18,6 +18,8 @@ package org.apache.fop; +import org.apache.fop.traits.BorderPropsTestCase; +import org.apache.fop.traits.TraitColorTestCase; import org.apache.fop.util.ASCII85InputStreamTestCase; import org.apache.fop.util.ASCII85OutputStreamTestCase; import org.apache.fop.util.PDFNumberTestCase; @@ -41,6 +43,8 @@ public class UtilityCodeTestSuite { suite.addTest(new TestSuite(ASCII85OutputStreamTestCase.class)); suite.addTest(new TestSuite(ASCII85InputStreamTestCase.class)); suite.addTest(new TestSuite(PDFNumberTestCase.class)); + suite.addTest(new TestSuite(TraitColorTestCase.class)); + suite.addTest(new TestSuite(BorderPropsTestCase.class)); //$JUnit-END$ return suite; } diff --git a/test/java/org/apache/fop/intermediate/AreaTreeParserTestCase.java b/test/java/org/apache/fop/intermediate/AreaTreeParserTestCase.java new file mode 100644 index 000000000..637966580 --- /dev/null +++ b/test/java/org/apache/fop/intermediate/AreaTreeParserTestCase.java @@ -0,0 +1,241 @@ +/* + * Copyright 2006 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.intermediate; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; + +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Templates; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.sax.TransformerHandler; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.output.ByteArrayOutputStream; +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.Fop; +import org.apache.fop.apps.MimeConstants; +import org.apache.fop.area.AreaTreeModel; +import org.apache.fop.area.AreaTreeParser; +import org.apache.fop.area.RenderPagesModel; +import org.apache.fop.fonts.FontInfo; +import org.apache.fop.render.Renderer; +import org.apache.fop.render.xml.XMLRenderer; + +//XML Unit 1.0: See http://xmlunit.sourceforge.net (BSD-style License) +import org.custommonkey.xmlunit.XMLTestCase; +import org.w3c.dom.Document; + +/** + * Tests the area tree parser. + */ +public class AreaTreeParserTestCase extends XMLTestCase { + + private static SAXTransformerFactory tFactory + = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); + private static Templates stylesheet = null; + + private File mainDir = new File("test/layoutengine"); + private File testDir = new File(mainDir, "standard-testcases"); + + private String name; + private File testFile; + + private File outputDir; + private Document intermediate; + + /** @see junit.framework.TestCase#TestCase(String) */ + public AreaTreeParserTestCase(String name) { + super(name); + } + + /** + * Constructor for the test suite that is used for each test file. + * @param testFile the test file to run + */ + public AreaTreeParserTestCase(File testFile) { + super(testFile.getName()); + this.testFile = testFile; + } + + private Templates getStylesheet() throws TransformerConfigurationException { + if (stylesheet == null) { + File xsltFile = new File(mainDir, "testcase2fo.xsl"); + stylesheet = tFactory.newTemplates(new StreamSource(xsltFile)); + } + return stylesheet; + } + + /** @see junit.framework.TestCase#setUp() */ + protected void setUp() throws Exception { + super.setUp(); + String s = System.getProperty("fop.intermediate.outdir"); + if (s != null && s.length() > 0) { + outputDir = new File(s); + outputDir.mkdirs(); + } + File srcFile; + if (testFile != null) { + srcFile = testFile; + } else { + srcFile = new File(testDir, "block_font-style.xml"); + } + this.name = srcFile.getName(); + intermediate = buildAreaTreeXML(new StreamSource(srcFile), getStylesheet()); + if (outputDir != null) { + saveDOM(intermediate, new File(outputDir, name + ".at1.xml")); + } + } + + + /** + * Tests the area tree parser by running the parsed area tree again through the area tree + * renderer. The source and result documents are compared to each other. + * @throws Exception if the test fails + */ + public void testParserToAT() throws Exception { + + Source src = new DOMSource(intermediate); + Document doc = parseAndRenderToAreaTree(src); + if (outputDir != null) { + File tgtFile = new File(outputDir, name + ".at2.xml"); + saveDOM(doc, tgtFile); + } + + assertXMLEqual(intermediate, doc); + } + + private void saveDOM(Document doc, File tgtFile) throws Exception { + Transformer transformer = tFactory.newTransformer(); + Source src = new DOMSource(doc); + Result res = new StreamResult(tgtFile); + transformer.transform(src, res); + } + + /** + * Tests the area tree parser by sending the parsed area tree to the PDF Renderer. Some + * errors might be caught by the PDFRenderer. + * @throws Exception if the test fails + */ + public void testParserToPDF() throws Exception { + OutputStream out; + if (outputDir != null) { + File tgtFile = new File(outputDir, name + ".pdf"); + out = new FileOutputStream(tgtFile); + out = new BufferedOutputStream(out); + } else { + out = new ByteArrayOutputStream(); + } + try { + Source src = new DOMSource(intermediate); + parseAndRender(src, out, MimeConstants.MIME_PDF); + } finally { + IOUtils.closeQuietly(out); + } + } + + private FOUserAgent createUserAgent() { + FOUserAgent userAgent = new FOUserAgent(); + try { + userAgent.setBaseURL(testDir.toURL().toExternalForm()); + } catch (MalformedURLException e) { + //ignore, won't happen + } + return userAgent; + } + + private Document buildAreaTreeXML(Source src, Templates stylesheet) throws Exception { + Transformer transformer; + if (stylesheet != null) { + transformer = stylesheet.newTransformer(); + } else { + transformer = tFactory.newTransformer(); + } + + //Set up XMLRenderer to render to a DOM + TransformerHandler handler = tFactory.newTransformerHandler(); + DOMResult domResult = new DOMResult(); + handler.setResult(domResult); + + FOUserAgent userAgent = createUserAgent(); + + //Create an instance of the target renderer so the XMLRenderer can use its font setup + Renderer targetRenderer = userAgent.getRendererFactory().createRenderer( + userAgent, MimeConstants.MIME_PDF); + + XMLRenderer renderer = new XMLRenderer(); + renderer.mimicRenderer(targetRenderer); + renderer.setTransformerHandler(handler); + renderer.setUserAgent(userAgent); + + userAgent.setRendererOverride(renderer); + + Fop fop = new Fop(MimeConstants.MIME_FOP_AREA_TREE, userAgent); + Result res = new SAXResult(fop.getDefaultHandler()); + transformer.transform(src, res); + + return (Document)domResult.getNode(); + } + + private void parseAndRender(Source src, OutputStream out, String mime) throws Exception { + AreaTreeParser parser = new AreaTreeParser(); + + FOUserAgent userAgent = createUserAgent(); + FontInfo fontInfo = new FontInfo(); + AreaTreeModel treeModel = new RenderPagesModel(userAgent, + mime, fontInfo, out); + parser.parse(src, treeModel, userAgent); + treeModel.endDocument(); + } + + private Document parseAndRenderToAreaTree(Source src) throws Exception { + AreaTreeParser parser = new AreaTreeParser(); + + //Set up XMLRenderer to render to a DOM + TransformerHandler handler = tFactory.newTransformerHandler(); + DOMResult domResult = new DOMResult(); + handler.setResult(domResult); + XMLRenderer renderer = new XMLRenderer(); + renderer.setTransformerHandler(handler); + + FOUserAgent userAgent = createUserAgent(); + userAgent.setRendererOverride(renderer); + renderer.setUserAgent(userAgent); + + FontInfo fontInfo = new FontInfo(); + AreaTreeModel treeModel = new RenderPagesModel(userAgent, + MimeConstants.MIME_FOP_AREA_TREE, fontInfo, null); + parser.parse(src, treeModel, userAgent); + treeModel.endDocument(); + + return (Document)domResult.getNode(); + } + +} diff --git a/test/java/org/apache/fop/intermediate/IntermediateFormatTestSuite.java b/test/java/org/apache/fop/intermediate/IntermediateFormatTestSuite.java new file mode 100644 index 000000000..77ade702c --- /dev/null +++ b/test/java/org/apache/fop/intermediate/IntermediateFormatTestSuite.java @@ -0,0 +1,66 @@ +/* + * Copyright 2006 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.intermediate; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.apache.fop.layoutengine.LayoutEngineTestSuite; + +/** + * JUnit test suite for the intermediate format + */ +public class IntermediateFormatTestSuite { + + /** + * @return the test suite with all the tests (one for each XML file) + * @throws IOException in case of an I/O problem + */ + public static Test suite() throws IOException { + TestSuite suite = new TestSuite(); + + Collection files = LayoutEngineTestSuite.getTestFiles(); + + Iterator i = files.iterator(); + while (i.hasNext()) { + File f = (File)i.next(); + addTestCase(suite, f); + } + + return suite; + } + + private static void addTestCase(TestSuite suite, + final File f) { + suite.addTest(new AreaTreeParserTestCase(f) { + public void runTest() throws Exception { + org.apache.commons.logging.LogFactory.getLog( + this.getClass()).info("Starting " + f.getName()); + testParserToAT(); + testParserToPDF(); + } + }); + } + +} diff --git a/test/java/org/apache/fop/layoutengine/LayoutEngineTestSuite.java b/test/java/org/apache/fop/layoutengine/LayoutEngineTestSuite.java index f90d9f8e7..12e6834f8 100644 --- a/test/java/org/apache/fop/layoutengine/LayoutEngineTestSuite.java +++ b/test/java/org/apache/fop/layoutengine/LayoutEngineTestSuite.java @@ -1,5 +1,5 @@ /* - * Copyright 2005 The Apache Software Foundation. + * Copyright 2006 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. @@ -18,11 +18,8 @@ package org.apache.fop.layoutengine; -import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; import java.io.IOException; -import java.io.Reader; import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -129,18 +126,11 @@ public class LayoutEngineTestSuite { } /** - * @return the test suite with all the tests (one for each XML file) - * @throws IOException in case of an I/O problem + * @return a Collection of File instances containing all the test cases set up for processing. + * @throws IOException if there's a problem gathering the list of test files */ - public static Test suite() throws IOException { - TestSuite suite = new TestSuite(); - + public static Collection getTestFiles() throws IOException { File mainDir = new File("test/layoutengine"); - File backupDir = new File("build/test-results/layoutengine"); - backupDir.mkdirs(); - - final LayoutEngineTester tester = new LayoutEngineTester(backupDir); - IOFileFilter filter; String single = System.getProperty("fop.layoutengine.single"); String startsWith = System.getProperty("fop.layoutengine.starts-with"); @@ -166,6 +156,22 @@ public class LayoutEngineTestSuite { filter, TrueFileFilter.INSTANCE); files.addAll(privateFiles); } + return files; + } + + /** + * @return the test suite with all the tests (one for each XML file) + * @throws IOException in case of an I/O problem + */ + public static Test suite() throws IOException { + TestSuite suite = new TestSuite(); + + File backupDir = new File("build/test-results/layoutengine"); + backupDir.mkdirs(); + + Collection files = getTestFiles(); + + final LayoutEngineTester tester = new LayoutEngineTester(backupDir); Iterator i = files.iterator(); while (i.hasNext()) { File f = (File)i.next(); diff --git a/test/java/org/apache/fop/traits/BorderPropsTestCase.java b/test/java/org/apache/fop/traits/BorderPropsTestCase.java new file mode 100644 index 000000000..17cbcb22e --- /dev/null +++ b/test/java/org/apache/fop/traits/BorderPropsTestCase.java @@ -0,0 +1,53 @@ +/* + * Copyright 2006 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.traits; + +import org.apache.fop.area.Trait; +import org.apache.fop.fo.Constants; + +import junit.framework.TestCase; + +/** + * Tests the BorderProps class. + */ +public class BorderPropsTestCase extends TestCase { + + /** + * Test serialization and deserialization to/from String. + * @throws Exception if an error occurs + */ + public void testSerialization() throws Exception { + Trait.Color col = new Trait.Color(1.0f, 1.0f, 0.5f, 1.0f); + //Normalize: Avoid false alarms due to color conversion (rounding) + col = Trait.Color.valueOf(col.toString()); + + BorderProps b1 = new BorderProps(Constants.EN_DOUBLE, 1250, + col, BorderProps.COLLAPSE_OUTER); + String ser = b1.toString(); + BorderProps b2 = BorderProps.valueOf(ser); + assertEquals(b1, b2); + + b1 = new BorderProps(Constants.EN_INSET, 9999, + col, BorderProps.SEPARATE); + ser = b1.toString(); + b2 = BorderProps.valueOf(ser); + assertEquals(b1, b2); + } + +} diff --git a/test/java/org/apache/fop/traits/TraitColorTestCase.java b/test/java/org/apache/fop/traits/TraitColorTestCase.java new file mode 100644 index 000000000..4df691f8d --- /dev/null +++ b/test/java/org/apache/fop/traits/TraitColorTestCase.java @@ -0,0 +1,75 @@ +/* + * Copyright 2006 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.traits; + +import org.apache.fop.area.Trait; + +import junit.framework.TestCase; + +/** + * Tests the Trait.Color class. + */ +public class TraitColorTestCase extends TestCase { + + /** + * Test serialization to String. + * @throws Exception if an error occurs + */ + public void testSerialization() throws Exception { + Trait.Color col = new Trait.Color(1.0f, 1.0f, 0.5f, 1.0f); + String s = col.toString(); + assertEquals("#ffff7f", s); + + col = new Trait.Color(1.0f, 0.0f, 0.0f, 0.8f); + s = col.toString(); + assertEquals("#ff0000cc", s); + } + + /** + * Test deserialization from String. + * @throws Exception if an error occurs + */ + public void testDeserialization() throws Exception { + float tolerance = 0.5f / 255; //due to color value conversion + + Trait.Color col = Trait.Color.valueOf("#ffff7f"); + assertEquals(1.0f, col.getRed(), 0.0f); + assertEquals(1.0f, col.getGreen(), 0.0f); + assertEquals(0.5f, col.getBlue(), tolerance); + assertEquals(1.0f, col.getAlpha(), 0.0f); + + col = Trait.Color.valueOf("#ff0000cc"); + assertEquals(1.0f, col.getRed(), 0.0f); + assertEquals(0.0f, col.getGreen(), 0.0f); + assertEquals(0.0f, col.getBlue(), 0.0f); + assertEquals(0.8f, col.getAlpha(), tolerance); + } + + /** + * Test equals(). + * @throws Exception if an error occurs + */ + public void testEquals() throws Exception { + Trait.Color col1 = Trait.Color.valueOf("#ff0000cc"); + Trait.Color col2 = Trait.Color.valueOf("#ff0000cc"); + assertTrue(col1 != col2); + assertEquals(col1, col2); + } + +} diff --git a/test/layoutengine/standard-testcases/external-graphic_border_padding.xml b/test/layoutengine/standard-testcases/external-graphic_border_padding.xml index a05c367b9..a91722b74 100644 --- a/test/layoutengine/standard-testcases/external-graphic_border_padding.xml +++ b/test/layoutengine/standard-testcases/external-graphic_border_padding.xml @@ -60,7 +60,7 @@ - + @@ -76,6 +76,6 @@ - + diff --git a/test/layoutengine/standard-testcases/external-graphic_display-align.xml b/test/layoutengine/standard-testcases/external-graphic_display-align.xml index 76d74b791..4a8d7eb9c 100644 --- a/test/layoutengine/standard-testcases/external-graphic_display-align.xml +++ b/test/layoutengine/standard-testcases/external-graphic_display-align.xml @@ -60,26 +60,26 @@ - + - + - + - + - + diff --git a/test/layoutengine/standard-testcases/external-graphic_svg.xml b/test/layoutengine/standard-testcases/external-graphic_svg.xml index 5f13dc313..6c60296b1 100644 --- a/test/layoutengine/standard-testcases/external-graphic_svg.xml +++ b/test/layoutengine/standard-testcases/external-graphic_svg.xml @@ -56,19 +56,19 @@ - + - + - + @@ -76,6 +76,6 @@ - + diff --git a/test/layoutengine/standard-testcases/instream-foreign-object_border_padding.xml b/test/layoutengine/standard-testcases/instream-foreign-object_border_padding.xml index 16d89eb22..aadcb4bf9 100644 --- a/test/layoutengine/standard-testcases/instream-foreign-object_border_padding.xml +++ b/test/layoutengine/standard-testcases/instream-foreign-object_border_padding.xml @@ -74,7 +74,7 @@ - + @@ -90,6 +90,6 @@ - + diff --git a/test/layoutengine/standard-testcases/instream-foreign-object_display-align.xml b/test/layoutengine/standard-testcases/instream-foreign-object_display-align.xml index a113ccc54..758c0f676 100644 --- a/test/layoutengine/standard-testcases/instream-foreign-object_display-align.xml +++ b/test/layoutengine/standard-testcases/instream-foreign-object_display-align.xml @@ -95,26 +95,26 @@ - + - + - + - + - + -- 2.39.5