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-ffa450edef68tags/fop-2_0
@@ -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<Table> tables = new Stack<Table>(); | |||
private final Stack<Boolean> inTableHeader = new Stack<Boolean>(); | |||
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); | |||
} | |||
@@ -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 | |||
@@ -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() { |
@@ -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"); | |||
} | |||
/** |
@@ -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<String> PROPERTY_ATTRIBUTES = new java.util.HashSet<String>(); | |||
static { | |||
//These are FOP's extension properties for accessibility | |||
PROPERTY_ATTRIBUTES.add(STRUCT_ID); | |||
PROPERTY_ATTRIBUTES.add(STRUCT_REF); | |||
PROPERTY_ATTRIBUTES.add(SCOPE); | |||
} | |||
/** |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} |
@@ -62,6 +62,11 @@ | |||
documents. Example: the fix of marks layering will be such a case when it's done. | |||
--> | |||
<release version="FOP Trunk" date="TBD"> | |||
<action context="Renderers" dev="VH" type="add" fixes-bug="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. | |||
</action> | |||
<action context="Fonts" dev="MH" type="add" fixes-bug="53868" importance="low" due-to="Luis Bernardo"> | |||
Full font embedding in PDF | |||
</action> |
@@ -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); | |||
} |
@@ -21,7 +21,7 @@ | |||
xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" | |||
xmlns:foi="http://xmlgraphics.apache.org/fop/internal"> | |||
<xsl:output method="xml" indent="no"/> | |||
<xsl:output method="xml" indent="yes"/> | |||
<xsl:template name="copy"> | |||
<xsl:copy> | |||
@@ -92,10 +92,37 @@ | |||
<xsl:call-template name="copy"/> | |||
</xsl:template> | |||
<xsl:template match="fo:table|fo:table-header|fo:table-footer|fo:table-body|fo:table-row|fo:table-cell"> | |||
<xsl:template match="fo:table|fo:table-header|fo:table-footer|fo:table-body|fo:table-row"> | |||
<xsl:call-template name="copy"/> | |||
</xsl:template> | |||
<xsl:template name="get.column.header"> | |||
<xsl:value-of select="ancestor::fo:table/fo:table-column[ | |||
count(preceding-sibling::fo:table-column) = count(current()/preceding-sibling::fo:table-cell)]/@fox:header"/> | |||
</xsl:template> | |||
<xsl:template match="fo:table-cell"> | |||
<xsl:variable name="header"><xsl:call-template name="get.column.header"/></xsl:variable> | |||
<xsl:copy> | |||
<xsl:if test="$header = 'true'"> | |||
<xsl:attribute name="role">TH</xsl:attribute> | |||
<xsl:attribute name="scope" namespace="http://xmlgraphics.apache.org/fop/internal">Row</xsl:attribute> | |||
</xsl:if> | |||
<xsl:apply-templates select="@*|node()"/> | |||
</xsl:copy> | |||
</xsl:template> | |||
<xsl:template match="fo:table-header/fo:table-cell|fo:table-header/fo:table-row/fo:table-cell"> | |||
<xsl:variable name="header"><xsl:call-template name="get.column.header"/></xsl:variable> | |||
<xsl:copy> | |||
<xsl:attribute name="role">TH</xsl:attribute> | |||
<xsl:if test="$header = 'true'"> | |||
<xsl:attribute name="scope" namespace="http://xmlgraphics.apache.org/fop/internal">Both</xsl:attribute> | |||
</xsl:if> | |||
<xsl:apply-templates select="@*|node()"/> | |||
</xsl:copy> | |||
</xsl:template> | |||
<!-- Formatting Objects for Lists --> | |||
<xsl:template match="fo:list-block|fo:list-item|fo:list-item-label|fo:list-item-body"> | |||
<xsl:call-template name="copy"/> |
@@ -0,0 +1,57 @@ | |||
<?xml version="1.0" standalone="no"?> | |||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" | |||
xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" | |||
font-family="sans-serif"> | |||
<fo:layout-master-set> | |||
<fo:simple-page-master master-name="page" | |||
page-height="120pt" page-width="220pt" margin="10pt"> | |||
<fo:region-body display-align="center"/> | |||
</fo:simple-page-master> | |||
</fo:layout-master-set> | |||
<fo:page-sequence master-reference="page"> | |||
<fo:flow flow-name="xsl-region-body" line-height="10pt" font-size="8pt"> | |||
<fo:table width="100%" table-layout="fixed"> | |||
<fo:table-column fox:header="true" column-width="proportional-column-width(1)"/> | |||
<fo:table-column column-width="proportional-column-width(1)"/> | |||
<fo:table-column column-width="proportional-column-width(1)"/> | |||
<fo:table-header font-weight="bold"> | |||
<fo:table-row> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt"> | |||
<fo:block>Table Header</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt"> | |||
<fo:block>Column 1</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt"> | |||
<fo:block>Column 2</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
</fo:table-header> | |||
<fo:table-body> | |||
<fo:table-row> | |||
<fo:table-cell id="Row1" border="1pt solid black" padding-left="1pt" font-weight="bold"> | |||
<fo:block>Row 1</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt"> | |||
<fo:block>Cell 1.1</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt"> | |||
<fo:block>Cell 1.2</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
<fo:table-row> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt" font-weight="bold" role="TH"> | |||
<fo:block>Row 2</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt"> | |||
<fo:block>Cell 2.1</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt"> | |||
<fo:block>Cell 2.2</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
</fo:table-body> | |||
</fo:table> | |||
</fo:flow> | |||
</fo:page-sequence> | |||
</fo:root> |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -0,0 +1,68 @@ | |||
<?xml version="1.0" standalone="no"?> | |||
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" | |||
xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" | |||
font-family="sans-serif"> | |||
<fo:layout-master-set> | |||
<fo:simple-page-master master-name="page" | |||
page-height="120pt" page-width="220pt" margin="10pt"> | |||
<fo:region-body display-align="center"/> | |||
</fo:simple-page-master> | |||
</fo:layout-master-set> | |||
<fo:page-sequence master-reference="page"> | |||
<fo:flow flow-name="xsl-region-body" line-height="10pt" font-size="8pt"> | |||
<fo:table width="100%" table-layout="fixed"> | |||
<fo:table-column fox:header="true" column-width="proportional-column-width(1)"/> | |||
<fo:table-column column-width="proportional-column-width(1)"/> | |||
<fo:table-column column-width="proportional-column-width(1)"/> | |||
<fo:table-header font-weight="bold"> | |||
<fo:table-row> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt"> | |||
<fo:block>Table Header</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt"> | |||
<fo:block>Column 1</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt"> | |||
<fo:block>Column 2</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
</fo:table-header> | |||
<fo:table-body> | |||
<fo:table-row> | |||
<fo:table-cell id="Row1" border="1pt solid black" padding-left="1pt" font-weight="bold"> | |||
<fo:block>Row 1</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt"> | |||
<fo:block>Cell 1.1</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt"> | |||
<fo:block>Cell 1.2</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
<fo:table-row> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt" font-weight="bold" role="TH"> | |||
<fo:block>Row 2</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt"> | |||
<fo:block>Cell 2.1</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt"> | |||
<fo:block>Cell 2.2</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
<fo:table-row> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt" role="TD"> | |||
<fo:block>Non-header</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt"> | |||
<fo:block>Cell 3.1</fo:block> | |||
</fo:table-cell> | |||
<fo:table-cell border="1pt solid black" padding-left="1pt"> | |||
<fo:block>Cell 3.2</fo:block> | |||
</fo:table-cell> | |||
</fo:table-row> | |||
</fo:table-body> | |||
</fo:table> | |||
</fo:flow> | |||
</fo:page-sequence> | |||
</fo:root> |