From c52db65ef80d7cd0e163f711117ef9a81f2fba34 Mon Sep 17 00:00:00 2001 From: Mehdi Houshmand Date: Fri, 17 Aug 2012 13:14:25 +0000 Subject: Bugzilla#53751: Slightly restructured the way the FontCache creates a cache-file git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1374238 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/fonts/FontManagerTestCase.java | 77 ++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 test/java/org/apache/fop/fonts/FontManagerTestCase.java (limited to 'test/java/org/apache/fop') diff --git a/test/java/org/apache/fop/fonts/FontManagerTestCase.java b/test/java/org/apache/fop/fonts/FontManagerTestCase.java new file mode 100644 index 000000000..9012f843b --- /dev/null +++ b/test/java/org/apache/fop/fonts/FontManagerTestCase.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +package org.apache.fop.fonts; + +import java.net.URI; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.apps.io.InternalResourceResolver; + +public class FontManagerTestCase { + + private FontManager sut; + private FontCacheManager fontCacheManager; + private FontDetector fontDetector; + private InternalResourceResolver resolver; + + @Before + public void setUp() { + resolver = mock(InternalResourceResolver.class); + fontCacheManager = mock(FontCacheManager.class); + fontDetector = mock(FontDetector.class); + + sut = new FontManager(resolver, fontDetector, fontCacheManager); + } + + @Test + public void testSetCacheFile() { + URI testURI = URI.create("test/uri"); + sut.setCacheFile(testURI); + + InOrder inOrder = inOrder(resolver, fontCacheManager); + inOrder.verify(resolver).resolveFromBase(testURI); + inOrder.verify(fontCacheManager).setCacheFile(any(URI.class)); + } + + @Test + public void testGetFontCache() { + sut.getFontCache(); + verify(fontCacheManager).load(); + } + + @Test + public void testSaveCache() throws FOPException { + sut.saveCache(); + verify(fontCacheManager).save(); + } + + @Test + public void testDeleteCache() throws FOPException { + sut.deleteCache(); + verify(fontCacheManager).delete(); + } +} -- cgit v1.2.3 From d6994df393ced4dd88e5edd5fdfdca38df0d875a Mon Sep 17 00:00:00 2001 From: Mehdi Houshmand Date: Thu, 23 Aug 2012 14:19:29 +0000 Subject: Bugfix#53766: Remove StandardEncoding as the encoding type from fonts used in the PDF renderer. Submitted by Robert Meyer. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1376500 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/pdf/PDFEncoding.java | 99 +++++++++++++++++----- src/java/org/apache/fop/pdf/PDFFactory.java | 37 +------- src/java/org/apache/fop/pdf/PDFFont.java | 2 +- status.xml | 3 + .../org/apache/fop/pdf/PDFEncodingTestCase.java | 76 +++++++++++++++++ 5 files changed, 159 insertions(+), 58 deletions(-) create mode 100644 test/java/org/apache/fop/pdf/PDFEncodingTestCase.java (limited to 'test/java/org/apache/fop') diff --git a/src/java/org/apache/fop/pdf/PDFEncoding.java b/src/java/org/apache/fop/pdf/PDFEncoding.java index bf2799c70..25e791e0d 100644 --- a/src/java/org/apache/fop/pdf/PDFEncoding.java +++ b/src/java/org/apache/fop/pdf/PDFEncoding.java @@ -23,6 +23,9 @@ package org.apache.fop.pdf; import java.util.Collections; import java.util.Set; +import org.apache.fop.fonts.CodePointMapping; +import org.apache.fop.fonts.SingleByteEncoding; + /** * Class representing an /Encoding object. * @@ -73,6 +76,38 @@ public class PDFEncoding extends PDFDictionary { } } + /** + * Creates a PDFEncoding instance from a CodePointMapping instance. + * @param encoding the code point mapping (encoding) + * @param fontName ... + * @return the PDF Encoding dictionary (or a String with the predefined encoding) + */ + static Object createPDFEncoding(SingleByteEncoding encoding, String fontName) { + //If encoding type is null, return null which causes /Encoding to be omitted. + if (encoding == null) { + return null; + } + String encodingName = null; + SingleByteEncoding baseEncoding; + if (fontName.indexOf("Symbol") >= 0) { + baseEncoding = CodePointMapping.getMapping(CodePointMapping.SYMBOL_ENCODING); + encodingName = baseEncoding.getName(); + } else { + baseEncoding = CodePointMapping.getMapping(CodePointMapping.STANDARD_ENCODING); + } + PDFEncoding pdfEncoding = new PDFEncoding(encodingName); + PDFEncoding.DifferencesBuilder builder = pdfEncoding.createDifferencesBuilder(); + PDFArray differences = builder.buildDifferencesArray(baseEncoding, encoding); + // TODO This method should not be returning an Object with two different outcomes + // resulting in subsequent `if (X instanceof Y)` statements. + if (differences.length() > 0) { + pdfEncoding.setDifferences(differences); + return pdfEncoding; + } else { + return encodingName; + } + } + /** * Indicates whether a given encoding is one of the predefined encodings. * @param name the encoding name (ex. "StandardEncoding") @@ -82,6 +117,15 @@ public class PDFEncoding extends PDFDictionary { return PREDEFINED_ENCODINGS.contains(name); } + /** + * Indicates whether the given encoding type is that of standard encoding + * @param name The encoding name + * @return Returns true if it is of type standard encoding + */ + static boolean hasStandardEncoding(String encodingName) { + return encodingName.equals(STANDARD_ENCODING); + } + /** * Creates and returns a new DifferencesBuilder instance for constructing the Differences * array. @@ -104,18 +148,44 @@ public class PDFEncoding extends PDFDictionary { */ public class DifferencesBuilder { - private PDFArray differences = new PDFArray(); private int currentCode = -1; + /** + * Creates an array containing the differences between two single-byte. + * font encodings. + * @param encoding_A The first single-byte encoding + * @param encoding_B The second single-byte encoding + * @return The PDFArray of differences between encodings + */ + public PDFArray buildDifferencesArray(SingleByteEncoding encodingA, + SingleByteEncoding encodingB) { + PDFArray differences = new PDFArray(); + int start = -1; + String[] baseNames = encodingA.getCharNameMap(); + String[] charNameMap = encodingB.getCharNameMap(); + for (int i = 0, ci = charNameMap.length; i < ci; i++) { + String basec = baseNames[i]; + String c = charNameMap[i]; + if (!basec.equals(c)) { + if (start != i) { + addDifference(i, differences); + start = i; + } + addName(c, differences); + start++; + } + } + return differences; + } + /** * Start a new difference. * @param code the starting code index inside the encoding * @return this builder instance */ - public DifferencesBuilder addDifference(int code) { + private void addDifference(int code, PDFArray differences) { this.currentCode = code; - this.differences.add(new Integer(code)); - return this; + differences.add(Integer.valueOf(code)); } /** @@ -123,28 +193,11 @@ public class PDFEncoding extends PDFDictionary { * @param name the character name * @return this builder instance */ - public DifferencesBuilder addName(String name) { + private void addName(String name, PDFArray differences) { if (this.currentCode < 0) { throw new IllegalStateException("addDifference(int) must be called first"); } - this.differences.add(new PDFName(name)); - return this; - } - - /** - * Indicates whether any differences have been recorded. - * @return true if there are differences. - */ - public boolean hasDifferences() { - return (this.differences.length() > 0); - } - - /** - * Creates and returns the PDFArray representing the Differences entry. - * @return the Differences entry - */ - public PDFArray toPDFArray() { - return this.differences; + differences.add(new PDFName(name)); } } diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java index 633ce9dd1..585f6d86d 100644 --- a/src/java/org/apache/fop/pdf/PDFFactory.java +++ b/src/java/org/apache/fop/pdf/PDFFactory.java @@ -1469,42 +1469,11 @@ public class PDFFactory { /** * Creates a PDFEncoding instance from a CodePointMapping instance. * @param encoding the code point mapping (encoding) - * @param fontNameHint ... + * @param fontName ... * @return the PDF Encoding dictionary (or a String with the predefined encoding) */ - public Object createPDFEncoding(SingleByteEncoding encoding, String fontNameHint) { - SingleByteEncoding baseEncoding; - if (fontNameHint.indexOf("Symbol") >= 0) { - baseEncoding = CodePointMapping.getMapping( - CodePointMapping.SYMBOL_ENCODING); - } else { - baseEncoding = CodePointMapping.getMapping( - CodePointMapping.STANDARD_ENCODING); - } - PDFEncoding pdfEncoding = new PDFEncoding(baseEncoding.getName()); - PDFEncoding.DifferencesBuilder builder - = pdfEncoding.createDifferencesBuilder(); - int start = -1; - String[] baseNames = baseEncoding.getCharNameMap(); - String[] charNameMap = encoding.getCharNameMap(); - for (int i = 0, ci = charNameMap.length; i < ci; i++) { - String basec = baseNames[i]; - String c = charNameMap[i]; - if (!basec.equals(c)) { - if (start != i) { - builder.addDifference(i); - start = i; - } - builder.addName(c); - start++; - } - } - if (builder.hasDifferences()) { - pdfEncoding.setDifferences(builder.toPDFArray()); - return pdfEncoding; - } else { - return baseEncoding.getName(); - } + public Object createPDFEncoding(SingleByteEncoding encoding, String fontName) { + return PDFEncoding.createPDFEncoding(encoding, fontName); } /** diff --git a/src/java/org/apache/fop/pdf/PDFFont.java b/src/java/org/apache/fop/pdf/PDFFont.java index 191fd223e..9b9d1c129 100644 --- a/src/java/org/apache/fop/pdf/PDFFont.java +++ b/src/java/org/apache/fop/pdf/PDFFont.java @@ -70,7 +70,7 @@ public class PDFFont extends PDFDictionary { * @param encoding the encoding */ public void setEncoding(String encoding) { - if (encoding != null) { + if (encoding != null && !PDFEncoding.hasStandardEncoding(encoding)) { put("Encoding", new PDFName(encoding)); } } diff --git a/status.xml b/status.xml index bfb14e0de..5927dd82c 100644 --- a/status.xml +++ b/status.xml @@ -62,6 +62,9 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> + + Remove StandardEncoding as the encoding type from fonts used in the PDF renderer + Cached AFP charactersets have more unique keys preventing the two characters with but different binaries conflicting. diff --git a/test/java/org/apache/fop/pdf/PDFEncodingTestCase.java b/test/java/org/apache/fop/pdf/PDFEncodingTestCase.java new file mode 100644 index 000000000..34e48f1a6 --- /dev/null +++ b/test/java/org/apache/fop/pdf/PDFEncodingTestCase.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +package org.apache.fop.pdf; + +import org.junit.Test; + +import org.apache.fop.fonts.CodePointMapping; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +public class PDFEncodingTestCase { + + /** + * Tests the createPDFEncoding method to ensure a null encoding type + * is handled correctly. + */ + @Test + public void testCreatePDFEncodingForNull() { + Object encoding = PDFEncoding.createPDFEncoding(null, "Test"); + assertEquals(encoding, null); + } + + /** + * Tests that when a PDFEncoding object is created, if the encoding type is + * that of StandardEncoding, the baseEncoding tag is omitted. + */ + @Test + public void testStandardEncodingDiffs() { + Object encoding = PDFEncoding.createPDFEncoding(CodePointMapping.getMapping( + CodePointMapping.SYMBOL_ENCODING), "Test"); + if (encoding instanceof PDFEncoding) { + PDFEncoding pdfEncoding = (PDFEncoding) encoding; + assertFalse(pdfEncoding.entries.containsKey("BaseEncoding")); + } + } + + /** + * Tests that when the StandardEncoding type is provided and there are no + * differences, the returned encoding object is null. + */ + @Test + public void testStandardEncodingNoDiff() { + Object encoding = PDFEncoding.createPDFEncoding(CodePointMapping.getMapping( + CodePointMapping.STANDARD_ENCODING), "Test"); + assertEquals(encoding, null); + } + + /** + * Tests that when the SymbolEncoding type is provided and there are no + * differences, the returned encoding string is that of SymbolEncoding. + */ + @Test + public void testCreatePDFEncodingSymbol() { + Object encoding = PDFEncoding.createPDFEncoding(CodePointMapping.getMapping( + CodePointMapping.SYMBOL_ENCODING), "Symbol"); + assert (encoding instanceof String); + String pdfEncoding = (String) encoding; + assertEquals(pdfEncoding, "SymbolEncoding"); + } +} -- cgit v1.2.3 From 69beec0c8ff0bb191c459184bc61b6a243ba693b Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Fri, 24 Aug 2012 14:10:39 +0000 Subject: Bugzilla 53778: When PDF accessibility is enabled, the contents for the different regions must appear in the proper order in the structure tree. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1376923 13f79535-47bb-0310-9956-ffa450edef68 --- .../fo/FO2StructureTreeConverter.java | 41 +- .../fop/accessibility/fo/FOEventRecorder.java | 508 --------------------- .../fo/StructureTreeEventTrigger.java | 37 +- .../apache/fop/fo/DelegatingFOEventHandler.java | 20 +- src/java/org/apache/fop/fo/FOEventHandler.java | 30 +- src/java/org/apache/fop/fo/FOPropertyMapping.java | 3 +- src/java/org/apache/fop/fo/pagination/Flow.java | 5 +- .../apache/fop/fo/pagination/LayoutMasterSet.java | 19 + .../apache/fop/fo/pagination/StaticContent.java | 2 +- src/java/org/apache/fop/pdf/PDFDocument.java | 28 +- src/java/org/apache/fop/pdf/PDFStructElem.java | 6 +- src/java/org/apache/fop/pdf/PDFStructTreeRoot.java | 3 +- .../org/apache/fop/pdf/StandardStructureTypes.java | 5 + .../apache/fop/pdf/StructureHierarchyMember.java | 37 ++ .../org/apache/fop/render/pdf/FOToPDFRoleMap.java | 162 ------- .../apache/fop/render/pdf/PDFEventProducer.java | 3 +- .../fop/render/pdf/PDFStructureTreeBuilder.java | 331 +++++++++++--- .../fop/render/pdf/PageSequenceStructElem.java | 79 ++++ .../org/apache/fop/render/pdf/TableStructElem.java | 48 ++ status.xml | 4 + .../fo/FO2StructureTreeConverterTestCase.java | 63 +-- .../fop/accessibility/fo/fo2StructureTree.xsl | 29 +- .../fo/wrapCompleteDocumentInTableFooter.xsl | 66 --- .../fop/fo/pagination/LayoutMasterSetTestCase.java | 95 ++++ .../org/apache/fop/fo/pagination/side-regions.fo | 181 ++++++++ .../apache/fop/pdf/TableHeaderScopeTestCase.java | 3 +- test/pdf/accessibility/pdf/role.pdf | Bin 18600 -> 18600 bytes test/pdf/accessibility/pdf/role_non-standard.pdf | Bin 18291 -> 18291 bytes test/pdf/accessibility/pdf/side-regions.pdf | Bin 0 -> 26963 bytes test/pdf/accessibility/side-regions.fo | 181 ++++++++ 30 files changed, 1044 insertions(+), 945 deletions(-) delete mode 100644 src/java/org/apache/fop/accessibility/fo/FOEventRecorder.java create mode 100644 src/java/org/apache/fop/pdf/StructureHierarchyMember.java delete mode 100644 src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java create mode 100644 src/java/org/apache/fop/render/pdf/PageSequenceStructElem.java create mode 100644 src/java/org/apache/fop/render/pdf/TableStructElem.java delete mode 100644 test/java/org/apache/fop/accessibility/fo/wrapCompleteDocumentInTableFooter.xsl create mode 100644 test/java/org/apache/fop/fo/pagination/LayoutMasterSetTestCase.java create mode 100644 test/java/org/apache/fop/fo/pagination/side-regions.fo create mode 100644 test/pdf/accessibility/pdf/side-regions.pdf create mode 100644 test/pdf/accessibility/side-regions.fo (limited to 'test/java/org/apache/fop') diff --git a/src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java b/src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java index 27469d6e0..aaf112cb5 100644 --- a/src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java +++ b/src/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverter.java @@ -72,8 +72,6 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { private final Stack converters = new Stack(); - private final Stack tableFooterRecorders = new Stack(); - private final FOEventHandler structureTreeEventTrigger; /** The descendants of some elements like fo:leader must be ignored. */ @@ -165,6 +163,20 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { super.endPageNumberCitationLast(pageLast); } + @Override + public void startStatic(StaticContent staticContent) { + handleStartArtifact(staticContent); + converter.startStatic(staticContent); + super.startStatic(staticContent); + } + + @Override + public void endStatic(StaticContent staticContent) { + converter.endStatic(staticContent); + handleEndArtifact(staticContent); + super.endStatic(staticContent); + } + @Override public void startFlow(Flow fl) { converter.startFlow(fl); @@ -216,16 +228,11 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { @Override public void startTable(Table tbl) { converter.startTable(tbl); - tableFooterRecorders.push(null); super.startTable(tbl); } @Override public void endTable(Table tbl) { - FOEventRecorder tableFooterRecorder = tableFooterRecorders.pop(); - if (tableFooterRecorder != null) { - tableFooterRecorder.replay(converter); - } converter.endTable(tbl); super.endTable(tbl); } @@ -256,8 +263,6 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { @Override public void startFooter(TableFooter footer) { - converters.push(converter); - converter = new FOEventRecorder(); converter.startFooter(footer); super.startFooter(footer); } @@ -265,10 +270,6 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { @Override public void endFooter(TableFooter footer) { converter.endFooter(footer); - /* Replace the dummy table footer with the real one. */ - tableFooterRecorders.pop(); - tableFooterRecorders.push((FOEventRecorder) converter); - converter = converters.pop(); super.endFooter(footer); } @@ -356,20 +357,6 @@ public class FO2StructureTreeConverter extends DelegatingFOEventHandler { super.endListBody(listItemBody); } - @Override - public void startStatic(StaticContent staticContent) { - handleStartArtifact(staticContent); - converter.startStatic(staticContent); - super.startStatic(staticContent); - } - - @Override - public void endStatic(StaticContent statisContent) { - converter.endStatic(statisContent); - handleEndArtifact(statisContent); - super.endStatic(statisContent); - } - @Override public void startMarkup() { converter.startMarkup(); diff --git a/src/java/org/apache/fop/accessibility/fo/FOEventRecorder.java b/src/java/org/apache/fop/accessibility/fo/FOEventRecorder.java deleted file mode 100644 index b2b18046d..000000000 --- a/src/java/org/apache/fop/accessibility/fo/FOEventRecorder.java +++ /dev/null @@ -1,508 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.accessibility.fo; - -import java.util.ArrayList; -import java.util.List; - -import org.apache.fop.fo.FOEventHandler; -import org.apache.fop.fo.FOText; -import org.apache.fop.fo.flow.BasicLink; -import org.apache.fop.fo.flow.Block; -import org.apache.fop.fo.flow.BlockContainer; -import org.apache.fop.fo.flow.Character; -import org.apache.fop.fo.flow.ExternalGraphic; -import org.apache.fop.fo.flow.Footnote; -import org.apache.fop.fo.flow.FootnoteBody; -import org.apache.fop.fo.flow.Inline; -import org.apache.fop.fo.flow.InstreamForeignObject; -import org.apache.fop.fo.flow.Leader; -import org.apache.fop.fo.flow.ListBlock; -import org.apache.fop.fo.flow.ListItem; -import org.apache.fop.fo.flow.ListItemBody; -import org.apache.fop.fo.flow.ListItemLabel; -import org.apache.fop.fo.flow.PageNumber; -import org.apache.fop.fo.flow.PageNumberCitation; -import org.apache.fop.fo.flow.PageNumberCitationLast; -import org.apache.fop.fo.flow.Wrapper; -import org.apache.fop.fo.flow.table.Table; -import org.apache.fop.fo.flow.table.TableBody; -import org.apache.fop.fo.flow.table.TableCell; -import org.apache.fop.fo.flow.table.TableColumn; -import org.apache.fop.fo.flow.table.TableFooter; -import org.apache.fop.fo.flow.table.TableHeader; -import org.apache.fop.fo.flow.table.TableRow; - -final class FOEventRecorder extends FOEventHandler { - - private interface Event { - void replay(FOEventHandler target); - } - - private final List events = new ArrayList(); - - public void replay(FOEventHandler target) { - for (Event event : events) { - event.replay(target); - } - } - - @Override - public void startPageNumber(final PageNumber pagenum) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startPageNumber(pagenum); - } - }); - } - - @Override - public void endPageNumber(final PageNumber pagenum) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endPageNumber(pagenum); - } - }); - } - - @Override - public void startPageNumberCitation(final PageNumberCitation pageCite) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startPageNumberCitation(pageCite); - } - }); - } - - @Override - public void endPageNumberCitation(final PageNumberCitation pageCite) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endPageNumberCitation(pageCite); - } - }); - } - - @Override - public void startPageNumberCitationLast(final PageNumberCitationLast pageLast) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startPageNumberCitationLast(pageLast); - } - }); - } - - @Override - public void endPageNumberCitationLast(final PageNumberCitationLast pageLast) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endPageNumberCitationLast(pageLast); - } - }); - } - - @Override - public void startBlock(final Block bl) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startBlock(bl); - } - }); - } - - @Override - public void endBlock(final Block bl) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endBlock(bl); - } - }); - } - - @Override - public void startBlockContainer(final BlockContainer blc) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startBlockContainer(blc); - } - }); - } - - @Override - public void endBlockContainer(final BlockContainer blc) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endBlockContainer(blc); - } - }); - } - - @Override - public void startInline(final Inline inl) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startInline(inl); - } - }); - } - - @Override - public void endInline(final Inline inl) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endInline(inl); - } - }); - } - - @Override - public void startTable(final Table tbl) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startTable(tbl); - } - }); - } - - @Override - public void endTable(final Table tbl) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endTable(tbl); - } - }); - } - - @Override - public void startColumn(final TableColumn tc) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startColumn(tc); - } - }); - } - - @Override - public void endColumn(final TableColumn tc) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endColumn(tc); - } - }); - } - - @Override - public void startHeader(final TableHeader header) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startHeader(header); - } - }); - } - - @Override - public void endHeader(final TableHeader header) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endHeader(header); - } - }); - } - - @Override - public void startFooter(final TableFooter footer) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startFooter(footer); - } - }); - } - - @Override - public void endFooter(final TableFooter footer) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endFooter(footer); - } - }); - } - - @Override - public void startBody(final TableBody body) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startBody(body); - } - }); - } - - @Override - public void endBody(final TableBody body) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endBody(body); - } - }); - } - - @Override - public void startRow(final TableRow tr) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startRow(tr); - } - }); - } - - @Override - public void endRow(final TableRow tr) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endRow(tr); - } - }); - } - - @Override - public void startCell(final TableCell tc) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startCell(tc); - } - }); - } - - @Override - public void endCell(final TableCell tc) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endCell(tc); - } - }); - } - - @Override - public void startList(final ListBlock lb) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startList(lb); - } - }); - } - - @Override - public void endList(final ListBlock lb) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endList(lb); - } - }); - } - - @Override - public void startListItem(final ListItem li) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startListItem(li); - } - }); - } - - @Override - public void endListItem(final ListItem li) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endListItem(li); - } - }); - } - - @Override - public void startListLabel(final ListItemLabel listItemLabel) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startListLabel(listItemLabel); - } - }); - } - - @Override - public void endListLabel(final ListItemLabel listItemLabel) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endListLabel(listItemLabel); - } - }); - } - - @Override - public void startListBody(final ListItemBody listItemBody) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startListBody(listItemBody); - } - }); - } - - @Override - public void endListBody(final ListItemBody listItemBody) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endListBody(listItemBody); - } - }); - } - - @Override - public void startLink(final BasicLink basicLink) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startLink(basicLink); - } - }); - } - - @Override - public void endLink(final BasicLink basicLink) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endLink(basicLink); - } - }); - } - - @Override - public void image(final ExternalGraphic eg) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.image(eg); - } - }); - } - - @Override - public void startInstreamForeignObject(final InstreamForeignObject ifo) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startInstreamForeignObject(ifo); - } - }); - } - - @Override - public void endInstreamForeignObject(final InstreamForeignObject ifo) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endInstreamForeignObject(ifo); - } - }); - } - - @Override - public void startFootnote(final Footnote footnote) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startFootnote(footnote); - } - }); - } - - @Override - public void endFootnote(final Footnote footnote) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endFootnote(footnote); - } - }); - } - - @Override - public void startFootnoteBody(final FootnoteBody body) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startFootnoteBody(body); - } - }); - } - - @Override - public void endFootnoteBody(final FootnoteBody body) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endFootnoteBody(body); - } - }); - } - - @Override - public void startLeader(final Leader l) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startLeader(l); - } - }); - } - - @Override - public void endLeader(final Leader l) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endLeader(l); - } - }); - } - - @Override - public void startWrapper(final Wrapper wrapper) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.startWrapper(wrapper); - } - }); - } - - @Override - public void endWrapper(final Wrapper wrapper) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.endWrapper(wrapper); - } - }); - } - - @Override - public void character(final Character c) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.character(c); - } - }); - } - - @Override - public void characters(final FOText foText) { - events.add(new Event() { - public void replay(FOEventHandler target) { - target.characters(foText); - } - }); - } - -} diff --git a/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java b/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java index 7e3ed0591..93b815d30 100644 --- a/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java +++ b/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java @@ -55,6 +55,7 @@ import org.apache.fop.fo.flow.table.TableFooter; import org.apache.fop.fo.flow.table.TableHeader; import org.apache.fop.fo.flow.table.TableRow; import org.apache.fop.fo.pagination.Flow; +import org.apache.fop.fo.pagination.LayoutMasterSet; import org.apache.fop.fo.pagination.PageSequence; import org.apache.fop.fo.pagination.StaticContent; import org.apache.fop.fo.properties.CommonAccessibilityHolder; @@ -67,6 +68,8 @@ class StructureTreeEventTrigger extends FOEventHandler { private StructureTreeEventHandler structureTreeEventHandler; + private LayoutMasterSet layoutMasterSet; + public StructureTreeEventTrigger(StructureTreeEventHandler structureTreeEventHandler) { this.structureTreeEventHandler = structureTreeEventHandler; } @@ -81,6 +84,9 @@ class StructureTreeEventTrigger extends FOEventHandler { @Override public void startPageSequence(PageSequence pageSeq) { + if (layoutMasterSet == null) { + layoutMasterSet = pageSeq.getRoot().getLayoutMasterSet(); + } Locale locale = null; if (pageSeq.getLanguage() != null) { if (pageSeq.getCountry() != null) { @@ -128,9 +134,28 @@ class StructureTreeEventTrigger extends FOEventHandler { endElement(pageLast); } + @Override + public void startStatic(StaticContent staticContent) { + AttributesImpl flowName = createFlowNameAttribute(staticContent.getFlowName()); + startElement(staticContent, flowName); + } + + private AttributesImpl createFlowNameAttribute(String flowName) { + String regionName = layoutMasterSet.getDefaultRegionNameFor(flowName); + AttributesImpl attribute = new AttributesImpl(); + addNoNamespaceAttribute(attribute, Flow.FLOW_NAME, regionName); + return attribute; + } + + @Override + public void endStatic(StaticContent staticContent) { + endElement(staticContent); + } + @Override public void startFlow(Flow fl) { - startElement(fl); + AttributesImpl flowName = createFlowNameAttribute(fl.getFlowName()); + startElement(fl, flowName); } @Override @@ -277,16 +302,6 @@ class StructureTreeEventTrigger extends FOEventHandler { endElement(listItemBody); } - @Override - public void startStatic(StaticContent staticContent) { - startElement(staticContent); - } - - @Override - public void endStatic(StaticContent statisContent) { - endElement(statisContent); - } - @Override public void startLink(BasicLink basicLink) { startElementWithID(basicLink); diff --git a/src/java/org/apache/fop/fo/DelegatingFOEventHandler.java b/src/java/org/apache/fop/fo/DelegatingFOEventHandler.java index 50026c996..bb0a1ba75 100644 --- a/src/java/org/apache/fop/fo/DelegatingFOEventHandler.java +++ b/src/java/org/apache/fop/fo/DelegatingFOEventHandler.java @@ -142,6 +142,16 @@ public abstract class DelegatingFOEventHandler extends FOEventHandler { delegate.endPageNumberCitationLast(pageLast); } + @Override + public void startStatic(StaticContent staticContent) { + delegate.startStatic(staticContent); + } + + @Override + public void endStatic(StaticContent statisContent) { + delegate.endStatic(statisContent); + } + @Override public void startFlow(Flow fl) { delegate.startFlow(fl); @@ -292,16 +302,6 @@ public abstract class DelegatingFOEventHandler extends FOEventHandler { delegate.endListBody(listItemBody); } - @Override - public void startStatic(StaticContent staticContent) { - delegate.startStatic(staticContent); - } - - @Override - public void endStatic(StaticContent statisContent) { - delegate.endStatic(statisContent); - } - @Override public void startMarkup() { delegate.startMarkup(); diff --git a/src/java/org/apache/fop/fo/FOEventHandler.java b/src/java/org/apache/fop/fo/FOEventHandler.java index 11b6d4a2f..ad647e42a 100644 --- a/src/java/org/apache/fop/fo/FOEventHandler.java +++ b/src/java/org/apache/fop/fo/FOEventHandler.java @@ -192,6 +192,20 @@ public abstract class FOEventHandler { public void endPageNumberCitationLast(PageNumberCitationLast pageLast) { } + /** + * Process start of a Static. + * @param staticContent StaticContent that is starting + */ + public void startStatic(StaticContent staticContent) { + } + + /** + * Process end of a Static. + * @param staticContent StaticContent that is ending + */ + public void endStatic(StaticContent staticContent) { + } + /** * This method is called to indicate the start of a new fo:flow * or fo:static-content. @@ -409,22 +423,6 @@ public abstract class FOEventHandler { public void endListBody(ListItemBody listItemBody) { } - // Static Regions - /** - * Process start of a Static. - * @param staticContent StaticContent that is starting - */ - public void startStatic(StaticContent staticContent) { - } - - /** - * Process end of a Static. - * @param staticContent StaticContent that is ending - */ - public void endStatic(StaticContent staticContent) { - } - - /** * Process start of a Markup. */ diff --git a/src/java/org/apache/fop/fo/FOPropertyMapping.java b/src/java/org/apache/fop/fo/FOPropertyMapping.java index b29571b09..d0d13fc17 100644 --- a/src/java/org/apache/fop/fo/FOPropertyMapping.java +++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java @@ -26,6 +26,7 @@ import org.apache.fop.apps.FOUserAgent; import org.apache.fop.datatypes.LengthBase; import org.apache.fop.fo.expr.PropertyException; import org.apache.fop.fo.flow.table.TableFObj.ColumnNumberPropertyMaker; +import org.apache.fop.fo.pagination.Flow; import org.apache.fop.fo.properties.BackgroundPositionShorthand; import org.apache.fop.fo.properties.BorderSpacingShorthandParser; import org.apache.fop.fo.properties.BorderWidthPropertyMaker; @@ -2267,7 +2268,7 @@ public final class FOPropertyMapping implements Constants { m = new StringProperty.Maker(PR_FLOW_NAME); m.setInherited(false); m.setDefault(""); - addPropertyMaker("flow-name", m); + addPropertyMaker(Flow.FLOW_NAME, m); // force-page-count m = new EnumProperty.Maker(PR_FORCE_PAGE_COUNT); diff --git a/src/java/org/apache/fop/fo/pagination/Flow.java b/src/java/org/apache/fop/fo/pagination/Flow.java index 981485e32..45c8f7804 100644 --- a/src/java/org/apache/fop/fo/pagination/Flow.java +++ b/src/java/org/apache/fop/fo/pagination/Flow.java @@ -36,6 +36,9 @@ import org.apache.fop.fo.properties.CommonAccessibilityHolder; */ public class Flow extends FObj implements CommonAccessibilityHolder { + /** The "flow-name" property name. */ + public static final String FLOW_NAME = "flow-name"; + private String flowName; private CommonAccessibility commonAccessibility; @@ -61,7 +64,7 @@ public class Flow extends FObj implements CommonAccessibilityHolder { /** {@inheritDoc} */ protected void startOfNode() throws FOPException { if (flowName == null || flowName.equals("")) { - missingPropertyError("flow-name"); + missingPropertyError(FLOW_NAME); } // according to communication from Paul Grosso (XSL-List, diff --git a/src/java/org/apache/fop/fo/pagination/LayoutMasterSet.java b/src/java/org/apache/fop/fo/pagination/LayoutMasterSet.java index 533b113d2..8b109dd0e 100644 --- a/src/java/org/apache/fop/fo/pagination/LayoutMasterSet.java +++ b/src/java/org/apache/fop/fo/pagination/LayoutMasterSet.java @@ -217,5 +217,24 @@ public class LayoutMasterSet extends FObj { public int getNameId() { return FO_LAYOUT_MASTER_SET; } + + /** + * Returns the default name of the region to which the flow or static-content having + * the given flow-name is assigned. + * + * @param flowName the value of the flow-name property + * @return the default region name ("xsl-region-body", "xsl-region-before", etc.) + */ + public String getDefaultRegionNameFor(String flowName) { + for (SimplePageMaster spm : simplePageMasters.values()) { + for (Region region : spm.getRegions().values()) { + if (region.getRegionName().equals(flowName)) { + return region.getDefaultRegionName(); + } + } + } + assert flowName.equals("xsl-before-float-separator") || flowName.equals("xsl-footnote-separator"); + return flowName; + } } diff --git a/src/java/org/apache/fop/fo/pagination/StaticContent.java b/src/java/org/apache/fop/fo/pagination/StaticContent.java index e70c80c5d..412cdbcca 100644 --- a/src/java/org/apache/fop/fo/pagination/StaticContent.java +++ b/src/java/org/apache/fop/fo/pagination/StaticContent.java @@ -42,7 +42,7 @@ public class StaticContent extends Flow { /** {@inheritDoc} */ protected void startOfNode() throws FOPException { if (getFlowName() == null || getFlowName().equals("")) { - missingPropertyError("flow-name"); + missingPropertyError(FLOW_NAME); } getFOEventHandler().startStatic(this); } diff --git a/src/java/org/apache/fop/pdf/PDFDocument.java b/src/java/org/apache/fop/pdf/PDFDocument.java index e46a22c4a..0b412842f 100644 --- a/src/java/org/apache/fop/pdf/PDFDocument.java +++ b/src/java/org/apache/fop/pdf/PDFDocument.java @@ -355,36 +355,20 @@ public class PDFDocument { } /** - * Creates and returns a structure element. - * - * @param structureType the structure type of the new element (value for the - * S entry) - * @param parent the parent of the new structure element in the structure - * hierarchy - * @return a dictionary of type StructElem + * Adds the given element to the structure tree. */ - public PDFStructElem makeStructureElement(StructureType structureType, PDFObject parent) { - PDFStructElem structElem = new PDFStructElem(parent, structureType); + public void registerStructureElement(PDFStructElem structElem) { assignObjectNumber(structElem); structureTreeElements.add(structElem); - return structElem; } /** - * Creates and returns a structure element. - * - * @param structureType the structure type of the new element (value for the - * S entry) - * @param parent the parent of the new structure element in the structure - * hierarchy - * @param scope the scope of the given table header element - * @return a dictionary of type StructElem + * Assigns the given scope to the given element and adds it to the structure tree. The + * scope may not be added if it's not compatible with this document's PDF version. */ - public PDFStructElem makeStructureElement(StructureType structureType, PDFObject parent, - Scope scope) { - PDFStructElem structElem = makeStructureElement(structureType, parent); + public void registerStructureElement(PDFStructElem structElem, Scope scope) { + registerStructureElement(structElem); versionController.addTableHeaderScopeAttribute(structElem, scope); - return structElem; } /** diff --git a/src/java/org/apache/fop/pdf/PDFStructElem.java b/src/java/org/apache/fop/pdf/PDFStructElem.java index 28cebb3ee..8250318d7 100644 --- a/src/java/org/apache/fop/pdf/PDFStructElem.java +++ b/src/java/org/apache/fop/pdf/PDFStructElem.java @@ -32,7 +32,8 @@ import org.apache.fop.util.LanguageTags; /** * Class representing a PDF Structure Element. */ -public class PDFStructElem extends PDFDictionary implements StructureTreeElement, CompressedObject { +public class PDFStructElem extends StructureHierarchyMember + implements StructureTreeElement, CompressedObject { private StructureType structureType; @@ -51,7 +52,7 @@ public class PDFStructElem extends PDFDictionary implements StructureTreeElement * @param parent parent of this element * @param structureType the structure type of this element */ - PDFStructElem(PDFObject parent, StructureType structureType) { + public PDFStructElem(PDFObject parent, StructureType structureType) { this(parent); this.structureType = structureType; put("S", structureType.getName()); @@ -86,6 +87,7 @@ public class PDFStructElem extends PDFDictionary implements StructureTreeElement * * @param kid element to be added */ + @Override public void addKid(PDFObject kid) { if (kids == null) { kids = new ArrayList(); diff --git a/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java b/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java index 5f19bb2e2..ca6d82d22 100644 --- a/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java +++ b/src/java/org/apache/fop/pdf/PDFStructTreeRoot.java @@ -22,7 +22,7 @@ package org.apache.fop.pdf; /** * Class representing a PDF /StructTreeRoot dictionary. */ -public class PDFStructTreeRoot extends PDFDictionary { +public class PDFStructTreeRoot extends StructureHierarchyMember { /** * Creates a new /StructTreeRoot dictionary. @@ -49,6 +49,7 @@ public class PDFStructTreeRoot extends PDFDictionary { * * @param kid an object to be added to the K entry */ + @Override public void addKid(PDFObject kid) { getKids().add(kid); } diff --git a/src/java/org/apache/fop/pdf/StandardStructureTypes.java b/src/java/org/apache/fop/pdf/StandardStructureTypes.java index dc045e180..69550119a 100644 --- a/src/java/org/apache/fop/pdf/StandardStructureTypes.java +++ b/src/java/org/apache/fop/pdf/StandardStructureTypes.java @@ -111,6 +111,11 @@ public final class StandardStructureTypes { return name; } + @Override + public String toString() { + return name.toString().substring(1); + } + } private static final Map STRUCTURE_TYPES = new HashMap(); diff --git a/src/java/org/apache/fop/pdf/StructureHierarchyMember.java b/src/java/org/apache/fop/pdf/StructureHierarchyMember.java new file mode 100644 index 000000000..e3be92102 --- /dev/null +++ b/src/java/org/apache/fop/pdf/StructureHierarchyMember.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.pdf; + +/** + * An element in the document's structure tree. This can be either the structure tree root + * or a structure element. + * + * @see "Section 10.6, Logical Structure of the PDF Reference, 4th edition (PDF 1.5)" + */ +public abstract class StructureHierarchyMember extends PDFDictionary { + + /** + * Adds the given object to the array of kids. + * + * @param kid an object to be added to the K entry + */ + public abstract void addKid(PDFObject kid); + +} diff --git a/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java b/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java deleted file mode 100644 index 68bfd8686..000000000 --- a/src/java/org/apache/fop/render/pdf/FOToPDFRoleMap.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.render.pdf; - -import java.util.Map; - -import org.apache.fop.events.EventBroadcaster; -import org.apache.fop.pdf.PDFObject; -import org.apache.fop.pdf.PDFStructElem; -import org.apache.fop.pdf.StandardStructureTypes; -import org.apache.fop.pdf.StructureType; - -/** - * This class provides the standard mappings from Formatting Objects to PDF structure types. - */ -final class FOToPDFRoleMap { - - private static final Map DEFAULT_MAPPINGS = new java.util.HashMap(); - - static { - // Create the standard mappings - // Declarations and Pagination and Layout Formatting Objects - addMapping("root", StandardStructureTypes.Grouping.DOCUMENT); - addMapping("page-sequence", StandardStructureTypes.Grouping.PART); - addMapping("flow", StandardStructureTypes.Grouping.SECT); - addMapping("static-content", StandardStructureTypes.Grouping.SECT); - // Block-level Formatting Objects - addMapping("block", StandardStructureTypes.Paragraphlike.P); - addMapping("block-container", StandardStructureTypes.Grouping.DIV); - // Inline-level Formatting Objects - addMapping("character", StandardStructureTypes.InlineLevelStructure.SPAN); - addMapping("external-graphic", StandardStructureTypes.Illustration.FIGURE); - addMapping("instream-foreign-object", StandardStructureTypes.Illustration.FIGURE); - addMapping("inline", StandardStructureTypes.InlineLevelStructure.SPAN); - addMapping("inline-container", StandardStructureTypes.Grouping.DIV); - addMapping("page-number", StandardStructureTypes.InlineLevelStructure.QUOTE); - addMapping("page-number-citation", StandardStructureTypes.InlineLevelStructure.QUOTE); - addMapping("page-number-citation-last", StandardStructureTypes.InlineLevelStructure.QUOTE); - // Formatting Objects for Tables - addMapping("table-and-caption", StandardStructureTypes.Grouping.DIV); - addMapping("table", StandardStructureTypes.Table.TABLE); - addMapping("table-caption", StandardStructureTypes.Grouping.CAPTION); - addMapping("table-header", StandardStructureTypes.Table.THEAD); - addMapping("table-footer", StandardStructureTypes.Table.TFOOT); - addMapping("table-body", StandardStructureTypes.Table.TBODY); - addMapping("table-row", StandardStructureTypes.Table.TR); - addMapping("table-cell", new TableCellMapper()); - // Formatting Objects for Lists - addMapping("list-block", StandardStructureTypes.List.L); - addMapping("list-item", StandardStructureTypes.List.LI); - addMapping("list-item-body", StandardStructureTypes.List.LBODY); - addMapping("list-item-label", StandardStructureTypes.List.LBL); - // Dynamic Effects: Link and Multi Formatting Objects - addMapping("basic-link", StandardStructureTypes.InlineLevelStructure.LINK); - // Out-of-Line Formatting Objects - addMapping("float", StandardStructureTypes.Grouping.DIV); - addMapping("footnote", StandardStructureTypes.InlineLevelStructure.NOTE); - addMapping("footnote-body", StandardStructureTypes.Grouping.SECT); - addMapping("wrapper", StandardStructureTypes.InlineLevelStructure.SPAN); - addMapping("marker", StandardStructureTypes.Grouping.PRIVATE); - } - - private static void addMapping(String fo, StructureType structureType) { - addMapping(fo, new SimpleMapper(structureType)); - } - - private static void addMapping(String fo, Mapper mapper) { - DEFAULT_MAPPINGS.put(fo, mapper); - } - - - /** - * Maps a Formatting Object to a PDFName representing the associated structure type. - * @param fo the formatting object's local name - * @param role the value of the formatting object's role property - * @param parent the parent of the structure element to be mapped - * @param eventBroadcaster the event broadcaster - * @return the structure type or null if no match could be found - */ - public static StructureType mapFormattingObject(String fo, String role, - PDFObject parent, EventBroadcaster eventBroadcaster) { - StructureType type = null; - if (role == null) { - type = getDefaultMappingFor(fo, parent); - } else { - type = StandardStructureTypes.get(role); - if (type == null) { - type = getDefaultMappingFor(fo, parent); - PDFEventProducer.Provider.get(eventBroadcaster).nonStandardStructureType(fo, - fo, role, type.toString().substring(1)); - } - } - assert type != null; - return type; - } - - /** - * Maps a Formatting Object to a PDFName representing the associated structure type. - * @param fo the formatting object's local name - * @param parent the parent of the structure element to be mapped - * @return the structure type or NonStruct if no match could be found - */ - private static StructureType getDefaultMappingFor(String fo, PDFObject parent) { - Mapper mapper = DEFAULT_MAPPINGS.get(fo); - if (mapper != null) { - return mapper.getStructureType(parent); - } else { - return StandardStructureTypes.Grouping.NON_STRUCT; - } - } - - private interface Mapper { - StructureType getStructureType(PDFObject parent); - } - - private static class SimpleMapper implements Mapper { - - private StructureType structureType; - - public SimpleMapper(StructureType structureType) { - this.structureType = structureType; - } - - public StructureType getStructureType(PDFObject parent) { - return structureType; - } - - } - - private static class TableCellMapper implements Mapper { - - public StructureType getStructureType(PDFObject parent) { - PDFStructElem grandParent = ((PDFStructElem) parent).getParentStructElem(); - //TODO What to do with cells from table-footer? Currently they are mapped on TD. - if (grandParent.getStructureType() == StandardStructureTypes.Table.THEAD) { - return StandardStructureTypes.Table.TH; - } else { - return StandardStructureTypes.Table.TD; - } - } - - } - - private FOToPDFRoleMap() { } -} diff --git a/src/java/org/apache/fop/render/pdf/PDFEventProducer.java b/src/java/org/apache/fop/render/pdf/PDFEventProducer.java index 40062f73f..4b8253867 100644 --- a/src/java/org/apache/fop/render/pdf/PDFEventProducer.java +++ b/src/java/org/apache/fop/render/pdf/PDFEventProducer.java @@ -59,12 +59,11 @@ public interface PDFEventProducer extends EventProducer { * Custom structure type is not standard as per the PDF reference. * * @param source the event source - * @param fo the local name of the formatting object having the custom type * @param type custom structure type * @param fallback default structure type used as a fallback * @event.severity WARN */ - void nonStandardStructureType(Object source, String fo, String type, String fallback); + void nonStandardStructureType(Object source, String type, String fallback); /** * The encryption length must be a multiple of 8 between 40 and 128. diff --git a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java index 1377bbfce..0d4a6b7fb 100644 --- a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java +++ b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java @@ -21,24 +21,271 @@ package org.apache.fop.render.pdf; import java.util.LinkedList; import java.util.Locale; +import java.util.Map; import org.xml.sax.Attributes; +import org.xml.sax.helpers.AttributesImpl; import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.accessibility.StructureTreeEventHandler; import org.apache.fop.events.EventBroadcaster; import org.apache.fop.fo.extensions.ExtensionElementMapping; +import org.apache.fop.fo.pagination.Flow; import org.apache.fop.pdf.PDFFactory; -import org.apache.fop.pdf.PDFObject; import org.apache.fop.pdf.PDFParentTree; import org.apache.fop.pdf.PDFStructElem; import org.apache.fop.pdf.PDFStructTreeRoot; import org.apache.fop.pdf.StandardStructureAttributes.Table.Scope; +import org.apache.fop.pdf.StandardStructureTypes; +import org.apache.fop.pdf.StandardStructureTypes.Grouping; import org.apache.fop.pdf.StandardStructureTypes.Table; +import org.apache.fop.pdf.StructureHierarchyMember; import org.apache.fop.pdf.StructureType; +import org.apache.fop.util.XMLUtil; class PDFStructureTreeBuilder implements StructureTreeEventHandler { + private static final String ROLE = "role"; + + private static final Map BUILDERS + = new java.util.HashMap(); + + private static final StructureElementBuilder DEFAULT_BUILDER + = new DefaultStructureElementBuilder(Grouping.NON_STRUCT); + + static { + // Declarations and Pagination and Layout Formatting Objects + StructureElementBuilder regionBuilder = new RegionBuilder(); + addBuilder("root", StandardStructureTypes.Grouping.DOCUMENT); + addBuilder("page-sequence", new PageSequenceBuilder()); + addBuilder("static-content", regionBuilder); + addBuilder("flow", regionBuilder); + // Block-level Formatting Objects + addBuilder("block", StandardStructureTypes.Paragraphlike.P); + addBuilder("block-container", StandardStructureTypes.Grouping.DIV); + // Inline-level Formatting Objects + addBuilder("character", StandardStructureTypes.InlineLevelStructure.SPAN); + addBuilder("external-graphic", new ImageBuilder()); + addBuilder("instream-foreign-object", new ImageBuilder()); + addBuilder("inline", StandardStructureTypes.InlineLevelStructure.SPAN); + addBuilder("inline-container", StandardStructureTypes.Grouping.DIV); + addBuilder("page-number", StandardStructureTypes.InlineLevelStructure.QUOTE); + addBuilder("page-number-citation", StandardStructureTypes.InlineLevelStructure.QUOTE); + addBuilder("page-number-citation-last", StandardStructureTypes.InlineLevelStructure.QUOTE); + // Formatting Objects for Tables + addBuilder("table-and-caption", StandardStructureTypes.Grouping.DIV); + addBuilder("table", new TableBuilder()); + addBuilder("table-caption", StandardStructureTypes.Grouping.CAPTION); + addBuilder("table-header", StandardStructureTypes.Table.THEAD); + addBuilder("table-footer", new TableFooterBuilder()); + addBuilder("table-body", StandardStructureTypes.Table.TBODY); + addBuilder("table-row", StandardStructureTypes.Table.TR); + addBuilder("table-cell", new TableCellBuilder()); + // Formatting Objects for Lists + addBuilder("list-block", StandardStructureTypes.List.L); + addBuilder("list-item", StandardStructureTypes.List.LI); + addBuilder("list-item-body", StandardStructureTypes.List.LBODY); + addBuilder("list-item-label", StandardStructureTypes.List.LBL); + // Dynamic Effects: Link and Multi Formatting Objects + addBuilder("basic-link", StandardStructureTypes.InlineLevelStructure.LINK); + // Out-of-Line Formatting Objects + addBuilder("float", StandardStructureTypes.Grouping.DIV); + addBuilder("footnote", StandardStructureTypes.InlineLevelStructure.NOTE); + addBuilder("footnote-body", StandardStructureTypes.Grouping.SECT); + addBuilder("wrapper", StandardStructureTypes.InlineLevelStructure.SPAN); + addBuilder("marker", StandardStructureTypes.Grouping.PRIVATE); + + addBuilder("#PCDATA", new PlaceholderBuilder()); + } + + private static void addBuilder(String fo, StructureType structureType) { + addBuilder(fo, new DefaultStructureElementBuilder(structureType)); + } + + private static void addBuilder(String fo, StructureElementBuilder mapper) { + BUILDERS.put(fo, mapper); + } + + private interface StructureElementBuilder { + + PDFStructElem build(StructureHierarchyMember parent, Attributes attributes, PDFFactory pdfFactory, + EventBroadcaster eventBroadcaster); + + } + + private static class DefaultStructureElementBuilder implements StructureElementBuilder { + + private final StructureType defaultStructureType; + + DefaultStructureElementBuilder(StructureType structureType) { + this.defaultStructureType = structureType; + } + + public final PDFStructElem build(StructureHierarchyMember parent, Attributes attributes, + PDFFactory pdfFactory, EventBroadcaster eventBroadcaster) { + String role = attributes.getValue(ROLE); + StructureType structureType; + if (role == null) { + structureType = defaultStructureType; + } else { + structureType = StandardStructureTypes.get(role); + if (structureType == null) { + structureType = defaultStructureType; + PDFEventProducer.Provider.get(eventBroadcaster).nonStandardStructureType(role, role, + structureType.toString()); + } + } + PDFStructElem structElem = createStructureElement(parent, structureType); + setAttributes(structElem, attributes); + addKidToParent(structElem, parent, attributes); + registerStructureElement(structElem, pdfFactory); + return structElem; + } + + protected PDFStructElem createStructureElement(StructureHierarchyMember parent, + StructureType structureType) { + return new PDFStructElem(parent, structureType); + } + + protected void setAttributes(PDFStructElem structElem, Attributes attributes) { + } + + protected void addKidToParent(PDFStructElem kid, StructureHierarchyMember parent, + Attributes attributes) { + parent.addKid(kid); + } + + protected void registerStructureElement(PDFStructElem structureElement, PDFFactory pdfFactory) { + pdfFactory.getDocument().registerStructureElement(structureElement); + } + + } + + private static class PageSequenceBuilder extends DefaultStructureElementBuilder { + + PageSequenceBuilder() { + super(StandardStructureTypes.Grouping.PART); + } + + @Override + protected PDFStructElem createStructureElement(StructureHierarchyMember parent, + StructureType structureType) { + return new PageSequenceStructElem(parent, structureType); + } + + } + + private static class RegionBuilder extends DefaultStructureElementBuilder { + + RegionBuilder() { + super(StandardStructureTypes.Grouping.SECT); + } + + @Override + protected void addKidToParent(PDFStructElem kid, StructureHierarchyMember parent, + Attributes attributes) { + String flowName = attributes.getValue(Flow.FLOW_NAME); + ((PageSequenceStructElem) parent).addContent(flowName, kid); + } + + } + + private static class ImageBuilder extends DefaultStructureElementBuilder { + + ImageBuilder() { + super(StandardStructureTypes.Illustration.FIGURE); + } + + @Override + protected void setAttributes(PDFStructElem structElem, Attributes attributes) { + String altTextNode = attributes.getValue(ExtensionElementMapping.URI, "alt-text"); + if (altTextNode == null) { + altTextNode = "No alternate text specified"; + } + structElem.put("Alt", altTextNode); + } + + } + + private static class TableBuilder extends DefaultStructureElementBuilder { + + TableBuilder() { + super(StandardStructureTypes.Table.TABLE); + } + + @Override + protected PDFStructElem createStructureElement(StructureHierarchyMember parent, + StructureType structureType) { + return new TableStructElem(parent, structureType); + } + } + + private static class TableFooterBuilder extends DefaultStructureElementBuilder { + + public TableFooterBuilder() { + super(StandardStructureTypes.Table.TFOOT); + } + + @Override + protected void addKidToParent(PDFStructElem kid, StructureHierarchyMember parent, + Attributes attributes) { + ((TableStructElem) parent).addTableFooter(kid); + } + } + + private static class TableCellBuilder extends DefaultStructureElementBuilder { + + TableCellBuilder() { + super(StandardStructureTypes.Table.TD); + } + + @Override + protected PDFStructElem createStructureElement(StructureHierarchyMember parent, + StructureType structureType) { + PDFStructElem grandParent = ((PDFStructElem) parent).getParentStructElem(); + //TODO What to do with cells from table-footer? Currently they are mapped on TD. + if (grandParent.getStructureType() == StandardStructureTypes.Table.THEAD) { + structureType = StandardStructureTypes.Table.TH; + } else { + structureType = StandardStructureTypes.Table.TD; + } + return super.createStructureElement(parent, structureType); + } + + @Override + protected void registerStructureElement(PDFStructElem structureElement, PDFFactory pdfFactory) { + if (structureElement.getStructureType() == Table.TH) { + pdfFactory.getDocument().registerStructureElement(structureElement, Scope.COLUMN); + } else { + pdfFactory.getDocument().registerStructureElement(structureElement); + } + } + + @Override + protected void setAttributes(PDFStructElem structElem, Attributes attributes) { + String columnSpan = attributes.getValue("number-columns-spanned"); + if (columnSpan != null) { + structElem.setTableAttributeColSpan(Integer.parseInt(columnSpan)); + } + String rowSpan = attributes.getValue("number-rows-spanned"); + if (rowSpan != null) { + structElem.setTableAttributeRowSpan(Integer.parseInt(rowSpan)); + } + } + + } + + private static class PlaceholderBuilder implements StructureElementBuilder { + + public PDFStructElem build(StructureHierarchyMember parent, Attributes attributes, + PDFFactory pdfFactory, EventBroadcaster eventBroadcaster) { + PDFStructElem elem = new PDFStructElem.Placeholder(parent); + parent.addKid(elem); + return elem; + } + + } + private PDFFactory pdfFactory; private EventBroadcaster eventBroadcaster; @@ -51,6 +298,10 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler { this.pdfFactory = pdfFactory; } + void setEventBroadcaster(EventBroadcaster eventBroadcaster) { + this.eventBroadcaster = eventBroadcaster; + } + void setLogicalStructureHandler(PDFLogicalStructureHandler logicalStructureHandler) { createRootStructureElement(logicalStructureHandler); } @@ -59,93 +310,53 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler { assert rootStructureElement == null; PDFParentTree parentTree = logicalStructureHandler.getParentTree(); PDFStructTreeRoot structTreeRoot = pdfFactory.getDocument().makeStructTreeRoot(parentTree); - rootStructureElement = createStructureElement("root", structTreeRoot, null); - structTreeRoot.addKid(rootStructureElement); + rootStructureElement = createStructureElement("root", structTreeRoot, + new AttributesImpl(), pdfFactory, eventBroadcaster); } - void setEventBroadcaster(EventBroadcaster eventBroadcaster) { - this.eventBroadcaster = eventBroadcaster; - } + private static PDFStructElem createStructureElement(String name, StructureHierarchyMember parent, + Attributes attributes, PDFFactory pdfFactory, EventBroadcaster eventBroadcaster) { + StructureElementBuilder builder = BUILDERS.get(name); + if (builder == null) { + // TODO is a fallback really necessary? + builder = DEFAULT_BUILDER; + } + return builder.build(parent, attributes, pdfFactory, eventBroadcaster); + } public void startPageSequence(Locale language, String role) { ancestors = new LinkedList(); - PDFStructElem structElem = createStructureElement("page-sequence", rootStructureElement, role); + AttributesImpl attributes = new AttributesImpl(); + attributes.addAttribute("", ROLE, ROLE, XMLUtil.CDATA, role); + PDFStructElem structElem = createStructureElement("page-sequence", + rootStructureElement, attributes, pdfFactory, eventBroadcaster); if (language != null) { structElem.setLanguage(language); } - rootStructureElement.addKid(structElem); ancestors.add(structElem); } - private PDFStructElem createStructureElement(String name, PDFObject parent, String role) { - StructureType structureType = FOToPDFRoleMap.mapFormattingObject(name, role, parent, - eventBroadcaster); - if (structureType == Table.TH) { - return pdfFactory.getDocument().makeStructureElement(structureType, parent, Scope.COLUMN); - } else { - return pdfFactory.getDocument().makeStructureElement(structureType, parent); - } - } - public void endPageSequence() { } public StructureTreeElement startNode(String name, Attributes attributes) { PDFStructElem parent = ancestors.getFirst(); - String role = attributes.getValue("role"); - PDFStructElem structElem = createStructureElement(name, parent, role); - setSpanAttributes(structElem, attributes); - parent.addKid(structElem); + PDFStructElem structElem = createStructureElement(name, parent, attributes, + pdfFactory, eventBroadcaster); ancestors.addFirst(structElem); return structElem; } - private void setSpanAttributes(PDFStructElem structElem, Attributes attributes) { - String columnSpan = attributes.getValue("number-columns-spanned"); - if (columnSpan != null) { - structElem.setTableAttributeColSpan(Integer.parseInt(columnSpan)); - } - String rowSpan = attributes.getValue("number-rows-spanned"); - if (rowSpan != null) { - structElem.setTableAttributeRowSpan(Integer.parseInt(rowSpan)); - } - } - public void endNode(String name) { - removeFirstAncestor(); - } - - private void removeFirstAncestor() { ancestors.removeFirst(); } public StructureTreeElement startImageNode(String name, Attributes attributes) { - PDFStructElem parent = ancestors.getFirst(); - String role = attributes.getValue("role"); - PDFStructElem structElem = createStructureElement(name, parent, role); - parent.addKid(structElem); - String altTextNode = attributes.getValue(ExtensionElementMapping.URI, "alt-text"); - if (altTextNode != null) { - structElem.put("Alt", altTextNode); - } else { - structElem.put("Alt", "No alternate text specified"); - } - ancestors.addFirst(structElem); - return structElem; + return startNode(name, attributes); } public StructureTreeElement startReferencedNode(String name, Attributes attributes) { - PDFStructElem parent = ancestors.getFirst(); - String role = attributes.getValue("role"); - PDFStructElem structElem; - if ("#PCDATA".equals(name)) { - structElem = new PDFStructElem.Placeholder(parent); - } else { - structElem = createStructureElement(name, parent, role); - } - parent.addKid(structElem); - ancestors.addFirst(structElem); - return structElem; + return startNode(name, attributes); } } diff --git a/src/java/org/apache/fop/render/pdf/PageSequenceStructElem.java b/src/java/org/apache/fop/render/pdf/PageSequenceStructElem.java new file mode 100644 index 000000000..09d5b81a2 --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/PageSequenceStructElem.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.render.pdf; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.fop.pdf.PDFArray; +import org.apache.fop.pdf.PDFObject; +import org.apache.fop.pdf.PDFStructElem; +import org.apache.fop.pdf.StructureType; + +class PageSequenceStructElem extends PDFStructElem { + + private List regionBefores = new ArrayList(); + + private List regionAfters = new ArrayList(); + + private List regionStarts = new ArrayList(); + + private List regionEnds = new ArrayList(); + + PageSequenceStructElem(PDFObject parent, StructureType structureType) { + super(parent, structureType); + } + + void addContent(String flowName, PDFStructElem content) { + if (flowName.equals("xsl-region-before")) { + regionBefores.add(content); + } else if (flowName.equals("xsl-region-after")) { + regionAfters.add(content); + } else if (flowName.equals("xsl-region-start")) { + regionStarts.add(content); + } else if (flowName.equals("xsl-region-end")) { + regionEnds.add(content); + } else { + addKid(content); + } + } + + @Override + protected boolean attachKids() { + assert !kids.isEmpty(); + PDFArray k = new PDFArray(); + addRegions(k, regionBefores); + addRegions(k, regionStarts); + addRegions(k, kids); + addRegions(k, regionEnds); + addRegions(k, regionAfters); + put("K", k); + return true; + } + + private void addRegions(PDFArray k, List regions) { + if (!regions.isEmpty()) { + for (PDFObject kid : regions) { + k.add(kid); + } + } + } + +} diff --git a/src/java/org/apache/fop/render/pdf/TableStructElem.java b/src/java/org/apache/fop/render/pdf/TableStructElem.java new file mode 100644 index 000000000..c44acb25c --- /dev/null +++ b/src/java/org/apache/fop/render/pdf/TableStructElem.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.render.pdf; + +import org.apache.fop.pdf.PDFObject; +import org.apache.fop.pdf.PDFStructElem; +import org.apache.fop.pdf.StructureType; + +class TableStructElem extends PDFStructElem { + + private PDFStructElem tableFooter; + + public TableStructElem(PDFObject parent, StructureType structureType) { + super(parent, structureType); + } + + void addTableFooter(PDFStructElem footer) { + assert tableFooter == null; + tableFooter = footer; + } + + @Override + protected boolean attachKids() { + assert !kids.isEmpty(); + if (tableFooter != null) { + kids.add(tableFooter); + } + return super.attachKids(); + } + +} diff --git a/status.xml b/status.xml index 5927dd82c..270496f59 100644 --- a/status.xml +++ b/status.xml @@ -62,6 +62,10 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> + + When PDF accessibility is enabled, the contents for the different regions must appear in the + proper order in the structure tree. + Remove StandardEncoding as the encoding type from fonts used in the PDF renderer diff --git a/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java b/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java index 863bfe797..6092912f9 100644 --- a/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java +++ b/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java @@ -19,8 +19,6 @@ package org.apache.fop.accessibility.fo; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -34,7 +32,6 @@ import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMResult; 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.custommonkey.xmlunit.Diff; @@ -57,9 +54,17 @@ import org.apache.fop.fotreetest.DummyFOEventHandler; public class FO2StructureTreeConverterTestCase { - private interface FOLoader { + private static class FOLoader { - InputStream getFoInputStream(); + private final String resourceName; + + FOLoader(String resourceName) { + this.resourceName = resourceName; + } + + public InputStream getFoInputStream() { + return getResource(resourceName); + } } private static final String STRUCTURE_TREE_SEQUENCE_NAME = "structure-tree-sequence"; @@ -68,62 +73,30 @@ public class FO2StructureTreeConverterTestCase { @Test public void testCompleteDocument() throws Exception { - foLoader = new FOLoader() { - public InputStream getFoInputStream() { - return getResource("/org/apache/fop/fo/complete_document.fo"); - } - }; - testConverter(); + testConverter("/org/apache/fop/fo/complete_document.fo"); } @Test public void testTableFooters() throws Exception { - foLoader = new FOLoader() { - public InputStream getFoInputStream() { - return getResource("table-footers.fo"); - } - }; - testConverter(); - } - - @Test - public void testCompleteContentWrappedInTableFooter() throws Exception { - Source xslt = new StreamSource(getResource("wrapCompleteDocumentInTableFooter.xsl")); - Transformer transformer = createTransformer(xslt); - InputStream originalFO = getResource("/org/apache/fop/fo/complete_document.fo"); - ByteArrayOutputStream transformedFoOutput = new ByteArrayOutputStream(); - transformer.transform(new StreamSource(originalFO), new StreamResult(transformedFoOutput)); - final byte[] transformedFoOutputBytes = transformedFoOutput.toByteArray(); - foLoader = new FOLoader() { - public InputStream getFoInputStream() { - return new ByteArrayInputStream(transformedFoOutputBytes); - } - }; - testConverter(); + testConverter("table-footers.fo"); } @Test public void testArtifact() throws Exception { - foLoader = new FOLoader() { - - public InputStream getFoInputStream() { - return getResource("artifact.fo"); - } - }; - testConverter(); + testConverter("artifact.fo"); } - private Transformer createTransformer(Source xslt) throws TransformerFactoryConfigurationError, - TransformerConfigurationException { - TransformerFactory transformerFactory = TransformerFactory.newInstance(); - return transformerFactory.newTransformer(xslt); + @Test + public void testSideRegions() throws Exception { + testConverter("/org/apache/fop/fo/pagination/side-regions.fo"); } private static InputStream getResource(String name) { return FO2StructureTreeConverterTestCase.class.getResourceAsStream(name); } - private void testConverter() throws Exception { + private void testConverter(String foResourceName) throws Exception { + foLoader = new FOLoader(foResourceName); DOMResult expectedStructureTree = loadExpectedStructureTree(); DOMResult actualStructureTree = buildActualStructureTree(); final Diff diff = createDiff(expectedStructureTree, actualStructureTree); diff --git a/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl b/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl index db0dffb14..c739462e4 100644 --- a/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl +++ b/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl @@ -50,6 +50,25 @@ + + + + + + + + + + + + + @@ -73,15 +92,7 @@ - - - - - - - - - + diff --git a/test/java/org/apache/fop/accessibility/fo/wrapCompleteDocumentInTableFooter.xsl b/test/java/org/apache/fop/accessibility/fo/wrapCompleteDocumentInTableFooter.xsl deleted file mode 100644 index 9608b2fb9..000000000 --- a/test/java/org/apache/fop/accessibility/fo/wrapCompleteDocumentInTableFooter.xsl +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The content below is in the table footer. - - - - - - - diff --git a/test/java/org/apache/fop/fo/pagination/LayoutMasterSetTestCase.java b/test/java/org/apache/fop/fo/pagination/LayoutMasterSetTestCase.java new file mode 100644 index 000000000..cfe71f529 --- /dev/null +++ b/test/java/org/apache/fop/fo/pagination/LayoutMasterSetTestCase.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.pagination; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.fo.FODocumentParser; +import org.apache.fop.fo.FODocumentParser.FOEventHandlerFactory; +import org.apache.fop.fo.FOEventHandler; + +public class LayoutMasterSetTestCase { + + private static class FlowMappingTester extends FOEventHandler { + + private static final String[][] FLOW_MAPPINGS = { + + {"first-page-before", "xsl-region-before"}, + {"first-page-after", "xsl-region-after"}, + {"first-page-start", "xsl-region-start"}, + {"first-page-end", "xsl-region-end"}, + + {"odd-page-before", "xsl-region-before"}, + {"odd-page-after", "xsl-region-after"}, + {"odd-page-start", "xsl-region-start"}, + {"odd-page-end", "xsl-region-end"}, + + {"odd-page-before", "xsl-region-before"}, + {"odd-page-after", "xsl-region-after"}, + {"odd-page-start", "xsl-region-start"}, + {"odd-page-end", "xsl-region-end"}, + + {"blank-page-before", "xsl-region-before"}, + {"blank-page-after", "xsl-region-after"}, + {"blank-page-start", "xsl-region-start"}, + {"blank-page-end", "xsl-region-end"}, + + {"last-page-before", "xsl-region-before"}, + {"last-page-after", "xsl-region-after"}, + {"last-page-start", "xsl-region-start"}, + {"last-page-end", "xsl-region-end"}, + + {"xsl-footnote-separator", "xsl-footnote-separator"} + + }; + + FlowMappingTester(FOUserAgent userAgent) { + super(userAgent); + } + + @Override + public void startPageSequence(PageSequence pageSeq) { + super.startPageSequence(pageSeq); + LayoutMasterSet layoutMasterSet = pageSeq.getRoot().getLayoutMasterSet(); + for (String[] mapping : FLOW_MAPPINGS) { + assertEquals(mapping[1], layoutMasterSet.getDefaultRegionNameFor(mapping[0])); + } + } + + } + + /** + * Tests the {@link LayoutMasterSet#getDefaultRegionNameFor(String)} method. + */ + @Test + public void testFlowMapping() throws Exception { + FODocumentParser foDocumentParser = FODocumentParser.newInstance(new FOEventHandlerFactory() { + + public FOEventHandler newFOEventHandler(FOUserAgent foUserAgent) { + return new FlowMappingTester(foUserAgent); + } + }); + foDocumentParser.parse(getClass().getResourceAsStream("side-regions.fo")); + } + +} diff --git a/test/java/org/apache/fop/fo/pagination/side-regions.fo b/test/java/org/apache/fop/fo/pagination/side-regions.fo new file mode 100644 index 000000000..8a0fba2ec --- /dev/null +++ b/test/java/org/apache/fop/fo/pagination/side-regions.fo @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + First Page Before. + + + First Page After. + + + First Page Start. + + + First Page End. + + + + Odd Page After. + + + Odd Page End. + + + Odd Page Start. + + + Odd Page Before. + + + + Even Page End. + + + Even Page Start. + + + Even Page After. + + + Even Page Before. + + + + Blank Page Start. + + + Blank Page After. + + + Blank Page Before. + + + Blank Page End. + + + + Last Page Before. + + + Last Page End. + + + Last Page After. + + + Last Page Start. + + + + + + + + + + Apacheâ„¢ FOP (Formatting Objects Processor) is a print formatter driven by XSL + formatting objects (XSL-FO) and an output independent formatter. + It is an application* + * written in + Java that reads a formatting object (FO) + tree and renders the resulting pages to a specified output. + The FOP project is part of the Apache Software Foundation, which + is a wider community of users and developers of open source projects. + Apacheâ„¢ FOP (Formatting Objects Processor) is a print formatter + driven by XSL formatting objects (XSL-FO) and an output independent formatter. + It is a Java application that reads a formatting object (FO) + tree and renders the resulting pages to a specified output. + The FOP project is part of the Apache Software Foundation, which + is a wider community of users and developers of open source projects. + + + diff --git a/test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java b/test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java index 89682628d..a1d581402 100644 --- a/test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java +++ b/test/java/org/apache/fop/pdf/TableHeaderScopeTestCase.java @@ -52,7 +52,8 @@ public class TableHeaderScopeTestCase { VersionController controller = mock(VersionController.class); PDFDocument document = new PDFDocument("Test", controller); document.makeStructTreeRoot(null); - document.makeStructureElement(Table.TH, null, scope); + PDFStructElem th = new PDFStructElem(null, Table.TH); + document.registerStructureElement(th, scope); verify(controller).addTableHeaderScopeAttribute(any(PDFStructElem.class), eq(scope)); } diff --git a/test/pdf/accessibility/pdf/role.pdf b/test/pdf/accessibility/pdf/role.pdf index 4dfb686c9..8fb665b79 100644 Binary files a/test/pdf/accessibility/pdf/role.pdf and b/test/pdf/accessibility/pdf/role.pdf differ diff --git a/test/pdf/accessibility/pdf/role_non-standard.pdf b/test/pdf/accessibility/pdf/role_non-standard.pdf index 6f1edea19..9effef793 100644 Binary files a/test/pdf/accessibility/pdf/role_non-standard.pdf and b/test/pdf/accessibility/pdf/role_non-standard.pdf differ diff --git a/test/pdf/accessibility/pdf/side-regions.pdf b/test/pdf/accessibility/pdf/side-regions.pdf new file mode 100644 index 000000000..6e5da9393 Binary files /dev/null and b/test/pdf/accessibility/pdf/side-regions.pdf differ diff --git a/test/pdf/accessibility/side-regions.fo b/test/pdf/accessibility/side-regions.fo new file mode 100644 index 000000000..8a0fba2ec --- /dev/null +++ b/test/pdf/accessibility/side-regions.fo @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + First Page Before. + + + First Page After. + + + First Page Start. + + + First Page End. + + + + Odd Page After. + + + Odd Page End. + + + Odd Page Start. + + + Odd Page Before. + + + + Even Page End. + + + Even Page Start. + + + Even Page After. + + + Even Page Before. + + + + Blank Page Start. + + + Blank Page After. + + + Blank Page Before. + + + Blank Page End. + + + + Last Page Before. + + + Last Page End. + + + Last Page After. + + + Last Page Start. + + + + + + + + + + Apacheâ„¢ FOP (Formatting Objects Processor) is a print formatter driven by XSL + formatting objects (XSL-FO) and an output independent formatter. + It is an application* + * written in + Java that reads a formatting object (FO) + tree and renders the resulting pages to a specified output. + The FOP project is part of the Apache Software Foundation, which + is a wider community of users and developers of open source projects. + Apacheâ„¢ FOP (Formatting Objects Processor) is a print formatter + driven by XSL formatting objects (XSL-FO) and an output independent formatter. + It is a Java application that reads a formatting object (FO) + tree and renders the resulting pages to a specified output. + The FOP project is part of the Apache Software Foundation, which + is a wider community of users and developers of open source projects. + + + -- cgit v1.2.3 From 008bf019ad96ab80f1b8b1897180e30dd3c56925 Mon Sep 17 00:00:00 2001 From: Mehdi Houshmand Date: Tue, 28 Aug 2012 15:08:23 +0000 Subject: Bugzilla#53790: Prevented the TIFF configurator from overriding the Bitmap configurator unless CCITT compression is enabled. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1378163 13f79535-47bb-0310-9956-ffa450edef68 --- .../bitmap/AbstractBitmapDocumentHandler.java | 2 +- .../fop/render/bitmap/BitmapRenderingSettings.java | 23 +++++ .../fop/render/bitmap/TIFFCompressionValue.java | 98 ++++++++++++++++++++++ .../fop/render/bitmap/TIFFCompressionValues.java | 61 -------------- .../org/apache/fop/render/bitmap/TIFFRenderer.java | 40 +++++---- .../fop/render/bitmap/TIFFRendererConfig.java | 11 ++- .../render/bitmap/TIFFRendererConfigurator.java | 37 +++----- status.xml | 4 + .../bitmap/TIFFCompressionValueTestCase.java | 77 +++++++++++++++++ .../bitmap/TIFFRendererConfigParserTestCase.java | 2 +- .../bitmap/TIFFRendererConfiguratorTestCase.java | 9 +- 11 files changed, 244 insertions(+), 120 deletions(-) create mode 100644 src/java/org/apache/fop/render/bitmap/TIFFCompressionValue.java delete mode 100644 src/java/org/apache/fop/render/bitmap/TIFFCompressionValues.java create mode 100644 test/java/org/apache/fop/render/bitmap/TIFFCompressionValueTestCase.java (limited to 'test/java/org/apache/fop') diff --git a/src/java/org/apache/fop/render/bitmap/AbstractBitmapDocumentHandler.java b/src/java/org/apache/fop/render/bitmap/AbstractBitmapDocumentHandler.java index 262caa53b..8d0ce14ac 100644 --- a/src/java/org/apache/fop/render/bitmap/AbstractBitmapDocumentHandler.java +++ b/src/java/org/apache/fop/render/bitmap/AbstractBitmapDocumentHandler.java @@ -84,7 +84,7 @@ public abstract class AbstractBitmapDocumentHandler extends AbstractBinaryWritin super(context); //Set target resolution int dpi = Math.round(context.getUserAgent().getTargetResolution()); - getSettings().getWriterParams().setResolution(dpi); + getSettings().setResolution(dpi); Map renderingOptions = getUserAgent().getRendererOptions(); setTargetBitmapSize((Dimension)renderingOptions.get(TARGET_BITMAP_SIZE)); diff --git a/src/java/org/apache/fop/render/bitmap/BitmapRenderingSettings.java b/src/java/org/apache/fop/render/bitmap/BitmapRenderingSettings.java index d239fe0fd..4363f1d5e 100644 --- a/src/java/org/apache/fop/render/bitmap/BitmapRenderingSettings.java +++ b/src/java/org/apache/fop/render/bitmap/BitmapRenderingSettings.java @@ -107,4 +107,27 @@ public class BitmapRenderingSettings extends Java2DRenderingSettings { return this.qualityRendering; } + /** + * Sets the compression method for the image writer. + * @param compressionMethod the compression method name + */ + public void setCompressionMethod(String compressionMethod) { + writerParams.setCompressionMethod(compressionMethod); + } + + /** + * Returns the compression method being used by the image writer. + * @return the compression method in use + */ + public String getCompressionMethod() { + return writerParams.getCompressionMethod(); + } + + /** + * Sets the resolution of the output image. + * @param dpi the dots-per-inch of the image + */ + public void setResolution(int dpi) { + writerParams.setResolution(dpi); + } } diff --git a/src/java/org/apache/fop/render/bitmap/TIFFCompressionValue.java b/src/java/org/apache/fop/render/bitmap/TIFFCompressionValue.java new file mode 100644 index 000000000..4e9c3bd26 --- /dev/null +++ b/src/java/org/apache/fop/render/bitmap/TIFFCompressionValue.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.render.bitmap; + +import java.awt.image.BufferedImage; + +/** + * Compression constants for TIFF image output. + */ +public enum TIFFCompressionValue { + /** No compression */ + NONE("NONE"), + /** JPEG compression */ + JPEG("JPEG"), + /** Packbits (RLE) compression */ + PACKBITS("PackBits"), + /** Deflate compression */ + DEFLATE("Deflate"), + /** LZW compression */ + LZW("LZW"), + /** ZLib compression */ + ZLIB("ZLib"), + /** CCITT Group 3 (T.4) compression */ + CCITT_T4("CCITT T.4", BufferedImage.TYPE_BYTE_BINARY, true), + /** CCITT Group 4 (T.6) compression */ + CCITT_T6("CCITT T.6", BufferedImage.TYPE_BYTE_BINARY, true); + + private final String name; + private final int imageType; + private boolean isCcitt; + + private TIFFCompressionValue(String name, int imageType, boolean isCcitt) { + this.name = name; + this.imageType = imageType; + this.isCcitt = isCcitt; + } + + private TIFFCompressionValue(String name) { + this(name, BufferedImage.TYPE_INT_ARGB, false); + } + + /** + * Returns the name of this compression type. + * @return the compression name + */ + String getName() { + return name; + } + + /** + * Returns an image type for this compression type, a constant from {@link BufferedImage} e.g. + * {@link BufferedImage#TYPE_INT_ARGB} for {@link #ZLIB} + * @return the image type + */ + int getImageType() { + return imageType; + } + + /** + * Returns whether or not this compression type is a CCITT type. + * @return true if the compression type is CCITT + */ + boolean hasCCITTCompression() { + return isCcitt; + } + + /** + * Return the TIFF compression constant given the string representing the type. In the case that + * the name doesn't match any of the compression values, null is returned. + * @param name the compression type name + * @return the compression constant + */ + static TIFFCompressionValue getType(String name) { + for (TIFFCompressionValue tiffConst : TIFFCompressionValue.values()) { + if (tiffConst.name.equalsIgnoreCase(name)) { + return tiffConst; + } + } + return null; + } +} diff --git a/src/java/org/apache/fop/render/bitmap/TIFFCompressionValues.java b/src/java/org/apache/fop/render/bitmap/TIFFCompressionValues.java deleted file mode 100644 index 71649022e..000000000 --- a/src/java/org/apache/fop/render/bitmap/TIFFCompressionValues.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.render.bitmap; - -/** - * Constants for TIFF output. - */ -public enum TIFFCompressionValues { - /** No compression */ - NONE("NONE"), - /** JPEG compression */ - JPEG("JPEG"), - /** Packbits (RLE) compression */ - PACKBITS("PackBits"), - /** Deflate compression */ - DEFLATE("Deflate"), - /** LZW compression */ - LZW("LZW"), - /** ZLib compression */ - ZLIB("ZLib"), - /** CCITT Group 4 (T.6) compression */ - CCITT_T6("CCITT T.6"), //CCITT Group 4 - /** CCITT Group 3 (T.4) compression */ - CCITT_T4("CCITT T.4"); //CCITT Group 3 - - private final String name; - - private TIFFCompressionValues(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public static TIFFCompressionValues getValue(String name) { - for (TIFFCompressionValues tiffConst : TIFFCompressionValues.values()) { - if (tiffConst.name.equalsIgnoreCase(name)) { - return tiffConst; - } - } - return null; - } -} diff --git a/src/java/org/apache/fop/render/bitmap/TIFFRenderer.java b/src/java/org/apache/fop/render/bitmap/TIFFRenderer.java index 5b75a372c..48da62ef7 100644 --- a/src/java/org/apache/fop/render/bitmap/TIFFRenderer.java +++ b/src/java/org/apache/fop/render/bitmap/TIFFRenderer.java @@ -38,7 +38,6 @@ import org.apache.commons.logging.Log; import org.apache.xmlgraphics.image.GraphicsUtil; import org.apache.xmlgraphics.image.rendered.FormatRed; import org.apache.xmlgraphics.image.writer.ImageWriter; -import org.apache.xmlgraphics.image.writer.ImageWriterParams; import org.apache.xmlgraphics.image.writer.ImageWriterRegistry; import org.apache.xmlgraphics.image.writer.MultiImageWriter; @@ -47,9 +46,9 @@ import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.MimeConstants; import org.apache.fop.render.java2d.Java2DRenderer; -import static org.apache.fop.render.bitmap.TIFFCompressionValues.CCITT_T4; -import static org.apache.fop.render.bitmap.TIFFCompressionValues.CCITT_T6; -import static org.apache.fop.render.bitmap.TIFFCompressionValues.PACKBITS; +import static org.apache.fop.render.bitmap.TIFFCompressionValue.CCITT_T4; +import static org.apache.fop.render.bitmap.TIFFCompressionValue.CCITT_T6; +import static org.apache.fop.render.bitmap.TIFFCompressionValue.PACKBITS; /** *

@@ -74,11 +73,7 @@ import static org.apache.fop.render.bitmap.TIFFCompressionValues.PACKBITS; */ public class TIFFRenderer extends Java2DRenderer { - /** ImageWriter parameters */ - private ImageWriterParams writerParams; - - /** Image Type as parameter for the BufferedImage constructor (see BufferedImage.TYPE_*) */ - private int bufferedImageType = BufferedImage.TYPE_INT_ARGB; + private BitmapRenderingSettings imageSettings; private OutputStream outputStream; @@ -94,11 +89,11 @@ public class TIFFRenderer extends Java2DRenderer { */ public TIFFRenderer(FOUserAgent userAgent) { super(userAgent); - writerParams = new ImageWriterParams(); - writerParams.setCompressionMethod(PACKBITS.getName()); - + imageSettings = new BitmapRenderingSettings(); + imageSettings.setCompressionMethod(PACKBITS.getName()); + imageSettings.setBufferedImageType(BufferedImage.TYPE_INT_ARGB); int dpi = Math.round(userAgent.getTargetResolution()); - writerParams.setResolution(dpi); + imageSettings.setResolution(dpi); } /** {@inheritDoc} */ @@ -129,7 +124,7 @@ public class TIFFRenderer extends Java2DRenderer { // Write all pages/images while (pageImagesItr.hasNext()) { RenderedImage img = (RenderedImage) pageImagesItr.next(); - multiWriter.writeImage(img, writerParams); + multiWriter.writeImage(img, imageSettings.getWriterParams()); } } finally { multiWriter.close(); @@ -139,7 +134,7 @@ public class TIFFRenderer extends Java2DRenderer { if (pageImagesItr.hasNext()) { renderedImage = (RenderedImage) pageImagesItr.next(); } - writer.writeImage(renderedImage, outputStream, writerParams); + writer.writeImage(renderedImage, outputStream, imageSettings.getWriterParams()); if (pageImagesItr.hasNext()) { BitmapRendererEventProducer eventProducer = BitmapRendererEventProducer.Provider.get( @@ -156,7 +151,7 @@ public class TIFFRenderer extends Java2DRenderer { /** {@inheritDoc} */ protected BufferedImage getBufferedImage(int bitmapWidth, int bitmapHeight) { - return new BufferedImage(bitmapWidth, bitmapHeight, bufferedImageType); + return new BufferedImage(bitmapWidth, bitmapHeight, imageSettings.getBufferedImageType()); } /** Private inner class to lazy page rendering. */ @@ -195,7 +190,7 @@ public class TIFFRenderer extends Java2DRenderer { throw new NoSuchElementException(e.getMessage()); } - TIFFCompressionValues compression = TIFFCompressionValues.getValue(writerParams.getCompressionMethod()); + TIFFCompressionValue compression = TIFFCompressionValue.getType(imageSettings.getCompressionMethod()); if (compression == CCITT_T4 || compression == CCITT_T6) { return pageImage; } else { @@ -226,11 +221,14 @@ public class TIFFRenderer extends Java2DRenderer { /** @param bufferedImageType an image type */ public void setBufferedImageType(int bufferedImageType) { - this.bufferedImageType = bufferedImageType; + imageSettings.setBufferedImageType(bufferedImageType); } - /** @return image writer parameters */ - public ImageWriterParams getWriterParams() { - return writerParams; + /** + * Returns the settings for the image rendering. + * @return the image rendering settings + */ + public BitmapRenderingSettings getRenderingSettings() { + return imageSettings; } } diff --git a/src/java/org/apache/fop/render/bitmap/TIFFRendererConfig.java b/src/java/org/apache/fop/render/bitmap/TIFFRendererConfig.java index 5417ecc1e..3c833d3b0 100644 --- a/src/java/org/apache/fop/render/bitmap/TIFFRendererConfig.java +++ b/src/java/org/apache/fop/render/bitmap/TIFFRendererConfig.java @@ -37,7 +37,7 @@ import org.apache.fop.render.RendererConfigOption; public final class TIFFRendererConfig extends BitmapRendererConfig { public enum TIFFRendererOption implements RendererConfigOption { - COMPRESSION("compression", TIFFCompressionValues.PACKBITS); + COMPRESSION("compression", TIFFCompressionValue.PACKBITS); private final String name; private final Object defaultValue; @@ -63,8 +63,8 @@ public final class TIFFRendererConfig extends BitmapRendererConfig { super(fontConfig); } - public TIFFCompressionValues getCompressionType() { - return (TIFFCompressionValues) params.get(TIFFRendererOption.COMPRESSION); + public TIFFCompressionValue getCompressionType() { + return (TIFFCompressionValue) params.get(TIFFRendererOption.COMPRESSION); } /** @@ -92,9 +92,8 @@ public final class TIFFRendererConfig extends BitmapRendererConfig { .parse(cfg, userAgent.validateStrictly())); super.build(config, userAgent, cfg); if (cfg != null) { - setParam(TIFFRendererOption.COMPRESSION, - TIFFCompressionValues.getValue(getValue(cfg, - TIFFRendererOption.COMPRESSION))); + setParam(TIFFRendererOption.COMPRESSION, + TIFFCompressionValue.getType(getValue(cfg, TIFFRendererOption.COMPRESSION))); } return config; } diff --git a/src/java/org/apache/fop/render/bitmap/TIFFRendererConfigurator.java b/src/java/org/apache/fop/render/bitmap/TIFFRendererConfigurator.java index b10f2a381..593934b45 100644 --- a/src/java/org/apache/fop/render/bitmap/TIFFRendererConfigurator.java +++ b/src/java/org/apache/fop/render/bitmap/TIFFRendererConfigurator.java @@ -19,13 +19,9 @@ package org.apache.fop.render.bitmap; -import java.awt.image.BufferedImage; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.xmlgraphics.image.writer.ImageWriterParams; - import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.render.Renderer; @@ -33,9 +29,7 @@ import org.apache.fop.render.RendererConfig.RendererConfigParser; import org.apache.fop.render.bitmap.TIFFRendererConfig.TIFFRendererConfigParser; import org.apache.fop.render.intermediate.IFDocumentHandler; -import static org.apache.fop.render.bitmap.TIFFCompressionValues.CCITT_T4; -import static org.apache.fop.render.bitmap.TIFFCompressionValues.CCITT_T6; -import static org.apache.fop.render.bitmap.TIFFCompressionValues.NONE; +import static org.apache.fop.render.bitmap.TIFFCompressionValue.NONE; /** * TIFF Renderer configurator @@ -62,43 +56,34 @@ public class TIFFRendererConfigurator extends BitmapRendererConfigurator { final TIFFRendererConfig config = (TIFFRendererConfig) getRendererConfig(renderer); if (config != null) { TIFFRenderer tiffRenderer = (TIFFRenderer) renderer; - //set compression - tiffRenderer.setBufferedImageType(getCompressionType(config, tiffRenderer.getWriterParams())); + setCompressionMethod(config.getCompressionType(), tiffRenderer.getRenderingSettings()); } super.configure(renderer); } - private int getCompressionType(TIFFRendererConfig config, ImageWriterParams writerParms) - throws FOPException { - //Some compression formats need a special image format: - TIFFCompressionValues compression = config.getCompressionType(); + private void setCompressionMethod(TIFFCompressionValue compression, + BitmapRenderingSettings settings) throws FOPException { if (compression != null) { if (compression != NONE) { - writerParms.setCompressionMethod(compression.getName()); + settings.setCompressionMethod(compression.getName()); } if (LOG.isInfoEnabled()) { LOG.info("TIFF compression set to " + compression.getName()); } - } - return getBufferedImageTypeFor(compression); - } - - private int getBufferedImageTypeFor(TIFFCompressionValues compressionType) { - if (compressionType == CCITT_T6 || compressionType == CCITT_T4) { - return BufferedImage.TYPE_BYTE_BINARY; - } else { - return BufferedImage.TYPE_INT_ARGB; + if (compression.hasCCITTCompression()) { + settings.setBufferedImageType(compression.getImageType()); + } } } /** {@inheritDoc} */ public void configure(IFDocumentHandler documentHandler) throws FOPException { - final TIFFRendererConfig tiffConfig = (TIFFRendererConfig) getRendererConfig(documentHandler); - if (tiffConfig != null) { + final TIFFRendererConfig config = (TIFFRendererConfig) getRendererConfig(documentHandler); + if (config != null) { TIFFDocumentHandler tiffHandler = (TIFFDocumentHandler) documentHandler; BitmapRenderingSettings settings = tiffHandler.getSettings(); configure(documentHandler, settings, new TIFFRendererConfigParser()); - settings.setBufferedImageType(getCompressionType(tiffConfig, settings.getWriterParams())); + setCompressionMethod(config.getCompressionType(), settings); } } diff --git a/status.xml b/status.xml index a0d2267a3..89f9bf4d2 100644 --- a/status.xml +++ b/status.xml @@ -62,6 +62,10 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> + + Prevented the TIFF configurator from overriding the Bitmap configurator unless CCITT + compression is enabled. + Removed the Attribute Qualifier on TLEs as they aren't used. diff --git a/test/java/org/apache/fop/render/bitmap/TIFFCompressionValueTestCase.java b/test/java/org/apache/fop/render/bitmap/TIFFCompressionValueTestCase.java new file mode 100644 index 000000000..c4285f034 --- /dev/null +++ b/test/java/org/apache/fop/render/bitmap/TIFFCompressionValueTestCase.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +package org.apache.fop.render.bitmap; + +import java.awt.image.BufferedImage; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import static org.apache.fop.render.bitmap.TIFFCompressionValue.CCITT_T4; +import static org.apache.fop.render.bitmap.TIFFCompressionValue.CCITT_T6; +import static org.apache.fop.render.bitmap.TIFFCompressionValue.DEFLATE; +import static org.apache.fop.render.bitmap.TIFFCompressionValue.JPEG; +import static org.apache.fop.render.bitmap.TIFFCompressionValue.LZW; +import static org.apache.fop.render.bitmap.TIFFCompressionValue.NONE; +import static org.apache.fop.render.bitmap.TIFFCompressionValue.PACKBITS; +import static org.apache.fop.render.bitmap.TIFFCompressionValue.ZLIB; + +public class TIFFCompressionValueTestCase { + + @Test + public void testGetName() { + testCompressionName("NONE", NONE); + testCompressionName("JPEG", JPEG); + testCompressionName("PackBits", PACKBITS); + testCompressionName("Deflate", DEFLATE); + testCompressionName("LZW", LZW); + testCompressionName("ZLib", ZLIB); + testCompressionName("CCITT T.4", CCITT_T4); + testCompressionName("CCITT T.6", CCITT_T6); + } + + private void testCompressionName(String name, TIFFCompressionValue expected) { + assertEquals(name, expected.getName()); + assertEquals(expected, TIFFCompressionValue.getType(name)); + } + + @Test + public void testGetImageType() { + for (TIFFCompressionValue value : TIFFCompressionValue.values()) { + if (value == CCITT_T4 || value == CCITT_T6) { + assertEquals(BufferedImage.TYPE_BYTE_BINARY, value.getImageType()); + } else { + assertEquals(BufferedImage.TYPE_INT_ARGB, value.getImageType()); + } + } + } + + @Test + public void testHasCCITTCompression() { + for (TIFFCompressionValue value : TIFFCompressionValue.values()) { + if (value == CCITT_T4 || value == CCITT_T6) { + assertTrue(value.hasCCITTCompression()); + } else { + assertFalse(value.hasCCITTCompression()); + } + } + } +} diff --git a/test/java/org/apache/fop/render/bitmap/TIFFRendererConfigParserTestCase.java b/test/java/org/apache/fop/render/bitmap/TIFFRendererConfigParserTestCase.java index d938d094d..487b8cb9f 100644 --- a/test/java/org/apache/fop/render/bitmap/TIFFRendererConfigParserTestCase.java +++ b/test/java/org/apache/fop/render/bitmap/TIFFRendererConfigParserTestCase.java @@ -47,7 +47,7 @@ public class TIFFRendererConfigParserTestCase @Test public void testCompression() throws Exception { - for (TIFFCompressionValues value : TIFFCompressionValues.values()) { + for (TIFFCompressionValue value : TIFFCompressionValue.values()) { parseConfig(createRenderer().setCompressionMode(value.getName())); assertEquals(value, getConfig().getCompressionType()); } diff --git a/test/java/org/apache/fop/render/bitmap/TIFFRendererConfiguratorTestCase.java b/test/java/org/apache/fop/render/bitmap/TIFFRendererConfiguratorTestCase.java index 9dd40e030..c97d73333 100644 --- a/test/java/org/apache/fop/render/bitmap/TIFFRendererConfiguratorTestCase.java +++ b/test/java/org/apache/fop/render/bitmap/TIFFRendererConfiguratorTestCase.java @@ -23,14 +23,15 @@ import java.awt.image.BufferedImage; import org.junit.Test; +import static org.junit.Assert.assertEquals; + import org.apache.fop.apps.FopConfBuilder; import org.apache.fop.apps.MimeConstants; import org.apache.fop.apps.TIFFRendererConfBuilder; import org.apache.fop.render.bitmap.TIFFRendererConfig.TIFFRendererConfigParser; -import static org.apache.fop.render.bitmap.TIFFCompressionValues.CCITT_T4; -import static org.apache.fop.render.bitmap.TIFFCompressionValues.CCITT_T6; -import static org.junit.Assert.assertEquals; +import static org.apache.fop.render.bitmap.TIFFCompressionValue.CCITT_T4; +import static org.apache.fop.render.bitmap.TIFFCompressionValue.CCITT_T6; public class TIFFRendererConfiguratorTestCase extends AbstractBitmapRendererConfiguratorTest { @@ -51,7 +52,7 @@ public class TIFFRendererConfiguratorTestCase extends AbstractBitmapRendererConf @Test @Override public void testColorModes() throws Exception { - for (TIFFCompressionValues value : TIFFCompressionValues.values()) { + for (TIFFCompressionValue value : TIFFCompressionValue.values()) { parseConfig(createBuilder().setCompressionMode(value.getName())); if (value == CCITT_T6 || value == CCITT_T4) { assertEquals(BufferedImage.TYPE_BYTE_BINARY, settings.getBufferedImageType()); -- cgit v1.2.3 From 10e39f016aa7dceacb24fdd8258c7e67e44f1495 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Tue, 4 Sep 2012 22:25:49 +0000 Subject: Bugzilla #53598: avoid IllegalArgumentException by setting the breakClass field in BreakElement to a legal default value Patch by Robert Meyer, applied with some modifications git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1380923 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/layoutmgr/BreakElement.java | 21 +++++++++--- status.xml | 4 +++ .../apache/fop/layoutmgr/BreakElementTestCase.java | 37 ++++++++++++++++++++++ 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 test/java/org/apache/fop/layoutmgr/BreakElementTestCase.java (limited to 'test/java/org/apache/fop') diff --git a/src/java/org/apache/fop/layoutmgr/BreakElement.java b/src/java/org/apache/fop/layoutmgr/BreakElement.java index 77e7f140b..f3f173d8c 100644 --- a/src/java/org/apache/fop/layoutmgr/BreakElement.java +++ b/src/java/org/apache/fop/layoutmgr/BreakElement.java @@ -21,6 +21,8 @@ package org.apache.fop.layoutmgr; import java.util.List; +import org.apache.fop.fo.Constants; + /** * This class represents an unresolved break possibility. */ @@ -28,7 +30,7 @@ public class BreakElement extends UnresolvedListElement { private int penaltyWidth; private int penaltyValue; - private int breakClass = -1; + private int breakClass; private List pendingBeforeMarks; private List pendingAfterMarks; @@ -39,7 +41,7 @@ public class BreakElement extends UnresolvedListElement { * @param context the layout context which contains the pending conditional elements */ public BreakElement(Position position, int penaltyValue, LayoutContext context) { - this(position, penaltyValue, -1, context); + this(position, penaltyValue, Constants.EN_AUTO, context); } /** @@ -80,7 +82,7 @@ public class BreakElement extends UnresolvedListElement { super(position); this.penaltyWidth = penaltyWidth; this.penaltyValue = penaltyValue; - this.breakClass = breakClass; + setBreakClass(breakClass); this.pendingBeforeMarks = context.getPendingBeforeMarks(); this.pendingAfterMarks = context.getPendingAfterMarks(); } @@ -142,13 +144,24 @@ public class BreakElement extends UnresolvedListElement { * * @param breakClass one of * {@link org.apache.fop.fo.Constants#EN_AUTO}, + * {@link org.apache.fop.fo.Constants#EN_LINE}, * {@link org.apache.fop.fo.Constants#EN_COLUMN}, * {@link org.apache.fop.fo.Constants#EN_PAGE}, * {@link org.apache.fop.fo.Constants#EN_EVEN_PAGE}, * {@link org.apache.fop.fo.Constants#EN_ODD_PAGE}. */ public void setBreakClass(int breakClass) { - this.breakClass = breakClass; + switch (breakClass) { + case Constants.EN_AUTO: + case Constants.EN_LINE: + case Constants.EN_COLUMN: + case Constants.EN_PAGE: + case Constants.EN_EVEN_PAGE: + case Constants.EN_ODD_PAGE: + this.breakClass = breakClass; + break; + default: throw new IllegalArgumentException("Illegal value for break class: " + breakClass); + } } /** @return the pending border and padding elements at the before edge */ diff --git a/status.xml b/status.xml index adde62df3..3e73488fb 100644 --- a/status.xml +++ b/status.xml @@ -62,6 +62,10 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> + + Always set the breakClass field to a legal value in BreakElement, so as to avoid + IllegalArgumentExceptions in other parts of the code. + Restored support for break-before on fo:table. diff --git a/test/java/org/apache/fop/layoutmgr/BreakElementTestCase.java b/test/java/org/apache/fop/layoutmgr/BreakElementTestCase.java new file mode 100644 index 000000000..c836bce7c --- /dev/null +++ b/test/java/org/apache/fop/layoutmgr/BreakElementTestCase.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +package org.apache.fop.layoutmgr; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +import org.apache.fop.fo.Constants; + +public class BreakElementTestCase { + + /** + * Tests that the constructor sets the break class to a valid default value. + */ + @Test + public void breakClassMustBeValid() { + LayoutContext context = LayoutContext.newInstance(); + BreakElement breakElement = new BreakElement(new Position(null), 0, context); + assertEquals(Constants.EN_AUTO, breakElement.getBreakClass()); + } +} -- cgit v1.2.3 From d99dc75ffa1d37817ce18cff188fdabc29f9f73e Mon Sep 17 00:00:00 2001 From: Peter Hancock Date: Thu, 13 Sep 2012 12:45:27 +0000 Subject: Bugzilla #53865: Add Rows per Strip configuration for Tiff renderer git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1384310 13f79535-47bb-0310-9956-ffa450edef68 --- lib/xmlgraphics-commons-1.5svn.jar | Bin 603006 -> 607764 bytes src/documentation/content/xdocs/trunk/output.xml | 5 +++++ .../fop/render/bitmap/TIFFDocumentHandler.java | 4 ++++ .../fop/render/bitmap/TIFFRendererConfig.java | 19 ++++++++++++++++--- .../render/bitmap/TIFFRendererConfigurator.java | 8 +++++++- status.xml | 5 +++++ .../apache/fop/apps/TIFFRendererConfBuilder.java | 7 ++++++- .../bitmap/TIFFRendererConfigParserTestCase.java | 12 +++++++++++- .../bitmap/TIFFRendererConfiguratorTestCase.java | 13 ++++++++++++- 9 files changed, 66 insertions(+), 7 deletions(-) (limited to 'test/java/org/apache/fop') diff --git a/lib/xmlgraphics-commons-1.5svn.jar b/lib/xmlgraphics-commons-1.5svn.jar index 802ae49ae..1a56594b2 100644 Binary files a/lib/xmlgraphics-commons-1.5svn.jar and b/lib/xmlgraphics-commons-1.5svn.jar differ diff --git a/src/documentation/content/xdocs/trunk/output.xml b/src/documentation/content/xdocs/trunk/output.xml index 4f792971b..5de820c18 100644 --- a/src/documentation/content/xdocs/trunk/output.xml +++ b/src/documentation/content/xdocs/trunk/output.xml @@ -1270,6 +1270,7 @@ Note that the value of the encoding attribute in the example is the double-byte true CCITT T.6 + true ]]>

@@ -1303,6 +1304,10 @@ Note that the value of the encoding attribute in the example is the double-byte added separately. The internal TIFF codec from XML Graphics Commons only supports PackBits, Deflate and JPEG compression for writing. +

+ The default value for the "single-strip" is "false" resulting in the RowsPerStrip Tiff Tag equal to the number of rows. + If set to true RowsPerStrip is set to 1. +

Runtime Rendering Options diff --git a/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java b/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java index 48e79520f..d536167c3 100644 --- a/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java +++ b/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java @@ -19,9 +19,12 @@ package org.apache.fop.render.bitmap; +import org.apache.xmlgraphics.image.writer.ResolutionUnit; + import org.apache.fop.apps.MimeConstants; import org.apache.fop.render.bitmap.TIFFRendererConfig.TIFFRendererConfigParser; import org.apache.fop.render.intermediate.IFContext; +import org.apache.fop.render.intermediate.IFDocumentHandler; import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator; /** @@ -32,6 +35,7 @@ public class TIFFDocumentHandler extends AbstractBitmapDocumentHandler { TIFFDocumentHandler(IFContext context) { super(context); + getSettings().getWriterParams().setResolutionUnit(ResolutionUnit.CENTIMETER); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/render/bitmap/TIFFRendererConfig.java b/src/java/org/apache/fop/render/bitmap/TIFFRendererConfig.java index 3c833d3b0..1e44397f6 100644 --- a/src/java/org/apache/fop/render/bitmap/TIFFRendererConfig.java +++ b/src/java/org/apache/fop/render/bitmap/TIFFRendererConfig.java @@ -23,21 +23,24 @@ import java.util.EnumMap; import org.apache.avalon.framework.configuration.Configuration; -import org.apache.xmlgraphics.util.MimeConstants; - import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.apps.MimeConstants; import org.apache.fop.fonts.DefaultFontConfig; import org.apache.fop.fonts.DefaultFontConfig.DefaultFontConfigParser; import org.apache.fop.render.RendererConfigOption; +import static org.apache.fop.render.bitmap.TIFFCompressionValue.PACKBITS; + /** * The renderer configuration object for the TIFF renderer. */ public final class TIFFRendererConfig extends BitmapRendererConfig { public enum TIFFRendererOption implements RendererConfigOption { - COMPRESSION("compression", TIFFCompressionValue.PACKBITS); + COMPRESSION("compression", PACKBITS), + /** option to encode one row per strip or a all rows in a single strip*/ + SINGLE_STRIP("single-strip", Boolean.FALSE); private final String name; private final Object defaultValue; @@ -67,6 +70,14 @@ public final class TIFFRendererConfig extends BitmapRendererConfig { return (TIFFCompressionValue) params.get(TIFFRendererOption.COMPRESSION); } + /** + * @return True if all rows are contained in a single strip, False each strip contains one row or null + * if not set. + */ + public Boolean isSingleStrip() { + return (Boolean) params.get(TIFFRendererOption.SINGLE_STRIP); + } + /** * The TIFF renderer configuration parser. */ @@ -94,6 +105,8 @@ public final class TIFFRendererConfig extends BitmapRendererConfig { if (cfg != null) { setParam(TIFFRendererOption.COMPRESSION, TIFFCompressionValue.getType(getValue(cfg, TIFFRendererOption.COMPRESSION))); + setParam(TIFFRendererOption.SINGLE_STRIP, Boolean.valueOf(getValue(cfg, + TIFFRendererOption.SINGLE_STRIP))); } return config; } diff --git a/src/java/org/apache/fop/render/bitmap/TIFFRendererConfigurator.java b/src/java/org/apache/fop/render/bitmap/TIFFRendererConfigurator.java index 593934b45..19abb8131 100644 --- a/src/java/org/apache/fop/render/bitmap/TIFFRendererConfigurator.java +++ b/src/java/org/apache/fop/render/bitmap/TIFFRendererConfigurator.java @@ -76,7 +76,12 @@ public class TIFFRendererConfigurator extends BitmapRendererConfigurator { } } - /** {@inheritDoc} */ + private boolean isSingleStrip(TIFFRendererConfig config) { + Boolean singleRowPerStrip = config.isSingleStrip(); + return singleRowPerStrip == null ? false : singleRowPerStrip; + } + + @Override public void configure(IFDocumentHandler documentHandler) throws FOPException { final TIFFRendererConfig config = (TIFFRendererConfig) getRendererConfig(documentHandler); if (config != null) { @@ -84,6 +89,7 @@ public class TIFFRendererConfigurator extends BitmapRendererConfigurator { BitmapRenderingSettings settings = tiffHandler.getSettings(); configure(documentHandler, settings, new TIFFRendererConfigParser()); setCompressionMethod(config.getCompressionType(), settings); + settings.getWriterParams().setSingleStrip(isSingleStrip(config)); } } diff --git a/status.xml b/status.xml index 3e73488fb..b39d3d9e0 100644 --- a/status.xml +++ b/status.xml @@ -62,6 +62,11 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> + + Added configuration for RowPerStrip configuration in the Tiff renderer. + RowsPerStrip can be configured to 1 or to the total # of rows. + See docs for fop.xconf configuration details. + Always set the breakClass field to a legal value in BreakElement, so as to avoid IllegalArgumentExceptions in other parts of the code. diff --git a/test/java/org/apache/fop/apps/TIFFRendererConfBuilder.java b/test/java/org/apache/fop/apps/TIFFRendererConfBuilder.java index 8b72a558d..f39df2988 100644 --- a/test/java/org/apache/fop/apps/TIFFRendererConfBuilder.java +++ b/test/java/org/apache/fop/apps/TIFFRendererConfBuilder.java @@ -20,7 +20,7 @@ package org.apache.fop.apps; import static org.apache.fop.render.bitmap.TIFFRendererConfig.TIFFRendererOption.COMPRESSION; - +import static org.apache.fop.render.bitmap.TIFFRendererConfig.TIFFRendererOption.SINGLE_STRIP; public class TIFFRendererConfBuilder extends BitmapRendererConfBuilder { public TIFFRendererConfBuilder() { @@ -31,4 +31,9 @@ public class TIFFRendererConfBuilder extends BitmapRendererConfBuilder { createTextElement(COMPRESSION, mode); return this; } + + public TIFFRendererConfBuilder setSingleStrip(boolean single) { + createTextElement(SINGLE_STRIP, String.valueOf(single)); + return this; + } } diff --git a/test/java/org/apache/fop/render/bitmap/TIFFRendererConfigParserTestCase.java b/test/java/org/apache/fop/render/bitmap/TIFFRendererConfigParserTestCase.java index 487b8cb9f..69326559e 100644 --- a/test/java/org/apache/fop/render/bitmap/TIFFRendererConfigParserTestCase.java +++ b/test/java/org/apache/fop/render/bitmap/TIFFRendererConfigParserTestCase.java @@ -26,9 +26,11 @@ import org.apache.fop.apps.TIFFRendererConfBuilder; import org.apache.fop.render.bitmap.TIFFRendererConfig.TIFFRendererConfigParser; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; public class TIFFRendererConfigParserTestCase - extends AbstractBitmapRendererConfigParserTester { +extends AbstractBitmapRendererConfigParserTester { public TIFFRendererConfigParserTestCase() { super(new TIFFRendererConfigParser()); @@ -52,4 +54,12 @@ public class TIFFRendererConfigParserTestCase assertEquals(value, getConfig().getCompressionType()); } } + + @Test + public void testSingleStrip() throws Exception { + parseConfig(createRenderer().setSingleStrip(true)); + assertTrue(getConfig().isSingleStrip()); + parseConfig(createRenderer().setSingleStrip(false)); + assertFalse(getConfig().isSingleStrip()); + } } diff --git a/test/java/org/apache/fop/render/bitmap/TIFFRendererConfiguratorTestCase.java b/test/java/org/apache/fop/render/bitmap/TIFFRendererConfiguratorTestCase.java index c97d73333..3e6c951a6 100644 --- a/test/java/org/apache/fop/render/bitmap/TIFFRendererConfiguratorTestCase.java +++ b/test/java/org/apache/fop/render/bitmap/TIFFRendererConfiguratorTestCase.java @@ -23,7 +23,6 @@ import java.awt.image.BufferedImage; import org.junit.Test; -import static org.junit.Assert.assertEquals; import org.apache.fop.apps.FopConfBuilder; import org.apache.fop.apps.MimeConstants; @@ -32,6 +31,9 @@ import org.apache.fop.render.bitmap.TIFFRendererConfig.TIFFRendererConfigParser; import static org.apache.fop.render.bitmap.TIFFCompressionValue.CCITT_T4; import static org.apache.fop.render.bitmap.TIFFCompressionValue.CCITT_T6; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; public class TIFFRendererConfiguratorTestCase extends AbstractBitmapRendererConfiguratorTest { @@ -61,4 +63,13 @@ public class TIFFRendererConfiguratorTestCase extends AbstractBitmapRendererConf } } } + + @Test + public void testSingleStrip() throws Exception { + parseConfig(createBuilder().setSingleStrip(true)); + assertTrue(settings.getWriterParams().isSingleStrip()); + parseConfig(createBuilder().setSingleStrip(false)); + assertFalse(settings.getWriterParams().isSingleStrip()); + } + } -- cgit v1.2.3 From ccd2678cd0e9c0d3b85117725bb7fb59d8c7338d Mon Sep 17 00:00:00 2001 From: Mehdi Houshmand Date: Fri, 14 Sep 2012 09:13:40 +0000 Subject: Bugzilla#53868: Full font embedding in PDF git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1384690 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/fonts/CIDFont.java | 4 +- src/java/org/apache/fop/fonts/CIDFull.java | 113 ++++++++++++++++++ src/java/org/apache/fop/fonts/CIDSet.java | 90 ++++++++++++++ src/java/org/apache/fop/fonts/CIDSubset.java | 129 ++++++++------------- src/java/org/apache/fop/fonts/FontReader.java | 2 +- src/java/org/apache/fop/fonts/LazyFont.java | 3 + src/java/org/apache/fop/fonts/MultiByteFont.java | 69 ++++++++--- .../apache/fop/fonts/truetype/TTFFontLoader.java | 2 +- src/java/org/apache/fop/pdf/PDFFactory.java | 75 +++++------- .../fop/render/bitmap/TIFFDocumentHandler.java | 1 - src/java/org/apache/fop/render/ps/PSFontUtils.java | 30 ++++- status.xml | 3 + .../java/org/apache/fop/fonts/CIDFullTestCase.java | 120 +++++++++++++++++++ .../org/apache/fop/pdf/PDFFactoryTestCase.java | 12 +- 14 files changed, 492 insertions(+), 161 deletions(-) create mode 100644 src/java/org/apache/fop/fonts/CIDFull.java create mode 100644 src/java/org/apache/fop/fonts/CIDSet.java create mode 100644 test/java/org/apache/fop/fonts/CIDFullTestCase.java (limited to 'test/java/org/apache/fop') diff --git a/src/java/org/apache/fop/fonts/CIDFont.java b/src/java/org/apache/fop/fonts/CIDFont.java index 82213dd65..dc398263e 100644 --- a/src/java/org/apache/fop/fonts/CIDFont.java +++ b/src/java/org/apache/fop/fonts/CIDFont.java @@ -29,7 +29,7 @@ import org.apache.fop.apps.io.InternalResourceResolver; public abstract class CIDFont extends CustomFont { /** Contains the character widths for all characters in the font */ - protected int[] width = null; + protected int[] width; /** * @param resourceResolver the URI resolver for controlling file access @@ -69,7 +69,7 @@ public abstract class CIDFont extends CustomFont { * Returns the subset information for this font. * @return the subset information */ - public abstract CIDSubset getCIDSubset(); + public abstract CIDSet getCIDSet(); // ---- Optional ---- /** diff --git a/src/java/org/apache/fop/fonts/CIDFull.java b/src/java/org/apache/fop/fonts/CIDFull.java new file mode 100644 index 000000000..ee062a2bb --- /dev/null +++ b/src/java/org/apache/fop/fonts/CIDFull.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.fonts; + +import java.util.BitSet; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.fop.util.CharUtilities; + +/** + * Provides methods to get font information. + * Naming: + * glyph index: original index of the glyph in the non-subset font (!= unicode index) + * character selector: index into a set of glyphs. For subset CID fonts, this starts at 0. For non-subset + * fonts, this is the same as the glyph index. + * Unicode index: The Unicode codepoint of a character. + * Glyph name: the Adobe glyph name (as found in Glyphs.java) + */ +public class CIDFull implements CIDSet { + + private BitSet glyphIndices; + private final MultiByteFont font; + + public CIDFull(MultiByteFont mbf) { + font = mbf; + } + + private void initGlyphIndices() { + // this cannot be called in the constructor since the font is not ready... + if (glyphIndices == null) { + glyphIndices = font.getGlyphIndices(); + } + } + + /** {@inheritDoc} */ + public int getOriginalGlyphIndex(int index) { + return index; + } + + /** {@inheritDoc} */ + public char getUnicode(int index) { + initGlyphIndices(); + if (glyphIndices.get(index)) { + return (char) index; + } else { + return CharUtilities.NOT_A_CHARACTER; + } + } + + /** {@inheritDoc} */ + public int mapChar(int glyphIndex, char unicode) { + return (char) glyphIndex; + } + + /** {@inheritDoc} */ + public Map getGlyphs() { + // this is never really called for full embedded fonts but the equivalent map would be the identity + initGlyphIndices(); + Map glyphs = new HashMap(); + int nextBitSet = 0; + for (int j = 0; j < glyphIndices.cardinality(); j++) { + nextBitSet = glyphIndices.nextSetBit(nextBitSet); + glyphs.put(Integer.valueOf(nextBitSet), Integer.valueOf(nextBitSet)); + nextBitSet++; + } + return Collections.unmodifiableMap(glyphs); + } + + /** {@inheritDoc} */ + public char[] getChars() { + return font.getChars(); + } + + /** {@inheritDoc} */ + public int getNumberOfGlyphs() { + initGlyphIndices(); + // note: the real number of glyphs is given by the cardinality() method (not the length()) but since + // we will pad gaps in the indices with zeros we really want the length() here. this method is only + // called when embedding a font in PostScript and this will be the value of the CIDCount entry + return glyphIndices.length(); + } + + /** {@inheritDoc} */ + public BitSet getGlyphIndices() { + initGlyphIndices(); + return glyphIndices; + } + + /** {@inheritDoc} */ + public int[] getWidths() { + return font.getWidths(); + } + +} diff --git a/src/java/org/apache/fop/fonts/CIDSet.java b/src/java/org/apache/fop/fonts/CIDSet.java new file mode 100644 index 000000000..30d1a5ec5 --- /dev/null +++ b/src/java/org/apache/fop/fonts/CIDSet.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.fonts; + +import java.util.BitSet; +import java.util.Map; + +/** + * Declares methods to retrieve font information (glyph indices, widths, unicode values) from a CID font. + */ +public interface CIDSet { + + /** + * Returns the original index of the glyph inside the (non-subset) font's glyph list. This + * index can be used to access the character width information, for example. + * @param index the subset index (character selector) to access the glyph + * @return the original index (or -1 if no glyph index is available for the subset index) + */ + int getOriginalGlyphIndex(int index); + + /** + * Returns the Unicode value for a subset index (character selector). If there's no such + * Unicode value, the "NOT A CHARACTER" (0xFFFF) is returned. + * @param subsetIndex the subset index (character selector) + * @return the Unicode value or "NOT A CHARACTER" (0xFFFF) + */ + char getUnicode(int index); + + /** + * Maps a character to a character selector for a font subset. If the character isn't in the + * subset, yet, it is added and a new character selector returned. Otherwise, the already + * allocated character selector is returned from the existing map/subset. + * @param glyphIndex the glyph index of the character + * @param unicode the Unicode index of the character + * @return the subset index + */ + int mapChar(int glyphIndex, char unicode); + + /** + * Returns an unmodifiable Map of the font subset. It maps from glyph index to + * character selector (i.e. the subset index in this case). + * @return Map Map<Integer, Integer> of the font subset + */ + Map getGlyphs(); + + /** + * Returns a char array containing all Unicode characters that are in the subset. + * @return a char array with all used Unicode characters + */ + char[] getChars(); + + /** + * Returns the number of glyphs in the subset. + * @return the number of glyphs in the subset + */ + int getNumberOfGlyphs(); + + /** + * Returns a BitSet with bits set for each available glyph index in the subset. + * @return a BitSet indicating available glyph indices + */ + BitSet getGlyphIndices(); + + /** + * Return the array of widths. + *

+ * This is used to get an array for inserting in an output format. + * It should not be used for lookup. + * @return an array of widths + */ + int[] getWidths(); + +} diff --git a/src/java/org/apache/fop/fonts/CIDSubset.java b/src/java/org/apache/fop/fonts/CIDSubset.java index 372a638d1..f442c13ed 100644 --- a/src/java/org/apache/fop/fonts/CIDSubset.java +++ b/src/java/org/apache/fop/fonts/CIDSubset.java @@ -26,18 +26,16 @@ import java.util.Map; import org.apache.fop.util.CharUtilities; -//Naming: -//glyph index: original index of the glyph in the non-subset font (!= unicode index) -//character selector: index into a set of glyphs. For subset CID fonts, this starts at 0. For -// non-subset fonts, this is the same as the glyph index. -//Unicode index: The Unicode codepoint of a character. -//Glyph name: the Adobe glyph name (as found in Glyphs.java) - /** - * Keeps track of the glyphs used in a document. This information is later used to build - * a subset of a font. + * Provides methods to get font information. + * Naming: + * glyph index: original index of the glyph in the non-subset font (!= unicode index) + * character selector: index into a set of glyphs. For subset CID fonts, this starts at 0. For non-subset + * fonts, this is the same as the glyph index. + * Unicode index: The Unicode codepoint of a character. + * Glyph name: the Adobe glyph name (as found in Glyphs.java) */ -public class CIDSubset { +public class CIDSubset implements CIDSet { /** * usedGlyphs contains orginal, new glyph index (glyph index -> char selector) @@ -48,51 +46,36 @@ public class CIDSubset { * usedGlyphsIndex contains new glyph, original index (char selector -> glyph index) */ private Map usedGlyphsIndex = new HashMap(); - private int usedGlyphsCount = 0; + private int usedGlyphsCount; /** * usedCharsIndex contains new glyph, original char (char selector -> Unicode) */ private Map usedCharsIndex = new HashMap(); - /** - * Default constructor. - */ - public CIDSubset() { - } + private final MultiByteFont font; - /** - * Adds the first glyph which is reserved for .notdef for all CID subsets. - */ - public void setupFirstGlyph() { - usedGlyphs.put(Integer.valueOf(0), Integer.valueOf(0)); - usedGlyphsIndex.put(Integer.valueOf(0), Integer.valueOf(0)); + public CIDSubset(MultiByteFont mbf) { + font = mbf; + // The zeroth value is reserved for .notdef + usedGlyphs.put(0, 0); + usedGlyphsIndex.put(0, 0); usedGlyphsCount++; } - /** - * Returns the original index of the glyph inside the (non-subset) font's glyph list. This - * index can be used to access the character width information, for example. - * @param subsetIndex the subset index (character selector) to access the glyph - * @return the original index (or -1 if no glyph index is available for the subset index) - */ - public int getGlyphIndexForSubsetIndex(int subsetIndex) { - Integer glyphIndex = usedGlyphsIndex.get(Integer.valueOf(subsetIndex)); + /** {@inheritDoc} */ + public int getOriginalGlyphIndex(int index) { + Integer glyphIndex = usedGlyphsIndex.get(index); if (glyphIndex != null) { - return glyphIndex.intValue(); + return glyphIndex; } else { return -1; } } - /** - * Returns the Unicode value for a subset index (character selector). If there's no such - * Unicode value, the "NOT A CHARACTER" (0xFFFF) is returned. - * @param subsetIndex the subset index (character selector) - * @return the Unicode value or "NOT A CHARACTER" (0xFFFF) - */ - public char getUnicodeForSubsetIndex(int subsetIndex) { - Character mapValue = usedCharsIndex.get(Integer.valueOf(subsetIndex)); + /** {@inheritDoc} */ + public char getUnicode(int index) { + Character mapValue = usedCharsIndex.get(index); if (mapValue != null) { return mapValue.charValue(); } else { @@ -100,72 +83,60 @@ public class CIDSubset { } } - /** - * Maps a character to a character selector for a font subset. If the character isn't in the - * subset, yet, it is added and a new character selector returned. Otherwise, the already - * allocated character selector is returned from the existing map/subset. - * @param glyphIndex the glyph index of the character - * @param unicode the Unicode index of the character - * @return the subset index - */ - public int mapSubsetChar(int glyphIndex, char unicode) { + /** {@inheritDoc} */ + public int mapChar(int glyphIndex, char unicode) { // Reencode to a new subset font or get the reencoded value // IOW, accumulate the accessed characters and build a character map for them - Integer subsetCharSelector = usedGlyphs.get(Integer.valueOf(glyphIndex)); + Integer subsetCharSelector = usedGlyphs.get(glyphIndex); if (subsetCharSelector == null) { int selector = usedGlyphsCount; - usedGlyphs.put(Integer.valueOf(glyphIndex), - Integer.valueOf(selector)); - usedGlyphsIndex.put(Integer.valueOf(selector), - Integer.valueOf(glyphIndex)); - usedCharsIndex.put(Integer.valueOf(selector), - Character.valueOf(unicode)); + usedGlyphs.put(glyphIndex, selector); + usedGlyphsIndex.put(selector, glyphIndex); + usedCharsIndex.put(selector, unicode); usedGlyphsCount++; return selector; } else { - return subsetCharSelector.intValue(); + return subsetCharSelector; } } - /** - * Returns an unmodifiable Map of the font subset. It maps from glyph index to - * character selector (i.e. the subset index in this case). - * @return Map Map<Integer, Integer> of the font subset - */ - public Map getSubsetGlyphs() { + /** {@inheritDoc} */ + public Map getGlyphs() { return Collections.unmodifiableMap(this.usedGlyphs); } - /** - * Returns a char array containing all Unicode characters that are in the subset. - * @return a char array with all used Unicode characters - */ - public char[] getSubsetChars() { + /** {@inheritDoc} */ + public char[] getChars() { char[] charArray = new char[usedGlyphsCount]; for (int i = 0; i < usedGlyphsCount; i++) { - charArray[i] = getUnicodeForSubsetIndex(i); + charArray[i] = getUnicode(i); } return charArray; } - /** - * Returns the number of glyphs in the subset. - * @return the number of glyphs in the subset - */ - public int getSubsetSize() { + /** {@inheritDoc} */ + public int getNumberOfGlyphs() { return this.usedGlyphsCount; } - /** - * Returns a BitSet with bits set for each available glyph index in the subset. - * @return a BitSet indicating available glyph indices - */ - public BitSet getGlyphIndexBitSet() { + /** {@inheritDoc} */ + public BitSet getGlyphIndices() { BitSet bitset = new BitSet(); for (Integer cid : usedGlyphs.keySet()) { - bitset.set(cid.intValue()); + bitset.set(cid); } return bitset; } + /** {@inheritDoc} */ + public int[] getWidths() { + int[] widths = font.getWidths(); + int[] tmpWidth = new int[getNumberOfGlyphs()]; + for (int i = 0, c = getNumberOfGlyphs(); i < c; i++) { + int nwx = Math.max(0, getOriginalGlyphIndex(i)); + tmpWidth[i] = widths[nwx]; + } + return tmpWidth; + } + } diff --git a/src/java/org/apache/fop/fonts/FontReader.java b/src/java/org/apache/fop/fonts/FontReader.java index 68c5c7177..0448c317e 100644 --- a/src/java/org/apache/fop/fonts/FontReader.java +++ b/src/java/org/apache/fop/fonts/FontReader.java @@ -157,7 +157,7 @@ public class FontReader extends DefaultHandler { throws SAXException { if (localName.equals("font-metrics")) { if ("TYPE0".equals(attributes.getValue("type"))) { - multiFont = new MultiByteFont(resourceResolver); + multiFont = new MultiByteFont(resourceResolver, EmbeddingMode.AUTO); returnFont = multiFont; isCID = true; TTFReader.checkMetricsVersion(attributes); diff --git a/src/java/org/apache/fop/fonts/LazyFont.java b/src/java/org/apache/fop/fonts/LazyFont.java index 4c42387f2..fef42f74c 100644 --- a/src/java/org/apache/fop/fonts/LazyFont.java +++ b/src/java/org/apache/fop/fonts/LazyFont.java @@ -438,6 +438,9 @@ public class LazyFont extends Typeface implements FontDescriptor, Substitutable, */ public boolean isSubsetEmbedded() { load(true); + if (realFont.isMultiByte() && this.embeddingMode == EmbeddingMode.FULL) { + return false; + } return realFont.isMultiByte(); } diff --git a/src/java/org/apache/fop/fonts/MultiByteFont.java b/src/java/org/apache/fop/fonts/MultiByteFont.java index a460140cb..da454c504 100644 --- a/src/java/org/apache/fop/fonts/MultiByteFont.java +++ b/src/java/org/apache/fop/fonts/MultiByteFont.java @@ -21,6 +21,7 @@ package org.apache.fop.fonts; import java.nio.CharBuffer; import java.nio.IntBuffer; +import java.util.BitSet; import java.util.Map; import org.apache.commons.logging.Log; @@ -44,13 +45,13 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl private static final Log log // CSOK: ConstantNameCheck = LogFactory.getLog(MultiByteFont.class); - private String ttcName = null; + private String ttcName; private String encoding = "Identity-H"; - private int defaultWidth = 0; + private int defaultWidth; private CIDFontType cidType = CIDFontType.CIDTYPE2; - private CIDSubset subset = new CIDSubset(); + private final CIDSet cidSet; /* advanced typographic support */ private GlyphDefinitionTable gdef; @@ -69,10 +70,15 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl /** * Default constructor */ - public MultiByteFont(InternalResourceResolver resourceResolver) { + public MultiByteFont(InternalResourceResolver resourceResolver, EmbeddingMode embeddingMode) { super(resourceResolver); - subset.setupFirstGlyph(); setFontType(FontType.TYPE0); + setEmbeddingMode(embeddingMode); + if (embeddingMode != EmbeddingMode.FULL) { + cidSet = new CIDSubset(this); + } else { + cidSet = new CIDFull(this); + } } /** {@inheritDoc} */ @@ -129,13 +135,16 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl } public boolean isSubsetEmbedded() { + if (getEmbeddingMode() == EmbeddingMode.FULL) { + return false; + } return true; } /** {@inheritDoc} */ @Override - public CIDSubset getCIDSubset() { - return this.subset; + public CIDSet getCIDSet() { + return this.cidSet; } /** {@inheritDoc} */ @@ -147,7 +156,7 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl /** {@inheritDoc} */ public int getWidth(int i, int size) { if (isEmbeddable()) { - int glyphIndex = subset.getGlyphIndexForSubsetIndex(i); + int glyphIndex = cidSet.getOriginalGlyphIndex(i); return size * width[glyphIndex]; } else { return size * width[i]; @@ -283,9 +292,39 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl glyphIndex = findGlyphIndex(Typeface.NOT_FOUND); } if (isEmbeddable()) { - glyphIndex = subset.mapSubsetChar(glyphIndex, c); + glyphIndex = cidSet.mapChar(glyphIndex, c); } - return (char)glyphIndex; + return (char) glyphIndex; + } + + protected BitSet getGlyphIndices() { + BitSet bitset = new BitSet(); + bitset.set(0); + bitset.set(1); + bitset.set(2); + for (int i = 0; i < cmap.length; i++) { + int start = cmap[i].getUnicodeStart(); + int end = cmap[i].getUnicodeEnd(); + int glyphIndex = cmap[i].getGlyphStartIndex(); + while (start++ < end + 1) { + bitset.set(glyphIndex++); + } + } + return bitset; + } + + protected char[] getChars() { + // the width array is set when the font is built + char[] chars = new char[width.length]; + for (int i = 0; i < cmap.length; i++) { + int start = cmap[i].getUnicodeStart(); + int end = cmap[i].getUnicodeEnd(); + int glyphIndex = cmap[i].getGlyphStartIndex(); + while (start < end + 1) { + chars[glyphIndex++] = (char) start++; + } + } + return chars; } /** {@inheritDoc} */ @@ -331,15 +370,7 @@ public class MultiByteFont extends CIDFont implements Substitutable, Positionabl * @return Map Map of used Glyphs */ public Map getUsedGlyphs() { - return subset.getSubsetGlyphs(); - } - - /** @return an array of the chars used */ - public char[] getCharsUsed() { - if (!isEmbeddable()) { - return null; - } - return subset.getSubsetChars(); + return cidSet.getGlyphs(); } /** diff --git a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java index c97b17211..a427fe54e 100644 --- a/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java +++ b/src/java/org/apache/fop/fonts/truetype/TTFFontLoader.java @@ -127,7 +127,7 @@ public class TTFFontLoader extends FontLoader { } if (isCid) { - multiFont = new MultiByteFont(resourceResolver); + multiFont = new MultiByteFont(resourceResolver, embeddingMode); returnFont = multiFont; multiFont.setTTCName(ttcFontName); } else { diff --git a/src/java/org/apache/fop/pdf/PDFFactory.java b/src/java/org/apache/fop/pdf/PDFFactory.java index 585f6d86d..31be73a00 100644 --- a/src/java/org/apache/fop/pdf/PDFFactory.java +++ b/src/java/org/apache/fop/pdf/PDFFactory.java @@ -44,9 +44,9 @@ import org.apache.xmlgraphics.java2d.color.NamedColorSpace; import org.apache.xmlgraphics.xmp.Metadata; import org.apache.fop.fonts.CIDFont; -import org.apache.fop.fonts.CIDSubset; import org.apache.fop.fonts.CodePointMapping; import org.apache.fop.fonts.CustomFont; +import org.apache.fop.fonts.EmbeddingMode; import org.apache.fop.fonts.FontDescriptor; import org.apache.fop.fonts.FontMetrics; import org.apache.fop.fonts.FontType; @@ -1369,23 +1369,14 @@ public class PDFFactory { } else { cidMetrics = (CIDFont)metrics; } - PDFCIDSystemInfo sysInfo - = new PDFCIDSystemInfo(cidMetrics.getRegistry(), - cidMetrics.getOrdering(), - cidMetrics.getSupplement()); - PDFCIDFont cidFont = new PDFCIDFont(subsetFontName, - cidMetrics.getCIDType(), - cidMetrics.getDefaultWidth(), - getSubsetWidths(cidMetrics), sysInfo, - (PDFCIDFontDescriptor)pdfdesc); + PDFCIDSystemInfo sysInfo = new PDFCIDSystemInfo(cidMetrics.getRegistry(), + cidMetrics.getOrdering(), cidMetrics.getSupplement()); + PDFCIDFont cidFont = new PDFCIDFont(subsetFontName, cidMetrics.getCIDType(), + cidMetrics.getDefaultWidth(), getFontWidths(cidMetrics), sysInfo, + (PDFCIDFontDescriptor) pdfdesc); getDocument().registerObject(cidFont); - - PDFCMap cmap = new PDFToUnicodeCMap( - cidMetrics.getCIDSubset().getSubsetChars(), - "fop-ucs-H", - new PDFCIDSystemInfo("Adobe", - "Identity", - 0), false); + PDFCMap cmap = new PDFToUnicodeCMap(cidMetrics.getCIDSet().getChars(), "fop-ucs-H", + new PDFCIDSystemInfo("Adobe", "Identity", 0), false); getDocument().registerObject(cmap); ((PDFFontType0)font).setCMAP(cmap); ((PDFFontType0)font).setDescendantFonts(cidFont); @@ -1476,23 +1467,11 @@ public class PDFFactory { return PDFEncoding.createPDFEncoding(encoding, fontName); } - /** - * Creates and returns a width array with the widths of all the characters in the subset. - * @param cidFont the font - * @return the width array - */ - public PDFWArray getSubsetWidths(CIDFont cidFont) { + private PDFWArray getFontWidths(CIDFont cidFont) { // Create widths for reencoded chars PDFWArray warray = new PDFWArray(); - int[] widths = cidFont.getWidths(); - CIDSubset subset = cidFont.getCIDSubset(); - int[] tmpWidth = new int[subset.getSubsetSize()]; - - for (int i = 0, c = subset.getSubsetSize(); i < c; i++) { - int nwx = Math.max(0, subset.getGlyphIndexForSubsetIndex(i)); - tmpWidth[i] = widths[nwx]; - } - warray.addEntry(0, tmpWidth); + int[] widths = cidFont.getCIDSet().getWidths(); + warray.addEntry(0, widths); return warray; } @@ -1560,13 +1539,13 @@ public class PDFFactory { } private void buildCIDSet(PDFFontDescriptor descriptor, CIDFont cidFont) { - BitSet cidSubset = cidFont.getCIDSubset().getGlyphIndexBitSet(); - PDFStream cidSet = makeStream(null, true); - ByteArrayOutputStream baout = new ByteArrayOutputStream(cidSubset.length() / 8 + 1); + BitSet cidSet = cidFont.getCIDSet().getGlyphIndices(); + PDFStream pdfStream = makeStream(null, true); + ByteArrayOutputStream baout = new ByteArrayOutputStream(cidSet.length() / 8 + 1); int value = 0; - for (int i = 0, c = cidSubset.length(); i < c; i++) { + for (int i = 0, c = cidSet.length(); i < c; i++) { int shift = i % 8; - boolean b = cidSubset.get(i); + boolean b = cidSet.get(i); if (b) { value |= 1 << 7 - shift; } @@ -1577,8 +1556,8 @@ public class PDFFactory { } baout.write(value); try { - cidSet.setData(baout.toByteArray()); - descriptor.setCIDSet(cidSet); + pdfStream.setData(baout.toByteArray()); + descriptor.setCIDSet(pdfStream); } catch (IOException ioe) { log.error( "Failed to write CIDSet [" + cidFont + "] " @@ -1609,14 +1588,16 @@ public class PDFFactory { if (desc.getFontType() == FontType.TYPE0) { MultiByteFont mbfont = (MultiByteFont) font; FontFileReader reader = new FontFileReader(in); - - TTFSubSetFile subset = new TTFSubSetFile(); - subset.readFont(reader, mbfont.getTTCName(), mbfont.getUsedGlyphs()); - byte[] subsetFont = subset.getFontSubset(); - // Only TrueType CID fonts are supported now - - embeddedFont = new PDFTTFStream(subsetFont.length); - ((PDFTTFStream) embeddedFont).setData(subsetFont, subsetFont.length); + byte[] fontBytes; + if (font.getEmbeddingMode() == EmbeddingMode.FULL) { + fontBytes = reader.getAllBytes(); + } else { + TTFSubSetFile ttfFile = new TTFSubSetFile(); + ttfFile.readFont(reader, mbfont.getTTCName(), mbfont.getUsedGlyphs()); + fontBytes = ttfFile.getFontSubset(); + } + embeddedFont = new PDFTTFStream(fontBytes.length); + ((PDFTTFStream) embeddedFont).setData(fontBytes, fontBytes.length); } else if (desc.getFontType() == FontType.TYPE1) { PFBParser parser = new PFBParser(); PFBData pfb = parser.parsePFB(in); diff --git a/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java b/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java index d536167c3..0de02c766 100644 --- a/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java +++ b/src/java/org/apache/fop/render/bitmap/TIFFDocumentHandler.java @@ -24,7 +24,6 @@ import org.apache.xmlgraphics.image.writer.ResolutionUnit; import org.apache.fop.apps.MimeConstants; import org.apache.fop.render.bitmap.TIFFRendererConfig.TIFFRendererConfigParser; import org.apache.fop.render.intermediate.IFContext; -import org.apache.fop.render.intermediate.IFDocumentHandler; import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator; /** diff --git a/src/java/org/apache/fop/render/ps/PSFontUtils.java b/src/java/org/apache/fop/render/ps/PSFontUtils.java index c99bb21e1..9e810c824 100644 --- a/src/java/org/apache/fop/render/ps/PSFontUtils.java +++ b/src/java/org/apache/fop/render/ps/PSFontUtils.java @@ -38,7 +38,7 @@ import org.apache.xmlgraphics.ps.dsc.ResourceTracker; import org.apache.fop.fonts.Base14Font; import org.apache.fop.fonts.CIDFontType; -import org.apache.fop.fonts.CIDSubset; +import org.apache.fop.fonts.CIDSet; import org.apache.fop.fonts.CMapSegment; import org.apache.fop.fonts.CustomFont; import org.apache.fop.fonts.EmbeddingMode; @@ -457,15 +457,17 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { // TODO /FontInfo gen.write("/CIDCount "); - CIDSubset cidSubset = font.getCIDSubset(); - int subsetSize = cidSubset.getSubsetSize(); - gen.write(subsetSize); + CIDSet cidSet = font.getCIDSet(); + int numberOfGlyphs = cidSet.getNumberOfGlyphs(); + gen.write(numberOfGlyphs); gen.writeln(" def"); gen.writeln("/GDBytes 2 def"); // TODO always 2? gen.writeln("/CIDMap [<"); int colCount = 0; int lineCount = 1; - for (int cid = 0; cid < subsetSize; cid++) { + int nextBitSet = 0; + int previousBitSet = 0; + for (int cid = 0; cid < numberOfGlyphs; cid++) { if (colCount++ == 20) { gen.newLine(); colCount = 1; @@ -478,7 +480,23 @@ public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils { if (font.getEmbeddingMode() != EmbeddingMode.FULL) { gid = HexEncoder.encode(cid, 4); } else { - gid = HexEncoder.encode(cidSubset.getGlyphIndexForSubsetIndex(cid), 4); + previousBitSet = nextBitSet; + nextBitSet = cidSet.getGlyphIndices().nextSetBit(nextBitSet); + while (previousBitSet++ < nextBitSet) { + // if there are gaps in the indices we pad them with zeros + gen.write("0000"); + cid++; + if (colCount++ == 20) { + gen.newLine(); + colCount = 1; + if (lineCount++ == 800) { + gen.writeln("> <"); + lineCount = 1; + } + } + } + gid = HexEncoder.encode(nextBitSet, 4); + nextBitSet++; } gen.write(gid); } diff --git a/status.xml b/status.xml index b39d3d9e0..7571ddfda 100644 --- a/status.xml +++ b/status.xml @@ -62,6 +62,9 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> + + Full font embedding in PDF + Added configuration for RowPerStrip configuration in the Tiff renderer. RowsPerStrip can be configured to 1 or to the total # of rows. diff --git a/test/java/org/apache/fop/fonts/CIDFullTestCase.java b/test/java/org/apache/fop/fonts/CIDFullTestCase.java new file mode 100644 index 000000000..7df6cc25b --- /dev/null +++ b/test/java/org/apache/fop/fonts/CIDFullTestCase.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.fonts; + +import java.util.BitSet; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.fop.util.CharUtilities; + +public class CIDFullTestCase { + + private CIDFull cidFull; + private MultiByteFont mbFont; + private BitSet bs; + private char[] chars; + private int[] widths; + private Map glyphs; + + @Before + public void setup() { + bs = new BitSet(); + glyphs = new HashMap(); + chars = new char[18]; + widths = new int[18]; + int i = 0; + for (int j = 0; j < 20; j++) { + if (j == 10 || j == 11) { + continue; + } + bs.set(j); + glyphs.put(Integer.valueOf(j), Integer.valueOf(j)); + chars[i] = (char) j; + widths[i] = 100; + i++; + } + mbFont = mock(MultiByteFont.class); + when(mbFont.getGlyphIndices()).thenReturn(bs); + when(mbFont.getChars()).thenReturn(chars); + when(mbFont.getWidths()).thenReturn(widths); + cidFull = new CIDFull(mbFont); + } + + @Test + public void testGetOriginalGlyphIndex() { + // index 5 exists + assertEquals(cidFull.getOriginalGlyphIndex(5), 5); + } + + @Test + public void testGetUnicode() { + // index 9 exists + assertEquals(cidFull.getUnicode(9), (char) 9); + // index 10 does not + assertEquals(cidFull.getUnicode(10), CharUtilities.NOT_A_CHARACTER); + } + + @Test + public void testMapChar() { + // index 9 exists + char c = 'a'; + assertEquals(cidFull.mapChar(9, c), (char) 9); + } + + @Test + public void testGetGlyphs() { + Map fontGlyphs = cidFull.getGlyphs(); + for (Integer key : fontGlyphs.keySet()) { + assertEquals(fontGlyphs.get(key), glyphs.get(key)); + } + assertTrue(fontGlyphs.size() == glyphs.size()); + } + + @Test + public void testGetChars() { + assertArrayEquals(cidFull.getChars(), chars); + } + + @Test + public void testGetNumberOfGlyphs() { + assertTrue(cidFull.getNumberOfGlyphs() == 20); + } + + @Test + public void testGetGlyphIndices() { + assertEquals(bs, cidFull.getGlyphIndices()); + } + + @Test + public void testGetWidths() { + assertArrayEquals(cidFull.getWidths(), widths); + } + +} diff --git a/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java b/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java index ac9df4046..971471fa2 100644 --- a/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java +++ b/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java @@ -24,14 +24,16 @@ import java.net.URI; import org.junit.Test; +import static org.junit.Assert.assertEquals; + import org.apache.fop.apps.io.InternalResourceResolver; import org.apache.fop.apps.io.ResourceResolver; import org.apache.fop.apps.io.ResourceResolverFactory; +import org.apache.fop.fonts.CIDSet; import org.apache.fop.fonts.CIDSubset; +import org.apache.fop.fonts.EmbeddingMode; import org.apache.fop.fonts.MultiByteFont; -import static org.junit.Assert.assertEquals; - /** * Test case for {@link PDFFactory}. */ @@ -45,7 +47,7 @@ public class PDFFactoryTestCase { public void testSubsetFontNamePrefix() { class MockedFont extends MultiByteFont { public MockedFont(InternalResourceResolver resolver) { - super(resolver); + super(resolver, EmbeddingMode.AUTO); } @Override @@ -54,8 +56,8 @@ public class PDFFactoryTestCase { } @Override - public CIDSubset getCIDSubset() { - return new CIDSubset(); + public CIDSet getCIDSet() { + return new CIDSubset(this); } } PDFDocument doc = new PDFDocument("Test"); -- cgit v1.2.3 From c9cbf7e0a418fb7735f6d4a42632b867f3dab36c Mon Sep 17 00:00:00 2001 From: Mehdi Houshmand Date: Wed, 19 Sep 2012 15:37:57 +0000 Subject: Moved the I/O interfaces to XGC and updated code with new resolution mechanism git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_XGC_URI_Resolution@1387645 13f79535-47bb-0310-9956-ffa450edef68 --- .../src/org/apache/fop/plan/PreloaderPlan.java | 1 + src/java/org/apache/fop/afp/AFPStreamer.java | 3 +- .../org/apache/fop/apps/EnvironmentProfile.java | 7 +- .../fop/apps/EnvironmentalProfileFactory.java | 21 +++- src/java/org/apache/fop/apps/FOUserAgent.java | 32 +++--- src/java/org/apache/fop/apps/FopConfParser.java | 2 +- src/java/org/apache/fop/apps/FopFactory.java | 6 ++ .../org/apache/fop/apps/FopFactoryBuilder.java | 7 +- src/java/org/apache/fop/apps/FopFactoryConfig.java | 10 +- .../fop/apps/io/InternalResourceResolver.java | 4 +- src/java/org/apache/fop/apps/io/Resource.java | 59 ----------- .../org/apache/fop/apps/io/ResourceResolver.java | 51 --------- .../fop/apps/io/ResourceResolverFactory.java | 118 +++++++++++---------- .../apache/fop/apps/io/TempResourceResolver.java | 48 --------- .../fop/apps/io/TempResourceURIGenerator.java | 57 ---------- .../apache/fop/area/CachedRenderPagesModel.java | 3 +- .../apache/fop/fonts/FontManagerConfigurator.java | 3 +- .../fop/image/loader/batik/PreloaderSVG.java | 5 +- .../fop/image/loader/batik/PreloaderWMF.java | 5 +- .../apache/fop/render/ps/PSDocumentHandler.java | 2 +- src/java/org/apache/fop/servlet/FopServlet.java | 5 +- .../java/org/apache/fop/URIResolutionTestCase.java | 5 +- .../apps/EnvironmentalProfileFactoryTestCase.java | 3 +- test/java/org/apache/fop/apps/MutableConfig.java | 7 +- .../apache/fop/apps/io/BaseURIResolutionTest.java | 2 + .../org/apache/fop/apps/io/FontURIResolver.java | 2 + .../apps/io/ResourceResolverFactoryTestCase.java | 10 +- .../fop/apps/io/TestingResourceResolver.java | 2 + .../fop/apps/io/URIResolverWrapperTestCase.java | 2 + .../org/apache/fop/intermediate/TestAssistant.java | 8 +- .../org/apache/fop/pdf/PDFFactoryTestCase.java | 3 +- 31 files changed, 177 insertions(+), 316 deletions(-) delete mode 100644 src/java/org/apache/fop/apps/io/Resource.java delete mode 100644 src/java/org/apache/fop/apps/io/ResourceResolver.java delete mode 100644 src/java/org/apache/fop/apps/io/TempResourceResolver.java delete mode 100644 src/java/org/apache/fop/apps/io/TempResourceURIGenerator.java (limited to 'test/java/org/apache/fop') diff --git a/examples/plan/src/org/apache/fop/plan/PreloaderPlan.java b/examples/plan/src/org/apache/fop/plan/PreloaderPlan.java index 8544ce544..d88658c37 100644 --- a/examples/plan/src/org/apache/fop/plan/PreloaderPlan.java +++ b/examples/plan/src/org/apache/fop/plan/PreloaderPlan.java @@ -43,6 +43,7 @@ import org.apache.xmlgraphics.image.loader.ImageSize; import org.apache.xmlgraphics.image.loader.impl.AbstractImagePreloader; import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; import org.apache.xmlgraphics.image.loader.util.ImageUtil; +import org.apache.xmlgraphics.io.XmlSourceUtil; import org.apache.fop.util.DefaultErrorListener; import org.apache.fop.util.UnclosableInputStream; diff --git a/src/java/org/apache/fop/afp/AFPStreamer.java b/src/java/org/apache/fop/afp/AFPStreamer.java index fb2b282f6..5e6b5a79e 100644 --- a/src/java/org/apache/fop/afp/AFPStreamer.java +++ b/src/java/org/apache/fop/afp/AFPStreamer.java @@ -32,10 +32,11 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.xmlgraphics.io.TempResourceURIGenerator; + import org.apache.fop.afp.modca.ResourceGroup; import org.apache.fop.afp.modca.StreamedResourceGroup; import org.apache.fop.apps.io.InternalResourceResolver; -import org.apache.fop.apps.io.TempResourceURIGenerator; /** * Manages the streaming of the AFP output diff --git a/src/java/org/apache/fop/apps/EnvironmentProfile.java b/src/java/org/apache/fop/apps/EnvironmentProfile.java index c964120e3..1a585353e 100644 --- a/src/java/org/apache/fop/apps/EnvironmentProfile.java +++ b/src/java/org/apache/fop/apps/EnvironmentProfile.java @@ -21,7 +21,9 @@ package org.apache.fop.apps; import java.net.URI; -import org.apache.fop.apps.io.ResourceResolver; +import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.FallbackResolver; +import org.apache.xmlgraphics.io.ResourceResolver; + import org.apache.fop.fonts.FontManager; /** @@ -52,4 +54,7 @@ public interface EnvironmentProfile { * @return the default base URI */ URI getDefaultBaseURI(); + + /** @see FopFactoryConfig#getFallbackResolver() */ + FallbackResolver getFallbackResolver(); } diff --git a/src/java/org/apache/fop/apps/EnvironmentalProfileFactory.java b/src/java/org/apache/fop/apps/EnvironmentalProfileFactory.java index 9ba7632d2..922ecff4b 100644 --- a/src/java/org/apache/fop/apps/EnvironmentalProfileFactory.java +++ b/src/java/org/apache/fop/apps/EnvironmentalProfileFactory.java @@ -21,8 +21,12 @@ package org.apache.fop.apps; import java.net.URI; +import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.FallbackResolver; +import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.RestrictedFallbackResolver; +import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.UnrestrictedFallbackResolver; +import org.apache.xmlgraphics.io.ResourceResolver; + import org.apache.fop.apps.io.InternalResourceResolver; -import org.apache.fop.apps.io.ResourceResolver; import org.apache.fop.apps.io.ResourceResolverFactory; import org.apache.fop.fonts.FontCacheManager; import org.apache.fop.fonts.FontCacheManagerFactory; @@ -51,7 +55,8 @@ public final class EnvironmentalProfileFactory { return new Profile(defaultBaseUri, resourceResolver, createFontManager(defaultBaseUri, resourceResolver, FontDetectorFactory.createDefault(), - FontCacheManagerFactory.createDefault())); + FontCacheManagerFactory.createDefault()), + new UnrestrictedFallbackResolver()); } /** @@ -67,7 +72,8 @@ public final class EnvironmentalProfileFactory { return new Profile(defaultBaseUri, resourceResolver, createFontManager(defaultBaseUri, resourceResolver, FontDetectorFactory.createDisabled(), - FontCacheManagerFactory.createDisabled())); + FontCacheManagerFactory.createDisabled()), + new RestrictedFallbackResolver()); } private static final class Profile implements EnvironmentProfile { @@ -78,8 +84,10 @@ public final class EnvironmentalProfileFactory { private final URI defaultBaseURI; + private final FallbackResolver fallbackResolver; + private Profile(URI defaultBaseURI, ResourceResolver resourceResolver, - FontManager fontManager) { + FontManager fontManager, FallbackResolver fallbackResolver) { if (defaultBaseURI == null) { throw new IllegalArgumentException("Default base URI must not be null"); } @@ -92,6 +100,7 @@ public final class EnvironmentalProfileFactory { this.defaultBaseURI = defaultBaseURI; this.resourceResolver = resourceResolver; this.fontManager = fontManager; + this.fallbackResolver = fallbackResolver; } public ResourceResolver getResourceResolver() { @@ -105,6 +114,10 @@ public final class EnvironmentalProfileFactory { public URI getDefaultBaseURI() { return defaultBaseURI; } + + public FallbackResolver getFallbackResolver() { + return fallbackResolver; + } } private static FontManager createFontManager(URI defaultBaseUri, ResourceResolver resourceResolver, diff --git a/src/java/org/apache/fop/apps/FOUserAgent.java b/src/java/org/apache/fop/apps/FOUserAgent.java index 0ed5b7228..68064343e 100644 --- a/src/java/org/apache/fop/apps/FOUserAgent.java +++ b/src/java/org/apache/fop/apps/FOUserAgent.java @@ -131,21 +131,7 @@ public class FOUserAgent { /** Set of keywords applicable to this document. */ protected String keywords = null; - private ImageSessionContext imageSessionContext = new AbstractImageSessionContext() { - - public ImageContext getParentContext() { - return factory; - } - - public float getTargetResolution() { - return FOUserAgent.this.getTargetResolution(); - } - - public Source resolveURI(String uri) { - return FOUserAgent.this.resolveURI(uri); - } - - }; + private final ImageSessionContext imageSessionContext; /** * Main constructor. This constructor should not be called directly. Please use the @@ -154,11 +140,25 @@ public class FOUserAgent { * @param resourceResolver the resolver used to acquire resources * @see org.apache.fop.apps.FopFactory */ - FOUserAgent(FopFactory factory, InternalResourceResolver resourceResolver) { + FOUserAgent(final FopFactory factory, InternalResourceResolver resourceResolver) { this.factory = factory; this.resourceResolver = resourceResolver; setTargetResolution(factory.getTargetResolution()); setAccessibility(factory.isAccessibilityEnabled()); + imageSessionContext = new AbstractImageSessionContext(factory.getFallbackResolver()) { + + public ImageContext getParentContext() { + return factory; + } + + public float getTargetResolution() { + return FOUserAgent.this.getTargetResolution(); + } + + public Source resolveURI(String uri) { + return FOUserAgent.this.resolveURI(uri); + } + }; } /** diff --git a/src/java/org/apache/fop/apps/FopConfParser.java b/src/java/org/apache/fop/apps/FopConfParser.java index c6e3dbd19..b4918ef30 100644 --- a/src/java/org/apache/fop/apps/FopConfParser.java +++ b/src/java/org/apache/fop/apps/FopConfParser.java @@ -39,9 +39,9 @@ import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.image.loader.spi.ImageImplRegistry; import org.apache.xmlgraphics.image.loader.util.Penalty; +import org.apache.xmlgraphics.io.ResourceResolver; import org.apache.fop.apps.io.InternalResourceResolver; -import org.apache.fop.apps.io.ResourceResolver; import org.apache.fop.apps.io.ResourceResolverFactory; import org.apache.fop.fonts.FontManagerConfigurator; import org.apache.fop.hyphenation.HyphenationTreeCache; diff --git a/src/java/org/apache/fop/apps/FopFactory.java b/src/java/org/apache/fop/apps/FopFactory.java index 80c957f9f..6bc744b7e 100644 --- a/src/java/org/apache/fop/apps/FopFactory.java +++ b/src/java/org/apache/fop/apps/FopFactory.java @@ -36,6 +36,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.xmlgraphics.image.loader.ImageContext; import org.apache.xmlgraphics.image.loader.ImageManager; +import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.FallbackResolver; import org.apache.xmlgraphics.util.UnitConv; import org.apache.fop.apps.io.InternalResourceResolver; @@ -418,6 +419,11 @@ public final class FopFactory implements ImageContext { return config.getFontManager(); } + /** @see FopFactoryConfig#getFallbackResolver() */ + FallbackResolver getFallbackResolver() { + return config.getFallbackResolver(); + } + /** * Returns the color space cache for this instance. *

diff --git a/src/java/org/apache/fop/apps/FopFactoryBuilder.java b/src/java/org/apache/fop/apps/FopFactoryBuilder.java index cfc47496c..b1fd5e979 100644 --- a/src/java/org/apache/fop/apps/FopFactoryBuilder.java +++ b/src/java/org/apache/fop/apps/FopFactoryBuilder.java @@ -30,8 +30,9 @@ import org.apache.avalon.framework.configuration.Configuration; import org.apache.xmlgraphics.image.loader.ImageContext; import org.apache.xmlgraphics.image.loader.ImageManager; +import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.FallbackResolver; +import org.apache.xmlgraphics.io.ResourceResolver; -import org.apache.fop.apps.io.ResourceResolver; import org.apache.fop.apps.io.ResourceResolverFactory; import org.apache.fop.fonts.FontManager; import org.apache.fop.layoutmgr.LayoutManagerMaker; @@ -464,6 +465,10 @@ public final class FopFactoryBuilder { public Map getHyphenationPatternNames() { return hyphPatNames; } + + public FallbackResolver getFallbackResolver() { + return enviro.getFallbackResolver(); + } } private interface FopFactoryConfigBuilder { diff --git a/src/java/org/apache/fop/apps/FopFactoryConfig.java b/src/java/org/apache/fop/apps/FopFactoryConfig.java index 60e8d98e7..d3ea3127f 100644 --- a/src/java/org/apache/fop/apps/FopFactoryConfig.java +++ b/src/java/org/apache/fop/apps/FopFactoryConfig.java @@ -26,8 +26,9 @@ import java.util.Set; import org.apache.avalon.framework.configuration.Configuration; import org.apache.xmlgraphics.image.loader.ImageManager; +import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.FallbackResolver; +import org.apache.xmlgraphics.io.ResourceResolver; -import org.apache.fop.apps.io.ResourceResolver; import org.apache.fop.fonts.FontManager; import org.apache.fop.layoutmgr.LayoutManagerMaker; @@ -163,4 +164,11 @@ public interface FopFactoryConfig { /** @return the hyphenation pattern names */ Map getHyphenationPatternNames(); + + /** + * Controls the mechanisms that are used in the event that {@link javax.xml.transform.Source} + * used for resources couldn't be read. + * @return the fallback resolver + */ + FallbackResolver getFallbackResolver(); } diff --git a/src/java/org/apache/fop/apps/io/InternalResourceResolver.java b/src/java/org/apache/fop/apps/io/InternalResourceResolver.java index 4d664c8f5..af0a26478 100644 --- a/src/java/org/apache/fop/apps/io/InternalResourceResolver.java +++ b/src/java/org/apache/fop/apps/io/InternalResourceResolver.java @@ -29,12 +29,14 @@ import javax.xml.transform.Source; import javax.xml.transform.TransformerException; import javax.xml.transform.stream.StreamSource; +import org.apache.xmlgraphics.io.Resource; +import org.apache.xmlgraphics.io.ResourceResolver; import org.apache.xmlgraphics.util.uri.DataURIResolver; /** * This object holds the base URI from which to resolve URIs against as well as the resolver for * resource acquisition. It also does some URI sanitization of common URI syntactical errors. This - * class takes in a {@link org.apache.fop.apps.io.ResourceResolver} and delegates all relevant + * class takes in a {@link org.apache.xmlgraphics.io.ResourceResolver} and delegates all relevant * URIs to it. */ public class InternalResourceResolver { diff --git a/src/java/org/apache/fop/apps/io/Resource.java b/src/java/org/apache/fop/apps/io/Resource.java deleted file mode 100644 index 0a8b8c22a..000000000 --- a/src/java/org/apache/fop/apps/io/Resource.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.apps.io; - -import java.io.FilterInputStream; -import java.io.InputStream; - -/** - * This class represents a resolved resource. The type property is used by FOP to identify the resource - * content. - * - */ -public class Resource extends FilterInputStream { - - private final String type; - - /** - * @param type resource type - * @param inputStream input stream of the resource - */ - public Resource(String type, InputStream inputStream) { - super(inputStream); - this.type = type; - } - - /** - * Constructs a resource of 'unknown' type. - * - * @param inputStream input stream of the resource - */ - public Resource(InputStream inputStream) { - this("unknown", inputStream); - } - - /** - * @return the resource type - */ - public String getType() { - return this.type; - } - -} diff --git a/src/java/org/apache/fop/apps/io/ResourceResolver.java b/src/java/org/apache/fop/apps/io/ResourceResolver.java deleted file mode 100644 index a3a9cf0c3..000000000 --- a/src/java/org/apache/fop/apps/io/ResourceResolver.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.apps.io; - -import java.io.IOException; -import java.io.OutputStream; -import java.net.URI; - -/** - * Implementations of this resource resolver allow FOP users to control the URI resolution - * mechanism. All resource and output stream acquisition goes through this when its implementation - * is given to the {@link org.apache.fop.apps.EnvironmentProfile}. - */ -public interface ResourceResolver { - - /** - * Get a resource given the URI pointing to said resource. - * - * @param uri the resource URI - * @return the resource - * @throws IOException if an I/O error occured during resource acquisition - */ - Resource getResource(URI uri) throws IOException; - - /** - * Gets an output stream of a given URI. - * - * @param uri the output stream URI - * @return the output stream - * @throws IOException if an I/O error occured while creating an output stream - */ - OutputStream getOutputStream(URI uri) throws IOException; - -} diff --git a/src/java/org/apache/fop/apps/io/ResourceResolverFactory.java b/src/java/org/apache/fop/apps/io/ResourceResolverFactory.java index 72eac456d..cdc9438b9 100644 --- a/src/java/org/apache/fop/apps/io/ResourceResolverFactory.java +++ b/src/java/org/apache/fop/apps/io/ResourceResolverFactory.java @@ -28,6 +28,11 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import org.apache.xmlgraphics.io.Resource; +import org.apache.xmlgraphics.io.ResourceResolver; +import org.apache.xmlgraphics.io.TempResourceResolver; +import org.apache.xmlgraphics.io.TempResourceURIGenerator; + /** * A factory class for {@link ResourceResolver}s. */ @@ -70,10 +75,10 @@ public final class ResourceResolverFactory { } /** - * Creates a temporary-resource-schema aware resource resolver. Temporary resource URIs are + * Creates a temporary-resource-scheme aware resource resolver. Temporary resource URIs are * created by {@link TempResourceURIGenerator}. * - * @param tempResourceResolver the temporary-resource-schema resolver to use + * @param tempResourceResolver the temporary-resource-scheme resolver to use * @param defaultResourceResolver the default resource resolver to use * @return the ressource resolver */ @@ -84,17 +89,17 @@ public final class ResourceResolverFactory { } /** - * This creates the builder class for binding URI schemas to implementations of - * {@link ResourceResolver}. This allows users to define their own URI schemas such that they + * This creates the builder class for binding URI schemes to implementations of + * {@link ResourceResolver}. This allows users to define their own URI schemes such that they * have finer control over the acquisition of resources. * * @param defaultResolver the default resource resolver that should be used in the event that - * none of the other registered resolvers match the schema - * @return the schema aware {@link ResourceResolver} builder + * none of the other registered resolvers match the scheme + * @return the scheme aware {@link ResourceResolver} builder */ - public static SchemaAwareResourceResolverBuilder createSchemaAwareResourceResolverBuilder( + public static SchemeAwareResourceResolverBuilder createSchemeAwareResourceResolverBuilder( ResourceResolver defaultResolver) { - return new SchemaAwareResourceResolverBuilderImpl(defaultResolver); + return new SchemeAwareResourceResolverBuilderImpl(defaultResolver); } private static final class DefaultResourceResolver implements ResourceResolver { @@ -132,13 +137,13 @@ public final class ResourceResolverFactory { this.defaultResourceResolver = defaultResourceResolver; } - private static boolean isTempUri(URI uri) { - return TempResourceURIGenerator.isTempUri(uri); + private static boolean isTempURI(URI uri) { + return TempResourceURIGenerator.isTempURI(uri); } /** {@inheritDoc} */ public Resource getResource(URI uri) throws IOException { - if (isTempUri(uri)) { + if (isTempURI(uri)) { return tempResourceResolver.getResource(uri.getPath()); } else { return defaultResourceResolver.getResource(uri); @@ -147,7 +152,7 @@ public final class ResourceResolverFactory { /** {@inheritDoc} */ public OutputStream getOutputStream(URI uri) throws IOException { - if (isTempUri(uri)) { + if (isTempURI(uri)) { return tempResourceResolver.getOutputStream(uri.getPath()); } else { return defaultResourceResolver.getOutputStream(uri); @@ -188,23 +193,23 @@ public final class ResourceResolverFactory { } } - private static final class SchemaAwareResourceResolver implements ResourceResolver { + private static final class SchemeAwareResourceResolver implements ResourceResolver { - private final Map schemaHandlingResourceResolvers; + private final Map schemeHandlingResourceResolvers; private final ResourceResolver defaultResolver; - private SchemaAwareResourceResolver( - Map schemaHandlingResourceResolvers, + private SchemeAwareResourceResolver( + Map schemEHandlingResourceResolvers, ResourceResolver defaultResolver) { - this.schemaHandlingResourceResolvers = schemaHandlingResourceResolvers; + this.schemeHandlingResourceResolvers = schemEHandlingResourceResolvers; this.defaultResolver = defaultResolver; } - private ResourceResolver getResourceResolverForSchema(URI uri) { - String schema = uri.getScheme(); - if (schemaHandlingResourceResolvers.containsKey(schema)) { - return schemaHandlingResourceResolvers.get(schema); + private ResourceResolver getResourceResolverForScheme(URI uri) { + String scheme = uri.getScheme(); + if (schemeHandlingResourceResolvers.containsKey(scheme)) { + return schemeHandlingResourceResolvers.get(scheme); } else { return defaultResolver; } @@ -212,58 +217,58 @@ public final class ResourceResolverFactory { /** {@inheritDoc} */ public Resource getResource(URI uri) throws IOException { - return getResourceResolverForSchema(uri).getResource(uri); + return getResourceResolverForScheme(uri).getResource(uri); } /** {@inheritDoc} */ public OutputStream getOutputStream(URI uri) throws IOException { - return getResourceResolverForSchema(uri).getOutputStream(uri); + return getResourceResolverForScheme(uri).getOutputStream(uri); } } /** * Implementations of this interface will be builders for {@link ResourceResolver}, they bind - * URI schemas to their respective resolver. This gives users more control over the mechanisms + * URI schemes to their respective resolver. This gives users more control over the mechanisms * by which URIs are resolved. *

* Here is an example of how this could be used: *

*

- * SchemaAwareResourceResolverBuilder builder - * = ResourceResolverFactory.createSchemaAwareResourceResolverBuilder(defaultResolver); - * builder.registerResourceResolverForSchema("test", testResolver); - * builder.registerResourceResolverForSchema("anotherTest", test2Resolver); + * SchemeAwareResourceResolverBuilder builder + * = ResourceResolverFactory.createSchemeAwareResourceResolverBuilder(defaultResolver); + * builder.registerResourceResolverForScheme("test", testResolver); + * builder.registerResourceResolverForScheme("anotherTest", test2Resolver); * ResourceResolver resolver = builder.build(); *

* This will result in all URIs for the form "test:///..." will be resolved using the * testResolver object; URIs of the form "anotherTest:///..." will be resolved * using test2Resolver; all other URIs will be resolved from the defaultResolver. */ - public interface SchemaAwareResourceResolverBuilder { + public interface SchemeAwareResourceResolverBuilder { /** - * Register a schema with its respective {@link ResourceResolver}. This resolver will be - * used as the only resolver for the specified schema. + * Register a scheme with its respective {@link ResourceResolver}. This resolver will be + * used as the only resolver for the specified scheme. * - * @param schema the schema to be used with the given resolver + * @param scheme the scheme to be used with the given resolver * @param resourceResolver the resource resolver */ - void registerResourceResolverForSchema(String schema, ResourceResolver resourceResolver); + void registerResourceResolverForScheme(String scheme, ResourceResolver resourceResolver); /** * Builds a {@link ResourceResolver} that will delegate to the respective resource resolver - * when a registered URI schema is given + * when a registered URI scheme is given * - * @return a resolver that delegates to the appropriate schema resolver + * @return a resolver that delegates to the appropriate scheme resolver */ ResourceResolver build(); } - private static final class CompletedSchemaAwareResourceResolverBuilder - implements SchemaAwareResourceResolverBuilder { + private static final class CompletedSchemeAwareResourceResolverBuilder + implements SchemeAwareResourceResolverBuilder { - private static final SchemaAwareResourceResolverBuilder INSTANCE - = new CompletedSchemaAwareResourceResolverBuilder(); + private static final SchemeAwareResourceResolverBuilder INSTANCE + = new CompletedSchemeAwareResourceResolverBuilder(); /** {@inheritDoc} */ public ResourceResolver build() { @@ -271,59 +276,58 @@ public final class ResourceResolverFactory { } /** {@inheritDoc} */ - public void registerResourceResolverForSchema(String schema, + public void registerResourceResolverForScheme(String scheme, ResourceResolver resourceResolver) { throw new IllegalStateException("Resource resolver already built"); } } - private static final class ActiveSchemaAwareResourceResolverBuilder - implements SchemaAwareResourceResolverBuilder { + private static final class ActiveSchemeAwareResourceResolverBuilder + implements SchemeAwareResourceResolverBuilder { - private final Map schemaHandlingResourceResolvers + private final Map schemeHandlingResourceResolvers = new HashMap(); private final ResourceResolver defaultResolver; - private ActiveSchemaAwareResourceResolverBuilder(ResourceResolver defaultResolver) { + private ActiveSchemeAwareResourceResolverBuilder(ResourceResolver defaultResolver) { this.defaultResolver = defaultResolver; } /** {@inheritDoc} */ - public void registerResourceResolverForSchema(String schema, + public void registerResourceResolverForScheme(String scheme, ResourceResolver resourceResolver) { - schemaHandlingResourceResolvers.put(schema, resourceResolver); + schemeHandlingResourceResolvers.put(scheme, resourceResolver); } /** {@inheritDoc} */ public ResourceResolver build() { - return new SchemaAwareResourceResolver( - Collections.unmodifiableMap(schemaHandlingResourceResolvers), defaultResolver); + return new SchemeAwareResourceResolver( + Collections.unmodifiableMap(schemeHandlingResourceResolvers), defaultResolver); } } - private static final class SchemaAwareResourceResolverBuilderImpl - implements SchemaAwareResourceResolverBuilder { + private static final class SchemeAwareResourceResolverBuilderImpl + implements SchemeAwareResourceResolverBuilder { - private SchemaAwareResourceResolverBuilder delegate; + private SchemeAwareResourceResolverBuilder delegate; - private SchemaAwareResourceResolverBuilderImpl(ResourceResolver defaultResolver) { - this.delegate = new ActiveSchemaAwareResourceResolverBuilder(defaultResolver); + private SchemeAwareResourceResolverBuilderImpl(ResourceResolver defaultResolver) { + this.delegate = new ActiveSchemeAwareResourceResolverBuilder(defaultResolver); } /** {@inheritDoc} */ - public void registerResourceResolverForSchema(String schema, + public void registerResourceResolverForScheme(String scheme, ResourceResolver resourceResolver) { - delegate.registerResourceResolverForSchema(schema, resourceResolver); + delegate.registerResourceResolverForScheme(scheme, resourceResolver); } /** {@inheritDoc} */ public ResourceResolver build() { ResourceResolver resourceResolver = delegate.build(); - delegate = CompletedSchemaAwareResourceResolverBuilder.INSTANCE; + delegate = CompletedSchemeAwareResourceResolverBuilder.INSTANCE; return resourceResolver; } } - } diff --git a/src/java/org/apache/fop/apps/io/TempResourceResolver.java b/src/java/org/apache/fop/apps/io/TempResourceResolver.java deleted file mode 100644 index cf307fab6..000000000 --- a/src/java/org/apache/fop/apps/io/TempResourceResolver.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.apps.io; - -import java.io.IOException; -import java.io.OutputStream; - -/** - * Implementations of this interface resolve URIs for temporary files used by FOP. The temporary- - * resource URI scheme comes from {@link TempResourceURIGenerator#TMP_SCHEMA}. - */ -public interface TempResourceResolver { - - /** - * Get a temporary-resource given the URI pointing to said resource. - * - * @param uri the resource URI - * @return the resource - * @throws IOException if an I/O error occured during resource acquisition - */ - Resource getResource(String uri) throws IOException; - - /** - * Gets an temporary-output stream of a given URI. - * - * @param uri the output stream URI - * @return the output stream - * @throws IOException if an I/O error occured while creating an output stream - */ - OutputStream getOutputStream(String uri) throws IOException; -} diff --git a/src/java/org/apache/fop/apps/io/TempResourceURIGenerator.java b/src/java/org/apache/fop/apps/io/TempResourceURIGenerator.java deleted file mode 100644 index 8e6c3606c..000000000 --- a/src/java/org/apache/fop/apps/io/TempResourceURIGenerator.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ - -package org.apache.fop.apps.io; - -import java.net.URI; -import java.util.concurrent.atomic.AtomicLong; - -/** - * Creates a URI for any temporary resource used within FOP. - */ -public final class TempResourceURIGenerator { - - public static final String TMP_SCHEMA = "tmp"; - - private final String tempURIPrefix; - - private final AtomicLong counter; - - /** - * @param uriPrefix a prefix used to name the unique URI - */ - public TempResourceURIGenerator(String uriPrefix) { - counter = new AtomicLong(); - tempURIPrefix = URI.create(TMP_SCHEMA + ":///" + uriPrefix).toASCIIString(); - } - - /** - * Generate a unique URI for a temporary resource - * @return the URI - */ - public URI generate() { - return URI.create(tempURIPrefix + getUniqueId()); - } - - private String getUniqueId() { - return Long.toHexString(counter.getAndIncrement()); - } - - public static boolean isTempUri(URI uri) { - return TMP_SCHEMA.equals(uri.getScheme()); - } -} diff --git a/src/java/org/apache/fop/area/CachedRenderPagesModel.java b/src/java/org/apache/fop/area/CachedRenderPagesModel.java index fcc82cdd3..0837e5ae5 100644 --- a/src/java/org/apache/fop/area/CachedRenderPagesModel.java +++ b/src/java/org/apache/fop/area/CachedRenderPagesModel.java @@ -35,9 +35,10 @@ import org.xml.sax.SAXException; import org.apache.commons.io.IOUtils; +import org.apache.xmlgraphics.io.TempResourceURIGenerator; + import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; -import org.apache.fop.apps.io.TempResourceURIGenerator; import org.apache.fop.fonts.FontInfo; /** diff --git a/src/java/org/apache/fop/fonts/FontManagerConfigurator.java b/src/java/org/apache/fop/fonts/FontManagerConfigurator.java index cf9296826..fc2ce06a6 100644 --- a/src/java/org/apache/fop/fonts/FontManagerConfigurator.java +++ b/src/java/org/apache/fop/fonts/FontManagerConfigurator.java @@ -29,9 +29,10 @@ import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.xmlgraphics.io.ResourceResolver; + import org.apache.fop.apps.FOPException; import org.apache.fop.apps.io.InternalResourceResolver; -import org.apache.fop.apps.io.ResourceResolver; import org.apache.fop.apps.io.ResourceResolverFactory; import org.apache.fop.fonts.substitute.FontSubstitutions; import org.apache.fop.fonts.substitute.FontSubstitutionsConfigurator; diff --git a/src/java/org/apache/fop/image/loader/batik/PreloaderSVG.java b/src/java/org/apache/fop/image/loader/batik/PreloaderSVG.java index 022ff462f..3aa340a4a 100644 --- a/src/java/org/apache/fop/image/loader/batik/PreloaderSVG.java +++ b/src/java/org/apache/fop/image/loader/batik/PreloaderSVG.java @@ -45,6 +45,7 @@ import org.apache.xmlgraphics.image.loader.ImageSize; import org.apache.xmlgraphics.image.loader.impl.AbstractImagePreloader; import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM; import org.apache.xmlgraphics.image.loader.util.ImageUtil; +import org.apache.xmlgraphics.io.XmlSourceUtil; import org.apache.xmlgraphics.util.MimeConstants; import org.apache.xmlgraphics.util.UnitConv; @@ -79,7 +80,7 @@ public class PreloaderSVG extends AbstractImagePreloader { } } if (info != null) { - ImageUtil.closeQuietly(src); //Image is fully read + XmlSourceUtil.closeQuietly(src); //Image is fully read } return info; } @@ -119,7 +120,7 @@ public class PreloaderSVG extends AbstractImagePreloader { DOMSource domSrc = (DOMSource)src; doc = (SVGDocument)domSrc.getNode(); } else { - in = new UnclosableInputStream(ImageUtil.needInputStream(src)); + in = new UnclosableInputStream(XmlSourceUtil.needInputStream(src)); int length = in.available(); in.mark(length + 1); SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory( diff --git a/src/java/org/apache/fop/image/loader/batik/PreloaderWMF.java b/src/java/org/apache/fop/image/loader/batik/PreloaderWMF.java index 3bef0f41d..43341cbe6 100644 --- a/src/java/org/apache/fop/image/loader/batik/PreloaderWMF.java +++ b/src/java/org/apache/fop/image/loader/batik/PreloaderWMF.java @@ -38,6 +38,7 @@ import org.apache.xmlgraphics.image.loader.ImageInfo; import org.apache.xmlgraphics.image.loader.ImageSize; import org.apache.xmlgraphics.image.loader.impl.AbstractImagePreloader; import org.apache.xmlgraphics.image.loader.util.ImageUtil; +import org.apache.xmlgraphics.io.XmlSourceUtil; import org.apache.fop.util.UnclosableInputStream; @@ -69,7 +70,7 @@ public class PreloaderWMF extends AbstractImagePreloader { } } if (info != null) { - ImageUtil.closeQuietly(src); //Image is fully read + XmlSourceUtil.closeQuietly(src); //Image is fully read } return info; } @@ -88,7 +89,7 @@ public class PreloaderWMF extends AbstractImagePreloader { ImageContext context) { // parse document and get the size attributes of the svg element - InputStream in = new UnclosableInputStream(ImageUtil.needInputStream(src)); + InputStream in = new UnclosableInputStream(XmlSourceUtil.needInputStream(src)); try { in.mark(4 + 1); diff --git a/src/java/org/apache/fop/render/ps/PSDocumentHandler.java b/src/java/org/apache/fop/render/ps/PSDocumentHandler.java index 0ec5fdda6..897ed8fb8 100644 --- a/src/java/org/apache/fop/render/ps/PSDocumentHandler.java +++ b/src/java/org/apache/fop/render/ps/PSDocumentHandler.java @@ -39,6 +39,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.xmlgraphics.io.TempResourceURIGenerator; import org.apache.xmlgraphics.java2d.Dimension2DDouble; import org.apache.xmlgraphics.ps.DSCConstants; import org.apache.xmlgraphics.ps.PSDictionary; @@ -53,7 +54,6 @@ import org.apache.xmlgraphics.ps.dsc.events.DSCCommentBoundingBox; import org.apache.xmlgraphics.ps.dsc.events.DSCCommentHiResBoundingBox; import org.apache.fop.apps.MimeConstants; -import org.apache.fop.apps.io.TempResourceURIGenerator; import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler; import org.apache.fop.render.intermediate.IFContext; import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator; diff --git a/src/java/org/apache/fop/servlet/FopServlet.java b/src/java/org/apache/fop/servlet/FopServlet.java index 9dcf10a2b..c65b6af3b 100644 --- a/src/java/org/apache/fop/servlet/FopServlet.java +++ b/src/java/org/apache/fop/servlet/FopServlet.java @@ -41,14 +41,15 @@ import javax.xml.transform.stream.StreamSource; import org.apache.commons.io.output.ByteArrayOutputStream; +import org.apache.xmlgraphics.io.Resource; +import org.apache.xmlgraphics.io.ResourceResolver; + import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.Fop; import org.apache.fop.apps.FopFactory; import org.apache.fop.apps.FopFactoryBuilder; import org.apache.fop.apps.MimeConstants; -import org.apache.fop.apps.io.Resource; -import org.apache.fop.apps.io.ResourceResolver; /** * Example servlet to generate a PDF from a servlet. diff --git a/test/java/org/apache/fop/URIResolutionTestCase.java b/test/java/org/apache/fop/URIResolutionTestCase.java index f1dd89725..e1015306e 100644 --- a/test/java/org/apache/fop/URIResolutionTestCase.java +++ b/test/java/org/apache/fop/URIResolutionTestCase.java @@ -46,14 +46,15 @@ import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.xpath.XPathAPI; import org.apache.xpath.objects.XObject; +import org.apache.xmlgraphics.io.Resource; +import org.apache.xmlgraphics.io.ResourceResolver; + import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.Fop; import org.apache.fop.apps.FopFactory; import org.apache.fop.apps.FopFactoryBuilder; import org.apache.fop.apps.MimeConstants; -import org.apache.fop.apps.io.Resource; -import org.apache.fop.apps.io.ResourceResolver; import org.apache.fop.apps.io.ResourceResolverFactory; import org.apache.fop.render.xml.XMLRenderer; diff --git a/test/java/org/apache/fop/apps/EnvironmentalProfileFactoryTestCase.java b/test/java/org/apache/fop/apps/EnvironmentalProfileFactoryTestCase.java index 7b033969b..e94403e71 100644 --- a/test/java/org/apache/fop/apps/EnvironmentalProfileFactoryTestCase.java +++ b/test/java/org/apache/fop/apps/EnvironmentalProfileFactoryTestCase.java @@ -21,11 +21,12 @@ import java.net.URI; import org.junit.Test; -import org.apache.fop.apps.io.ResourceResolver; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; +import org.apache.xmlgraphics.io.ResourceResolver; + public class EnvironmentalProfileFactoryTestCase { private final URI testURI = URI.create("this.is.purely.for.test.purposes"); diff --git a/test/java/org/apache/fop/apps/MutableConfig.java b/test/java/org/apache/fop/apps/MutableConfig.java index 79f038f24..3ff3c10fa 100644 --- a/test/java/org/apache/fop/apps/MutableConfig.java +++ b/test/java/org/apache/fop/apps/MutableConfig.java @@ -24,8 +24,9 @@ import java.util.Set; import org.apache.avalon.framework.configuration.Configuration; import org.apache.xmlgraphics.image.loader.ImageManager; +import org.apache.xmlgraphics.image.loader.impl.AbstractImageSessionContext.FallbackResolver; +import org.apache.xmlgraphics.io.ResourceResolver; -import org.apache.fop.apps.io.ResourceResolver; import org.apache.fop.fonts.FontManager; import org.apache.fop.layoutmgr.LayoutManagerMaker; @@ -130,4 +131,8 @@ public final class MutableConfig implements FopFactoryConfig { public Map getHyphenationPatternNames() { return delegate.getHyphenationPatternNames(); } + + public FallbackResolver getFallbackResolver() { + return delegate.getFallbackResolver(); + } } diff --git a/test/java/org/apache/fop/apps/io/BaseURIResolutionTest.java b/test/java/org/apache/fop/apps/io/BaseURIResolutionTest.java index ee337c39d..2131407c1 100644 --- a/test/java/org/apache/fop/apps/io/BaseURIResolutionTest.java +++ b/test/java/org/apache/fop/apps/io/BaseURIResolutionTest.java @@ -37,6 +37,8 @@ import org.xml.sax.SAXException; import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.ByteArrayOutputStream; +import org.apache.xmlgraphics.io.ResourceResolver; + import org.apache.fop.apps.FOPException; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.Fop; diff --git a/test/java/org/apache/fop/apps/io/FontURIResolver.java b/test/java/org/apache/fop/apps/io/FontURIResolver.java index d6f836783..40d6c7402 100644 --- a/test/java/org/apache/fop/apps/io/FontURIResolver.java +++ b/test/java/org/apache/fop/apps/io/FontURIResolver.java @@ -39,6 +39,8 @@ import org.apache.fop.apps.PDFRendererConfBuilder; import static org.junit.Assert.assertTrue; +import org.apache.xmlgraphics.io.Resource; + public class FontURIResolver extends BaseURIResolutionTest { public enum Event { diff --git a/test/java/org/apache/fop/apps/io/ResourceResolverFactoryTestCase.java b/test/java/org/apache/fop/apps/io/ResourceResolverFactoryTestCase.java index b6b2eea00..6ae34b4f9 100644 --- a/test/java/org/apache/fop/apps/io/ResourceResolverFactoryTestCase.java +++ b/test/java/org/apache/fop/apps/io/ResourceResolverFactoryTestCase.java @@ -36,6 +36,10 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import org.apache.xmlgraphics.io.Resource; +import org.apache.xmlgraphics.io.ResourceResolver; +import org.apache.xmlgraphics.io.TempResourceResolver; + public class ResourceResolverFactoryTestCase { private static final byte[] DATA = new byte[]{(byte) 0, (byte) 1, (byte) 2}; @@ -193,10 +197,10 @@ public class ResourceResolverFactoryTestCase { final ResourceResolver sut; TestCreateSchemaAwareResourceResolverBuilderHelper() { - ResourceResolverFactory.SchemaAwareResourceResolverBuilder builder - = ResourceResolverFactory.createSchemaAwareResourceResolverBuilder( + ResourceResolverFactory.SchemeAwareResourceResolverBuilder builder + = ResourceResolverFactory.createSchemeAwareResourceResolverBuilder( defaultResourceResolver); - builder.registerResourceResolverForSchema(SCHEMA, registedResourceResolver); + builder.registerResourceResolverForScheme(SCHEMA, registedResourceResolver); sut = builder.build(); } diff --git a/test/java/org/apache/fop/apps/io/TestingResourceResolver.java b/test/java/org/apache/fop/apps/io/TestingResourceResolver.java index 532353337..f1cd06138 100644 --- a/test/java/org/apache/fop/apps/io/TestingResourceResolver.java +++ b/test/java/org/apache/fop/apps/io/TestingResourceResolver.java @@ -24,6 +24,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import org.apache.xmlgraphics.io.ResourceResolver; + abstract class TestingResourceResolver implements ResourceResolver { private final Map checker; diff --git a/test/java/org/apache/fop/apps/io/URIResolverWrapperTestCase.java b/test/java/org/apache/fop/apps/io/URIResolverWrapperTestCase.java index 440b16abb..b2044e3b9 100644 --- a/test/java/org/apache/fop/apps/io/URIResolverWrapperTestCase.java +++ b/test/java/org/apache/fop/apps/io/URIResolverWrapperTestCase.java @@ -35,6 +35,8 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import org.apache.xmlgraphics.io.ResourceResolver; + public class URIResolverWrapperTestCase { private static final List BASE_URIS = Collections.unmodifiableList(Arrays.asList( diff --git a/test/java/org/apache/fop/intermediate/TestAssistant.java b/test/java/org/apache/fop/intermediate/TestAssistant.java index 2be41dca1..2b138de55 100644 --- a/test/java/org/apache/fop/intermediate/TestAssistant.java +++ b/test/java/org/apache/fop/intermediate/TestAssistant.java @@ -42,8 +42,11 @@ import org.w3c.dom.Element; import org.apache.xpath.XPathAPI; import org.apache.xpath.objects.XObject; +import org.apache.fop.apps.EnvironmentProfile; +import org.apache.fop.apps.EnvironmentalProfileFactory; import org.apache.fop.apps.FopFactory; import org.apache.fop.apps.FopFactoryBuilder; +import org.apache.fop.apps.io.ResourceResolverFactory; /** * Helper class for running FOP tests. @@ -115,7 +118,10 @@ public class TestAssistant { public FopFactory getFopFactory(Document testDoc) { boolean base14KerningEnabled = isBase14KerningEnabled(testDoc); boolean strictValidation = isStrictValidation(testDoc); - FopFactoryBuilder builder = new FopFactoryBuilder(testDir.getParentFile().toURI()); + EnvironmentProfile envProfile = EnvironmentalProfileFactory.createRestrictedIO( + testDir.getParentFile().toURI(), + ResourceResolverFactory.createDefaultResourceResolver()); + FopFactoryBuilder builder = new FopFactoryBuilder(envProfile); builder.setStrictFOValidation(strictValidation); builder.getFontManager().setBase14KerningEnabled(base14KerningEnabled); return builder.build(); diff --git a/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java b/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java index 971471fa2..5e4b9e2e4 100644 --- a/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java +++ b/test/java/org/apache/fop/pdf/PDFFactoryTestCase.java @@ -26,8 +26,9 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import org.apache.xmlgraphics.io.ResourceResolver; + import org.apache.fop.apps.io.InternalResourceResolver; -import org.apache.fop.apps.io.ResourceResolver; import org.apache.fop.apps.io.ResourceResolverFactory; import org.apache.fop.fonts.CIDSet; import org.apache.fop.fonts.CIDSubset; -- cgit v1.2.3 From a1af0b2481adf1d4bdb1279972a9d4e51e381f6a Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Wed, 26 Sep 2012 11:31:40 +0000 Subject: Bugzilla #53902: Added possibility to define 'header' table columns (the same way as fo:table-header allows to define header rows). When accessibility is enabled, this allows to set the appropriate Scope attribute on the corresponding TH cells. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1390412 13f79535-47bb-0310-9956-ffa450edef68 --- .../fo/StructureTreeEventTrigger.java | 33 ++++++ src/java/org/apache/fop/fo/Constants.java | 5 +- src/java/org/apache/fop/fo/FOPropertyMapping.java | 6 ++ .../fop/fo/extensions/ExtensionElementMapping.java | 1 + .../fop/fo/extensions/InternalElementMapping.java | 3 + .../org/apache/fop/fo/flow/table/TableColumn.java | 12 +++ .../fop/render/pdf/PDFStructureTreeBuilder.java | 29 +++--- status.xml | 5 + .../fo/FO2StructureTreeConverterTestCase.java | 5 + .../fop/accessibility/fo/fo2StructureTree.xsl | 31 +++++- .../fop/accessibility/fo/table-header_scope.fo | 57 +++++++++++ test/java/org/apache/fop/fo/FONodeMocks.java | 30 ++++-- .../fop/fo/flow/table/HeaderColumnTestCase.java | 112 +++++++++++++++++++++ .../pdf/background-image_svg_repeat.pdf | Bin 16997 -> 17081 bytes .../pdf/background-image_svg_single.pdf | Bin 9872 -> 9893 bytes test/pdf/accessibility/pdf/image_svg.pdf | Bin 14161 -> 14203 bytes test/pdf/accessibility/pdf/image_wmf.pdf | Bin 247758 -> 248963 bytes test/pdf/accessibility/pdf/text_font-embedding.pdf | Bin 19682 -> 28436 bytes test/pdf/accessibility/pdf/th_scope.pdf | Bin 0 -> 15495 bytes test/pdf/accessibility/th_scope.fo | 68 +++++++++++++ 20 files changed, 371 insertions(+), 26 deletions(-) create mode 100644 test/java/org/apache/fop/accessibility/fo/table-header_scope.fo create mode 100644 test/java/org/apache/fop/fo/flow/table/HeaderColumnTestCase.java create mode 100644 test/pdf/accessibility/pdf/th_scope.pdf create mode 100644 test/pdf/accessibility/th_scope.fo (limited to 'test/java/org/apache/fop') diff --git a/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java b/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java index 93b815d30..09a814ef5 100644 --- a/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java +++ b/src/java/org/apache/fop/accessibility/fo/StructureTreeEventTrigger.java @@ -20,6 +20,7 @@ package org.apache.fop.accessibility.fo; import java.util.Locale; +import java.util.Stack; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; @@ -30,6 +31,7 @@ import org.apache.fop.fo.FOEventHandler; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FOText; import org.apache.fop.fo.extensions.ExtensionElementMapping; +import org.apache.fop.fo.extensions.InternalElementMapping; import org.apache.fop.fo.flow.AbstractGraphics; import org.apache.fop.fo.flow.BasicLink; import org.apache.fop.fo.flow.Block; @@ -70,6 +72,10 @@ class StructureTreeEventTrigger extends FOEventHandler { private LayoutMasterSet layoutMasterSet; + private final Stack tables = new Stack
(); + + private final Stack inTableHeader = new Stack(); + public StructureTreeEventTrigger(StructureTreeEventHandler structureTreeEventHandler) { this.structureTreeEventHandler = structureTreeEventHandler; } @@ -195,42 +201,51 @@ class StructureTreeEventTrigger extends FOEventHandler { @Override public void startTable(Table tbl) { + tables.push(tbl); startElement(tbl); } @Override public void endTable(Table tbl) { endElement(tbl); + tables.pop(); } @Override public void startHeader(TableHeader header) { + inTableHeader.push(Boolean.TRUE); startElement(header); } @Override public void endHeader(TableHeader header) { endElement(header); + inTableHeader.pop(); } @Override public void startFooter(TableFooter footer) { + // TODO Shouldn't it be true? + inTableHeader.push(Boolean.FALSE); startElement(footer); } @Override public void endFooter(TableFooter footer) { endElement(footer); + inTableHeader.pop(); } @Override public void startBody(TableBody body) { + inTableHeader.push(Boolean.FALSE); startElement(body); } @Override public void endBody(TableBody body) { endElement(body); + inTableHeader.pop(); } @Override @@ -248,6 +263,24 @@ class StructureTreeEventTrigger extends FOEventHandler { AttributesImpl attributes = new AttributesImpl(); addSpanAttribute(attributes, "number-columns-spanned", tc.getNumberColumnsSpanned()); addSpanAttribute(attributes, "number-rows-spanned", tc.getNumberRowsSpanned()); + boolean rowHeader = inTableHeader.peek(); + boolean columnHeader = tables.peek().getColumn(tc.getColumnNumber() - 1).isHeader(); + if (rowHeader || columnHeader) { + final String th = "TH"; + String role = tc.getCommonAccessibility().getRole(); + /* Do not override a custom role */ + if (role == null) { + role = th; + addNoNamespaceAttribute(attributes, "role", th); + } + if (role.equals(th)) { + if (columnHeader) { + String scope = rowHeader ? "Both" : "Row"; + addAttribute(attributes, InternalElementMapping.URI, InternalElementMapping.SCOPE, + InternalElementMapping.STANDARD_PREFIX, scope); + } + } + } startElement(tc, attributes); } diff --git a/src/java/org/apache/fop/fo/Constants.java b/src/java/org/apache/fop/fo/Constants.java index 2d10dcdd9..6688042e2 100644 --- a/src/java/org/apache/fop/fo/Constants.java +++ b/src/java/org/apache/fop/fo/Constants.java @@ -785,8 +785,11 @@ public interface Constants { */ int PR_X_NUMBER_CONVERSION_FEATURES = 276; + /** Scope for table header */ + int PR_X_HEADER_COLUMN = 277; + /** Number of property constants defined */ - int PROPERTY_COUNT = 276; + int PROPERTY_COUNT = 277; // compound property constants diff --git a/src/java/org/apache/fop/fo/FOPropertyMapping.java b/src/java/org/apache/fop/fo/FOPropertyMapping.java index d0d13fc17..a92b71ef9 100644 --- a/src/java/org/apache/fop/fo/FOPropertyMapping.java +++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java @@ -2513,6 +2513,12 @@ public final class FOPropertyMapping implements Constants { m.setInherited(false); m.setDefault("false"); addPropertyMaker("table-omit-header-at-break", m); + + // fox:scope + m = new EnumProperty.Maker(PR_X_HEADER_COLUMN); + m.useGeneric(genericBoolean); + m.setDefault("false"); + addPropertyMaker("fox:header", m); } private void createWritingModeProperties() { diff --git a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java index f0e03399f..a040edf1a 100644 --- a/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java +++ b/src/java/org/apache/fop/fo/extensions/ExtensionElementMapping.java @@ -52,6 +52,7 @@ public class ExtensionElementMapping extends ElementMapping { PROPERTY_ATTRIBUTES.add("disable-column-balancing"); //These are FOP's extension properties for accessibility PROPERTY_ATTRIBUTES.add("alt-text"); + PROPERTY_ATTRIBUTES.add("header"); } /** diff --git a/src/java/org/apache/fop/fo/extensions/InternalElementMapping.java b/src/java/org/apache/fop/fo/extensions/InternalElementMapping.java index 687952d25..f257dd79d 100644 --- a/src/java/org/apache/fop/fo/extensions/InternalElementMapping.java +++ b/src/java/org/apache/fop/fo/extensions/InternalElementMapping.java @@ -43,12 +43,15 @@ public class InternalElementMapping extends ElementMapping { /** The "struct-ref" attribute, to refer to a structure tree element. */ public static final String STRUCT_REF = "struct-ref"; + public static final String SCOPE = "scope"; + private static final Set PROPERTY_ATTRIBUTES = new java.util.HashSet(); static { //These are FOP's extension properties for accessibility PROPERTY_ATTRIBUTES.add(STRUCT_ID); PROPERTY_ATTRIBUTES.add(STRUCT_REF); + PROPERTY_ATTRIBUTES.add(SCOPE); } /** diff --git a/src/java/org/apache/fop/fo/flow/table/TableColumn.java b/src/java/org/apache/fop/fo/flow/table/TableColumn.java index 5047822da..33cbff884 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableColumn.java +++ b/src/java/org/apache/fop/fo/flow/table/TableColumn.java @@ -24,6 +24,7 @@ import org.xml.sax.Locator; import org.apache.fop.apps.FOPException; import org.apache.fop.datatypes.Length; +import org.apache.fop.fo.Constants; import org.apache.fop.fo.FONode; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; @@ -44,6 +45,7 @@ public class TableColumn extends TableFObj { private Length columnWidth; private int numberColumnsRepeated; private int numberColumnsSpanned; + private boolean isHeader; // Unused but valid items, commented out for performance: // private int visibility; // End of property values @@ -120,6 +122,7 @@ public class TableColumn extends TableFObj { if (!this.implicitColumn) { this.pList = pList; } + isHeader = (pList.get(Constants.PR_X_HEADER_COLUMN).getEnum() == Constants.EN_TRUE); } /** {@inheritDoc} */ @@ -263,4 +266,13 @@ public class TableColumn extends TableFObj { this.pList = null; } + /** + * Returns {@code true} if this column is made of header cells. + * + * @return {@code true} if cells in this column are like TH cells in HTML + */ + public boolean isHeader() { + return isHeader; + } + } diff --git a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java index 0d4a6b7fb..031224ffb 100644 --- a/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java +++ b/src/java/org/apache/fop/render/pdf/PDFStructureTreeBuilder.java @@ -30,6 +30,7 @@ import org.apache.fop.accessibility.StructureTreeElement; import org.apache.fop.accessibility.StructureTreeEventHandler; import org.apache.fop.events.EventBroadcaster; import org.apache.fop.fo.extensions.ExtensionElementMapping; +import org.apache.fop.fo.extensions.InternalElementMapping; import org.apache.fop.fo.pagination.Flow; import org.apache.fop.pdf.PDFFactory; import org.apache.fop.pdf.PDFParentTree; @@ -138,7 +139,7 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler { PDFStructElem structElem = createStructureElement(parent, structureType); setAttributes(structElem, attributes); addKidToParent(structElem, parent, attributes); - registerStructureElement(structElem, pdfFactory); + registerStructureElement(structElem, pdfFactory, attributes); return structElem; } @@ -155,7 +156,8 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler { parent.addKid(kid); } - protected void registerStructureElement(PDFStructElem structureElement, PDFFactory pdfFactory) { + protected void registerStructureElement(PDFStructElem structureElement, PDFFactory pdfFactory, + Attributes attributes) { pdfFactory.getDocument().registerStructureElement(structureElement); } @@ -240,22 +242,15 @@ class PDFStructureTreeBuilder implements StructureTreeEventHandler { } @Override - protected PDFStructElem createStructureElement(StructureHierarchyMember parent, - StructureType structureType) { - PDFStructElem grandParent = ((PDFStructElem) parent).getParentStructElem(); - //TODO What to do with cells from table-footer? Currently they are mapped on TD. - if (grandParent.getStructureType() == StandardStructureTypes.Table.THEAD) { - structureType = StandardStructureTypes.Table.TH; - } else { - structureType = StandardStructureTypes.Table.TD; - } - return super.createStructureElement(parent, structureType); - } - - @Override - protected void registerStructureElement(PDFStructElem structureElement, PDFFactory pdfFactory) { + protected void registerStructureElement(PDFStructElem structureElement, PDFFactory pdfFactory, + Attributes attributes) { if (structureElement.getStructureType() == Table.TH) { - pdfFactory.getDocument().registerStructureElement(structureElement, Scope.COLUMN); + String scopeAttribute = attributes.getValue(InternalElementMapping.URI, + InternalElementMapping.SCOPE); + Scope scope = (scopeAttribute == null) + ? Scope.COLUMN + : Scope.valueOf(scopeAttribute.toUpperCase(Locale.ENGLISH)); + pdfFactory.getDocument().registerStructureElement(structureElement, scope); } else { pdfFactory.getDocument().registerStructureElement(structureElement); } diff --git a/status.xml b/status.xml index 7571ddfda..40e4c8db6 100644 --- a/status.xml +++ b/status.xml @@ -62,6 +62,11 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> + + Added possibility to define ‘header’ table columns (the same way as fo:table-header allows + to define header rows). When accessibility is enabled, this allows to set the appropriate + Scope attribute on the corresponding TH cells. + Full font embedding in PDF diff --git a/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java b/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java index 6092912f9..87b970576 100644 --- a/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java +++ b/test/java/org/apache/fop/accessibility/fo/FO2StructureTreeConverterTestCase.java @@ -91,6 +91,11 @@ public class FO2StructureTreeConverterTestCase { testConverter("/org/apache/fop/fo/pagination/side-regions.fo"); } + @Test + public void headerTableCellMustPropagateScope() throws Exception { + testConverter("table-header_scope.fo"); + } + private static InputStream getResource(String name) { return FO2StructureTreeConverterTestCase.class.getResourceAsStream(name); } diff --git a/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl b/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl index c739462e4..90d74a7c4 100644 --- a/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl +++ b/test/java/org/apache/fop/accessibility/fo/fo2StructureTree.xsl @@ -21,7 +21,7 @@ xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" xmlns:foi="http://xmlgraphics.apache.org/fop/internal"> - + @@ -92,10 +92,37 @@ - + + + + + + + + + + TH + Row + + + + + + + + + TH + + Both + + + + + diff --git a/test/java/org/apache/fop/accessibility/fo/table-header_scope.fo b/test/java/org/apache/fop/accessibility/fo/table-header_scope.fo new file mode 100644 index 000000000..c6272d546 --- /dev/null +++ b/test/java/org/apache/fop/accessibility/fo/table-header_scope.fo @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + Table Header + + + Column 1 + + + Column 2 + + + + + + + Row 1 + + + Cell 1.1 + + + Cell 1.2 + + + + + Row 2 + + + Cell 2.1 + + + Cell 2.2 + + + + + + + diff --git a/test/java/org/apache/fop/fo/FONodeMocks.java b/test/java/org/apache/fop/fo/FONodeMocks.java index 001173179..ba969b9fa 100644 --- a/test/java/org/apache/fop/fo/FONodeMocks.java +++ b/test/java/org/apache/fop/fo/FONodeMocks.java @@ -19,19 +19,22 @@ package org.apache.fop.fo; +import java.io.IOException; + import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.io.IOException; - import org.apache.xmlgraphics.image.loader.ImageException; import org.apache.xmlgraphics.image.loader.ImageManager; import org.apache.xmlgraphics.image.loader.ImageSessionContext; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.FopFactory; +import org.apache.fop.events.EventBroadcaster; +import org.apache.fop.fo.flow.table.ColumnNumberManager; +import org.apache.fop.fo.flow.table.ColumnNumberManagerHolder; /** * A helper class for creating mocks of {@link FONode} and its descendants. @@ -51,27 +54,36 @@ public final class FONodeMocks { public static FONode mockFONode() { FONode mockFONode = mock(FONode.class); mockGetFOEventHandler(mockFONode); + mockGetImageManager(mockFONode.getFOEventHandler().getUserAgent()); return mockFONode; } - private static void mockGetFOEventHandler(FONode mockFONode) { + public static FOEventHandler mockGetFOEventHandler(FONode mockFONode) { FOEventHandler mockFOEventHandler = mock(FOEventHandler.class); mockGetUserAgent(mockFOEventHandler); when(mockFONode.getFOEventHandler()).thenReturn(mockFOEventHandler); + return mockFOEventHandler; } - private static void mockGetUserAgent(FOEventHandler mockFOEventHandler) { + public static FOUserAgent mockGetUserAgent(FOEventHandler mockFOEventHandler) { FOUserAgent mockFOUserAgent = mock(FOUserAgent.class); - mockGetImageManager(mockFOUserAgent); when(mockFOEventHandler.getUserAgent()).thenReturn(mockFOUserAgent); + return mockFOUserAgent; } - private static void mockGetImageManager(FOUserAgent mockFOUserAgent) { + public static EventBroadcaster mockGetEventBroadcaster(FOUserAgent mockFOUserAgent) { + EventBroadcaster mockBroadcaster = mock(EventBroadcaster.class); + when(mockFOUserAgent.getEventBroadcaster()).thenReturn(mockBroadcaster); + return mockBroadcaster; + } + + public static ImageManager mockGetImageManager(FOUserAgent mockFOUserAgent) { try { ImageManager mockImageManager = mock(ImageManager.class); when(mockImageManager.getImageInfo(anyString(), any(ImageSessionContext.class))) .thenReturn(null); when(mockFOUserAgent.getImageManager()).thenReturn(mockImageManager); + return mockImageManager; } catch (ImageException e) { throw new RuntimeException(e); } catch (IOException e) { @@ -79,4 +91,10 @@ public final class FONodeMocks { } } + public static ColumnNumberManager mockGetColumnNumberManager(ColumnNumberManagerHolder mock) { + ColumnNumberManager mockColumnNumberManager = mock(ColumnNumberManager.class); + when(mock.getColumnNumberManager()).thenReturn(mockColumnNumberManager); + return mockColumnNumberManager; + } + } diff --git a/test/java/org/apache/fop/fo/flow/table/HeaderColumnTestCase.java b/test/java/org/apache/fop/fo/flow/table/HeaderColumnTestCase.java new file mode 100644 index 000000000..4cc94b5e0 --- /dev/null +++ b/test/java/org/apache/fop/fo/flow/table/HeaderColumnTestCase.java @@ -0,0 +1,112 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.flow.table; + +import org.junit.Test; +import org.xml.sax.Attributes; +import org.xml.sax.Locator; +import org.xml.sax.helpers.AttributesImpl; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.fop.apps.FOUserAgent; +import org.apache.fop.events.EventBroadcaster; +import org.apache.fop.fo.ElementMappingRegistry; +import org.apache.fop.fo.FOEventHandler; +import org.apache.fop.fo.FONodeMocks; +import org.apache.fop.fo.FOValidationEventProducer; +import org.apache.fop.fo.PropertyList; +import org.apache.fop.fo.StaticPropertyList; +import org.apache.fop.fo.ValidationException; +import org.apache.fop.fo.expr.PropertyException; +import org.apache.fop.fo.extensions.ExtensionElementMapping; +import org.apache.fop.util.XMLUtil; + +/** + * Tests that the fox:header property is correctly parsed and set up at the FO tree level. + */ +public class HeaderColumnTestCase { + + @Test + public void testWrongValue() throws ValidationException { + Table parent = createTableParent(); + EventBroadcaster mockEventBroadcaster = FONodeMocks.mockGetEventBroadcaster( + parent.getFOEventHandler().getUserAgent()); + FOValidationEventProducer eventProducer = mockGetEventProducerFor(mockEventBroadcaster); + TableColumn column = new TableColumn(parent); + PropertyList propertyList = new StaticPropertyList(column, null); + Attributes atts = createScopeAttribute("blah"); + propertyList.addAttributesToList(atts); + verify(eventProducer).invalidPropertyValue(any(), eq("fo:table-column"), + eq("fox:header"), eq("blah"), any(PropertyException.class), any(Locator.class)); + } + + @Test + public void testCorrectValue() throws Exception { + testCorrectValue(true); + testCorrectValue(false); + } + + private void testCorrectValue(boolean expectedValue) throws Exception { + Table parent = createTableParent(); + FONodeMocks.mockGetColumnNumberManager(parent); + TableColumn column = new TableColumn(parent, true); + PropertyList propertyList = new StaticPropertyList(column, null); + Attributes atts = createScopeAttribute(String.valueOf(expectedValue)); + propertyList.addAttributesToList(atts); + column.bind(propertyList); + assertEquals(expectedValue, column.isHeader()); + } + + private Table createTableParent() { + Table parent = mock(Table.class); + FOEventHandler mockFOEventHandler = FONodeMocks.mockGetFOEventHandler(parent); + FOUserAgent mockUserAgent = mockFOEventHandler.getUserAgent(); + mockGetElementMappingRegistry(mockUserAgent); + return parent; + } + + private Attributes createScopeAttribute(String value) { + AttributesImpl atts = new AttributesImpl(); + atts.addAttribute(ExtensionElementMapping.URI, "header", "fox:header", XMLUtil.CDATA, value); + return atts; + } + + private ElementMappingRegistry mockGetElementMappingRegistry(FOUserAgent mockUserAgent) { + ElementMappingRegistry mockRegistry = mock(ElementMappingRegistry.class); + when(mockRegistry.getElementMapping(anyString())).thenReturn(new ExtensionElementMapping()); + when(mockUserAgent.getElementMappingRegistry()).thenReturn(mockRegistry); + return mockRegistry; + } + + private FOValidationEventProducer mockGetEventProducerFor(EventBroadcaster mockEventBroadcaster) { + FOValidationEventProducer mockEventProducer = mock(FOValidationEventProducer.class); + when(mockEventBroadcaster.getEventProducerFor(eq(FOValidationEventProducer.class))) + .thenReturn(mockEventProducer); + return mockEventProducer; + } + +} diff --git a/test/pdf/accessibility/pdf/background-image_svg_repeat.pdf b/test/pdf/accessibility/pdf/background-image_svg_repeat.pdf index 41c2ee500..0921d734f 100644 Binary files a/test/pdf/accessibility/pdf/background-image_svg_repeat.pdf and b/test/pdf/accessibility/pdf/background-image_svg_repeat.pdf differ diff --git a/test/pdf/accessibility/pdf/background-image_svg_single.pdf b/test/pdf/accessibility/pdf/background-image_svg_single.pdf index 8c2dc1006..9c8af4fb6 100644 Binary files a/test/pdf/accessibility/pdf/background-image_svg_single.pdf and b/test/pdf/accessibility/pdf/background-image_svg_single.pdf differ diff --git a/test/pdf/accessibility/pdf/image_svg.pdf b/test/pdf/accessibility/pdf/image_svg.pdf index a9428fd3f..cc0a3ebba 100644 Binary files a/test/pdf/accessibility/pdf/image_svg.pdf and b/test/pdf/accessibility/pdf/image_svg.pdf differ diff --git a/test/pdf/accessibility/pdf/image_wmf.pdf b/test/pdf/accessibility/pdf/image_wmf.pdf index c15a05223..368afe60d 100644 Binary files a/test/pdf/accessibility/pdf/image_wmf.pdf and b/test/pdf/accessibility/pdf/image_wmf.pdf differ diff --git a/test/pdf/accessibility/pdf/text_font-embedding.pdf b/test/pdf/accessibility/pdf/text_font-embedding.pdf index d1e4c6e28..47ca60bdb 100644 Binary files a/test/pdf/accessibility/pdf/text_font-embedding.pdf and b/test/pdf/accessibility/pdf/text_font-embedding.pdf differ diff --git a/test/pdf/accessibility/pdf/th_scope.pdf b/test/pdf/accessibility/pdf/th_scope.pdf new file mode 100644 index 000000000..596ffb7db Binary files /dev/null and b/test/pdf/accessibility/pdf/th_scope.pdf differ diff --git a/test/pdf/accessibility/th_scope.fo b/test/pdf/accessibility/th_scope.fo new file mode 100644 index 000000000..4e22b9160 --- /dev/null +++ b/test/pdf/accessibility/th_scope.fo @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + Table Header + + + Column 1 + + + Column 2 + + + + + + + Row 1 + + + Cell 1.1 + + + Cell 1.2 + + + + + Row 2 + + + Cell 2.1 + + + Cell 2.2 + + + + + Non-header + + + Cell 3.1 + + + Cell 3.2 + + + + + + + -- cgit v1.2.3 From d261730a4f7be752ae12a5ec52e6bad8ee4fa5c1 Mon Sep 17 00:00:00 2001 From: Mehdi Houshmand Date: Fri, 28 Sep 2012 15:22:49 +0000 Subject: Bugzilla#53924: Support for retrieve-table-markers, submitted by Luis Bernardo. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1391502 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/area/PageViewport.java | 177 +---- src/java/org/apache/fop/fo/Constants.java | 4 +- src/java/org/apache/fop/fo/FONode.java | 13 + src/java/org/apache/fop/fo/FOPropertyMapping.java | 5 +- .../apache/fop/fo/FOValidationEventProducer.java | 12 + .../apache/fop/fo/FOValidationEventProducer.xml | 1 + src/java/org/apache/fop/fo/FObj.java | 13 +- .../apache/fop/fo/flow/AbstractRetrieveMarker.java | 44 ++ src/java/org/apache/fop/fo/flow/Markers.java | 212 ++++++ .../org/apache/fop/fo/flow/RetrieveMarker.java | 17 +- .../apache/fop/fo/flow/RetrieveTableMarker.java | 25 +- .../org/apache/fop/fo/flow/table/TableCell.java | 9 + .../org/apache/fop/fo/flow/table/TablePart.java | 2 + .../fop/layoutmgr/AbstractBaseLayoutManager.java | 7 + .../fop/layoutmgr/AbstractLayoutManager.java | 61 +- .../AbstractPageSequenceLayoutManager.java | 11 +- .../org/apache/fop/layoutmgr/AreaAdditionUtil.java | 5 +- .../fop/layoutmgr/BlockContainerLayoutManager.java | 4 +- .../apache/fop/layoutmgr/BlockLayoutManager.java | 4 +- .../apache/fop/layoutmgr/LayoutManagerMapping.java | 21 +- .../org/apache/fop/layoutmgr/LocalBreaker.java | 137 ++++ .../RetrieveTableMarkerLayoutManager.java | 75 +++ .../fop/layoutmgr/StaticContentLayoutManager.java | 113 +--- .../fop/layoutmgr/inline/InlineLayoutManager.java | 4 +- .../fop/layoutmgr/list/ListBlockLayoutManager.java | 4 +- .../list/ListItemContentLayoutManager.java | 4 +- .../fop/layoutmgr/list/ListItemLayoutManager.java | 4 +- .../org/apache/fop/layoutmgr/table/RowPainter.java | 7 +- .../layoutmgr/table/TableCellLayoutManager.java | 187 +++++- .../layoutmgr/table/TableContentLayoutManager.java | 26 +- .../fop/layoutmgr/table/TableLayoutManager.java | 121 ++++ status.xml | 3 + .../org/apache/fop/fo/flow/MarkersTestCase.java | 128 ++++ .../PageSequenceLayoutManagerTestCase.java | 3 +- ...rieveTableMarkerLayoutManagerMakerTestCase.java | 56 ++ .../RetrieveTableMarkerLayoutManagerTestCase.java | 82 +++ .../table/TableCellLayoutManagerTestCase.java | 116 ++++ .../table/TableContentLayoutManagerTestCase.java | 64 ++ .../table/TableLayoutManagerTestCase.java | 119 ++++ .../layoutengine/standard-testcases/markers_10.xml | 144 +++++ .../retrieve-table-marker_multicolumn.xml | 720 +++++++++++++++++++++ 41 files changed, 2432 insertions(+), 332 deletions(-) create mode 100644 src/java/org/apache/fop/fo/flow/Markers.java create mode 100644 src/java/org/apache/fop/layoutmgr/LocalBreaker.java create mode 100644 src/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManager.java create mode 100644 test/java/org/apache/fop/fo/flow/MarkersTestCase.java create mode 100644 test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerMakerTestCase.java create mode 100644 test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerTestCase.java create mode 100644 test/java/org/apache/fop/layoutmgr/table/TableCellLayoutManagerTestCase.java create mode 100644 test/java/org/apache/fop/layoutmgr/table/TableContentLayoutManagerTestCase.java create mode 100644 test/java/org/apache/fop/layoutmgr/table/TableLayoutManagerTestCase.java create mode 100644 test/layoutengine/standard-testcases/markers_10.xml create mode 100644 test/layoutengine/standard-testcases/retrieve-table-marker_multicolumn.xml (limited to 'test/java/org/apache/fop') diff --git a/src/java/org/apache/fop/area/PageViewport.java b/src/java/org/apache/fop/area/PageViewport.java index f38964ebc..d1250238a 100644 --- a/src/java/org/apache/fop/area/PageViewport.java +++ b/src/java/org/apache/fop/area/PageViewport.java @@ -34,6 +34,10 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.apps.FOPException; import org.apache.fop.fo.flow.Marker; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.flow.AbstractRetrieveMarker; +import org.apache.fop.fo.flow.Marker; +import org.apache.fop.fo.flow.Markers; import org.apache.fop.fo.pagination.SimplePageMaster; import org.apache.fop.traits.WritingModeTraitsGetter; @@ -81,13 +85,7 @@ public class PageViewport extends AreaTreeObject implements Resolvable { private Map> pendingResolved = null; - // hashmap of markers for this page - // start and end are added by the fo that contains the markers - private Map markerFirstStart = null; - private Map markerLastStart = null; - private Map markerFirstAny = null; - private Map markerLastEnd = null; - private Map markerLastAny = null; + private Markers pageMarkers; /** * logging instance @@ -352,115 +350,23 @@ public class PageViewport extends AreaTreeObject implements Resolvable { } /** - * Add the markers for this page. - * Only the required markers are kept. - * For "first-starting-within-page" it adds the markers - * that are starting only if the marker class name is not - * already added. - * For "first-including-carryover" it adds any starting marker - * if the marker class name is not already added. - * For "last-starting-within-page" it adds all marks that - * are starting, replacing earlier markers. - * For "last-ending-within-page" it adds all markers that - * are ending, replacing earlier markers. - * - * Should this logic be placed in the Page layout manager. + * Register the markers for this page. * * @param marks the map of markers to add * @param starting if the area being added is starting or ending * @param isfirst if the area being added has is-first trait * @param islast if the area being added has is-last trait */ - public void addMarkers(Map marks, boolean starting, - boolean isfirst, boolean islast) { - - if (marks == null) { - return; - } - if (log.isDebugEnabled()) { - log.debug("--" + marks.keySet() + ": " - + (starting ? "starting" : "ending") - + (isfirst ? ", first" : "") - + (islast ? ", last" : "")); - } - - // at the start of the area, register is-first and any areas - if (starting) { - if (isfirst) { - if (markerFirstStart == null) { - markerFirstStart = new HashMap(); - } - if (markerFirstAny == null) { - markerFirstAny = new HashMap(); - } - // first on page: only put in new values, leave current - for (String key : marks.keySet()) { - if (!markerFirstStart.containsKey(key)) { - markerFirstStart.put(key, marks.get(key)); - if (log.isTraceEnabled()) { - log.trace("page " + pageNumberString + ": " - + "Adding marker " + key + " to FirstStart"); - } - } - if (!markerFirstAny.containsKey(key)) { - markerFirstAny.put(key, marks.get(key)); - if (log.isTraceEnabled()) { - log.trace("page " + pageNumberString + ": " - + "Adding marker " + key + " to FirstAny"); - } - } - } - if (markerLastStart == null) { - markerLastStart = new HashMap(); - } - // last on page: replace all - markerLastStart.putAll(marks); - if (log.isTraceEnabled()) { - log.trace("page " + pageNumberString + ": " - + "Adding all markers to LastStart"); - } - } else { - if (markerFirstAny == null) { - markerFirstAny = new HashMap(); - } - // first on page: only put in new values, leave current - for (String key : marks.keySet()) { - if (!markerFirstAny.containsKey(key)) { - markerFirstAny.put(key, marks.get(key)); - if (log.isTraceEnabled()) { - log.trace("page " + pageNumberString + ": " - + "Adding marker " + key + " to FirstAny"); - } - } - } - } - } else { - // at the end of the area, register is-last and any areas - if (islast) { - if (markerLastEnd == null) { - markerLastEnd = new HashMap(); - } - // last on page: replace all - markerLastEnd.putAll(marks); - if (log.isTraceEnabled()) { - log.trace("page " + pageNumberString + ": " - + "Adding all markers to LastEnd"); - } - } - if (markerLastAny == null) { - markerLastAny = new HashMap(); - } - // last on page: replace all - markerLastAny.putAll(marks); - if (log.isTraceEnabled()) { - log.trace("page " + pageNumberString + ": " - + "Adding all markers to LastAny"); - } + public void registerMarkers(Map marks, boolean starting, boolean isfirst, boolean islast) { + if (pageMarkers == null) { + pageMarkers = new Markers(); } + pageMarkers.register(marks, starting, isfirst, islast); } + /** - * Get a marker from this page. + * Resolve a marker from this page. * This will retrieve a marker with the class name * and position. * @@ -468,64 +374,17 @@ public class PageViewport extends AreaTreeObject implements Resolvable { * @param pos the position to retrieve * @return Object the marker found or null */ - public Marker getMarker(String name, int pos) { - Marker mark = null; - String posName = null; - switch (pos) { - case EN_FSWP: - if (markerFirstStart != null) { - mark = markerFirstStart.get(name); - posName = "FSWP"; - } - if (mark == null && markerFirstAny != null) { - mark = markerFirstAny.get(name); - posName = "FirstAny after " + posName; - } - break; - case EN_FIC: - if (markerFirstAny != null) { - mark = markerFirstAny.get(name); - posName = "FIC"; - } - break; - case EN_LSWP: - if (markerLastStart != null) { - mark = markerLastStart.get(name); - posName = "LSWP"; - } - if (mark == null && markerLastAny != null) { - mark = markerLastAny.get(name); - posName = "LastAny after " + posName; - } - break; - case EN_LEWP: - if (markerLastEnd != null) { - mark = markerLastEnd.get(name); - posName = "LEWP"; - } - if (mark == null && markerLastAny != null) { - mark = markerLastAny.get(name); - posName = "LastAny after " + posName; - } - break; - default: - assert false; - } - if (log.isTraceEnabled()) { - log.trace("page " + pageNumberString + ": " + "Retrieving marker " + name - + " at position " + posName); + public Marker resolveMarker(AbstractRetrieveMarker rm) { + if (pageMarkers == null) { + return null; } - return mark; + return pageMarkers.resolve(rm); } /** Dumps the current marker data to the logger. */ public void dumpMarkers() { - if (log.isTraceEnabled()) { - log.trace("FirstAny: " + this.markerFirstAny); - log.trace("FirstStart: " + this.markerFirstStart); - log.trace("LastAny: " + this.markerLastAny); - log.trace("LastEnd: " + this.markerLastEnd); - log.trace("LastStart: " + this.markerLastStart); + if (pageMarkers != null) { + pageMarkers.dump(); } } diff --git a/src/java/org/apache/fop/fo/Constants.java b/src/java/org/apache/fop/fo/Constants.java index 6688042e2..1f098fe05 100644 --- a/src/java/org/apache/fop/fo/Constants.java +++ b/src/java/org/apache/fop/fo/Constants.java @@ -1227,6 +1227,8 @@ public interface Constants { int EN_BT = 204; // bottom to top /** Enumeration constant */ int EN_TB_LR = 205; // for top-to-bottom, left-to-right writing mode + /** Enumeration constant -- for fo:retrieve-table-marker */ + int EN_FIRST_INCLUDING_CARRYOVER = 206; /** Number of enumeration constants defined */ - int ENUM_COUNT = 205; + int ENUM_COUNT = 206; } diff --git a/src/java/org/apache/fop/fo/FONode.java b/src/java/org/apache/fop/fo/FONode.java index 8b55e6d30..707dae91e 100644 --- a/src/java/org/apache/fop/fo/FONode.java +++ b/src/java/org/apache/fop/fo/FONode.java @@ -562,6 +562,19 @@ public abstract class FONode implements Cloneable { getFOValidationEventProducer().invalidChild(this, parentName, qn, ruleViolated, loc); } + /** + * Helper function to return "not supported child" exceptions. Note that the child is valid, just not + * supported yet by FOP. + * + * @param loc org.xml.sax.Locator object of the error (*not* parent node) + * @param nsURI namespace URI of incoming invalid node + * @param lName local name (i.e., no prefix) of incoming node + * @throws ValidationException the validation error provoked by the method call + */ + protected void notSupportedChildError(Locator loc, String nsURI, String lName) throws ValidationException { + getFOValidationEventProducer().notSupportedChild(this, getName(), new QName(nsURI, lName), loc); + } + /** * Helper function to throw an error caused by missing mandatory child elements. * (e.g., fo:layout-master-set not having any fo:page-master diff --git a/src/java/org/apache/fop/fo/FOPropertyMapping.java b/src/java/org/apache/fop/fo/FOPropertyMapping.java index a92b71ef9..6f77efc93 100644 --- a/src/java/org/apache/fop/fo/FOPropertyMapping.java +++ b/src/java/org/apache/fop/fo/FOPropertyMapping.java @@ -2178,7 +2178,8 @@ public final class FOPropertyMapping implements Constants { m = new EnumProperty.Maker(PR_RETRIEVE_POSITION_WITHIN_TABLE); m.setInherited(false); m.addEnum("first-starting", getEnumProperty(EN_FIRST_STARTING, "FIRST_STARTING")); - m.addEnum("first-including-carryover", getEnumProperty(EN_FIC, "FIC")); + m.addEnum("first-including-carryover", + getEnumProperty(EN_FIRST_INCLUDING_CARRYOVER, "FIRST_INCLUDING_CARRYOVER")); m.addEnum("last-starting", getEnumProperty(EN_LAST_STARTING, "LAST_STARTING")); m.addEnum("last-ending", getEnumProperty(EN_LAST_ENDING, "LAST_ENDING")); m.setDefault("first-starting"); @@ -2189,7 +2190,7 @@ public final class FOPropertyMapping implements Constants { m.setInherited(false); m.addEnum("table", getEnumProperty(EN_TABLE, "TABLE")); m.addEnum("table-fragment", getEnumProperty(EN_TABLE_FRAGMENT, "TABLE_FRAGMENT")); - m.addEnum("page", getEnumProperty(EN_DOCUMENT, "PAGE")); + m.addEnum("page", getEnumProperty(EN_PAGE, "PAGE")); m.setDefault("table"); addPropertyMaker("retrieve-boundary-within-table", m); } diff --git a/src/java/org/apache/fop/fo/FOValidationEventProducer.java b/src/java/org/apache/fop/fo/FOValidationEventProducer.java index ff005b1b4..ee6b078b5 100644 --- a/src/java/org/apache/fop/fo/FOValidationEventProducer.java +++ b/src/java/org/apache/fop/fo/FOValidationEventProducer.java @@ -89,6 +89,18 @@ public interface FOValidationEventProducer extends EventProducer { void invalidChild(Object source, String elementName, QName offendingNode, String ruleViolated, Locator loc) throws ValidationException; + /** + * A valid but not yet supported child was encountered. + * + * @param source the event source + * @param elementName the name of the context node + * @param offendingNode the offending node + * @param loc the location of the error or null + * @throws ValidationException the validation error provoked by the method call + */ + void notSupportedChild(Object source, String elementName, QName offendingNode, Locator loc) + throws ValidationException; + /** * A required child element is missing. * @param source the event source diff --git a/src/java/org/apache/fop/fo/FOValidationEventProducer.xml b/src/java/org/apache/fop/fo/FOValidationEventProducer.xml index 509f7c1d3..a8b2fffb1 100644 --- a/src/java/org/apache/fop/fo/FOValidationEventProducer.xml +++ b/src/java/org/apache/fop/fo/FOValidationEventProducer.xml @@ -17,6 +17,7 @@ For "{elementName}", only one "{offendingNode}" may be declared.{{locator}} For "{elementName}", "{tooLateNode}" must be declared before "{tooEarlyNode}"!{{locator}} "{offendingNode}" is not a valid child of "{elementName}"![ {ruleViolated,lookup}]{{locator}} + "{offendingNode}" as a child of "{elementName}" is not supported yet!{{locator}} "{elementName}" is missing child elements.[ Required content model: {contentModel}]{{locator}} Element "{elementName}" is missing required property "{propertyName}"!{{locator}} Property ID "{id}" (found on "{elementName}") previously used; ID values must be unique within a document!{severity,equals,EventSeverity:FATAL,, Any reference to it will be considered a reference to the first occurrence in the document.}{{locator}} diff --git a/src/java/org/apache/fop/fo/FObj.java b/src/java/org/apache/fop/fo/FObj.java index 241a442ab..0e7e55aa9 100644 --- a/src/java/org/apache/fop/fo/FObj.java +++ b/src/java/org/apache/fop/fo/FObj.java @@ -20,6 +20,7 @@ package org.apache.fop.fo; import java.util.Collections; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.ListIterator; @@ -35,6 +36,7 @@ import org.apache.xmlgraphics.util.QName; import org.apache.fop.apps.FOPException; import org.apache.fop.fo.extensions.ExtensionAttachment; import org.apache.fop.fo.flow.Marker; +import org.apache.fop.fo.flow.table.TableCell; import org.apache.fop.fo.properties.PropertyMaker; /** @@ -65,7 +67,7 @@ public abstract class FObj extends FONode implements Constants { private boolean isOutOfLineFODescendant = false; /** Markers added to this element. */ - private Map markers = null; + private Map markers; private int bidiLevel = -1; @@ -356,7 +358,7 @@ public abstract class FObj extends FONode implements Constants { } } if (markers == null) { - markers = new java.util.HashMap(); + markers = new HashMap(); } if (!markers.containsKey(mcname)) { markers.put(mcname, marker); @@ -376,7 +378,7 @@ public abstract class FObj extends FONode implements Constants { /** * @return the collection of Markers attached to this object */ - public Map getMarkers() { + public Map getMarkers() { return markers; } @@ -522,6 +524,11 @@ public abstract class FObj extends FONode implements Constants { int found = 1; FONode temp = getParent(); while (temp != null) { + if (temp instanceof TableCell && (ancestorID == FO_TABLE_HEADER || ancestorID == FO_TABLE_FOOTER)) { + // note that if the retrieve-table-marker is not in a table-header/footer an exception is + // thrown, so no need to reset this flag in that case + ((TableCell) temp).flagAsHavingRetrieveTableMarker(); + } if (temp.getNameId() == ancestorID) { return found; } diff --git a/src/java/org/apache/fop/fo/flow/AbstractRetrieveMarker.java b/src/java/org/apache/fop/fo/flow/AbstractRetrieveMarker.java index 62c821504..636bc04df 100644 --- a/src/java/org/apache/fop/fo/flow/AbstractRetrieveMarker.java +++ b/src/java/org/apache/fop/fo/flow/AbstractRetrieveMarker.java @@ -46,6 +46,11 @@ public abstract class AbstractRetrieveMarker extends FObjMixed { private String retrieveClassName; + private int position; + private String positionLabel; + private int boundary; + private String boundaryLabel; + /** * Create a new AbstractRetrieveMarker instance that * is a child of the given {@link FONode} @@ -206,4 +211,43 @@ public abstract class AbstractRetrieveMarker extends FObjMixed { return this.retrieveClassName; } + protected void setBoundaryLabel(String label) { + this.boundaryLabel = label; + } + + protected void setPositionLabel(String label) { + this.positionLabel = label; + } + + public String getBoundaryLabel() { + return this.boundaryLabel; + } + + public String getPositionLabel() { + return this.positionLabel; + } + + protected void setPosition(int position) { + this.position = position; + } + + protected void setBoundary(int boundary) { + this.boundary = boundary; + } + + public int getPosition() { + return this.position; + } + + public int getBoundary() { + return this.boundary; + } + + public abstract String getLocalName(); + + public abstract int getNameId(); + + public void changePositionTo(int position) { + this.position = position; + } } diff --git a/src/java/org/apache/fop/fo/flow/Markers.java b/src/java/org/apache/fop/fo/flow/Markers.java new file mode 100644 index 000000000..24da818af --- /dev/null +++ b/src/java/org/apache/fop/fo/flow/Markers.java @@ -0,0 +1,212 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.flow; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fop.fo.Constants; + +/** + * A class to register and resolve markers. + */ +public final class Markers { + + // IsAny means either IsFirst or IsLast + private Map firstQualifyingIsFirst; + private Map firstQualifyingIsAny; + private Map lastQualifyingIsFirst; + private Map lastQualifyingIsLast; + private Map lastQualifyingIsAny; + + private static Log log = LogFactory.getLog(Markers.class); + + /** + * Registers a marker with the position traits set. + * Only the required markers are kept. + * For "first-starting-within-page" it adds the markers + * that are starting only if the marker class name is not + * already added. + * For "first-including-carryover" it adds any starting marker + * if the marker class name is not already added. + * For "last-starting-within-page" it adds all marks that + * are starting, replacing earlier markers. + * For "last-ending-within-page" it adds all markers that + * are ending, replacing earlier markers. + * + * @param marks a map of markers to register + * @param starting whether the registration happens at the start (true) or end (false) the the area + * @param isfirst whether it is the first area of the parent LM + * @param islast whether it is the last area of the parent LM + */ + public void register(Map marks, boolean starting, boolean isfirst, boolean islast) { + // TODO: find way to put the page number in the log tracing + + if (marks == null) { + return; + } + if (log.isDebugEnabled()) { + log.debug("--" + marks.keySet() + ": " + (starting ? "starting" : "ending") + + (isfirst ? ", first" : "") + (islast ? ", last" : "")); + } + + if (starting) { + // at the start of the area, register is-first and any areas + if (firstQualifyingIsAny == null) { + firstQualifyingIsAny = new HashMap(); + } + if (isfirst) { + if (firstQualifyingIsFirst == null) { + firstQualifyingIsFirst = new HashMap(); + } + // first on scope: only put in new values, leave current + for (Iterator iter = marks.keySet().iterator(); iter.hasNext();) { + String key = iter.next(); + if (!firstQualifyingIsFirst.containsKey(key)) { + firstQualifyingIsFirst.put(key, marks.get(key)); + if (log.isTraceEnabled()) { + log.trace("Adding marker " + key + " to firstQualifyingIsFirst"); + } + } + if (!firstQualifyingIsAny.containsKey(key)) { + firstQualifyingIsAny.put(key, marks.get(key)); + if (log.isTraceEnabled()) { + log.trace("Adding marker " + key + " to firstQualifyingIsAny"); + } + } + } + if (lastQualifyingIsFirst == null) { + lastQualifyingIsFirst = new HashMap(); + } + // last on scope: replace all + lastQualifyingIsFirst.putAll(marks); + if (log.isTraceEnabled()) { + log.trace("Adding all markers to LastStart"); + } + } else { + // first on scope: only put in new values, leave current + for (Iterator iter = marks.keySet().iterator(); iter.hasNext();) { + String key = iter.next(); + if (!firstQualifyingIsAny.containsKey(key)) { + firstQualifyingIsAny.put(key, marks.get(key)); + if (log.isTraceEnabled()) { + log.trace("Adding marker " + key + " to firstQualifyingIsAny"); + } + } + } + } + } else { + // at the end of the area, register is-last and any areas + if (islast) { + if (lastQualifyingIsLast == null) { + lastQualifyingIsLast = new HashMap(); + } + // last on page: replace all + lastQualifyingIsLast.putAll(marks); + if (log.isTraceEnabled()) { + log.trace("Adding all markers to lastQualifyingIsLast"); + } + } + if (lastQualifyingIsAny == null) { + lastQualifyingIsAny = new HashMap(); + } + // last on page: replace all + lastQualifyingIsAny.putAll(marks); + if (log.isTraceEnabled()) { + log.trace("Adding all markers to lastQualifyingIsAny"); + } + } + } + + /** + * Retrieves the best candidate marker for the given position. + * @param name the key used to register the marker + * @param pos the retrieval scope position + * @return a Marker instance + */ + public Marker resolve(AbstractRetrieveMarker arm) { + Marker mark = null; + int pos = arm.getPosition(); + String name = arm.getRetrieveClassName(); + String posName = arm.getPositionLabel(); + String localName = arm.getLocalName(); + switch (pos) { + case Constants.EN_FSWP: // retrieve-marker + case Constants.EN_FIRST_STARTING: // retrieve-table-marker + if (firstQualifyingIsFirst != null) { + mark = firstQualifyingIsFirst.get(name); + } + if (mark == null && firstQualifyingIsAny != null) { + mark = firstQualifyingIsAny.get(name); + posName = "FirstAny after " + posName; + } + break; + case Constants.EN_FIC: // retrieve-marker + case Constants.EN_FIRST_INCLUDING_CARRYOVER: // retrieve-table-marker + if (firstQualifyingIsAny != null) { + mark = firstQualifyingIsAny.get(name); + } + break; + case Constants.EN_LSWP: // retrieve-marker + case Constants.EN_LAST_STARTING: // retrieve-table-marker + if (lastQualifyingIsFirst != null) { + mark = lastQualifyingIsFirst.get(name); + } + if (mark == null && lastQualifyingIsAny != null) { + mark = lastQualifyingIsAny.get(name); + posName = "LastAny after " + posName; + } + break; + case Constants.EN_LEWP: // retrieve-marker + case Constants.EN_LAST_ENDING: // retrieve-table-marker + if (lastQualifyingIsLast != null) { + mark = lastQualifyingIsLast.get(name); + } + if (mark == null && lastQualifyingIsAny != null) { + mark = lastQualifyingIsAny.get(name); + posName = "LastAny after " + posName; + } + break; + default: + throw new RuntimeException("Invalid position attribute in " + localName + "."); + } + if (log.isTraceEnabled()) { + // TODO: find way to put the page number here + log.trace(localName + ": name[" + name + "]; position [" + posName + "]"); + } + return mark; + } + + /** Dumps the current marker data to the logger. */ + public void dump() { + if (log.isTraceEnabled()) { + log.trace("FirstAny: " + this.firstQualifyingIsAny); + log.trace("FirstStart: " + this.firstQualifyingIsFirst); + log.trace("LastAny: " + this.lastQualifyingIsAny); + log.trace("LastEnd: " + this.lastQualifyingIsLast); + log.trace("LastStart: " + this.lastQualifyingIsFirst); + } + } + +} diff --git a/src/java/org/apache/fop/fo/flow/RetrieveMarker.java b/src/java/org/apache/fop/fo/flow/RetrieveMarker.java index 5fc70c7f2..b001a41ee 100644 --- a/src/java/org/apache/fop/fo/flow/RetrieveMarker.java +++ b/src/java/org/apache/fop/fo/flow/RetrieveMarker.java @@ -34,11 +34,6 @@ import org.apache.fop.fo.PropertyList; */ public class RetrieveMarker extends AbstractRetrieveMarker { - // The value of properties relevant for fo:retrieve-marker. - private int retrievePosition; - private int retrieveBoundary; - // End of property values - /** * Create a new RetrieveMarker instance that is a * child of the given {@link FONode}. @@ -70,8 +65,10 @@ public class RetrieveMarker extends AbstractRetrieveMarker { /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { super.bind(pList); - this.retrievePosition = pList.get(PR_RETRIEVE_POSITION).getEnum(); - this.retrieveBoundary = pList.get(PR_RETRIEVE_BOUNDARY).getEnum(); + setPosition(pList.get(PR_RETRIEVE_POSITION).getEnum()); + setPositionLabel((String) pList.get(PR_RETRIEVE_POSITION).getObject()); + setBoundary(pList.get(PR_RETRIEVE_BOUNDARY).getEnum()); + setBoundaryLabel((String) pList.get(PR_RETRIEVE_BOUNDARY).getObject()); } /** @@ -84,19 +81,19 @@ public class RetrieveMarker extends AbstractRetrieveMarker { * {@link org.apache.fop.fo.Constants#EN_LEWP}. */ public int getRetrievePosition() { - return this.retrievePosition; + return getPosition(); } /** * Return the value for the retrieve-boundary * property - * @return the value for retrieve-boundary-within-table; one of + * @return the value for retrieve-boundary; one of * {@link org.apache.fop.fo.Constants#EN_PAGE}, * {@link org.apache.fop.fo.Constants#EN_PAGE_SEQUENCE}, * {@link org.apache.fop.fo.Constants#EN_DOCUMENT}. */ public int getRetrieveBoundary() { - return this.retrieveBoundary; + return getBoundary(); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/fo/flow/RetrieveTableMarker.java b/src/java/org/apache/fop/fo/flow/RetrieveTableMarker.java index 3090cb702..efacba864 100644 --- a/src/java/org/apache/fop/fo/flow/RetrieveTableMarker.java +++ b/src/java/org/apache/fop/fo/flow/RetrieveTableMarker.java @@ -32,11 +32,6 @@ import org.apache.fop.fo.PropertyList; */ public class RetrieveTableMarker extends AbstractRetrieveMarker { - // The value of properties relevant for fo:retrieve-table-marker. - private int retrievePositionWithinTable; - private int retrieveBoundaryWithinTable; - // end property values - /** * Create a new RetrieveTableMarker instance that is * a child of the given {@link FONode}. @@ -67,10 +62,10 @@ public class RetrieveTableMarker extends AbstractRetrieveMarker { /** {@inheritDoc} */ public void bind(PropertyList pList) throws FOPException { super.bind(pList); - this.retrievePositionWithinTable - = pList.get(PR_RETRIEVE_POSITION_WITHIN_TABLE).getEnum(); - this.retrieveBoundaryWithinTable - = pList.get(PR_RETRIEVE_BOUNDARY_WITHIN_TABLE).getEnum(); + setPosition(pList.get(PR_RETRIEVE_POSITION_WITHIN_TABLE).getEnum()); + setPositionLabel((String) pList.get(PR_RETRIEVE_POSITION_WITHIN_TABLE).getObject()); + setBoundary(pList.get(PR_RETRIEVE_BOUNDARY_WITHIN_TABLE).getEnum()); + setBoundaryLabel((String) pList.get(PR_RETRIEVE_BOUNDARY_WITHIN_TABLE).getObject()); } /** @@ -83,7 +78,7 @@ public class RetrieveTableMarker extends AbstractRetrieveMarker { * {@link org.apache.fop.fo.Constants#EN_LAST_ENDING}. */ public int getRetrievePositionWithinTable() { - return this.retrievePositionWithinTable; + return getPosition(); } /** @@ -95,7 +90,7 @@ public class RetrieveTableMarker extends AbstractRetrieveMarker { * {@link org.apache.fop.fo.Constants#EN_PAGE}. */ public int getRetrieveBoundaryWithinTable() { - return this.retrieveBoundaryWithinTable; + return getBoundary(); } /** {@inheritDoc} */ @@ -110,4 +105,12 @@ public class RetrieveTableMarker extends AbstractRetrieveMarker { public int getNameId() { return FO_RETRIEVE_TABLE_MARKER; } + + /** {@inheritDoc} */ + public void clearChildNodes() { + super.clearChildNodes(); + this.currentTextNode = null; + this.lastFOTextProcessed = null; + } + } diff --git a/src/java/org/apache/fop/fo/flow/table/TableCell.java b/src/java/org/apache/fop/fo/flow/table/TableCell.java index c4f9c2aa6..f198f3aad 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableCell.java +++ b/src/java/org/apache/fop/fo/flow/table/TableCell.java @@ -62,6 +62,8 @@ public class TableCell extends TableFObj implements CommonAccessibilityHolder { /** used for FO validation */ private boolean blockItemFound = false; + private boolean hasRetrieveTableMarker; + /** * Create a TableCell instance with the given {@link FONode} * as parent. @@ -247,4 +249,11 @@ public class TableCell extends TableFObj implements CommonAccessibilityHolder { return FO_TABLE_CELL; } + public void flagAsHavingRetrieveTableMarker() { + hasRetrieveTableMarker = true; + } + + public boolean hasRetrieveTableMarker() { + return hasRetrieveTableMarker; + } } diff --git a/src/java/org/apache/fop/fo/flow/table/TablePart.java b/src/java/org/apache/fop/fo/flow/table/TablePart.java index 3ab92cc94..032340681 100644 --- a/src/java/org/apache/fop/fo/flow/table/TablePart.java +++ b/src/java/org/apache/fop/fo/flow/table/TablePart.java @@ -169,6 +169,8 @@ public abstract class TablePart extends TableCellContainer { getUserAgent().getEventBroadcaster()); eventProducer.noMixRowsAndCells(this, getName(), getLocator()); } + } else if (localName.equals("retrieve-table-marker")) { + notSupportedChildError(loc, nsURI, localName); } else { invalidChildError(loc, nsURI, localName); } diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java index 8c213d7d5..5d7cc0b64 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java @@ -273,4 +273,11 @@ public abstract class AbstractBaseLayoutManager throw new UnsupportedOperationException("Not implemented"); } + public void preserveChildrenAtEndOfLayout() { + + } + + public void recreateChildrenLMs() { + + } } diff --git a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java index 0089f228f..3d64c436c 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java @@ -37,6 +37,7 @@ import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; import org.apache.fop.fo.flow.Marker; import org.apache.fop.fo.flow.RetrieveMarker; +import org.apache.fop.layoutmgr.table.TableLayoutManager; /** * The base class for most LayoutManagers. @@ -67,6 +68,8 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im private int lastGeneratedPosition = -1; private int smallestPosNumberChecked = Integer.MAX_VALUE; + private boolean preserveChildrenAtEndOfLayout; + /** * Abstract layout manager. */ @@ -370,19 +373,20 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im } /** - * Registers the FO's markers on the current PageViewport + * Registers the FO's markers on the current PageViewport, and if applicable on the parent TableLM. * * @param isStarting boolean indicating whether the markers qualify as 'starting' * @param isFirst boolean indicating whether the markers qualify as 'first' * @param isLast boolean indicating whether the markers qualify as 'last' */ - protected void addMarkersToPage(boolean isStarting, boolean isFirst, boolean isLast) { + protected void registerMarkers(boolean isStarting, boolean isFirst, boolean isLast) { if (this.markers != null) { - getCurrentPV().addMarkers( + getCurrentPV().registerMarkers( this.markers, isStarting, isFirst, isLast); + possiblyRegisterMarkersForTables(markers, isStarting, isFirst, isLast); } } @@ -419,11 +423,12 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im notifyEndOfLayout(); - /* References to the child LMs are no longer needed - */ - childLMs = null; - curChildLM = null; - childLMiter = null; + if (!preserveChildrenAtEndOfLayout) { + // References to the child LMs are no longer needed + childLMs = null; + curChildLM = null; + childLMiter = null; + } /* markers that qualify have been transferred to the page */ @@ -438,13 +443,21 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im || lm instanceof PageSequenceLayoutManager)) { lm = lm.getParent(); } - if (lm instanceof FlowLayoutManager) { + if (lm instanceof FlowLayoutManager && !preserveChildrenAtEndOfLayout) { fobj.clearChildNodes(); fobjIter = null; } } } + /* + * Preserves the children LMs at the end of layout. This is necessary if the layout is expected to be + * repeated, as when using retrieve-table-markers. + */ + public void preserveChildrenAtEndOfLayout() { + preserveChildrenAtEndOfLayout = true; + } + /** {@inheritDoc} */ @Override public String toString() { @@ -467,4 +480,34 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager im lastGeneratedPosition = -1; } + public void recreateChildrenLMs() { + childLMs = new ArrayList(); + isFinished = false; + if (fobj == null) { + return; + } + fobjIter = fobj.getChildNodes(); + int position = 0; + while (createNextChildLMs(position++)) { + // + } + childLMiter = new LMiter(this); + for (LMiter iter = new LMiter(this); iter.hasNext();) { + AbstractBaseLayoutManager alm = (AbstractBaseLayoutManager) iter.next(); + alm.initialize(); + alm.recreateChildrenLMs(); + alm.preserveChildrenAtEndOfLayout(); + } + curChildLM = getChildLM(); + } + + protected void possiblyRegisterMarkersForTables(Map markers, boolean isStarting, + boolean isFirst, boolean isLast) { + LayoutManager lm = this.parentLayoutManager; + if (lm instanceof FlowLayoutManager || lm instanceof PageSequenceLayoutManager + || !(lm instanceof AbstractLayoutManager)) { + return; + } + ((AbstractLayoutManager) lm).possiblyRegisterMarkersForTables(markers, isStarting, isFirst, isLast); + } } diff --git a/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java index f36cde158..edfb389ed 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java @@ -226,15 +226,14 @@ public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutMa public RetrieveMarker resolveRetrieveMarker(RetrieveMarker rm) { AreaTreeModel areaTreeModel = areaTreeHandler.getAreaTreeModel(); String name = rm.getRetrieveClassName(); - int pos = rm.getRetrievePosition(); int boundary = rm.getRetrieveBoundary(); // get marker from the current markers on area tree - Marker mark = (Marker)getCurrentPV().getMarker(name, pos); + Marker mark = getCurrentPV().resolveMarker(rm); if (mark == null && boundary != EN_PAGE) { // go back over pages until mark found // if document boundary then keep going - boolean doc = boundary == EN_DOCUMENT; + boolean doc = (boundary == EN_DOCUMENT); int seq = areaTreeModel.getPageSequenceCount(); int page = areaTreeModel.getPageCount(seq) - 1; while (page < 0 && doc && seq > 1) { @@ -243,7 +242,11 @@ public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutMa } while (page >= 0) { PageViewport pv = areaTreeModel.getPage(seq, page); - mark = (Marker)pv.getMarker(name, Constants.EN_LEWP); + int originalPosition = rm.getPosition(); + rm.changePositionTo(Constants.EN_LEWP); + mark = (Marker) pv.resolveMarker(rm); + // this is probably not necessary since the RM will not be used again, but to be safe... + rm.changePositionTo(originalPosition); if (mark != null) { break; } diff --git a/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java b/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java index c80982cce..0ed6cb69b 100644 --- a/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java +++ b/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java @@ -87,7 +87,7 @@ public final class AreaAdditionUtil { } if (bslm != null) { - bslm.addMarkersToPage( + bslm.registerMarkers( true, bslm.isFirst(firstPos), bslm.isLast(lastPos)); @@ -114,11 +114,10 @@ public final class AreaAdditionUtil { } if (bslm != null) { - bslm.addMarkersToPage( + bslm.registerMarkers( false, bslm.isFirst(firstPos), bslm.isLast(lastPos)); - bslm.checkEndOfLayout(lastPos); } diff --git a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java index 920589657..d20215151 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java @@ -806,7 +806,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl addId(); - addMarkersToPage(true, isFirst(firstPos), isLast(lastPos)); + registerMarkers(true, isFirst(firstPos), isLast(lastPos)); if (bcpos == null) { // the Positions in positionList were inside the elements @@ -826,7 +826,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager impl bcpos.getBreaker().addContainedAreas(layoutContext); } - addMarkersToPage(false, isFirst(firstPos), isLast(lastPos)); + registerMarkers(false, isFirst(firstPos), isLast(lastPos)); TraitSetter.addSpaceBeforeAfter(viewportBlockArea, layoutContext.getSpaceAdjust(), effSpaceBefore, effSpaceAfter); diff --git a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java index a99b45620..f62d7f946 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java @@ -312,7 +312,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co addId(); - addMarkersToPage(true, isFirst(firstPos), isLast(lastPos)); + registerMarkers(true, isFirst(firstPos), isLast(lastPos)); // the Positions in positionList were inside the elements // created by the LineLM @@ -327,7 +327,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager implements Co childLM.addAreas(childPosIter, lc); } - addMarkersToPage(false, isFirst(firstPos), isLast(lastPos)); + registerMarkers(false, isFirst(firstPos), isLast(lastPos)); TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(), effSpaceBefore, effSpaceAfter); diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java index 76a1cb9e4..5f4aa5725 100644 --- a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java +++ b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java @@ -31,6 +31,7 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.area.AreaTreeHandler; import org.apache.fop.fo.FOElementMapping; import org.apache.fop.fo.FONode; +import org.apache.fop.fo.FONode.FONodeIterator; import org.apache.fop.fo.FOText; import org.apache.fop.fo.FObjMixed; import org.apache.fop.fo.extensions.ExternalDocument; @@ -117,7 +118,7 @@ public class LayoutManagerMapping implements LayoutManagerMaker { registerMaker(Block.class, new BlockLayoutManagerMaker()); registerMaker(Leader.class, new LeaderLayoutManagerMaker()); registerMaker(RetrieveMarker.class, new RetrieveMarkerLayoutManagerMaker()); - registerMaker(RetrieveTableMarker.class, new Maker()); + registerMaker(RetrieveTableMarker.class, new RetrieveTableMarkerLayoutManagerMaker()); registerMaker(Character.class, new CharacterLayoutManagerMaker()); registerMaker(ExternalGraphic.class, new ExternalGraphicLayoutManagerMaker()); @@ -407,6 +408,24 @@ public class LayoutManagerMapping implements LayoutManagerMaker { } } + public class RetrieveTableMarkerLayoutManagerMaker extends Maker { + public void make(FONode node, List lms) { + FONodeIterator baseIter = node.getChildNodes(); + if (baseIter == null) { + // this happens when the retrieve-table-marker cannot be resolved yet + RetrieveTableMarker rtm = (RetrieveTableMarker) node; + RetrieveTableMarkerLayoutManager rtmlm = new RetrieveTableMarkerLayoutManager(rtm); + lms.add(rtmlm); + return; + } + while (baseIter.hasNext()) { + // this happens when the retrieve-table-marker has been resolved + FONode child = (FONode) baseIter.next(); + makeLayoutManagers(child, lms); + } + } + } + /** a layout manager maker */ public class WrapperLayoutManagerMaker extends Maker { /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/layoutmgr/LocalBreaker.java b/src/java/org/apache/fop/layoutmgr/LocalBreaker.java new file mode 100644 index 000000000..bb08446cc --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/LocalBreaker.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.layoutmgr; + +import java.util.LinkedList; +import java.util.List; + +import org.apache.fop.fo.FObj; +import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener; +import org.apache.fop.layoutmgr.inline.TextLayoutManager; + +public abstract class LocalBreaker extends AbstractBreaker { + protected BlockStackingLayoutManager lm; + private int displayAlign; + private int ipd; + private int overflow = 0; + + public LocalBreaker(BlockStackingLayoutManager lm, int ipd, int displayAlign) { + this.lm = lm; + this.ipd = ipd; + this.displayAlign = displayAlign; + } + + /** {@inheritDoc} */ + protected boolean isPartOverflowRecoveryActivated() { + // For side regions, this must be disabled because of wanted overflow. + return false; + } + + public boolean isOverflow() { + return (this.overflow != 0); + } + + public int getOverflowAmount() { + return this.overflow; + } + + /** {@inheritDoc} */ + protected PageBreakingLayoutListener createLayoutListener() { + return new PageBreakingLayoutListener() { + + public void notifyOverflow(int part, int amount, FObj obj) { + if (LocalBreaker.this.overflow == 0) { + LocalBreaker.this.overflow = amount; + } + } + + }; + } + + protected LayoutManager getTopLevelLM() { + return lm; + } + + protected LayoutContext createLayoutContext() { + LayoutContext lc = super.createLayoutContext(); + lc.setRefIPD(ipd); + return lc; + } + + protected List getNextKnuthElements(LayoutContext context, int alignment) { + LayoutManager curLM; // currently active LM + List returnList = new LinkedList(); + + while ((curLM = lm.getChildLM()) != null) { + LayoutContext childLC = LayoutContext.newInstance(); + childLC.setStackLimitBP(context.getStackLimitBP()); + childLC.setRefIPD(context.getRefIPD()); + childLC.setWritingMode(context.getWritingMode()); + + List returnedList = null; + // The following is a HACK! Ignore leading and trailing white space + boolean ignore = curLM instanceof TextLayoutManager; + if (!curLM.isFinished()) { + returnedList = curLM.getNextKnuthElements(childLC, alignment); + } + if (returnedList != null && !ignore) { + lm.wrapPositionElements(returnedList, returnList); + } + } + SpaceResolver.resolveElementList(returnList); + lm.setFinished(true); + return returnList; + } + + protected int getCurrentDisplayAlign() { + return displayAlign; + } + + protected boolean hasMoreContent() { + return !lm.isFinished(); + } + + protected void addAreas(PositionIterator posIter, LayoutContext context) { + AreaAdditionUtil.addAreas(lm, posIter, context); + } + + protected void doPhase3(PageBreakingAlgorithm alg, int partCount, BlockSequence originalList, + BlockSequence effectiveList) { + if (partCount > 1) { + PageBreakPosition pos = (PageBreakPosition) alg.getPageBreaks().getFirst(); + int firstPartLength = ElementListUtils.calcContentLength(effectiveList, + effectiveList.ignoreAtStart, pos.getLeafPos()); + overflow += alg.totalWidth - firstPartLength; + } + // Rendering all parts (not just the first) at once for the case where the parts that + // overflow should be visible. + alg.removeAllPageBreaks(); + // Directly add areas after finding the breaks + this.addAreas(alg, 1, originalList, effectiveList); + } + + protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) { + // nop for static content + } + + protected LayoutManager getCurrentChildLM() { + return null; // TODO NYI + } +} diff --git a/src/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManager.java new file mode 100644 index 000000000..9d0979a29 --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManager.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.layoutmgr; + +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.flow.RetrieveTableMarker; +import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager; +import org.apache.fop.layoutmgr.inline.LeafNodeLayoutManager; +import org.apache.fop.layoutmgr.table.TableLayoutManager; + +public class RetrieveTableMarkerLayoutManager extends LeafNodeLayoutManager { + + private static Log log = LogFactory.getLog(RetrieveTableMarkerLayoutManager.class); + + public RetrieveTableMarkerLayoutManager(RetrieveTableMarker node) { + super(node); + } + + /** {@inheritDoc} */ + public List getNextKnuthElements(LayoutContext context, int alignment) { + setFinished(true); + FONode foNode = (FONode) getFObj(); + foNode = getTableLayoutManager().resolveRetrieveTableMarker((RetrieveTableMarker) foNode); + if (foNode != null) { + // resolve the RTM and replace current LM by the resolved target LM + InlineLevelLayoutManager illm = (InlineLevelLayoutManager) getPSLM().getLayoutManagerMaker() + .makeLayoutManager(foNode); + if (illm instanceof RetrieveTableMarkerLayoutManager) { + // happens if the retrieve-marker was empty + return null; + } + illm.setParent(getParent()); + illm.initialize(); + return illm.getNextKnuthElements(context, alignment); + } else { + return null; + } + } + + /** {@inheritDoc} */ + public void addAreas(PositionIterator posIter, LayoutContext context) { + } + + private TableLayoutManager getTableLayoutManager() { + LayoutManager parentLM = getParent(); + while (!(parentLM instanceof TableLayoutManager)) { + parentLM = parentLM.getParent(); + } + TableLayoutManager tlm = (TableLayoutManager) parentLM; + return tlm; + } + +} diff --git a/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java index 403daceb3..058b02270 100644 --- a/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java @@ -167,125 +167,22 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager { return (StaticContent) fobj; } - private class StaticContentBreaker extends AbstractBreaker { - private StaticContentLayoutManager lm; - private int displayAlign; - private int ipd; - private int overflow = 0; - - public StaticContentBreaker(StaticContentLayoutManager lm, int ipd, - int displayAlign) { - this.lm = lm; - this.ipd = ipd; - this.displayAlign = displayAlign; + private class StaticContentBreaker extends LocalBreaker { + + public StaticContentBreaker(StaticContentLayoutManager lm, int ipd, int displayAlign) { + super(lm, ipd, displayAlign); } /** {@inheritDoc} */ protected void observeElementList(List elementList) { String elementListID = getStaticContentFO().getFlowName(); - String pageSequenceID = ((PageSequence)lm.getParent().getFObj()).getId(); + String pageSequenceID = ((PageSequence) lm.getParent().getFObj()).getId(); if (pageSequenceID != null && pageSequenceID.length() > 0) { elementListID += "-" + pageSequenceID; } ElementListObserver.observe(elementList, "static-content", elementListID); } - /** {@inheritDoc} */ - protected boolean isPartOverflowRecoveryActivated() { - //For side regions, this must be disabled because of wanted overflow. - return false; - } - - public boolean isOverflow() { - return (this.overflow != 0); - } - - public int getOverflowAmount() { - return this.overflow; - } - - /** {@inheritDoc} */ - protected PageBreakingLayoutListener createLayoutListener() { - return new PageBreakingLayoutListener() { - - public void notifyOverflow(int part, int amount, FObj obj) { - if (StaticContentBreaker.this.overflow == 0) { - StaticContentBreaker.this.overflow = amount; - } - } - - }; - } - - protected LayoutManager getTopLevelLM() { - return lm; - } - - protected LayoutContext createLayoutContext() { - LayoutContext lc = super.createLayoutContext(); - lc.setRefIPD(ipd); - return lc; - } - - protected List getNextKnuthElements(LayoutContext context, int alignment) { - LayoutManager curLM; // currently active LM - List returnList = new LinkedList(); - - while ((curLM = getChildLM()) != null) { - LayoutContext childLC = LayoutContext.newInstance(); - childLC.setStackLimitBP(context.getStackLimitBP()); - childLC.setRefIPD(context.getRefIPD()); - childLC.setWritingMode(context.getWritingMode()); - - List returnedList = null; - //The following is a HACK! Ignore leading and trailing white space - boolean ignore = curLM instanceof TextLayoutManager; - if (!curLM.isFinished()) { - returnedList = curLM.getNextKnuthElements(childLC, alignment); - } - if (returnedList != null && !ignore) { - lm.wrapPositionElements(returnedList, returnList); - } - } - SpaceResolver.resolveElementList(returnList); - setFinished(true); - return returnList; - } - - protected int getCurrentDisplayAlign() { - return displayAlign; - } - - protected boolean hasMoreContent() { - return !lm.isFinished(); - } - - protected void addAreas(PositionIterator posIter, LayoutContext context) { - AreaAdditionUtil.addAreas(lm, posIter, context); - } - - protected void doPhase3(PageBreakingAlgorithm alg, int partCount, - BlockSequence originalList, BlockSequence effectiveList) { - if (partCount > 1) { - PageBreakPosition pos = (PageBreakPosition)alg.getPageBreaks().getFirst(); - int firstPartLength = ElementListUtils.calcContentLength(effectiveList, - effectiveList.ignoreAtStart, pos.getLeafPos()); - overflow += alg.totalWidth - firstPartLength; - } - //Rendering all parts (not just the first) at once for the case where the parts that - //overflow should be visible. - alg.removeAllPageBreaks(); - //Directly add areas after finding the breaks - this.addAreas(alg, 1, originalList, effectiveList); - } - - protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) { - //nop for static content - } - - protected LayoutManager getCurrentChildLM() { - return null; //TODO NYI - } } /** diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java index 77ac8c4d6..ebf7a9ccb 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java @@ -480,7 +480,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager { context.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart(), this)); } - addMarkersToPage( + registerMarkers( true, !areaCreated, lastPos == null || isLast(lastPos)); @@ -542,7 +542,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager { setTraits(areaCreated, lastPos == null || !isLast(lastPos)); parentLayoutManager.addChildArea(getCurrentArea()); - addMarkersToPage( + registerMarkers( false, !areaCreated, lastPos == null || isLast(lastPos)); diff --git a/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java index 480934bf3..61d8a891d 100644 --- a/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java @@ -171,7 +171,7 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager } } - addMarkersToPage(true, isFirst(firstPos), isLast(lastPos)); + registerMarkers(true, isFirst(firstPos), isLast(lastPos)); PositionIterator childPosIter = new PositionIterator(positionList.listIterator()); while ((childLM = childPosIter.getNextChildLM()) != null) { @@ -184,7 +184,7 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager childLM.addAreas(childPosIter, lc); } - addMarkersToPage(false, isFirst(firstPos), isLast(lastPos)); + registerMarkers(false, isFirst(firstPos), isLast(lastPos)); // We are done with this area add the background TraitSetter.addBackground(curBlockArea, diff --git a/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java index 3204a867e..f017da381 100644 --- a/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java @@ -136,7 +136,7 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager imp } } - addMarkersToPage(true, isFirst(firstPos), isLast(lastPos)); + registerMarkers(true, isFirst(firstPos), isLast(lastPos)); PositionIterator childPosIter = new PositionIterator(positionList.listIterator()); while ((childLM = childPosIter.getNextChildLM()) != null) { @@ -149,7 +149,7 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager imp childLM.addAreas(childPosIter, lc); } - addMarkersToPage(false, isFirst(firstPos), isLast(lastPos)); + registerMarkers(false, isFirst(firstPos), isLast(lastPos)); flush(); diff --git a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java index 32aa4c674..083e4ee1b 100644 --- a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java @@ -509,7 +509,7 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements } } - addMarkersToPage(true, isFirst(firstPos), isLast(lastPos)); + registerMarkers(true, isFirst(firstPos), isLast(lastPos)); // use the first and the last ListItemPosition to determine the // corresponding indexes in the original labelList and bodyList @@ -563,7 +563,7 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager implements } curBlockArea.setBPD(itemBPD); - addMarkersToPage(false, isFirst(firstPos), isLast(lastPos)); + registerMarkers(false, isFirst(firstPos), isLast(lastPos)); // We are done with this area add the background TraitSetter.addBackground(curBlockArea, diff --git a/src/java/org/apache/fop/layoutmgr/table/RowPainter.java b/src/java/org/apache/fop/layoutmgr/table/RowPainter.java index 955dafabd..6b0ef6ebb 100644 --- a/src/java/org/apache/fop/layoutmgr/table/RowPainter.java +++ b/src/java/org/apache/fop/layoutmgr/table/RowPainter.java @@ -303,11 +303,16 @@ class RowPainter { borderAfterWhich = ConditionalBorder.REST; } + // when adding the areas for the TableCellLayoutManager this helps with the isLast trait + // if, say, the first cell of a row has content that fits in the page, but the content of + // the second cell does not fit this will assure that the isLast trait for the first cell + // will also be false + lastCellParts[i].pgu.getCellLM().setLastTrait(lastCellParts[i].isLastPart()); addAreasForCell(firstCellParts[i].pgu, firstCellParts[i].start, lastCellParts[i].end, actualRowHeight, borderBeforeWhich, borderAfterWhich, lastOnPage); - firstCellParts[i] = null; + firstCellParts[i] = null; // why? what about the lastCellParts[i]? Arrays.fill(firstCellOnPage, i, i + currentGU.getCell().getNumberColumnsSpanned(), false); } diff --git a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java index 2ca5a26d1..f810d20c5 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java @@ -21,6 +21,7 @@ package org.apache.fop.layoutmgr.table; import java.util.LinkedList; import java.util.List; +import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -28,19 +29,24 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.area.Area; import org.apache.fop.area.Block; import org.apache.fop.area.Trait; +import org.apache.fop.fo.flow.Marker; import org.apache.fop.fo.flow.table.ConditionalBorder; import org.apache.fop.fo.flow.table.GridUnit; import org.apache.fop.fo.flow.table.PrimaryGridUnit; import org.apache.fop.fo.flow.table.Table; import org.apache.fop.fo.flow.table.TableCell; import org.apache.fop.fo.flow.table.TableColumn; +import org.apache.fop.fo.flow.table.TableFooter; +import org.apache.fop.fo.flow.table.TableHeader; import org.apache.fop.fo.flow.table.TablePart; import org.apache.fop.fo.flow.table.TableRow; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; +import org.apache.fop.layoutmgr.AbstractLayoutManager; import org.apache.fop.layoutmgr.AreaAdditionUtil; import org.apache.fop.layoutmgr.BlockLevelLayoutManager; import org.apache.fop.layoutmgr.BlockStackingLayoutManager; +import org.apache.fop.layoutmgr.ElementListObserver; import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.Keep; import org.apache.fop.layoutmgr.KnuthBox; @@ -49,8 +55,10 @@ import org.apache.fop.layoutmgr.KnuthGlue; import org.apache.fop.layoutmgr.KnuthPenalty; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.LayoutManager; +import org.apache.fop.layoutmgr.LocalBreaker; import org.apache.fop.layoutmgr.Position; import org.apache.fop.layoutmgr.PositionIterator; +import org.apache.fop.layoutmgr.RetrieveTableMarkerLayoutManager; import org.apache.fop.layoutmgr.SpaceResolver; import org.apache.fop.layoutmgr.TraitSetter; import org.apache.fop.traits.BorderProps; @@ -79,6 +87,28 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager private int totalHeight; private int usedBPD; private boolean emptyCell = true; + private boolean isDescendantOfTableFooter; + private boolean isDescendantOfTableHeader; + private boolean hasRetrieveTableMarker; + + // place holder for the addAreas arguments + private boolean savedAddAreasArguments; + private PositionIterator savedParentIter; + private LayoutContext savedLayoutContext; + private int[] savedSpannedGridRowHeights; + private int savedStartRow; + private int savedEndRow; + private int savedBorderBeforeWhich; + private int savedBorderAfterWhich; + private boolean savedFirstOnPage; + private boolean savedLastOnPage; + private RowPainter savedPainter; + private int savedFirstRowHeight; + // this is set to false when the table-cell has a retrieve-table-marker and is in the table-header + private boolean flushArea = true; + + // this information is set by the RowPainter + private boolean isLastTrait; /** * Create a new Cell layout manager. @@ -88,6 +118,11 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager public TableCellLayoutManager(TableCell node, PrimaryGridUnit pgu) { super(node); this.primaryGridUnit = pgu; + this.isDescendantOfTableHeader = node.getParent().getParent() instanceof TableHeader + || node.getParent() instanceof TableHeader; + this.isDescendantOfTableFooter = node.getParent().getParent() instanceof TableFooter + || node.getParent() instanceof TableFooter; + this.hasRetrieveTableMarker = node.hasRetrieveTableMarker(); } /** @return the table-cell FO */ @@ -248,6 +283,84 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager totalHeight = h; } + private void clearRetrieveTableMarkerChildNodes(List childrenLMs) { + if (childrenLMs == null) { + return; + } + int n = childrenLMs.size(); + for (int j = 0; j < n; j++) { + LayoutManager lm = (LayoutManager) childrenLMs.get(j); + if (lm == null) { + return; + } else if (lm instanceof RetrieveTableMarkerLayoutManager) { + ((AbstractLayoutManager) lm).getFObj().clearChildNodes(); + } else { + List lms = lm.getChildLMs(); + clearRetrieveTableMarkerChildNodes(lms); + } + } + } + + /** + * Checks whether the associated table cell of this LM is in a table header or footer. + * @return true if descendant of table header or footer + */ + private boolean isDescendantOfTableHeaderOrFooter() { + return (isDescendantOfTableFooter || isDescendantOfTableHeader); + } + + private void saveAddAreasArguments(PositionIterator parentIter, LayoutContext layoutContext, + int[] spannedGridRowHeights, int startRow, int endRow, int borderBeforeWhich, + int borderAfterWhich, boolean firstOnPage, boolean lastOnPage, RowPainter painter, + int firstRowHeight) { + // checks for savedAddAreasArguments and isDescendantOfTableHeader were already made but repeat them + if (savedAddAreasArguments) { + return; + } + if (isDescendantOfTableHeader) { + savedAddAreasArguments = true; + savedParentIter = null /* parentIter */; + savedLayoutContext = null /* layoutContext */; + savedSpannedGridRowHeights = spannedGridRowHeights; + savedStartRow = startRow; + savedEndRow = endRow; + savedBorderBeforeWhich = borderBeforeWhich; + savedBorderAfterWhich = borderAfterWhich; + savedFirstOnPage = firstOnPage; + savedLastOnPage = lastOnPage; + savedPainter = painter; + savedFirstRowHeight = firstRowHeight; + TableLayoutManager parentTableLayoutManager = getTableLayoutManager(); + parentTableLayoutManager.saveTableHeaderTableCellLayoutManagers(this); + // this saving is done the first time the addArea() is called; since the retrieve-table-markers + // cannot be resolved at this time we do not want to flush the area; the area needs nevertheless + // be built so that space is allocated for it. + flushArea = false; + } + } + + private TableLayoutManager getTableLayoutManager() { + LayoutManager parentLM = getParent(); + while (!(parentLM instanceof TableLayoutManager)) { + parentLM = parentLM.getParent(); + } + TableLayoutManager tlm = (TableLayoutManager) parentLM; + return tlm; + } + + /** + * Calls the addAreas() using the original arguments. + */ + protected void repeatAddAreas() { + if (savedAddAreasArguments) { + addAreas(savedParentIter, savedLayoutContext, savedSpannedGridRowHeights, savedStartRow, + savedEndRow, savedBorderBeforeWhich, savedBorderAfterWhich, savedFirstOnPage, + savedLastOnPage, savedPainter, savedFirstRowHeight); + // so that the arguments of the next table fragment header can be saved + savedAddAreasArguments = false; + } + } + /** * Add the areas for the break points. The cell contains block stacking layout * managers that add block areas. @@ -407,7 +520,28 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager } } - AreaAdditionUtil.addAreas(this, parentIter, layoutContext); + if (isDescendantOfTableHeaderOrFooter()) { + if (hasRetrieveTableMarker) { + if (isDescendantOfTableHeader && !savedAddAreasArguments) { + saveAddAreasArguments(parentIter, layoutContext, spannedGridRowHeights, startRow, endRow, + borderBeforeWhich, borderAfterWhich, firstOnPage, lastOnPage, painter, + firstRowHeight); + } + recreateChildrenLMs(); + int displayAlign = ((TableCell) this.getFObj()).getDisplayAlign(); + TableCellBreaker breaker = new TableCellBreaker(this, cellIPD, displayAlign); + breaker.doLayout(usedBPD, false); + // this is needed so the next time the LMs are recreated they look like the originals; this + // is due to the fact that during the doLayout() above the FO tree changes when the + // retrieve-table-markers are resolved + clearRetrieveTableMarkerChildNodes(getChildLMs()); + } + } + + // if hasRetrieveTableMarker == true the areas were already added when the re-layout was done above + if (!hasRetrieveTableMarker) { + AreaAdditionUtil.addAreas(this, parentIter, layoutContext); + } // Re-adjust the cell's bpd as it may have been modified by the previous call // for some reason (?) curBlockArea.setBPD(cellBPD); @@ -418,7 +552,11 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager getTableCell().getCommonBorderPaddingBackground(), this); } - flush(); + if (flushArea) { + flush(); + } else { + flushArea = true; + } curBlockArea = null; @@ -604,4 +742,49 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager return true; } + private class TableCellBreaker extends LocalBreaker { + + public TableCellBreaker(TableCellLayoutManager lm, int ipd, int displayAlign) { + super(lm, ipd, displayAlign); + } + + /** + * {@inheritDoc} + */ + protected void observeElementList(List elementList) { + String elementListID = lm.getParent().getFObj().getId() + "-" + lm.getFObj().getId(); + ElementListObserver.observe(elementList, "table-cell", elementListID); + } + + } + + /** + * Registers the FO's markers on the current PageViewport and parent Table. + * + * @param isStarting boolean indicating whether the markers qualify as 'starting' + * @param isFirst boolean indicating whether the markers qualify as 'first' + * @param isLast boolean indicating whether the markers qualify as 'last' + */ + protected void registerMarkers(boolean isStarting, boolean isFirst, boolean isLast) { + Map markers = getTableCell().getMarkers(); + if (markers != null) { + getCurrentPV().registerMarkers(markers, isStarting, isFirst, isLast && isLastTrait); + if (!isDescendantOfTableHeaderOrFooter()) { + getTableLayoutManager().registerMarkers(markers, isStarting, isFirst, isLast && isLastTrait); + } + } + } + + void setLastTrait(boolean isLast) { + isLastTrait = isLast; + } + + /** {@inheritDoc} */ + public void setParent(LayoutManager lm) { + this.parentLayoutManager = lm; + if (this.hasRetrieveTableMarker) { + this.getTableLayoutManager().flagAsHavingRetrieveTableMarker(); + } + } + } diff --git a/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java index ea8774716..d423b339d 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java @@ -31,9 +31,11 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.datatypes.PercentBaseContext; import org.apache.fop.fo.Constants; import org.apache.fop.fo.FObj; +import org.apache.fop.fo.flow.Marker; import org.apache.fop.fo.flow.table.EffRow; import org.apache.fop.fo.flow.table.PrimaryGridUnit; import org.apache.fop.fo.flow.table.Table; +import org.apache.fop.fo.flow.table.TableBody; import org.apache.fop.fo.flow.table.TablePart; import org.apache.fop.layoutmgr.BreakElement; import org.apache.fop.layoutmgr.ElementListUtils; @@ -400,9 +402,13 @@ public class TableContentLayoutManager implements PercentBaseContext { } } - Map markers = getTableLM().getTable().getMarkers(); + // there may be table fragment markers stored; clear them since we are starting a new fragment + tableLM.clearTableFragmentMarkers(); + + // note: markers at table level are to be retrieved by the page, not by the table itself + Map markers = getTableLM().getTable().getMarkers(); if (markers != null) { - getTableLM().getCurrentPV().addMarkers(markers, + getTableLM().getCurrentPV().registerMarkers(markers, true, getTableLM().isFirst(firstPos), getTableLM().isLast(lastCheckPos)); } @@ -430,6 +436,10 @@ public class TableContentLayoutManager implements PercentBaseContext { addBodyAreas(tablePositions.iterator(), painter, footerElements == null); } + // if there are TCLMs saved because they have a RetrieveTableMarker, we repeat the header areas now; + // this can also be done after the areas for the footer are added but should be the same as here + tableLM.repeatAddAreasForSavedTableHeaderTableCellLayoutManagers(); + if (footerElements != null) { boolean ancestorTreatAsArtifact = layoutContext.treatAsArtifact(); layoutContext.setTreatAsArtifact(treatFooterAsArtifact); @@ -442,7 +452,7 @@ public class TableContentLayoutManager implements PercentBaseContext { this.usedBPD += painter.getAccumulatedBPD(); if (markers != null) { - getTableLM().getCurrentPV().addMarkers(markers, + getTableLM().getCurrentPV().registerMarkers(markers, false, getTableLM().isFirst(firstPos), getTableLM().isLast(lastCheckPos)); } } @@ -503,14 +513,20 @@ public class TableContentLayoutManager implements PercentBaseContext { */ private void addTablePartAreas(List positions, RowPainter painter, TablePart body, boolean isFirstPos, boolean isLastPos, boolean lastInBody, boolean lastOnPage) { - getTableLM().getCurrentPV().addMarkers(body.getMarkers(), + getTableLM().getCurrentPV().registerMarkers(body.getMarkers(), true, isFirstPos, isLastPos); + if (body instanceof TableBody) { + getTableLM().registerMarkers(body.getMarkers(), true, isFirstPos, isLastPos); + } painter.startTablePart(body); for (Iterator iter = positions.iterator(); iter.hasNext();) { painter.handleTableContentPosition((TableContentPosition) iter.next()); } - getTableLM().getCurrentPV().addMarkers(body.getMarkers(), + getTableLM().getCurrentPV().registerMarkers(body.getMarkers(), false, isFirstPos, isLastPos); + if (body instanceof TableBody) { + getTableLM().registerMarkers(body.getMarkers(), false, isFirstPos, isLastPos); + } painter.endTablePart(lastInBody, lastOnPage); } diff --git a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java index 928be43c4..7d4c26546 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -33,6 +34,9 @@ import org.apache.fop.datatypes.LengthBase; import org.apache.fop.fo.Constants; import org.apache.fop.fo.FONode; import org.apache.fop.fo.FObj; +import org.apache.fop.fo.flow.Marker; +import org.apache.fop.fo.flow.Markers; +import org.apache.fop.fo.flow.RetrieveTableMarker; import org.apache.fop.fo.flow.table.Table; import org.apache.fop.fo.flow.table.TableColumn; import org.apache.fop.fo.properties.KeepProperty; @@ -93,6 +97,15 @@ public class TableLayoutManager extends BlockStackingLayoutManager private Position auxiliaryPosition; + // this holds a possible list of TCLMs that needed to have their addAreas() repeated + private List savedTCLMs; + private boolean areAllTCLMsSaved; + + private Markers tableMarkers; + private Markers tableFragmentMarkers; + + private boolean hasRetrieveTableMarker; + /** * Temporary holder of column background informations for a table-cell's area. * @@ -560,4 +573,112 @@ public class TableLayoutManager extends BlockStackingLayoutManager tableUnit = 0.0; } + /** + * Saves a TableCellLayoutManager for later use. + * + * @param tclm a TableCellLayoutManager that has a RetrieveTableMarker + */ + protected void saveTableHeaderTableCellLayoutManagers(TableCellLayoutManager tclm) { + if (savedTCLMs == null) { + savedTCLMs = new ArrayList(); + } + if (!areAllTCLMsSaved) { + savedTCLMs.add(tclm); + } + } + + /** + * Calls addAreas() for each of the saved TableCellLayoutManagers. + */ + protected void repeatAddAreasForSavedTableHeaderTableCellLayoutManagers() { + if (savedTCLMs == null) { + return; + } + // if we get to this stage then we are at the footer of the table fragment; this means that no more + // different TCLM need to be saved (we already have all); we flag the list as being complete then + areAllTCLMsSaved = true; + for (int i = 0; i < savedTCLMs.size(); i++) { + TableCellLayoutManager tclm = savedTCLMs.get(i); + tclm.repeatAddAreas(); + } + } + + /** + * Resolves a RetrieveTableMarker by finding a qualifying Marker to which it is bound to. + * @param rtm the RetrieveTableMarker to be resolved + * @return a bound RetrieveTableMarker instance or null if no qualifying Marker found + */ + public RetrieveTableMarker resolveRetrieveTableMarker(RetrieveTableMarker rtm) { + String name = rtm.getRetrieveClassName(); + int originalPosition = rtm.getPosition(); + boolean changedPosition = false; + + Marker mark = null; + // try the primary retrieve scope area, which is the same as table-fragment + mark = (tableFragmentMarkers == null) ? null : tableFragmentMarkers.resolve(rtm); + if (mark == null && rtm.getBoundary() != Constants.EN_TABLE_FRAGMENT) { + rtm.changePositionTo(Constants.EN_LAST_ENDING); + changedPosition = true; + // try the page scope area + mark = getCurrentPV().resolveMarker(rtm); + if (mark == null && rtm.getBoundary() != Constants.EN_PAGE) { + // try the table scope area + mark = (tableMarkers == null) ? null : tableMarkers.resolve(rtm); + } + } + if (changedPosition) { + // so that the next time it is called looks unchanged + rtm.changePositionTo(originalPosition); + } + if (mark == null) { + log.debug("found no marker with name: " + name); + return null; + } else { + rtm.bindMarker(mark); + return rtm; + } + } + + /** + * Register the markers for this table. + * + * @param marks the map of markers to add + * @param starting if the area being added is starting or ending + * @param isfirst if the area being added has is-first trait + * @param islast if the area being added has is-last trait + */ + public void registerMarkers(Map marks, boolean starting, boolean isfirst, + boolean islast) { + if (tableMarkers == null) { + tableMarkers = new Markers(); + } + tableMarkers.register(marks, starting, isfirst, islast); + if (tableFragmentMarkers == null) { + tableFragmentMarkers = new Markers(); + } + tableFragmentMarkers.register(marks, starting, isfirst, islast); + } + + /** + * Clears the list of markers in the current table fragment. Should be called just before starting a new + * header (that belongs to the next table fragment). + */ + protected void clearTableFragmentMarkers() { + tableFragmentMarkers = null; + } + + public void flagAsHavingRetrieveTableMarker() { + hasRetrieveTableMarker = true; + } + + protected void possiblyRegisterMarkersForTables(Map markers, boolean isStarting, + boolean isFirst, boolean isLast) { + // note: if we allow table-footer after a table-body this check should not be made and the markers + // should be registered regardless because the retrieval may be done only in the footer + if (hasRetrieveTableMarker) { + registerMarkers(markers, isStarting, isFirst, isLast); + } + super.possiblyRegisterMarkersForTables(markers, isStarting, isFirst, isLast); + } + } diff --git a/status.xml b/status.xml index 40e4c8db6..76b25a9eb 100644 --- a/status.xml +++ b/status.xml @@ -62,6 +62,9 @@ documents. Example: the fix of marks layering will be such a case when it's done. --> + + Support for retrieve-table-markers + Added possibility to define ‘header’ table columns (the same way as fo:table-header allows to define header rows). When accessibility is enabled, this allows to set the appropriate diff --git a/test/java/org/apache/fop/fo/flow/MarkersTestCase.java b/test/java/org/apache/fop/fo/flow/MarkersTestCase.java new file mode 100644 index 000000000..983a531b0 --- /dev/null +++ b/test/java/org/apache/fop/fo/flow/MarkersTestCase.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.flow; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.flow.Marker; +import org.apache.fop.fo.flow.Markers; +import org.apache.fop.fo.flow.RetrieveMarker; +import org.apache.fop.fo.flow.RetrieveTableMarker; + +public class MarkersTestCase { + + @Test + public void testRegisterAndResolve() { + // consider 3 regions, and a boundary; the first region starts before the boundary and ends inside + // the boundary, the second region is fully inside the boundary, and the third region starts inside + // the boundary and ends after the boundary. in every region there are 2 markers, A and B. + // ======== region 1 + Map markers_region_1 = new HashMap(); + Marker marker_1A = mock(Marker.class); + Marker marker_1B = mock(Marker.class); + markers_region_1.put("A", marker_1A); + markers_region_1.put("B", marker_1B); + // ======== region 2 + Map markers_region_2 = new HashMap(); + Marker marker_2A = mock(Marker.class); + Marker marker_2B = mock(Marker.class); + markers_region_2.put("A", marker_2A); + markers_region_2.put("B", marker_2B); + // ======== region 3 + Map markers_region_3 = new HashMap(); + Marker marker_3A = mock(Marker.class); + Marker marker_3B = mock(Marker.class); + markers_region_3.put("A", marker_3A); + markers_region_3.put("B", marker_3B); + // instantiate markers for the boundary + Markers markers = new Markers(); + // register the markers for the different regions + // region 1 + markers.register(markers_region_1, true, false, true); + markers.register(markers_region_1, false, false, true); + // region 2 + markers.register(markers_region_2, true, true, true); + markers.register(markers_region_2, false, true, true); + // region 3 + markers.register(markers_region_3, true, true, false); + markers.register(markers_region_3, false, true, false); + // now prepare a RetrieveMarker + RetrieveMarker rm = mock(RetrieveMarker.class); + when(rm.getRetrieveClassName()).thenReturn("A"); + when(rm.getLocalName()).thenReturn("retrieve-marker"); + when(rm.getPositionLabel()).thenReturn("position-label"); // not relevant for the test + // and resolve the marker for different positions + // EN_FSWP + when(rm.getPosition()).thenReturn(Constants.EN_FSWP); + // expect marker_2A + assertEquals(marker_2A, markers.resolve(rm)); + // EN_LSWP + when(rm.getPosition()).thenReturn(Constants.EN_LSWP); + // expect marker_3A + assertEquals(marker_3A, markers.resolve(rm)); + // EN_LEWP + when(rm.getPosition()).thenReturn(Constants.EN_LEWP); + // expect marker_2A + assertEquals(marker_2A, markers.resolve(rm)); + // EN_FIC + when(rm.getPosition()).thenReturn(Constants.EN_FIC); + // expect marker_1A + assertEquals(marker_1A, markers.resolve(rm)); + // now prepare a RetrieveTableMarker + RetrieveTableMarker rtm = mock(RetrieveTableMarker.class); + when(rtm.getRetrieveClassName()).thenReturn("B"); + when(rtm.getLocalName()).thenReturn("retrieve-table-marker"); + when(rtm.getPositionLabel()).thenReturn("position-label"); // not relevant for the test + // and resolve the marker for different positions + // EN_FIRST_STARTING + when(rtm.getPosition()).thenReturn(Constants.EN_FIRST_STARTING); + // expect marker_2B + assertEquals(marker_2B, markers.resolve(rtm)); + // EN_LAST_STARTING + when(rtm.getPosition()).thenReturn(Constants.EN_LAST_STARTING); + // expect marker_3B + assertEquals(marker_3B, markers.resolve(rtm)); + // EN_LAST_ENDING + when(rtm.getPosition()).thenReturn(Constants.EN_LAST_ENDING); + // expect marker_2B + assertEquals(marker_2B, markers.resolve(rtm)); + // EN_FIRST_INCLUDING_CARRYOVER + when(rtm.getPosition()).thenReturn(Constants.EN_FIRST_INCLUDING_CARRYOVER); + // expect marker_1B + assertEquals(marker_1B, markers.resolve(rtm)); + // test also an invalid position + when(rm.getPosition()).thenReturn(Constants.EN_ABSOLUTE); + try { + Marker m = markers.resolve(rm); + fail("Expected an exception... instead got:" + m.toString()); + } catch (RuntimeException re) { + // do nothing + } + } +} diff --git a/test/java/org/apache/fop/layoutmgr/PageSequenceLayoutManagerTestCase.java b/test/java/org/apache/fop/layoutmgr/PageSequenceLayoutManagerTestCase.java index eb2a4fc92..e810f80c5 100644 --- a/test/java/org/apache/fop/layoutmgr/PageSequenceLayoutManagerTestCase.java +++ b/test/java/org/apache/fop/layoutmgr/PageSequenceLayoutManagerTestCase.java @@ -19,6 +19,8 @@ package org.apache.fop.layoutmgr; +import org.junit.Test; + import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.mock; @@ -31,7 +33,6 @@ import org.apache.fop.fo.pagination.PageSequence; import org.apache.fop.fo.pagination.Region; import org.apache.fop.fo.pagination.Root; import org.apache.fop.fo.pagination.SimplePageMaster; -import org.junit.Test; public class PageSequenceLayoutManagerTestCase { diff --git a/test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerMakerTestCase.java b/test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerMakerTestCase.java new file mode 100644 index 000000000..6a37de33c --- /dev/null +++ b/test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerMakerTestCase.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.layoutmgr; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.apache.fop.apps.FOPException; +import org.apache.fop.fo.FObj.FObjIterator; +import org.apache.fop.fo.flow.RetrieveTableMarker; +import org.apache.fop.layoutmgr.LayoutManagerMapping.RetrieveTableMarkerLayoutManagerMaker; + +public class RetrieveTableMarkerLayoutManagerMakerTestCase { + + @Test + public void testMake() throws FOPException { + // mock + FObjIterator foi = mock(FObjIterator.class); + when(foi.hasNext()).thenReturn(true).thenReturn(false); + // mock + RetrieveTableMarker rtm = mock(RetrieveTableMarker.class); + // real RTMLMM, not mock + List l = new ArrayList(); + LayoutManagerMapping lmm = new LayoutManagerMapping(); + RetrieveTableMarkerLayoutManagerMaker rtmlmm = lmm.new RetrieveTableMarkerLayoutManagerMaker(); + // test the case rtm has no child nodes + when(rtm.getChildNodes()).thenReturn(null); + rtmlmm.make(rtm, l); + assertTrue(l.size() == 1); + assertTrue(l.get(0) instanceof RetrieveTableMarkerLayoutManager); + } + +} diff --git a/test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerTestCase.java b/test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerTestCase.java new file mode 100644 index 000000000..fda9cbccb --- /dev/null +++ b/test/java/org/apache/fop/layoutmgr/RetrieveTableMarkerLayoutManagerTestCase.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.layoutmgr; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.flow.RetrieveTableMarker; +import org.apache.fop.fo.flow.table.Table; +import org.apache.fop.layoutmgr.inline.TextLayoutManager; +import org.apache.fop.layoutmgr.table.TableLayoutManager; + +public class RetrieveTableMarkerLayoutManagerTestCase { + + @Test + public void testGetNextKnuthElementsLayoutContextInt() { + LayoutContext lc = LayoutContext.newInstance(); + // mock + Table t = mock(Table.class); + // mock + RetrieveTableMarker rtm = mock(RetrieveTableMarker.class); + when(rtm.getRetrieveClassName()).thenReturn("A"); + when(rtm.getPosition()).thenReturn(Constants.EN_FIRST_STARTING); + when(rtm.getBoundary()).thenReturn(Constants.EN_TABLE_FRAGMENT); + // mock + TextLayoutManager tlm = mock(TextLayoutManager.class); + // mock + LayoutManagerMapping lmm = mock(LayoutManagerMapping.class); + when(lmm.makeLayoutManager(rtm)).thenReturn(tlm); + // mock + PageSequenceLayoutManager pslm = mock(PageSequenceLayoutManager.class); + when(pslm.getPSLM()).thenReturn(pslm); + when(pslm.getLayoutManagerMaker()).thenReturn(lmm); + // mock + TableLayoutManager tablelm = mock(TableLayoutManager.class); + when(tablelm.getTable()).thenReturn(t); + // mock + BlockLayoutManager blm = mock(BlockLayoutManager.class); + when(blm.getPSLM()).thenReturn(pslm); + when(blm.getParent()).thenReturn(tablelm); + // real RTMLM, not mock + RetrieveTableMarkerLayoutManager rtmlm = new RetrieveTableMarkerLayoutManager(rtm); + rtmlm.setParent(blm); + // test the case where resolution returns null + when(tablelm.resolveRetrieveTableMarker(rtm)).thenReturn(null); + assertNull(rtmlm.getNextKnuthElements(lc, 0)); + // test the case where resolution returns non null + List l = new ArrayList(); + when(tablelm.resolveRetrieveTableMarker(rtm)).thenReturn(rtm); + when(tlm.getNextKnuthElements(lc, 0)).thenReturn(l); + assertEquals(l, rtmlm.getNextKnuthElements(lc, 0)); + verify(tlm).setParent(blm); + verify(tlm).initialize(); + } + +} diff --git a/test/java/org/apache/fop/layoutmgr/table/TableCellLayoutManagerTestCase.java b/test/java/org/apache/fop/layoutmgr/table/TableCellLayoutManagerTestCase.java new file mode 100644 index 000000000..a370e5dd5 --- /dev/null +++ b/test/java/org/apache/fop/layoutmgr/table/TableCellLayoutManagerTestCase.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.layoutmgr.table; + +import java.awt.Color; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.fop.fo.flow.table.PrimaryGridUnit; +import org.apache.fop.fo.flow.table.Table; +import org.apache.fop.fo.flow.table.TableCell; +import org.apache.fop.fo.flow.table.TableColumn; +import org.apache.fop.fo.flow.table.TableHeader; +import org.apache.fop.fo.flow.table.TableRow; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; +import org.apache.fop.fo.properties.CondLengthProperty; +import org.apache.fop.layoutmgr.LayoutContext; +import org.apache.fop.layoutmgr.PageSequenceLayoutManager; +import org.apache.fop.layoutmgr.PositionIterator; +import org.apache.fop.layoutmgr.RetrieveTableMarkerLayoutManager; + +public class TableCellLayoutManagerTestCase { + + // this test aims to check that the first call to addAreas() calls + // TLM.saveTableHeaderTableCellLayoutManagers() but the second call, through repeatAddAreas() + // does not call it again; there are a lot of mocks here, but just the necessary so that the + // addAreas() call can run to completion without NPE; also, the mocking needs to be done so + // the methods isDecendantOfTableHeaderOrFooter() and hasRetrieveTableMarker() return true. + @Test + public void testRepeatAddAreas() { + LayoutContext lc = LayoutContext.newInstance(); + // mock background + CommonBorderPaddingBackground cbpb = mock(CommonBorderPaddingBackground.class); + // mock conditional length property + CondLengthProperty clp = mock(CondLengthProperty.class); + when(clp.getLengthValue()).thenReturn(0); + // real border info + BorderInfo bi = BorderInfo.getInstance(0, clp, Color.BLACK); + // mock column + TableColumn tcol = mock(TableColumn.class); + when(tcol.getCommonBorderPaddingBackground()).thenReturn(cbpb); + // mock table + Table t = mock(Table.class); + when(t.getColumn(0)).thenReturn(tcol); + // mock header + TableHeader th = mock(TableHeader.class); + when(th.getCommonBorderPaddingBackground()).thenReturn(cbpb); + // mock row + TableRow tr = mock(TableRow.class); + when(tr.getParent()).thenReturn(th); + // mock cell + TableCell tc = mock(TableCell.class); + when(tc.hasRetrieveTableMarker()).thenReturn(true); + when(tc.getTable()).thenReturn(t); + when(tc.getId()).thenReturn("cellId"); + when(tc.getCommonBorderPaddingBackground()).thenReturn(cbpb); + when(tc.getParent()).thenReturn(tr); + // mock PGU + PrimaryGridUnit pgu = mock(PrimaryGridUnit.class); + when(pgu.getCell()).thenReturn(tc); + when(pgu.getColIndex()).thenReturn(0); + when(pgu.getBorderBefore(0)).thenReturn(bi); + when(pgu.getBorderAfter(0)).thenReturn(bi); + when(pgu.getBorderEnd()).thenReturn(bi); + when(pgu.getBorderStart()).thenReturn(bi); + when(pgu.getTablePart()).thenReturn(th); + // mock RTMLM + RetrieveTableMarkerLayoutManager rtmlm = mock(RetrieveTableMarkerLayoutManager.class); + when(rtmlm.isFinished()).thenReturn(true); // avoids infinite loop + // mock PSLM + PageSequenceLayoutManager pslm = mock(PageSequenceLayoutManager.class); + // mock TLM + TableLayoutManager tlm = mock(TableLayoutManager.class); + when(tlm.getPSLM()).thenReturn(pslm); + // mock PI + PositionIterator pi = mock(PositionIterator.class); + // mock RP + RowPainter rp = mock(RowPainter.class); + + // real TCLM, not a mock! + TableCellLayoutManager tclm = new TableCellLayoutManager(tc, pgu); + tclm.addChildLM(rtmlm); + tclm.setParent(tlm); + // lets call addAreas + int[] n = {}; + tclm.addAreas(pi, lc, n, 0, 0, 0, 0, true, true, rp, 0); + // check the TCLM is added to the TLM + verify(tlm).saveTableHeaderTableCellLayoutManagers(tclm); + // call the repeat + tclm.repeatAddAreas(); + // check the TCLM was not added again + verify(tlm).saveTableHeaderTableCellLayoutManagers(tclm); + } +} diff --git a/test/java/org/apache/fop/layoutmgr/table/TableContentLayoutManagerTestCase.java b/test/java/org/apache/fop/layoutmgr/table/TableContentLayoutManagerTestCase.java new file mode 100644 index 000000000..2bc41979c --- /dev/null +++ b/test/java/org/apache/fop/layoutmgr/table/TableContentLayoutManagerTestCase.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.layoutmgr.table; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.fop.fo.FONode.FONodeIterator; +import org.apache.fop.fo.flow.table.Table; +import org.apache.fop.layoutmgr.LayoutContext; +import org.apache.fop.layoutmgr.PositionIterator; + +public class TableContentLayoutManagerTestCase { + + @Test + public void testAddAreas() { + LayoutContext lc = LayoutContext.newInstance(); + // mock + ColumnSetup cs = mock(ColumnSetup.class); + when(cs.getColumnCount()).thenReturn(3); + // mock + FONodeIterator foni = mock(FONodeIterator.class); + when(foni.hasNext()).thenReturn(false); + // mock + Table t = mock(Table.class); + when(t.getChildNodes()).thenReturn(foni); + when(t.getMarkers()).thenReturn(null); + // mock + TableLayoutManager tlm = mock(TableLayoutManager.class); + when(tlm.getTable()).thenReturn(t); + when(tlm.getColumns()).thenReturn(cs); + // mock + PositionIterator pi = mock(PositionIterator.class); + when(pi.hasNext()).thenReturn(false); + // real TCLM, not a mock + TableContentLayoutManager tclm = new TableContentLayoutManager(tlm); + // check that addAreas() calls the clearTableFragments() on the table and the + // repeatAddAreasForSavedTableHeaderTableCellLayoutManagers on the TLM + tclm.addAreas(pi, lc); + verify(tlm).clearTableFragmentMarkers(); + verify(tlm).repeatAddAreasForSavedTableHeaderTableCellLayoutManagers(); + } + +} diff --git a/test/java/org/apache/fop/layoutmgr/table/TableLayoutManagerTestCase.java b/test/java/org/apache/fop/layoutmgr/table/TableLayoutManagerTestCase.java new file mode 100644 index 000000000..46a394eb7 --- /dev/null +++ b/test/java/org/apache/fop/layoutmgr/table/TableLayoutManagerTestCase.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.layoutmgr.table; + +import java.util.HashMap; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.apache.fop.area.PageViewport; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.flow.Marker; +import org.apache.fop.fo.flow.RetrieveTableMarker; +import org.apache.fop.fo.flow.table.Table; +import org.apache.fop.layoutmgr.BlockLayoutManager; +import org.apache.fop.layoutmgr.Page; +import org.apache.fop.layoutmgr.PageSequenceLayoutManager; + +public class TableLayoutManagerTestCase { + + @Test + public void testSavedTableCellLayoutManagersFunctionality() { + Table t = mock(Table.class); + TableCellLayoutManager tclm1 = mock(TableCellLayoutManager.class); + TableLayoutManager tlm = new TableLayoutManager(t); + tlm.saveTableHeaderTableCellLayoutManagers(tclm1); + tlm.repeatAddAreasForSavedTableHeaderTableCellLayoutManagers(); + verify(tclm1).repeatAddAreas(); // called once + // test that after the first repeatAddAreas() call the list closes to new additions + TableCellLayoutManager tclm2 = mock(TableCellLayoutManager.class); + tlm.saveTableHeaderTableCellLayoutManagers(tclm2); + tlm.repeatAddAreasForSavedTableHeaderTableCellLayoutManagers(); + verify(tclm1, times(2)).repeatAddAreas(); // called twice + verify(tclm2, never()).repeatAddAreas(); // never called + } + + @Test + public void testResolveRetrieveTableMarker() { + // mock + Table t = mock(Table.class); + // mock + Marker m = mock(Marker.class); + // mock + RetrieveTableMarker rtm = mock(RetrieveTableMarker.class); + when(rtm.getRetrieveClassName()).thenReturn("A"); + when(rtm.getPosition()).thenReturn(Constants.EN_FIRST_STARTING); + // mock + PageViewport pv = mock(PageViewport.class); + when(pv.resolveMarker(rtm)).thenReturn(m); + // mock + Page p = mock(Page.class); + when(p.getPageViewport()).thenReturn(pv); + // mock + PageSequenceLayoutManager pslm = mock(PageSequenceLayoutManager.class); + when(pslm.getPSLM()).thenReturn(pslm); + when(pslm.getCurrentPage()).thenReturn(p); + // mock + BlockLayoutManager blm = mock(BlockLayoutManager.class); + blm.setParent(pslm); + when(blm.getPSLM()).thenReturn(pslm); + // real TLM, not mock + TableLayoutManager tlm = new TableLayoutManager(t); + tlm.setParent(blm); + // register a marker + HashMap markers1 = new HashMap(); + Marker m1 = mock(Marker.class); + markers1.put("A", m1); + tlm.registerMarkers(markers1, true, true, true); + tlm.registerMarkers(markers1, false, true, true); + // check that if there is a marker at table fragment level the RTM is returned + assertEquals(rtm, tlm.resolveRetrieveTableMarker(rtm)); + verify(rtm, never()).getBoundary(); + // check that if there is no marker at table fragment level and that is the boundary + // we get a null return value + when(rtm.getBoundary()).thenReturn(Constants.EN_TABLE_FRAGMENT); + when(rtm.getRetrieveClassName()).thenReturn("B"); + assertNull(tlm.resolveRetrieveTableMarker(rtm)); + verify(rtm).getBoundary(); + verify(rtm, never()).changePositionTo(Constants.EN_LAST_ENDING); + // check that if there is no marker at table fragment level and the boundary is page + // then we try to do the resolution at page level; test the case a marker is found + when(rtm.getBoundary()).thenReturn(Constants.EN_PAGE); + assertEquals(rtm, tlm.resolveRetrieveTableMarker(rtm)); + verify(rtm).changePositionTo(Constants.EN_LAST_ENDING); + verify(rtm).changePositionTo(Constants.EN_FIRST_STARTING); + verify(pv).resolveMarker(rtm); + // test the same situation but in this case the marker is not found + when(pv.resolveMarker(rtm)).thenReturn(null); + assertNull(tlm.resolveRetrieveTableMarker(rtm)); + // test the situation where the marker is not found at page level but the boundary is table + when(rtm.getBoundary()).thenReturn(Constants.EN_TABLE); + assertNull(tlm.resolveRetrieveTableMarker(rtm)); + } + +} diff --git a/test/layoutengine/standard-testcases/markers_10.xml b/test/layoutengine/standard-testcases/markers_10.xml new file mode 100644 index 000000000..0921da0aa --- /dev/null +++ b/test/layoutengine/standard-testcases/markers_10.xml @@ -0,0 +1,144 @@ + + + + + +

+ This test checks markers on broken tables. +

+
+ + + + + + + + + + + + + f-i-c: + + + f-s-w-p: + + + + + l-s-w-p: + + + l-e-w-p: + + + + + + + + + row1 + row1 + row1 + row1 + row1 + + + row1 + row1 + + + + + row2 + row2 + + + row2 + row2 + row2 + row2 + row2 + + + + + row3 + row3 + + + row3 + row3 + row3 + row3 + row3 + + + + + row4 + row4 + + + + + row5 + row5 + + + row5 + row5 + row5 + + + + + row6 + row6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/test/layoutengine/standard-testcases/retrieve-table-marker_multicolumn.xml b/test/layoutengine/standard-testcases/retrieve-table-marker_multicolumn.xml new file mode 100644 index 000000000..db578f95e --- /dev/null +++ b/test/layoutengine/standard-testcases/retrieve-table-marker_multicolumn.xml @@ -0,0 +1,720 @@ + + + + + +

This test checks the retrieve-table-marker implementation. Rather, it serves as a regression test.

+
+ + + + + + + + + + + + The table has two columns, State and Facts. In the rows corresponding to states in the + Pacific Time Zone (PST), the State and Facts table + cells have a marker with the value of the State. That marker is retrieved in the table header and + footer. All the possible boundary and positions are considered in this example. + + rbwt = retrieve-boundary-within-table; rpwt = retrieve-position-within-table; + f-i-c = first-including-carryover; f-s = first-starting; l-s = last-starting; l-e = last-ending; + t = table; p = page; p-s = page-sequence; t-f = table-fragment. + + + + + + + + + + [rbwt:t][rpwt:f-i-c]( + + ) + + + [rbwt:t][rpwt:f-s]( + + ) + + + [rbwt:p][rpwt:f-i-c]( + + ) + + + [rbwt:p][rpwt:f-s]( + + ) + + + [rbwt:t-f][rpwt:f-i-c]( + + ) + + + [rbwt:t-f][rpwt:f-s]( + + ) + + + + + + State + + + Facts + + + + + + + + [rbwt:t][rpwt:l-s]( + + ) + + + [rbwt:t][rpwt:l-e]( + + ) + + + [rbwt:p][rpwt:l-s]( + + ) + + + [rbwt:p][rpwt:l-e]( + + ) + + + [rbwt:t-f][rpwt:l-s]( + + ) + + + [rbwt:t-f][rpwt:l-e]( + + ) + + + + + + + + + Alabama + + + Capital: Montgomery + Area: 52,423 square miles + Population: 4,447,100 + Time Zone: CST + + + + + Alaska + + + Capital: Juneau + Area: 656,425 square miles + Population: 626,932 + Time Zone: GMT -9:00 + + + + + Arizona + Arizona + + + Arizona + Capital: Phoenix + Area: 114,006 square miles + Population: 5,130,632 + Time Zone: PST and MST + + + + + Arkansas + + + Capital: Little Rock + Area: 53,182 square miles + Population: 2,673,400 + Time Zone: CST + + + + + California + California + + + California + Capital: Sacramento + Area: 163,707 square miles + Population: 33,871,648 + Time Zone: PST + + + + + Colorado + + + Capital: Denver + Area: 104,100 square miles + Population: 4,301,261 + Time Zone: MST + + + + + Connecticut + + + Capital: Hartford + Area: 5,544 square miles + Population: 3,405,565 + Time Zone: EST + + + + + Delaware + + + Capital: Dover + Area: 1,954 square miles + Population: 783,600 + Time Zone: EST + + + + + Florida + + + Capital: Tallahassee + Area: 65,758 square miles + Population: 15,982,378 + Time Zone: EST + + + + + Georgia + + + Capital: Atlanta + Area: 59,441 square miles + Population: 8,186,453 + Time Zone: EST + + + + + Hawaii + + + Capital: Honolulu + Area: 10,932 square miles + Population: 1,211,537 + Time Zone: GMT -11:00 + + + + + Idaho + + + Capital: Boise + Area: 83,574 square miles + Population: 1,293,953 + Time Zone: MST + + + + + Illinois + + + Capital: Springfield + Area: 57,918 square miles + Population: 12,419,293 + Time Zone: CST + + + + + Indiana + + + Capital: Indianapolis + Area: 36,420 square miles + Population: 6,080,485 + Time Zone: EST and CST + + + + + Iowa + + + Capital: Des Moines + Area: 56,276 square miles + Population: 2,926,324 + Time Zone: CST + + + + + Kansas + + + Capital: Topeka + Area: 82,282 square miles + Population: 2,688,418 + Time Zone: CST + + + + + Kentucky + + + Capital: Frankfort + Area: 40,411 square miles + Population: 4,041,769 + Time Zone: EST + + + + + Louisiana + + + Capital: Baton Rouge + Area: 51,843 square miles + Population: 4,468,976 + Time Zone: CST + + + + + Maine + + + Capital: Augusta + Area: 35,387 square miles + Population: 1,274,923 + Time Zone: EST + + + + + Maryland + + + Capital: Annapolis + Area: 12,407 square miles + Population: 5,296,486 + Time Zone: EST + + + + + Massachusetts + + + Capital: Boston + Area: 10,555 square miles + Population: 6,349,097 + Time Zone: EST + + + + + Michigan + + + Capital: Lansing + Area: 96,810 square miles + Population: 9,938,444 + Time Zone: EST + + + + + Minnesota + + + Capital: St. Paul + Area: 86,943 square miles + Population: 4,919,479 + Time Zone: CST + + + + + Mississippi + + + Capital: Jackson + Area: 48,434 square miles + Population: 2,844,658 + Time Zone: CST + + + + + Missouri + + + Capital: Jefferson City + Area: 69,709 square miles + Population: 5,595,211 + Time Zone: CST + + + + + Montana + + + Capital: Helena + Area: 147,046 square miles + Population: 902,195 + Time Zone: MST + + + + + Nebraska + + + Capital: Lincoln + Area: XXXXXX square miles + Population: 77,358 + Time Zone: CST + + + + + Nevada + Nevada + + + Nevada + Capital: Carson City + Area: 110,567 square miles + Population: 1,998,257 + Time Zone: PST + + + + + New Hampshire + + + Capital: Concord + Area: 9,351 square miles + Population: 1,235,786 + Time Zone: EST + + + + + New Jersey + + + Capital: Trenton + Area: 8,722 square miles + Population: 8,414,350 + Time Zone: EST + + + + + New Mexico + + + Capital: Santa Fe + Area: 121,593 square miles + Population: 1,819,046 + Time Zone: MST + + + + + New York + + + Capital: Albany + Area: 54,475 square miles + Population: 18,976,457 + Time Zone: EST + + + + + North Carolina + + + Capital: Raleigh + Area: 53,821 square miles + Population: 8,049,313 + Time Zone: EST + + + + + North Dakota + + + Capital: Bismarck + Area: 70,704 square miles + Population: 642,200 + Time Zone: CST + + + + + Ohio + + + Capital: Columbus + Area: 44,828 square miles + Population: 11,353,140 + Time Zone: EST + + + + + Oklahoma + + + Capital: Oklahoma City + Area: 69,903 square miles + Population: 3,450,654 + Time Zone: CST + + + + + Oregon + Oregon + + + Oregon + Capital: Salem + Area: 98,386 square miles + Population: 3,421,399 + Time Zone: PST + + + + + Pennsylvania + + + Capital: Harrisburg + Area: 46,058 square miles + Population: 12,281,054 + Time Zone: EST + + + + + Rhode Island + + + Capital: Providence + Area: 1,045 square miles + Population: 1,048,319 + Time Zone: EST + + + + + South Carolina + + + Capital: Columbia + Area: 32,007 square miles + Population: 4,012,012 + Time Zone: EST + + + + + South Dakota + + + Capital: Pierre + Area: 77,121 square miles + Population: 754,844 + Time Zone: CST + + + + + Tennessee + + + Capital: Nashville + Area: 42,146 square miles + Population: 5,689,283 + Time Zone: CST + + + + + Texas + + + Capital: Austin + Area: 268,601 square miles + Population: 20,851,820 + Time Zone: CST + + + + + Utah + + + Capital: Salt Lake City + Area: 84,904 square miles + Population: 2,233,169 + Time Zone: MST + + + + + Vermont + + + Capital: Montpelier + Area: 9,615 square miles + Population: 608,827 + Time Zone: EST + + + + + Virginia + + + Capital: Richmond + Area: 42,769 square miles + Population: 7,078,515 + Time Zone: EST + + + + + Washington + Washington + + + Washington + Capital: Olympia + Area: 71,303 square miles + Population: 5,894,121 + Time Zone: PST + + + + + West Virginia + + + Capital: Charleston + Area: 24,231 square miles + Population: 1,808,344 + Time Zone: EST + + + + + Wisconsin + + + Capital: Madison + Area: 65,503 square miles + Population: 5,363,675 + Time Zone: CST + + + + + Wyoming + + + Capital: Cheyenne + Area: 97,818 square miles + Population: 493,782 + Time Zone: MST + + + + + + + + + + + + + + + + + + +
\ No newline at end of file -- cgit v1.2.3