// private CommonAccessibility commonAccessibility;
// End of property values
- private FONode currentlyVisibleMultiCase;
private String autoToggle;
- private String fittingStrategy;
/**
* Base constructor
return FO_MULTI_SWITCH;
}
- public void setCurrentlyVisibleNode(FONode node) {
- currentlyVisibleMultiCase = node;
- }
-
- public FONode getCurrentlyVisibleNode() {
- return currentlyVisibleMultiCase;
- }
-
- public String getFittingStrategy() {
- return fittingStrategy;
- }
-
public String getAutoToggle() {
return autoToggle;
}
+++ /dev/null
-/*
- * 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 java.util.LinkedList;
-import java.util.List;
-
-import org.apache.fop.layoutmgr.BestFitPenalty.Variant;
-
-/**
- * Utility class used in {@link MultiSwitchLayoutManager}
- * to handle the <i>best-fit</i> property value if specified in {@link org.apache.fop.fo.flow.MultiSwitch}
- */
-public final class BestFitLayoutUtils {
-
- private BestFitLayoutUtils() { }
-
- static class BestFitPosition extends Position {
-
- private List<ListElement> knuthList;
-
- public BestFitPosition(LayoutManager lm) {
- super(lm);
- }
-
- public List<Position> getPositionList() {
- List<Position> positions = new LinkedList<Position>();
- if (knuthList != null && !knuthList.isEmpty()) {
- SpaceResolver.performConditionalsNotification(knuthList, 0, knuthList.size() - 1, -1);
- for (ListElement el : knuthList) {
- if (el.getPosition() != null) {
- positions.add(el.getPosition());
- }
- }
- }
- return positions;
- }
-
- public void setKnuthList(List<ListElement> knuthList) {
- this.knuthList = knuthList;
- }
-
- }
-
- public static List<ListElement> getKnuthList(LayoutManager lm,
- List<List<ListElement>> childrenLists) {
-
- List<ListElement> knuthList = new LinkedList<ListElement>();
-
- BestFitPenalty bestFitPenalty = new BestFitPenalty(new BestFitPosition(lm));
- for (List<ListElement> childList : childrenLists) {
- // TODO Doing space resolution here is not correct.
- // Space elements will simply be nulled.
- SpaceResolver.resolveElementList(childList);
- int contentLength = ElementListUtils.calcContentLength(childList);
- bestFitPenalty.addVariant(new Variant(childList, contentLength));
- }
- // TODO Adding two enclosing boxes is definitely a dirty hack.
- // The first box forces the breaking algorithm to consider the penalty
- // in case there are no elements preceding it
- // and the last box prevents the glue and penalty from getting deleted
- // when they are at the end of the Knuth list.
- knuthList.add(new KnuthBox(0, new Position(lm), false));
- knuthList.add(bestFitPenalty);
- knuthList.add(new KnuthBox(0, new Position(lm), false));
- return knuthList;
- }
-
- public static List<Position> getPositionList(LayoutManager lm, PositionIterator posIter) {
-
- // "unwrap" the NonLeafPositions stored in parentIter
- // and put them in a new list;
- LinkedList<Position> positionList = new LinkedList<Position>();
- while (posIter.hasNext()) {
- Position pos = posIter.next();
- if (pos instanceof BestFitPosition) {
- positionList.addAll(((BestFitPosition) pos).getPositionList());
- } else {
- positionList.add(pos);
- }
- }
- return positionList;
- }
-
-}
+++ /dev/null
-/*
- * 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.apache.fop.layoutmgr.BestFitLayoutUtils.BestFitPosition;
-
-/**
- * A type of penalty used to specify a set of alternatives for the layout engine
- * to choose from. The chosen alternative must have an occupied size
- * less than the remaining BPD on the active page.
- */
-public class BestFitPenalty extends KnuthPenalty {
-
- public static class Variant {
-
- public final List<ListElement> knuthList;
- public final int width;
- public int penaltyIndex;
-
- public Variant(List<ListElement> knuthList, int width) {
- this.knuthList = knuthList;
- this.width = width;
- this.penaltyIndex = -1;
- }
-
- public KnuthElement toPenalty() {
- return new KnuthPenalty(width, 0, false, null, false);
- }
-
- }
-
- private final BestFitPosition bestFitPosition;
- private final List<Variant> variantList;
-
- public BestFitPenalty(BestFitPosition pos) {
- super(0, 0, false, pos, false);
- this.bestFitPosition = pos;
- variantList = new ArrayList<Variant>();
- }
-
- public void addVariant(Variant variant) {
- variantList.add(variant);
- }
-
- public void setActiveVariant(Variant bestVariant) {
- bestFitPosition.setKnuthList(bestVariant.knuthList);
- }
-
- public List<Variant> getVariants() {
- return variantList;
- }
-
- @Override
- public String toString() {
- String str = super.toString();
- StringBuffer buffer = new StringBuffer(64);
- buffer.append(" number of variants = " + variantList.size());
- return str + buffer.toString();
- }
-
-}
@Override
public void make(FONode node, List lms) {
- MultiSwitch multiSwitch = (MultiSwitch) node;
- MultiSwitchLayoutManager mslm = new MultiSwitchLayoutManager(multiSwitch);
- FONode multiCase = multiSwitch.getCurrentlyVisibleNode();
- if (multiCase != null) {
- FONodeIterator childIter = multiCase.getChildNodes();
- while (childIter.hasNext()) {
- FONode child = (FONode) childIter.next();
- makeLayoutManagers(child, lms);
- }
- } else {
- lms.add(mslm);
- }
+ MultiSwitchLayoutManager mslm = new MultiSwitchLayoutManager((MultiSwitch) node);
+ lms.add(mslm);
}
}
package org.apache.fop.layoutmgr;
-import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.apache.fop.area.Area;
import org.apache.fop.fo.FObj;
+import org.apache.fop.fo.flow.MultiSwitch;
public class MultiSwitchLayoutManager extends BlockStackingLayoutManager {
+ static class WhitespaceManagementPosition extends Position {
+
+ private List<ListElement> knuthList;
+
+ public WhitespaceManagementPosition(LayoutManager lm) {
+ super(lm);
+ }
+
+ public List<Position> getPositionList() {
+ List<Position> positions = new LinkedList<Position>();
+ if (knuthList != null && !knuthList.isEmpty()) {
+ SpaceResolver.performConditionalsNotification(knuthList, 0, knuthList.size() - 1, -1);
+ for (ListElement el : knuthList) {
+ if (el.getPosition() != null) {
+ positions.add(el.getPosition());
+ }
+ }
+ }
+ return positions;
+ }
+
+ public void setKnuthList(List<ListElement> knuthList) {
+ this.knuthList = knuthList;
+ }
+
+ public List<ListElement> getKnuthList() {
+ return knuthList;
+ }
+
+ }
+
+ private interface KnuthElementsGenerator {
+ List<ListElement> getKnuthElement(LayoutContext context, int alignment);
+ }
+
+ private class DefaultKnuthListGenerator implements KnuthElementsGenerator {
+
+ public List<ListElement> getKnuthElement(LayoutContext context, int alignment) {
+
+ List<ListElement> knuthList = new LinkedList<ListElement>();
+ LayoutManager childLM;
+ while ((childLM = getChildLM()) != null) {
+ if (!childLM.isFinished()) {
+ LayoutContext childLC = makeChildLayoutContext(context);
+ List childElements = childLM.getNextKnuthElements(childLC, alignment);
+ if (childElements != null) {
+ List<ListElement> newList = new LinkedList<ListElement>();
+ wrapPositionElements(childElements, newList);
+ knuthList.addAll(newList);
+ }
+ }
+ }
+ return knuthList;
+ }
+
+ }
+
+ private class WhitespaceManagement implements KnuthElementsGenerator {
+
+ public List<ListElement> getKnuthElement(LayoutContext context, int alignment) {
+
+ MultiSwitchLayoutManager mslm = MultiSwitchLayoutManager.this;
+ List<ListElement> knuthList = new LinkedList<ListElement>();
+ WhitespaceManagementPenalty penalty = new WhitespaceManagementPenalty(
+ new WhitespaceManagementPosition(mslm));
+ LayoutManager childLM;
+ while ((childLM = getChildLM()) != null) {
+ if (!childLM.isFinished()) {
+ LayoutContext childLC = makeChildLayoutContext(context);
+ List childElements = childLM.getNextKnuthElements(childLC, alignment);
+ if (childElements != null) {
+ List<ListElement> newList = new LinkedList<ListElement>();
+ wrapPositionElements(childElements, newList);
+ // TODO Doing space resolution here is wrong.
+ SpaceResolver.resolveElementList(newList);
+ int contentLength = ElementListUtils.calcContentLength(newList);
+ penalty.addVariant(penalty.new Variant(newList, contentLength));
+ }
+ }
+ }
+ // Prevent the penalty from being ignored if it is at the beginning of the content
+ knuthList.add(new KnuthBox(0, new Position(mslm), false));
+ knuthList.add(penalty);
+ // Prevent the penalty from being ignored if it is at the end of the content
+ knuthList.add(new KnuthBox(0, new Position(mslm), false));
+ return knuthList;
+ }
+
+ }
+
+ private KnuthElementsGenerator knuthGen;
+
public MultiSwitchLayoutManager(FObj node) {
super(node);
+ MultiSwitch multiSwitchNode = (MultiSwitch) node;
+ if (multiSwitchNode.getAutoToggle().equals("best-fit")) {
+ knuthGen = new WhitespaceManagement();
+ } else {
+ knuthGen = new DefaultKnuthListGenerator();
+ }
}
@Override
public List<ListElement> getNextKnuthElements(LayoutContext context, int alignment) {
-
referenceIPD = context.getRefIPD();
- List<List<ListElement>> childrenLists = new ArrayList<List<ListElement>>();
- LayoutManager childLM;
- while ((childLM = getChildLM()) != null) {
- if (!childLM.isFinished()) {
- LayoutContext childLC = makeChildLayoutContext(context);
- List childElements = childLM.getNextKnuthElements(childLC, alignment);
- if (childElements != null) {
- List<ListElement> newList = new LinkedList<ListElement>();
- wrapPositionElements(childElements, newList);
- childrenLists.add(newList);
- }
- }
- }
+ List<ListElement> knuthList = knuthGen.getKnuthElement(context, alignment);
setFinished(true);
- return BestFitLayoutUtils.getKnuthList(this, childrenLists);
+ return knuthList;
}
@Override
@Override
public void addAreas(PositionIterator posIter, LayoutContext context) {
-
- List<Position> positionList = BestFitLayoutUtils.getPositionList(this, posIter);
- PositionIterator newPosIter = new PositionIterator(
- positionList.listIterator());
-
+ LinkedList<Position> positionList = new LinkedList<Position>();
+ while (posIter.hasNext()) {
+ Position pos = posIter.next();
+ if (pos instanceof WhitespaceManagementPosition) {
+ positionList.addAll(((WhitespaceManagementPosition) pos).getPositionList());
+ } else {
+ positionList.add(pos);
+ }
+ }
+ PositionIterator newPosIter = new PositionIterator(positionList.listIterator());
AreaAdditionUtil.addAreas(this, newPosIter, context);
flush();
}
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FObj;
import org.apache.fop.layoutmgr.AbstractBreaker.PageBreakPosition;
-import org.apache.fop.layoutmgr.BestFitPenalty.Variant;
+import org.apache.fop.layoutmgr.WhitespaceManagementPenalty.Variant;
import org.apache.fop.traits.MinOptMax;
import org.apache.fop.util.ListUtil;
/** Index of the last inserted element of the last inserted footnote. */
public int footnoteElementIndex;
- /** Current active variant attached to this node */
- public final Variant variant;
- /** Pending variant to be assigned to all descending nodes */
- public Variant pendingVariant;
+ /**
+ * Pending variants of dynamic contents that were evaluated WRT this node.
+ * When computing page difference for a break element, the total width of these variants
+ * will be added to 'actualWidth'.
+ */
+ private final List<Variant> pendingVariants = new ArrayList<Variant>();
+ private int totalVariantsWidth;
public KnuthPageNode(int position,
int line, int fitness,
int insertedFootnotes, int totalFootnotes,
int footnoteListIndex, int footnoteElementIndex,
double adjustRatio, int availableShrink, int availableStretch,
- int difference, double totalDemerits, KnuthNode previous, Variant variant) {
+ int difference, double totalDemerits, KnuthNode previous) {
super(position, line, fitness,
totalWidth, totalStretch, totalShrink,
adjustRatio, availableShrink, availableStretch,
this.insertedFootnotes = insertedFootnotes;
this.footnoteListIndex = footnoteListIndex;
this.footnoteElementIndex = footnoteElementIndex;
- this.variant = variant;
+ }
+
+ public void addVariant(Variant variant) {
+ pendingVariants.add(variant);
+ totalVariantsWidth += variant.width;
}
}
private final int[] bestTotalFootnotesLength = new int[4];
private final int[] bestFootnoteListIndex = new int[4];
private final int[] bestFootnoteElementIndex = new int[4];
- private final Variant[] bestVariant = new Variant[4];
@Override
public void addRecord(double demerits, KnuthNode node, double adjust,
bestTotalFootnotesLength[fitness] = totalFootnotesLength;
bestFootnoteListIndex[fitness] = footnoteListIndex;
bestFootnoteElementIndex[fitness] = footnoteElementIndex;
- bestVariant[fitness] = ((KnuthPageNode) node).pendingVariant;
}
public int getInsertedFootnotesLength(int fitness) {
return bestFootnoteElementIndex[fitness];
}
- public Variant getVariant(int fitness) {
- return bestVariant[fitness];
- }
}
insertedFootnotesLength, totalFootnotesLength,
footnoteListIndex, footnoteElementIndex,
adjustRatio, availableShrink, availableStretch,
- difference, totalDemerits, previous,
- (previous != null) ? ((KnuthPageNode)previous).pendingVariant : null);
+ difference, totalDemerits, previous);
}
/** {@inheritDoc} */
((BestPageRecords) best).getFootnoteElementIndex(fitness),
best.getAdjust(fitness), best.getAvailableShrink(fitness),
best.getAvailableStretch(fitness), best.getDifference(fitness),
- best.getDemerits(fitness), best.getNode(fitness),
- ((BestPageRecords) best).getVariant(fitness));
+ best.getDemerits(fitness), best.getNode(fitness));
}
/**
int actualWidth = totalWidth - pageNode.totalWidth;
int footnoteSplit;
boolean canDeferOldFN;
- if (element.isPenalty()) {
- if (element instanceof BestFitPenalty) {
- actualWidth += handleBestFitPenalty(pageNode, (BestFitPenalty) element, elementIndex);
- } else {
- actualWidth += element.getWidth();
- if (pageNode.pendingVariant != null) {
- actualWidth += ((KnuthPageNode) activeNode).pendingVariant.width;
- }
- }
+ actualWidth += pageNode.totalVariantsWidth;
+ if (element instanceof WhitespaceManagementPenalty) {
+ actualWidth += handleWhitespaceManagementPenalty(pageNode,
+ (WhitespaceManagementPenalty) element, elementIndex);
+ } else if (element.isPenalty()) {
+ actualWidth += element.getWidth();
}
if (footnotesPending) {
// compute the total length of the footnotes not yet inserted
}
}
- private int handleBestFitPenalty(KnuthPageNode activeNode, BestFitPenalty penalty, int elementIndex) {
+ /**
+ * Evaluates the variants corresponding to the given penalty until one that
+ * leads to an acceptable adjustment ratio is found. That variant will
+ * be added to the list of pending variants in the given active node.
+ */
+ private int handleWhitespaceManagementPenalty(KnuthPageNode activeNode,
+ WhitespaceManagementPenalty penalty, int elementIndex) {
for (Variant var : penalty.getVariants()) {
int difference = computeDifference(activeNode, var.toPenalty(), elementIndex);
double r = computeAdjustmentRatio(activeNode, difference);
if (r >= -1.0) {
- var.penaltyIndex = elementIndex;
- activeNode.pendingVariant = var;
+ activeNode.addVariant(var);
return var.width;
}
}
// ? bestActiveNode.difference : bestActiveNode.difference + fillerMinWidth;
// Check if the given node has an attached variant of a dynamic content
KnuthPageNode pageNode = (KnuthPageNode) bestActiveNode;
- if (pageNode.variant != null) {
- BestFitPenalty penalty = (BestFitPenalty) par.get(pageNode.variant.penaltyIndex);
- penalty.setActiveVariant(pageNode.variant);
+ KnuthPageNode previousPageNode = ((KnuthPageNode) pageNode.previous);
+ for (Variant var : previousPageNode.pendingVariants) {
+ WhitespaceManagementPenalty penalty = var.getBestFitPenalty();
+ int penaltyIndex = this.par.indexOf(penalty);
+ // Make sure penalty is inside the range of the current page node
+ if (penaltyIndex <= pageNode.position) {
+ penalty.setActiveVariant(var);
+ }
}
int difference = bestActiveNode.difference;
if (difference + bestActiveNode.availableShrink < 0) {
--- /dev/null
+/*
+ * 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.apache.fop.layoutmgr.MultiSwitchLayoutManager.WhitespaceManagementPosition;
+
+/**
+ * A special penalty used to specify content having multiple variants. At most
+ * only one variant will be inserted into the final document. If none of the
+ * variants fit into the remaining space on the current page, the dynamic
+ * content will be completely ignored.
+ */
+public class WhitespaceManagementPenalty extends KnuthPenalty {
+
+ public class Variant {
+
+ public final List<ListElement> knuthList;
+ public final int width;
+
+ public Variant(List<ListElement> knuthList, int width) {
+ this.knuthList = knuthList;
+ this.width = width;
+ }
+
+ public KnuthElement toPenalty() {
+ return new KnuthPenalty(width, 0, false, null, false);
+ }
+
+ public WhitespaceManagementPenalty getBestFitPenalty() {
+ return WhitespaceManagementPenalty.this;
+ }
+
+ }
+
+ private final WhitespaceManagementPosition whitespaceManagementPosition;
+ private final List<Variant> variantList;
+
+ public WhitespaceManagementPenalty(WhitespaceManagementPosition pos) {
+ super(0, 0, false, pos, false);
+ this.whitespaceManagementPosition = pos;
+ variantList = new ArrayList<Variant>();
+ }
+
+ public void addVariant(Variant variant) {
+ variantList.add(variant);
+ }
+
+ public void setActiveVariant(Variant bestVariant) {
+ whitespaceManagementPosition.setKnuthList(bestVariant.knuthList);
+ }
+
+ public boolean isActivated() {
+ return whitespaceManagementPosition.getKnuthList() != null;
+ }
+
+ public List<Variant> getVariants() {
+ return variantList;
+ }
+
+ @Override
+ public String toString() {
+ String str = super.toString();
+ StringBuffer buffer = new StringBuffer(64);
+ buffer.append(" number of variants = " + variantList.size());
+ return str + buffer.toString();
+ }
+
+}
--- /dev/null
+<?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.
+-->
+<testcase>
+ <info>
+ <p>
+ Test that multiple whitespace managment elements on the same page are handled nicely.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xmlns:fox="http://xmlgraphics.apache.org/fop/extensions" font-size="8pt" line-height="10pt">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="page"
+ page-height="70pt" page-width="220pt" margin="10pt">
+ <fo:region-body background-color="#F0F0F0"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="page">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:multi-switch fox:auto-toggle="best-fit">
+ <fo:multi-case>
+ <fo:block>MS 1 Variant 1</fo:block>
+ </fo:multi-case>
+ </fo:multi-switch>
+ <fo:multi-switch fox:auto-toggle="best-fit">
+ <fo:multi-case>
+ <fo:block>MS 2 Variant 1</fo:block>
+ </fo:multi-case>
+ </fo:multi-switch>
+ <fo:multi-switch fox:auto-toggle="best-fit">
+ <fo:multi-case>
+ <fo:block>MS 3 Variant 1</fo:block>
+ </fo:multi-case>
+ </fo:multi-switch>
+ </fo:flow>
+ </fo:page-sequence>
+
+ <fo:page-sequence master-reference="page">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:multi-switch fox:auto-toggle="best-fit">
+ <fo:multi-case>
+ <fo:block line-height="70pt">MS 1 Variant 1</fo:block>
+ </fo:multi-case>
+ </fo:multi-switch>
+ <fo:multi-switch fox:auto-toggle="best-fit">
+ <fo:multi-case>
+ <fo:block>MS 2 Variant 1</fo:block>
+ </fo:multi-case>
+ </fo:multi-switch>
+ <fo:multi-switch fox:auto-toggle="best-fit">
+ <fo:multi-case>
+ <fo:block line-height="50pt">MS 3 Variant 1</fo:block>
+ </fo:multi-case>
+ <fo:multi-case>
+ <fo:block>MS 3 Variant 2</fo:block>
+ </fo:multi-case>
+ </fo:multi-switch>
+ <fo:multi-switch fox:auto-toggle="best-fit">
+ <fo:multi-case>
+ <fo:block>MS 4 Variant 1</fo:block>
+ </fo:multi-case>
+ </fo:multi-switch>
+ </fo:flow>
+ </fo:page-sequence>
+
+ </fo:root>
+ </fo>
+ <checks>
+ <!-- 1. 3 multi-switch on the same page -->
+ <eval expected="1" xpath="count(//pageSequence[1]/pageViewport)"/>
+ <eval expected="3" xpath="count(//pageSequence[1]/pageViewport[1]//flow/block)"/>
+ <eval expected="MS 1 Variant 1" xpath="//pageSequence[1]/pageViewport[1]//flow/block[1]"/>
+ <eval expected="MS 2 Variant 1" xpath="//pageSequence[1]/pageViewport[1]//flow/block[2]"/>
+ <eval expected="MS 3 Variant 1" xpath="//pageSequence[1]/pageViewport[1]//flow/block[3]"/>
+
+ <!-- 2. 4 multi-switch, one cannot fit, one uses the 2nd variant -->
+ <eval expected="1" xpath="count(//pageSequence[2]/pageViewport)"/>
+ <eval expected="3" xpath="count(//pageSequence[2]/pageViewport[1]//flow/block)"/>
+ <eval expected="MS 2 Variant 1" xpath="//pageSequence[2]/pageViewport[1]//flow/block[1]"/>
+ <eval expected="MS 3 Variant 2" xpath="//pageSequence[2]/pageViewport[1]//flow/block[2]"/>
+ <eval expected="MS 4 Variant 1" xpath="//pageSequence[2]/pageViewport[1]//flow/block[3]"/>
+ </checks>
+</testcase>