Browse Source

Bugzilla#50391: Adding support for different flow-name of fo:region-body in FOP

Unit tests and a bugfix related to a repeatable sub-sequence specifier combo


git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1296104 13f79535-47bb-0310-9956-ffa450edef68
pull/26/head
Peter Hancock 12 years ago
parent
commit
83728fedff

+ 1
- 1
src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java View File

@@ -218,7 +218,7 @@ public class PageSequenceMaster extends FObj {
if (nextSubSequence == null) {
//Sub-sequence exhausted so attempt to reuse it
blockLevelEventProducer.pageSequenceMasterExhausted(this,
masterName, canRecover, getLocator());
masterName, canRecover & currentSubSequence.isReusable(), getLocator());
currentSubSequence.reset();
if (!currentSubSequence.canProcess(mainFlowName)) {
throw new PageProductionException(

+ 4
- 1
src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java View File

@@ -233,6 +233,9 @@ public class RepeatablePageMasterAlternatives extends FObj
return getMaximumRepeats() == INFINITE;
}


/** {@inheritDoc} */
public boolean isReusable() {
return false;
}

}

+ 4
- 1
src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java View File

@@ -181,6 +181,9 @@ public class RepeatablePageMasterReference extends FObj
return getMaximumRepeats() == INFINITE;
}


/** {@inheritDoc} */
public boolean isReusable() {
return false;
}

}

+ 5
- 0
src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java View File

@@ -159,5 +159,10 @@ public class SinglePageMasterReference extends FObj
return false;
}

/** {@inheritDoc} */
public boolean isReusable() {
return true;
}

}


+ 7
- 0
src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java View File

@@ -82,5 +82,12 @@ public interface SubSequenceSpecifier {
*/
boolean isInfinite();

/**
* Test if this can be reused when it is the last sub-sequence specifer,
* and has been exhausted
* @return true if and only if it can be reused
*/
boolean isReusable();

}


+ 0
- 1
src/java/org/apache/fop/layoutmgr/PageProvider.java View File

@@ -330,7 +330,6 @@ public class PageProvider implements Constants {
boolean isFirstPage = (startPageOfPageSequence == index);
SimplePageMaster spm = pageSeq.getNextSimplePageMaster(
index, isFirstPage, isLastPage, isBlank);

Page page = new Page(spm, index, pageNumberString, isBlank, spanAll);
//Set unique key obtained from the AreaTreeHandler
page.getPageViewport().setKey(areaTreeHandler.generatePageViewportKey());

+ 11
- 5
src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java View File

@@ -148,12 +148,18 @@ public class PageSequenceLayoutManager extends AbstractPageSequenceLayoutManager

@Override
protected Page makeNewPage(boolean isBlank) {
Page newPage;
Page newPage = super.makeNewPage(isBlank);

do {
newPage = super.makeNewPage(isBlank);
} while (!getPageSequence().getMainFlow().getFlowName()
.equals(newPage.getSimplePageMaster().getRegion(FO_REGION_BODY).getRegionName()));
// Empty pages (pages that have been generated from a SPM that has an un-mapped flow name)
// cannot layout areas from the main flow. Blank pages can be created from empty pages.

if (!isBlank) {
while (!getPageSequence().getMainFlow().getFlowName()
.equals(newPage.getSimplePageMaster()
.getRegion(FO_REGION_BODY).getRegionName())) {
newPage = super.makeNewPage(isBlank);
}
}

return newPage;
}

+ 2
- 0
test/java/org/apache/fop/StandardTestSuite.java View File

@@ -36,6 +36,7 @@ import org.apache.fop.image.loader.batik.ImagePreloaderTestCase;
import org.apache.fop.intermediate.IFMimickingTestCase;
import org.apache.fop.render.extensions.prepress.PageBoundariesTestCase;
import org.apache.fop.render.extensions.prepress.PageScaleTestCase;
import org.apache.fop.layoutmgr.PageSequenceLayoutManagerTestCase;
import org.apache.fop.render.pdf.PDFAConformanceTestCase;
import org.apache.fop.render.pdf.PDFCMapTestCase;
import org.apache.fop.render.pdf.PDFEncodingTestCase;
@@ -62,6 +63,7 @@ import org.apache.fop.pdf.PDFLibraryTestSuite;
ImageLoaderTestCase.class,
ImagePreloaderTestCase.class,
IFMimickingTestCase.class,
PageSequenceLayoutManagerTestCase.class,
PageBoundariesTestCase.class,
PageScaleTestCase.class,
org.apache.fop.afp.AFPTestSuite.class,

+ 1
- 2
test/java/org/apache/fop/fo/flow/table/AllTests.java View File

@@ -21,14 +21,13 @@ package org.apache.fop.fo.flow.table;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

/**
* All test to be added in FOTreeTestSuite
*
*/
@RunWith(Suite.class)
@SuiteClasses({
@Suite.SuiteClasses({
CollapsedConditionalBorderTestCase.class,
IllegalRowSpanTestCase.class,
RowGroupBuilderTestCase.class,

+ 0
- 2
test/java/org/apache/fop/fo/pagination/AllTests.java View File

@@ -29,7 +29,5 @@ import org.junit.runner.RunWith;
@RunWith(Suite.class)
@Suite.SuiteClasses({ PageSequenceMasterTestCase.class,
RepeatablePageMasterAlternativesTestCase.class})

public final class AllTests {

}

+ 118
- 52
test/java/org/apache/fop/fo/pagination/PageSequenceMasterTestCase.java View File

@@ -23,14 +23,18 @@ import static org.junit.Assert.fail;

import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyObject;
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.FOPException;
import org.apache.fop.fo.FONode;
import org.apache.fop.layoutmgr.BlockLevelEventProducer;

import org.junit.Test;
import org.xml.sax.Locator;


/**
@@ -39,57 +43,119 @@ import org.junit.Test;
*/
public class PageSequenceMasterTestCase {

/**
* Test that PageProductionException is thrown if the final simple-page-master
* cannot handle the main-flow of the page sequence
* @throws Exception exception
*/
@Test
public void testGetNextSimplePageMasterException() throws Exception {

final String mainFlowRegionName = "main";
final String emptyFlowRegionName = "empty";
// Create stubs

FONode mockParent = mock(FONode.class);
Root mockRoot = mock(Root.class);
LayoutMasterSet mockLayoutMasterSet = mock(LayoutMasterSet.class);

// This will represent a page master that does not map to the main flow
// of the page sequence
SimplePageMaster mockEmptySPM = mock(SimplePageMaster.class);
Region mockRegion = mock(Region.class);
SinglePageMasterReference mockSinglePageMasterReference
= mock(SinglePageMasterReference.class);
BlockLevelEventProducer mockBlockLevelEventProducer = mock(BlockLevelEventProducer.class);

//Stub behaviour
when(mockParent.getRoot()).thenReturn(mockRoot);
when(mockRoot.getLayoutMasterSet()).thenReturn(mockLayoutMasterSet);

//The layout master set should return the empty page master
when(mockLayoutMasterSet.getSimplePageMaster(anyString())).thenReturn(mockEmptySPM);
when(mockEmptySPM.getRegion(anyInt())).thenReturn(mockRegion);

when(mockRegion.getRegionName()).thenReturn(emptyFlowRegionName);

when(mockSinglePageMasterReference.getNextPageMaster(anyBoolean(), anyBoolean(),
anyBoolean(), anyBoolean()))
.thenReturn(null, mockEmptySPM);

PageSequenceMaster pageSequenceMaster = new PageSequenceMaster(mockParent,
mockBlockLevelEventProducer);
pageSequenceMaster.startOfNode();
pageSequenceMaster.addSubsequenceSpecifier(mockSinglePageMasterReference);

try {
pageSequenceMaster.getNextSimplePageMaster(false, false, false, false,
mainFlowRegionName);
fail("The next simple page master does not refer to the main flow");
} catch (PageProductionException ppe) {
//Passed test
}
}
/**
* Test that block level events are produced in line with
* XSL:FO - 6.4.8 fo:page-sequence-master -
* "It is an error if the entire sequence of sub-sequence-specifiers children is exhausted
* while some areas returned by an fo:flow are not placed. Implementations may recover,
* if possible, by re-using the sub-sequence-specifier that was last used to generate a page."
*
* @throws Exception exception
*/
@Test
public void testGetNextSimplePageMasterExhausted() throws Exception {

//Test when the last sub-sequence specifier is not repeatable
testGetNextSimplePageMasterExhausted(true);

//Test when the last sub-sequence specifier is repeatable
testGetNextSimplePageMasterExhausted(false);

}

private void testGetNextSimplePageMasterExhausted(boolean canResume) throws Exception {

SimplePageMaster spm = mock(SimplePageMaster.class);
SubSequenceSpecifier mockSinglePageMasterReference
= mock(SubSequenceSpecifier.class);
BlockLevelEventProducer mockBlockLevelEventProducer = mock(BlockLevelEventProducer.class);

// subject under test
PageSequenceMaster pageSequenceMaster = createPageSequenceMaster(
mockBlockLevelEventProducer);
pageSequenceMaster.addSubsequenceSpecifier(mockSinglePageMasterReference);

//Setup to mock the exhaustion of the last sub-sequence specifier
when(mockSinglePageMasterReference.getNextPageMaster(anyBoolean(), anyBoolean(),
anyBoolean(), anyBoolean())).thenReturn(null, spm);

//Need this for the method to return normally
when(mockSinglePageMasterReference.canProcess(anyString())).thenReturn(true);

when(mockSinglePageMasterReference.isReusable()).thenReturn(canResume);

pageSequenceMaster.getNextSimplePageMaster(false, false, false, false, null);

verify(mockBlockLevelEventProducer).pageSequenceMasterExhausted((Locator)anyObject(),
anyString(), eq(canResume), (Locator)anyObject());
}

/**
* Test that PageProductionException is thrown if the final simple-page-master
* cannot handle the main-flow of the page sequence
* @throws Exception exception
*/
@Test
public void testGetNextSimplePageMasterException() throws Exception {

final String mainFlowRegionName = "main";
final String emptyFlowRegionName = "empty";

// This will represent a page master that does not map to the main flow
// of the page sequence
SimplePageMaster mockEmptySPM = mock(SimplePageMaster.class);
Region mockRegion = mock(Region.class);
SinglePageMasterReference mockSinglePageMasterReference
= mock(SinglePageMasterReference.class);
BlockLevelEventProducer mockBlockLevelEventProducer = mock(BlockLevelEventProducer.class);

LayoutMasterSet mockLayoutMasterSet = mock(LayoutMasterSet.class);
//The layout master set should return the empty page master
when(mockLayoutMasterSet.getSimplePageMaster(anyString())).thenReturn(mockEmptySPM);
when(mockEmptySPM.getRegion(anyInt())).thenReturn(mockRegion);

when(mockRegion.getRegionName()).thenReturn(emptyFlowRegionName);

when(mockSinglePageMasterReference.getNextPageMaster(anyBoolean(), anyBoolean(),
anyBoolean(), anyBoolean()))
.thenReturn(null, mockEmptySPM);

PageSequenceMaster pageSequenceMaster = createPageSequenceMaster(mockLayoutMasterSet,
mockBlockLevelEventProducer);

pageSequenceMaster.startOfNode();
pageSequenceMaster.addSubsequenceSpecifier(mockSinglePageMasterReference);

try {
pageSequenceMaster.getNextSimplePageMaster(false, false, false, false,
mainFlowRegionName);
fail("The next simple page master does not refer to the main flow");
} catch (PageProductionException ppe) {
//Passed test
}
}


private PageSequenceMaster createPageSequenceMaster(
BlockLevelEventProducer blockLevelEventProducer) throws FOPException {

return createPageSequenceMaster(mock(LayoutMasterSet.class), blockLevelEventProducer);
}

private PageSequenceMaster createPageSequenceMaster(LayoutMasterSet layoutMasterSet,
BlockLevelEventProducer blockLevelEventProducer) throws FOPException {
FONode mockParent = mock(FONode.class);
Root mockRoot = mock(Root.class);

//Stub generic components
when(mockParent.getRoot()).thenReturn(mockRoot);
when(mockRoot.getLayoutMasterSet()).thenReturn(layoutMasterSet);

PageSequenceMaster psm = new PageSequenceMaster(mockParent, blockLevelEventProducer);
psm.startOfNode();

return psm;
}

}


+ 2
- 5
test/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternativesTestCase.java View File

@@ -25,21 +25,18 @@ import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.junit.Test;

import org.apache.fop.fo.Constants;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.expr.NumericProperty;
import org.apache.fop.fo.properties.Property;

import org.junit.Test;

/**
* Unit Test for RepeatablePageMasterAlternatives
*
*/
public class RepeatablePageMasterAlternativesTestCase
implements Constants {
public class RepeatablePageMasterAlternativesTestCase implements Constants {

/**
*

+ 1
- 0
test/java/org/apache/fop/fotreetest/FOTreeTestSuite.java View File

@@ -34,4 +34,5 @@ import org.junit.runners.Suite;
org.apache.fop.fo.DelegatingFOEventHandlerTestCase.class
})
public final class FOTreeTestSuite {

}

+ 118
- 0
test/java/org/apache/fop/layoutmgr/PageSequenceLayoutManagerTestCase.java View File

@@ -0,0 +1,118 @@
/*
* 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 static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import org.apache.fop.area.AreaTreeHandler;
import org.apache.fop.area.PageViewport;
import org.apache.fop.fo.pagination.Flow;
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 {

private static final String MAIN_FLOW_NAME = "main";
private static final String EMPTY_FLOW_NAME = "empty";

/**
* Blank pages can be created from empty pages
*
* @throws Exception
*/
@Test
public void testGetNextPageBlank() throws Exception {

final Page expectedPage = createPageForRegionName(EMPTY_FLOW_NAME);
final Page[] providedPages = new Page[]{expectedPage};

testGetNextPage(providedPages, expectedPage, true);
}

/**
* Empty pages should not be provided by the PageSequenceLayoutManager
* to layout the main flow
*
* @throws Exception
*/
@Test
public void testGetNextPageFirstEmpty() throws Exception {

final Page emptyPage = createPageForRegionName(EMPTY_FLOW_NAME);
final Page expectedPage = createPageForRegionName(MAIN_FLOW_NAME);
final Page[] providedPages = new Page[]{emptyPage, expectedPage};

testGetNextPage(providedPages, expectedPage, false);
}

private void testGetNextPage(final Page[] providedPages, Page expectedPage, boolean isBlank) {

final Flow flow = mock(Flow.class);
final PageSequence pseq = mock(PageSequence.class);
final Root root = mock(Root.class);
final AreaTreeHandler ath = mock(AreaTreeHandler.class);

when(flow.getFlowName()).thenReturn(MAIN_FLOW_NAME);
when(pseq.getMainFlow()).thenReturn(flow);
when(pseq.getRoot()).thenReturn(root);

PageSequenceLayoutManager sut = new PageSequenceLayoutManager(ath, pseq) {

@Override
protected Page createPage(int i, boolean b) {
return providedPages[i - 1];
}

@Override
protected void finishPage() {
//nop
}

// Expose the protected method for testing
public Page makeNewPage(boolean isBlank) {
return super.makeNewPage(isBlank);
}
};

assertEquals(expectedPage, sut.makeNewPage(isBlank));
}


private static Page createPageForRegionName(final String regionName) {
final Page page = mock(Page.class);
final SimplePageMaster spm = mock(SimplePageMaster.class);
final PageViewport pageViewport = mock(PageViewport.class);
final Region region = mock(Region.class);

when(page.getSimplePageMaster()).thenReturn(spm);
when(page.getPageViewport()).thenReturn(pageViewport);
when(spm.getRegion(anyInt())).thenReturn(region);

when(region.getRegionName()).thenReturn(regionName);

return page;
}
}

+ 65
- 0
test/layoutengine/standard-testcases/simple-page-master_unmapped_flow-name.xml View File

@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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$ -->
<testcase>
<info>
<p>
This test checks that when the region-body@region-name of simple-page-master does not map to
the main flow, the PageSequenceLayoutManager treats the generated Page as an empty/blank page.
</p>
</info>
<fo>
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<fo:layout-master-set>
<fo:simple-page-master master-name="main" page-width="2in" page-height="2in">
<fo:region-body region-name="main" margin-top="1in"/>
<fo:region-before region-name="main-before"/>
</fo:simple-page-master>
<fo:simple-page-master master-name="empty" page-width="2in" page-height="2in">
<fo:region-body region-name="empty"/>
<fo:region-before region-name="empty-before" />
</fo:simple-page-master>
<fo:page-sequence-master master-name="psm">
<fo:repeatable-page-master-reference maximum-repeats="1" master-reference="main"/>
<fo:repeatable-page-master-reference maximum-repeats="1" master-reference="empty"/>
<fo:repeatable-page-master-reference master-reference="main"/>
</fo:page-sequence-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="psm">

<fo:static-content flow-name="main-before">
<fo:block background-color="#afa">main <fo:page-number/></fo:block>
</fo:static-content>

<fo:static-content flow-name="empty-before">
<fo:block background-color="#faa">empty <fo:page-number/></fo:block>
</fo:static-content>

<fo:flow flow-name="main">
<fo:block break-before="page">flow 1</fo:block>
<fo:block break-before="page">flow 2</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
</fo>
<checks>
<eval expected="main-before" xpath="//pageViewport[1]//regionBefore/@name"/>
<eval expected="empty-before" xpath="//pageViewport[2]//regionBefore/@name"/>
<eval expected="main-before" xpath="//pageViewport[3]//regionBefore/@name"/>
</checks>
</testcase>

Loading…
Cancel
Save