From 698dd5fa4d20d372211770ece51c4634b75c24f7 Mon Sep 17 00:00:00 2001 From: Simon Steiner Date: Wed, 25 Mar 2020 14:44:35 +0000 Subject: [PATCH] FOP-2925: Change in IPD on empty block NPE git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1875645 13f79535-47bb-0310-9956-ffa450edef68 --- .../src/main/java/org/apache/fop/fo/FObj.java | 10 ++ .../apache/fop/layoutmgr/AbstractBreaker.java | 119 ++-------------- .../layoutmgr/BlockStackingLayoutManager.java | 3 + .../org/apache/fop/layoutmgr/RestartAtLM.java | 133 ++++++++++++++++++ .../flow_changing-ipd_last-page_15.xml | 61 ++++++++ 5 files changed, 219 insertions(+), 107 deletions(-) create mode 100644 fop-core/src/main/java/org/apache/fop/layoutmgr/RestartAtLM.java create mode 100755 fop/test/layoutengine/standard-testcases/flow_changing-ipd_last-page_15.xml diff --git a/fop-core/src/main/java/org/apache/fop/fo/FObj.java b/fop-core/src/main/java/org/apache/fop/fo/FObj.java index f57e59bd1..d938eded0 100644 --- a/fop-core/src/main/java/org/apache/fop/fo/FObj.java +++ b/fop-core/src/main/java/org/apache/fop/fo/FObj.java @@ -78,6 +78,8 @@ public abstract class FObj extends FONode implements Constants { private String layer; // End of property values + private boolean forceKeepTogether; + /** * Create a new formatting object. * @@ -775,6 +777,14 @@ public abstract class FObj extends FONode implements Constants { return (super.toString() + "[@id=" + this.id + "]"); } + public boolean isForceKeepTogether() { + return forceKeepTogether; + } + + public void setForceKeepTogether(boolean b) { + forceKeepTogether = b; + } + /** Basic {@link FONode.FONodeIterator} implementation */ public static class FObjIterator implements FONodeIterator { diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/AbstractBreaker.java index d57148290..625b520ed 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/AbstractBreaker.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/AbstractBreaker.java @@ -19,16 +19,12 @@ package org.apache.fop.layoutmgr; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.fop.events.EventBroadcaster; import org.apache.fop.fo.Constants; import org.apache.fop.layoutmgr.BreakingAlgorithm.KnuthNode; import org.apache.fop.traits.MinOptMax; @@ -42,9 +38,9 @@ public abstract class AbstractBreaker { /** logging instance */ protected static final Log log = LogFactory.getLog(AbstractBreaker.class); - private LayoutManager originalRestartAtLM; - private Position positionAtBreak; - private List firstElementsForRestart; + protected LayoutManager originalRestartAtLM; + protected Position positionAtBreak; + protected List firstElementsForRestart; protected PageSequenceLayoutManager pslm; /** @@ -433,12 +429,16 @@ public abstract class AbstractBreaker { } firstElementsForRestart = null; - LayoutManager restartAtLM = getRestartAtLM(alg, ipdChangesOnNextPage, onLastPageAndIPDChanges, - visitedBefore, blockList, 1); + RestartAtLM restartAtLMClass = new RestartAtLM(); + LayoutManager restartAtLM = restartAtLMClass.getRestartAtLM(this, alg, ipdChangesOnNextPage, + onLastPageAndIPDChanges, visitedBefore, blockList, 1); + if (restartAtLMClass.invalidPosition) { + return false; + } if (restartAtLM == null || restartAtLM.getChildLMs().isEmpty()) { firstElementsForRestart = null; - LayoutManager restartAtLM2 = getRestartAtLM(alg, ipdChangesOnNextPage, onLastPageAndIPDChanges, - visitedBefore, blockList, 0); + LayoutManager restartAtLM2 = new RestartAtLM().getRestartAtLM(this, alg, ipdChangesOnNextPage, + onLastPageAndIPDChanges, visitedBefore, blockList, 0); if (restartAtLM2 != null) { restartAtLM = restartAtLM2; } @@ -465,101 +465,6 @@ public abstract class AbstractBreaker { return true; } - private LayoutManager getRestartAtLM(PageBreakingAlgorithm alg, boolean ipdChangesOnNextPage, - boolean onLastPageAndIPDChanges, boolean visitedBefore, - BlockSequence blockList, int start) { - KnuthNode optimalBreak = ipdChangesOnNextPage ? alg.getBestNodeBeforeIPDChange() : alg - .getBestNodeForLastPage(); - if (onLastPageAndIPDChanges && visitedBefore && this.originalRestartAtLM == null) { - optimalBreak = null; - } - int positionIndex = findPositionIndex(optimalBreak, alg, start); - if (ipdChangesOnNextPage || (positionAtBreak != null && positionAtBreak.getIndex() > -1)) { - firstElementsForRestart = Collections.EMPTY_LIST; - if (ipdChangesOnNextPage) { - if (containsNonRestartableLM(positionAtBreak)) { - if (alg.getIPDdifference() > 0) { - EventBroadcaster eventBroadcaster = getCurrentChildLM().getFObj() - .getUserAgent().getEventBroadcaster(); - BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider - .get(eventBroadcaster); - eventProducer.nonRestartableContentFlowingToNarrowerPage(this); - } - firstElementsForRestart = new LinkedList(); - boolean boxFound = false; - Iterator iter = blockList.listIterator(positionIndex + 1); - Position position = null; - while (iter.hasNext() - && (position == null || containsNonRestartableLM(position))) { - positionIndex++; - KnuthElement element = (KnuthElement) iter.next(); - position = element.getPosition(); - if (element.isBox()) { - boxFound = true; - firstElementsForRestart.add(element); - } else if (boxFound) { - firstElementsForRestart.add(element); - } - } - if (position instanceof SpaceResolver.SpaceHandlingBreakPosition) { - /* Retrieve the original position wrapped into this space position */ - positionAtBreak = position.getPosition(); - } else { - positionAtBreak = null; - } - } - } - } - LayoutManager restartAtLM = null; - if (ipdChangesOnNextPage || !(positionAtBreak != null && positionAtBreak.getIndex() > -1)) { - if (positionAtBreak != null && positionAtBreak.getIndex() == -1) { - Position position; - Iterator iter = blockList.listIterator(positionIndex + 1); - do { - KnuthElement nextElement = (KnuthElement) iter.next(); - position = nextElement.getPosition(); - } while (position == null - || position instanceof SpaceResolver.SpaceHandlingPosition - || position instanceof SpaceResolver.SpaceHandlingBreakPosition - && position.getPosition().getIndex() == -1); - LayoutManager surroundingLM = positionAtBreak.getLM(); - while (position.getLM() != surroundingLM) { - position = position.getPosition(); - } - restartAtLM = position.getPosition().getLM(); - } - if (onLastPageAndIPDChanges && restartAtLM != null) { - if (originalRestartAtLM == null) { - originalRestartAtLM = restartAtLM; - } else { - restartAtLM = originalRestartAtLM; - } - firstElementsForRestart = Collections.EMPTY_LIST; - } - } - if (onLastPageAndIPDChanges && !visitedBefore && positionAtBreak.getPosition() != null) { - restartAtLM = positionAtBreak.getPosition().getLM(); - } - return restartAtLM; - } - - private int findPositionIndex(KnuthNode optimalBreak, PageBreakingAlgorithm alg, int start) { - int positionIndex = (optimalBreak != null) ? optimalBreak.position : start; - for (int i = positionIndex; i < alg.par.size(); i++) { - KnuthElement elementAtBreak = alg.getElement(i); - if (elementAtBreak.getPosition() == null) { - elementAtBreak = alg.getElement(0); - } - positionAtBreak = elementAtBreak.getPosition(); - /* Retrieve the original position wrapped into this space position */ - positionAtBreak = positionAtBreak.getPosition(); - if (positionAtBreak != null) { - return i; - } - } - return positionIndex; - } - /** * Returns {@code true} if the given position or one of its descendants * corresponds to a non-restartable LM. @@ -567,7 +472,7 @@ public abstract class AbstractBreaker { * @param position a position * @return {@code true} if there is a non-restartable LM in the hierarchy */ - private boolean containsNonRestartableLM(Position position) { + protected boolean containsNonRestartableLM(Position position) { LayoutManager lm = position.getLM(); if (lm != null && !lm.isRestartable()) { return true; diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java index 3e8bd992b..4dddbcf11 100644 --- a/fop-core/src/main/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java @@ -833,6 +833,9 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager public Keep getKeepTogether() { Keep keep = Keep.getKeep(getKeepTogetherProperty()); keep = keep.compare(getParentKeepTogether()); + if (getFObj().isForceKeepTogether()) { + keep = Keep.KEEP_ALWAYS; + } return keep; } diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/RestartAtLM.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/RestartAtLM.java new file mode 100644 index 000000000..9c8aa8252 --- /dev/null +++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/RestartAtLM.java @@ -0,0 +1,133 @@ +/* + * 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.Collections; +import java.util.Iterator; +import java.util.LinkedList; + +import org.apache.fop.events.EventBroadcaster; + +/** + * Class to find the restart layoutmanager for changing IPD + */ +class RestartAtLM { + protected boolean invalidPosition; + + protected LayoutManager getRestartAtLM(AbstractBreaker breaker, PageBreakingAlgorithm alg, + boolean ipdChangesOnNextPage, boolean onLastPageAndIPDChanges, + boolean visitedBefore, AbstractBreaker.BlockSequence blockList, int start) { + BreakingAlgorithm.KnuthNode optimalBreak = ipdChangesOnNextPage ? alg.getBestNodeBeforeIPDChange() : alg + .getBestNodeForLastPage(); + if (onLastPageAndIPDChanges && visitedBefore && breaker.originalRestartAtLM == null) { + optimalBreak = null; + } + int positionIndex = findPositionIndex(breaker, optimalBreak, alg, start); + if (ipdChangesOnNextPage || (breaker.positionAtBreak != null && breaker.positionAtBreak.getIndex() > -1)) { + breaker.firstElementsForRestart = Collections.EMPTY_LIST; + if (ipdChangesOnNextPage) { + if (breaker.containsNonRestartableLM(breaker.positionAtBreak)) { + if (alg.getIPDdifference() > 0) { + EventBroadcaster eventBroadcaster = breaker.getCurrentChildLM().getFObj() + .getUserAgent().getEventBroadcaster(); + BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider + .get(eventBroadcaster); + eventProducer.nonRestartableContentFlowingToNarrowerPage(this); + } + breaker.firstElementsForRestart = new LinkedList(); + boolean boxFound = false; + Iterator iter = blockList.listIterator(positionIndex + 1); + Position position = null; + while (iter.hasNext() + && (position == null || breaker.containsNonRestartableLM(position))) { + positionIndex++; + KnuthElement element = (KnuthElement) iter.next(); + position = element.getPosition(); + if (element.isBox()) { + boxFound = true; + breaker.firstElementsForRestart.add(element); + } else if (boxFound) { + breaker.firstElementsForRestart.add(element); + } + } + if (position instanceof SpaceResolver.SpaceHandlingBreakPosition) { + /* Retrieve the original position wrapped into this space position */ + breaker.positionAtBreak = position.getPosition(); + } else { + breaker.positionAtBreak = null; + } + } + } + } + LayoutManager restartAtLM = null; + if (ipdChangesOnNextPage || !(breaker.positionAtBreak != null && breaker.positionAtBreak.getIndex() > -1)) { + if (breaker.positionAtBreak != null && breaker.positionAtBreak.getIndex() == -1) { + Position position; + Iterator iter = blockList.listIterator(positionIndex + 1); + do { + KnuthElement nextElement = (KnuthElement) iter.next(); + position = nextElement.getPosition(); + } while (position == null + || position instanceof SpaceResolver.SpaceHandlingPosition + || position instanceof SpaceResolver.SpaceHandlingBreakPosition + && position.getPosition().getIndex() == -1); + LayoutManager surroundingLM = breaker.positionAtBreak.getLM(); + while (position.getLM() != surroundingLM) { + position = position.getPosition(); + } + if (position.getPosition() == null) { + position.getLM().getFObj().setForceKeepTogether(true); + invalidPosition = true; + return null; + } + restartAtLM = position.getPosition().getLM(); + } + if (onLastPageAndIPDChanges && restartAtLM != null) { + if (breaker.originalRestartAtLM == null) { + breaker.originalRestartAtLM = restartAtLM; + } else { + restartAtLM = breaker.originalRestartAtLM; + } + breaker.firstElementsForRestart = Collections.EMPTY_LIST; + } + } + if (onLastPageAndIPDChanges && !visitedBefore && breaker.positionAtBreak.getPosition() != null) { + restartAtLM = breaker.positionAtBreak.getPosition().getLM(); + } + return restartAtLM; + } + + private int findPositionIndex(AbstractBreaker breaker, BreakingAlgorithm.KnuthNode optimalBreak, + PageBreakingAlgorithm alg, int start) { + int positionIndex = (optimalBreak != null) ? optimalBreak.position : start; + for (int i = positionIndex; i < alg.par.size(); i++) { + KnuthElement elementAtBreak = alg.getElement(i); + if (elementAtBreak.getPosition() == null) { + elementAtBreak = alg.getElement(0); + } + breaker.positionAtBreak = elementAtBreak.getPosition(); + /* Retrieve the original position wrapped into this space position */ + breaker.positionAtBreak = breaker.positionAtBreak.getPosition(); + if (breaker.positionAtBreak != null) { + return i; + } + } + return positionIndex; + } +} diff --git a/fop/test/layoutengine/standard-testcases/flow_changing-ipd_last-page_15.xml b/fop/test/layoutengine/standard-testcases/flow_changing-ipd_last-page_15.xml new file mode 100755 index 000000000..6640fdb42 --- /dev/null +++ b/fop/test/layoutengine/standard-testcases/flow_changing-ipd_last-page_15.xml @@ -0,0 +1,61 @@ + + + + + +

+ This test checks that the definition of a special page-master for the last page with a + different width that the previous "rest" page causes FOP to redo the line breaking layout. +

+
+ + + + + + + + + + + + + + + + + + + + + test + + + Step 2 – Finance + + If there is any Hire Purchase (HP) / Finance agreement associated with this vehicle, please contact the finance company to provide your authorisation for Bank Box to liaise with them in order to obtain the early settlement figure. + + + + + + + + + +
-- 2.39.5