From 973d676bae6d1253f8675b216704752f5897e2fa Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Wed, 7 Dec 2005 15:15:38 +0000 Subject: [PATCH] Alternative set of rules for text indent calculation (start-indent and end-indent) which tries to mimic many commercial FO implementation that have chosen to break the specification in this aspect. I think I have found the behaviour for most cases. But I'm operating in reverse-engineering mode here and not all FO implementations behave in the same way! This is an optional feature that has to be explicitely enabled through the user agent. Otherwise, FOP will behave like before. In the FO tree tests a processing instruction is used to enable the feature/bug ;-) in the user agent so I can test both cases. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@354763 13f79535-47bb-0310-9956-ffa450edef68 --- .../content/xdocs/trunk/configuration.xml | 22 +++ .../content/xdocs/trunk/embedding.xml | 9 + src/java/org/apache/fop/apps/FOUserAgent.java | 36 +++- .../fo/properties/IndentPropertyMaker.java | 88 ++++++++++ test/fotree/testcases/indent-inheritance1.fo | 53 ++++++ test/fotree/testcases/indent-inheritance2.fo | 133 ++++++++++++++ test/fotree/testcases/indent-inheritance2a.fo | 163 ++++++++++++++++++ .../apache/fop/fotreetest/FOTreeTester.java | 45 ++++- 8 files changed, 543 insertions(+), 6 deletions(-) create mode 100644 test/fotree/testcases/indent-inheritance1.fo create mode 100644 test/fotree/testcases/indent-inheritance2.fo create mode 100644 test/fotree/testcases/indent-inheritance2a.fo diff --git a/src/documentation/content/xdocs/trunk/configuration.xml b/src/documentation/content/xdocs/trunk/configuration.xml index c5e1b8973..16465f54b 100644 --- a/src/documentation/content/xdocs/trunk/configuration.xml +++ b/src/documentation/content/xdocs/trunk/configuration.xml @@ -75,6 +75,28 @@ Integer, dpi Resolution in dpi (dots per inch) which is used internally. + + strict-validation + Boolean (true, false) + + Setting this option to 'false' causes FOP to be more forgiving about XSL-FO validity, + for example, you're allowed to specify a border on a region-body which is supported + by some FO implementations but is non-standard. Note that such a border would + currently have no effect in Apache FOP. + + + break-indent-inheritance + Boolean (true, false) + + Setting this option to 'true' causes FOP to use an alternative rule set to determine + text indents specified through margins, start-indent and end-indent. Many commercial + FO implementations have chosen to break the XSL specification in this aspect. This + option tries to mimic their behaviour. Please note that Apache FOP may still not + behave exactly like those implementations either because FOP has not fully matched + the desired behaviour and because the behaviour among the commercial implementations + varies. The default for this option (i.e. false) is to behave exactly like the + specification describes. + default-page-settings n/a diff --git a/src/documentation/content/xdocs/trunk/embedding.xml b/src/documentation/content/xdocs/trunk/embedding.xml index 2e77c11ab..f54af136a 100644 --- a/src/documentation/content/xdocs/trunk/embedding.xml +++ b/src/documentation/content/xdocs/trunk/embedding.xml @@ -359,6 +359,15 @@ Fop fop = new Fop(MimeConstants.MIME_POSTSCRIPT, userAgent);]]>

userAgent.setPDFEncryptionParams(new PDFEncryptionParams(null, "owner", false, false, true, true)); +
  • +

    + Enable an alternative set of rules for text indents that tries to mimic the behaviour of many commercial + FO implementations that chose to break the specification in this aspect. The default of this option is + 'false' which causes Apache FOP to behave exactly as describes in the specification. To enable the + alternative behaviour, call: +

    + userAgent.setBreakIndentInheritanceOnReferenceAreaBoundary(true); +
  • You should not reuse an FOUserAgent instance between FOP rendering runs although you can. Especially diff --git a/src/java/org/apache/fop/apps/FOUserAgent.java b/src/java/org/apache/fop/apps/FOUserAgent.java index 0af0463c1..0bd803ec8 100644 --- a/src/java/org/apache/fop/apps/FOUserAgent.java +++ b/src/java/org/apache/fop/apps/FOUserAgent.java @@ -111,6 +111,9 @@ public class FOUserAgent { * ability for FOP to halt on all content model violations if desired. */ private boolean strictValidation = true; + + /** @see #setBreakIndentInheritanceOnReferenceAreaBoundary(boolean) */ + private boolean breakIndentInheritanceOnReferenceAreaBoundary = false; /* Additional fo.ElementMapping subclasses set by user */ private List additionalElementMappings = null; @@ -207,6 +210,31 @@ public class FOUserAgent { return strictValidation; } + /** + * @return true if the indent inheritance should be broken when crossing reference area + * boundaries (for more info, see the javadoc for the relative member variable) + */ + public boolean isBreakIndentInheritanceOnReferenceAreaBoundary() { + return breakIndentInheritanceOnReferenceAreaBoundary; + } + + /** + * Controls whether to enable a feature that breaks indent inheritance when crossing + * reference area boundaries. + *

    + * This flag controls whether FOP will enable special code that breaks property + * inheritance for start-indent and end-indent when the evaluation of the inherited + * value would cross a reference area. This is described under + * http://wiki.apache.org/xmlgraphics-fop/IndentInheritance as is intended to + * improve interoperability with commercial FO implementations and to produce + * results that are more in line with the expectation of unexperienced FO users. + * Note: Enabling this features violates the XSL specification! + * @param value true to enable the feature + */ + public void setBreakIndentInheritanceOnReferenceAreaBoundary(boolean value) { + this.breakIndentInheritanceOnReferenceAreaBoundary = value; + } + /** * Sets an explicit LayoutManagerMaker instance which overrides the one * defined by the AreaTreeHandler. @@ -394,6 +422,13 @@ public class FOUserAgent { log.info("resolution set to: " + resolution + "dpi (px2mm=" + getPixelUnitToMillimeter() + ")"); } + if (userConfig.getChild("strict-validation", false) != null) { + this.strictValidation = userConfig.getChild("strict-validation").getValueAsBoolean(); + } + if (userConfig.getChild("break-indent-inheritance", false) != null) { + this.breakIndentInheritanceOnReferenceAreaBoundary + = userConfig.getChild("break-indent-inheritance").getValueAsBoolean(); + } Configuration pageConfig = userConfig.getChild("default-page-settings"); if (pageConfig.getAttribute("height", null) != null) { setPageHeight(pageConfig.getAttribute("height")); @@ -616,6 +651,5 @@ public class FOUserAgent { return this.xmlHandlers; } - } diff --git a/src/java/org/apache/fop/fo/properties/IndentPropertyMaker.java b/src/java/org/apache/fop/fo/properties/IndentPropertyMaker.java index 5a338d3ee..4e9a990a0 100644 --- a/src/java/org/apache/fop/fo/properties/IndentPropertyMaker.java +++ b/src/java/org/apache/fop/fo/properties/IndentPropertyMaker.java @@ -19,6 +19,8 @@ package org.apache.fop.fo.properties; import org.apache.fop.datatypes.Numeric; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.expr.NumericOp; import org.apache.fop.fo.expr.PropertyException; @@ -68,6 +70,19 @@ public class IndentPropertyMaker extends CorrespondingPropertyMaker { * @see CorrespondingPropertyMaker#compute(PropertyList) */ public Property compute(PropertyList propertyList) throws PropertyException { + if (propertyList.getFObj().getUserAgent() + .isBreakIndentInheritanceOnReferenceAreaBoundary()) { + return computeAlternativeRuleset(propertyList); + } else { + return computeConforming(propertyList); + } + } + + /** + * Calculate the corresponding value for start-indent and end-indent. + * @see CorrespondingPropertyMaker#compute(PropertyList) + */ + public Property computeConforming(PropertyList propertyList) throws PropertyException { PropertyList pList = getWMPropertyList(propertyList); if (pList == null) { return null; @@ -107,6 +122,79 @@ public class IndentPropertyMaker extends CorrespondingPropertyMaker { return (Property) v; } + private boolean isInherited(PropertyList pList) { + if (pList.getFObj().getUserAgent().isBreakIndentInheritanceOnReferenceAreaBoundary()) { + FONode nd = pList.getFObj().getParent(); + return !((nd instanceof FObj) && ((FObj)nd).generatesReferenceAreas()); + } else { + return true; + } + } + + /** + * Calculate the corresponding value for start-indent and end-indent. + * This method calculates indent following an alternative rule set that + * tries to mimic many commercial solutions that chose to violate the + * XSL specification. + * @see CorrespondingPropertyMaker#compute(PropertyList) + */ + public Property computeAlternativeRuleset(PropertyList propertyList) throws PropertyException { + PropertyList pList = getWMPropertyList(propertyList); + if (pList == null) { + return null; + } + + // Calculate the values as described in 5.3.2. + + Numeric padding = getCorresponding(paddingCorresponding, propertyList).getNumeric(); + Numeric border = getCorresponding(borderWidthCorresponding, propertyList).getNumeric(); + + int marginProp = pList.getWritingMode(lr_tb, rl_tb, tb_rl); + + //Determine whether the nearest anscestor indent was specified through + //start-indent|end-indent or through a margin property. + boolean marginNearest = false; + PropertyList pl = propertyList.getParentPropertyList(); + while (pl != null) { + if (pl.getExplicit(baseMaker.propId) != null) { + break; + } else if (pl.getExplicitOrShorthand(marginProp) != null) { + marginNearest = true; + break; + } + pl = pl.getParentPropertyList(); + } + + Numeric margin; + // Calculate the absolute margin. + if (propertyList.getExplicitOrShorthand(marginProp) == null) { + Property indent = propertyList.getExplicit(baseMaker.propId); + if (indent == null) { + //Neither start-indent nor margin is specified, use inherited + if (isInherited(propertyList) || !marginNearest) { + return null; + } else { + return new FixedLength(0); + } + } else { + return indent; + } + } else { + margin = propertyList.get(marginProp).getNumeric(); + } + + Numeric v = new FixedLength(0); + if (isInherited(propertyList)) { + // The inherited_value_of([start|end]-indent) + v = NumericOp.addition(v, propertyList.getInherited(baseMaker.propId).getNumeric()); + } + // The corresponding absolute margin-[right|left}. + v = NumericOp.addition(v, margin); + v = NumericOp.addition(v, padding); + v = NumericOp.addition(v, border); + return (Property) v; + } + private Property getCorresponding(int[] corresponding, PropertyList propertyList) throws PropertyException { PropertyList pList = getWMPropertyList(propertyList); diff --git a/test/fotree/testcases/indent-inheritance1.fo b/test/fotree/testcases/indent-inheritance1.fo new file mode 100644 index 000000000..e797c9adf --- /dev/null +++ b/test/fotree/testcases/indent-inheritance1.fo @@ -0,0 +1,53 @@ + + + + + + + + + + + + + Hello World! + + + + Hello World! + + + nested + + + + + + + nested in b-c + + + nested2 in b-c + + + + + + + + + diff --git a/test/fotree/testcases/indent-inheritance2.fo b/test/fotree/testcases/indent-inheritance2.fo new file mode 100644 index 000000000..1b07cd110 --- /dev/null +++ b/test/fotree/testcases/indent-inheritance2.fo @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + start-indent specified on outer block + unindented + + + + fo:block + + + fo:block|fo:block + + + + + + + fo:block|fo:block-container|fo:block + + + + + + start-indent specified on outer block (reset to 0pt on inner block and the block-container) + unindented + fo:block + + + fo:block|fo:block + + + + + + + fo:block|fo:block-container|fo:block + + + + + + start-indent specified on outer block (reset to 0pt on nested block in the block-container) + unindented + fo:block + + + + + + fo:block|fo:block-container|fo:block + + + + + + margin-left specified on outer block + unindented + fo:block + + + fo:block|fo:block + + + + + + + fo:block|fo:block-container|fo:block + + + + + + margin-left specified on outer block (set to 0pt on inner block and the block-container) + unindented + fo:block + + + fo:block|fo:block + + + + + + + fo:block|fo:block-container|fo:block + + + + + + margin-left specified on outer block (reset to 0pt on nested block in the block-container) + unindented + fo:block + + + + + + fo:block|fo:block-container|fo:block + + + + + + + + diff --git a/test/fotree/testcases/indent-inheritance2a.fo b/test/fotree/testcases/indent-inheritance2a.fo new file mode 100644 index 000000000..924354288 --- /dev/null +++ b/test/fotree/testcases/indent-inheritance2a.fo @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + start-indent specified on outer block + unindented + + + + fo:block + + + fo:block|fo:block + + + + + + + fo:block|fo:block-container|fo:block + + + + + + start-indent specified on outer block (reset to 0pt on inner block and the block-container) + unindented + fo:block + + + fo:block|fo:block + + + + + + + fo:block|fo:block-container|fo:block + + + + + + start-indent specified on outer block (reset to 0pt on nested block in the block-container) + unindented + fo:block + + + + + + fo:block|fo:block-container|fo:block + + + + + + margin-left specified on outer block + unindented + fo:block + + + fo:block|fo:block + + + + fo:block|fo:block (further indented using margin-left) + + + + + + + fo:block|fo:block-container|fo:block + + + + + + margin-left specified on outer block (set to 0pt on inner block and the block-container) + unindented + fo:block + + + fo:block|fo:block + + + + + + + fo:block|fo:block-container|fo:block + + + + + + margin-left specified on outer block (reset to 0pt on nested block in the block-container) + unindented + fo:block + + + + + + fo:block|fo:block-container|fo:block + + + + + + margin-left specified on outer block and start-indent on nested elements + unindented + fo:block + + + fo:block|fo:block + + + + + + + fo:block|fo:block-container|fo:block + + + + fo:block|fo:block-container|fo:block + + + + fo:block|fo:block-container|fo:block + + + + + + + + diff --git a/test/java/org/apache/fop/fotreetest/FOTreeTester.java b/test/java/org/apache/fop/fotreetest/FOTreeTester.java index 0a79aea61..773593f09 100644 --- a/test/java/org/apache/fop/fotreetest/FOTreeTester.java +++ b/test/java/org/apache/fop/fotreetest/FOTreeTester.java @@ -21,6 +21,8 @@ package org.apache.fop.fotreetest; import java.io.File; import java.util.List; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.sax.SAXResult; @@ -32,6 +34,10 @@ import org.apache.fop.apps.Fop; import org.apache.fop.apps.MimeConstants; import org.apache.fop.fotreetest.ext.TestElementMapping; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLFilterImpl; /** * Test driver class for FO tree tests. @@ -50,19 +56,28 @@ public class FOTreeTester { ResultCollector collector = ResultCollector.getInstance(); collector.reset(); - //Setup identity Transformer - Transformer transformer = tfactory.newTransformer(); - Source src = new StreamSource(testFile); + SAXParserFactory spf = SAXParserFactory.newInstance(); + spf.setNamespaceAware(true); + spf.setValidating(false); + SAXParser parser = spf.newSAXParser(); + XMLReader reader = parser.getXMLReader(); //Setup FOP for area tree rendering FOUserAgent ua = new FOUserAgent(); ua.setBaseURL(testFile.getParentFile().toURL().toString()); ua.setFOEventHandlerOverride(new DummyFOEventHandler(ua)); ua.addElementMapping(new TestElementMapping()); + + //Used to set values in the user agent through processing instructions + reader = new PIListener(reader, ua); + Fop fop = new Fop(MimeConstants.MIME_FOP_AREA_TREE, ua); - SAXResult fores = new SAXResult(fop.getDefaultHandler()); - transformer.transform(src, fores); + reader.setContentHandler(fop.getDefaultHandler()); + reader.setDTDHandler(fop.getDefaultHandler()); + reader.setErrorHandler(fop.getDefaultHandler()); + reader.setEntityResolver(fop.getDefaultHandler()); + reader.parse(testFile.toURL().toExternalForm()); List results = collector.getResults(); if (results.size() > 0) { @@ -73,4 +88,24 @@ public class FOTreeTester { } } + private class PIListener extends XMLFilterImpl { + + private FOUserAgent userAgent; + + public PIListener(XMLReader parent, FOUserAgent userAgent) { + super(parent); + this.userAgent = userAgent; + } + + /** @see org.xml.sax.helpers.XMLFilterImpl */ + public void processingInstruction(String target, String data) throws SAXException { + if ("fop-useragent-break-indent-inheritance".equals(target)) { + userAgent.setBreakIndentInheritanceOnReferenceAreaBoundary( + Boolean.valueOf(data).booleanValue()); + } + super.processingInstruction(target, data); + } + + } + } -- 2.39.5