/*
 * 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: PageSequence.java 1733431 2016-03-03 09:40:50Z gadams $ */

package org.apache.fop.fo.pagination;

import java.util.Locale;
import java.util.Map;
import java.util.Stack;

import org.xml.sax.Locator;

import org.apache.fop.apps.FOPException;
import org.apache.fop.complexscripts.bidi.DelimitedTextRange;
import org.apache.fop.datatypes.Numeric;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
import org.apache.fop.fo.properties.CommonHyphenation;
import org.apache.fop.traits.Direction;
import org.apache.fop.traits.WritingMode;
import org.apache.fop.traits.WritingModeTraits;
import org.apache.fop.traits.WritingModeTraitsGetter;

Class modelling the fo:page-sequence object.
/** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_page-sequence"> * <code>fo:page-sequence</code></a> object. */
public class PageSequence extends AbstractPageSequence implements WritingModeTraitsGetter { private String masterReference; private Numeric referenceOrientation; private WritingModeTraits writingModeTraits; private Locale locale; // There doesn't seem to be anything in the spec requiring flows // to be in the order given, only that they map to the regions // defined in the page sequence, so all we need is this one hashmap // the set of flows includes StaticContent flows also
Map of flows to their flow name (flow-name, Flow)
/** Map of flows to their flow name (flow-name, Flow) */
private Map<String, FONode> flowMap;
The currentSimplePageMaster is either the page master for the whole page sequence if master-reference refers to a simple-page-master, or the simple page master produced by the page sequence master otherwise. The pageSequenceMaster is null if master-reference refers to a simple-page-master.
/** * The currentSimplePageMaster is either the page master for the * whole page sequence if master-reference refers to a simple-page-master, * or the simple page master produced by the page sequence master otherwise. * The pageSequenceMaster is null if master-reference refers to a * simple-page-master. */
private SimplePageMaster simplePageMaster; private PageSequenceMaster pageSequenceMaster;
The fo:title object for this page-sequence.
/** * The fo:title object for this page-sequence. */
private Title titleFO;
The fo:flow object for this page-sequence.
/** * The fo:flow object for this page-sequence. */
private Flow mainFlow;
Create a PageSequence instance that is a child of the given FONode.
Params:
/** * Create a PageSequence instance that is a child of the * given {@link FONode}. * * @param parent the parent {@link FONode} */
public PageSequence(FONode parent) { super(parent); }
{@inheritDoc}
/** {@inheritDoc} */
public void bind(PropertyList pList) throws FOPException { super.bind(pList); String country = pList.get(PR_COUNTRY).getString(); String language = pList.get(PR_LANGUAGE).getString(); locale = CommonHyphenation.toLocale(language, country); masterReference = pList.get(PR_MASTER_REFERENCE).getString(); referenceOrientation = pList.get(PR_REFERENCE_ORIENTATION).getNumeric(); writingModeTraits = new WritingModeTraits( WritingMode.valueOf(pList.get(PR_WRITING_MODE).getEnum()), pList.getExplicit(PR_WRITING_MODE) != null); if (masterReference == null || masterReference.equals("")) { missingPropertyError("master-reference"); } }
{@inheritDoc}
/** {@inheritDoc} */
public void startOfNode() throws FOPException { super.startOfNode(); flowMap = new java.util.HashMap<String, FONode>(); this.simplePageMaster = getRoot().getLayoutMasterSet().getSimplePageMaster(masterReference); if (simplePageMaster == null) { this.pageSequenceMaster = getRoot().getLayoutMasterSet().getPageSequenceMaster(masterReference); if (pageSequenceMaster == null) { getFOValidationEventProducer().masterNotFound(this, getName(), masterReference, getLocator()); } } getFOEventHandler().startPageSequence(this); }
{@inheritDoc}
/** {@inheritDoc} */
public void endOfNode() throws FOPException { if (mainFlow == null) { missingChildElementError("(title?,static-content*,flow)"); } getFOEventHandler().endPageSequence(this); }
{@inheritDoc} XSL Content Model: (title?,static-content*,flow)
/** * {@inheritDoc} XSL Content Model: (title?,static-content*,flow) */
protected void validateChildNode(Locator loc, String nsURI, String localName) throws ValidationException { if (FO_URI.equals(nsURI)) { if ("title".equals(localName)) { if (titleFO != null) { tooManyNodesError(loc, "fo:title"); } else if (!flowMap.isEmpty()) { nodesOutOfOrderError(loc, "fo:title", "fo:static-content"); } else if (mainFlow != null) { nodesOutOfOrderError(loc, "fo:title", "fo:flow"); } } else if ("static-content".equals(localName)) { if (mainFlow != null) { nodesOutOfOrderError(loc, "fo:static-content", "fo:flow"); } } else if ("flow".equals(localName)) { if (mainFlow != null) { tooManyNodesError(loc, "fo:flow"); } } else { invalidChildError(loc, nsURI, localName); } } }
{@inheritDoc} TODO see if addChildNode() should also be called for fo's other than fo:flow.
/** * {@inheritDoc} * TODO see if addChildNode() should also be called for fo's other than * fo:flow. */
public void addChildNode(FONode child) throws FOPException { int childId = child.getNameId(); switch (childId) { case FO_TITLE: this.titleFO = (Title)child; break; case FO_FLOW: this.mainFlow = (Flow)child; addFlow(mainFlow); break; case FO_STATIC_CONTENT: addFlow((StaticContent)child); flowMap.put(((Flow)child).getFlowName(), (Flow)child); break; default: super.addChildNode(child); } }
Add a flow or static content, mapped by its flow-name. The flow-name is used to associate the flow with a region on a page, based on the region-names given to the regions in the page-master used to generate that page.
Params:
  • flow – the Flow instance to be added
Throws:
/** * Add a flow or static content, mapped by its flow-name. * The flow-name is used to associate the flow with a region on a page, * based on the region-names given to the regions in the page-master * used to generate that page. * @param flow the {@link Flow} instance to be added * @throws org.apache.fop.fo.ValidationException if the fo:flow maps * to an invalid page-region */
private void addFlow(Flow flow) throws ValidationException { String flowName = flow.getFlowName(); if (hasFlowName(flowName)) { getFOValidationEventProducer().duplicateFlowNameInPageSequence(this, flow.getName(), flowName, flow.getLocator()); } if (!hasRegion(flowName) && !flowName.equals("xsl-before-float-separator") && !flowName.equals("xsl-footnote-separator")) { getFOValidationEventProducer().flowNameNotMapped(this, flow.getName(), flowName, flow.getLocator()); } } private boolean hasRegion(String flowName) { LayoutMasterSet set = getRoot().getLayoutMasterSet(); PageSequenceMaster psm = set.getPageSequenceMaster(masterReference); return (psm != null) ? psm.getLayoutMasterSet().regionNameExists(flowName) : set.getSimplePageMaster(masterReference).regionNameExists(flowName); }
Get the static content FO node from the flow map. This gets the static content flow for the given flow name.
Params:
  • name – the flow name to find
Returns:the static content FO node
/** * Get the static content FO node from the flow map. * This gets the static content flow for the given flow name. * * @param name the flow name to find * @return the static content FO node */
public StaticContent getStaticContent(String name) { return (StaticContent) flowMap.get(name); }
Accessor method for the fo:title associated with this fo:page-sequence
Returns:titleFO for this object
/** * Accessor method for the fo:title associated with this fo:page-sequence * @return titleFO for this object */
public Title getTitleFO() { return titleFO; }
Public accessor for getting the MainFlow to which this PageSequence is attached.
Returns:the MainFlow object to which this PageSequence is attached.
/** * Public accessor for getting the MainFlow to which this PageSequence is * attached. * @return the MainFlow object to which this PageSequence is attached. */
public Flow getMainFlow() { return mainFlow; }
Determine if this PageSequence already has a flow with the given flow-name Used for validation of incoming fo:flow or fo:static-content objects
Params:
  • flowName – The flow-name to search for
Returns:true if flow-name already defined within this page sequence, false otherwise
/** * Determine if this PageSequence already has a flow with the given flow-name * Used for validation of incoming fo:flow or fo:static-content objects * @param flowName The flow-name to search for * @return true if flow-name already defined within this page sequence, * false otherwise */
public boolean hasFlowName(String flowName) { return flowMap.containsKey(flowName); }
Returns:the flow map for this page-sequence
/** @return the flow map for this page-sequence */
public Map<String, FONode> getFlowMap() { return this.flowMap; }
Public accessor for determining the next page master to use within this page sequence.
Params:
  • page – the page number of the page to be created
  • isFirstPage – indicator whether this page is the first page of the page sequence
  • isLastPage – indicator whether this page is the last page of the page sequence
  • isBlank – indicator whether the page will be blank
Throws:
Returns:the SimplePageMaster to use for this page
/** * Public accessor for determining the next page master to use within this page sequence. * @param page the page number of the page to be created * @param isFirstPage indicator whether this page is the first page of the * page sequence * @param isLastPage indicator whether this page is the last page of the * page sequence * @param isBlank indicator whether the page will be blank * @return the SimplePageMaster to use for this page * @throws PageProductionException if there's a problem determining the page master */
public SimplePageMaster getNextSimplePageMaster( int page, boolean isFirstPage, boolean isLastPage, boolean isBlank) throws PageProductionException { if (pageSequenceMaster == null) { return simplePageMaster; } boolean isOddPage = ((page % 2) != 0); if (log.isDebugEnabled()) { log.debug("getNextSimplePageMaster(page=" + page + " isOdd=" + isOddPage + " isFirst=" + isFirstPage + " isLast=" + isLastPage + " isBlank=" + isBlank + ")"); } return pageSequenceMaster.getNextSimplePageMaster(isOddPage, isFirstPage, isLastPage, isBlank, getMainFlow().getFlowName()); }
Used to set the "cursor position" for the page masters to the previous item.
Returns:true if there is a previous item, false if the current one was the first one.
/** * Used to set the "cursor position" for the page masters to the previous item. * @return true if there is a previous item, false if the current one was the first one. */
public boolean goToPreviousSimplePageMaster() { return pageSequenceMaster == null || pageSequenceMaster.goToPreviousSimplePageMaster(); }
Returns:true if the page-sequence has a page-master with page-position="last"
/** @return true if the page-sequence has a page-master with page-position="last" */
public boolean hasPagePositionLast() { return pageSequenceMaster != null && pageSequenceMaster.hasPagePositionLast(); }
Returns:true if the page-sequence has a page-master with page-position="only"
/** @return true if the page-sequence has a page-master with page-position="only" */
public boolean hasPagePositionOnly() { return pageSequenceMaster != null && pageSequenceMaster.hasPagePositionOnly(); }
Get the value of the master-reference trait.
Returns:the "master-reference" trait
/** * Get the value of the <code>master-reference</code> trait. * @return the "master-reference" trait */
public String getMasterReference() { return masterReference; }
{@inheritDoc}
/** {@inheritDoc} */
public String getLocalName() { return "page-sequence"; }
{@inheritDoc}
Returns:Constants.FO_PAGE_SEQUENCE
/** * {@inheritDoc} * @return {@link org.apache.fop.fo.Constants#FO_PAGE_SEQUENCE} */
public int getNameId() { return FO_PAGE_SEQUENCE; } public Locale getLocale() { return locale; }
Get the value of the reference-orientation trait.
Returns:the reference orientation trait value
/** * Get the value of the <code>reference-orientation</code> trait. * @return the reference orientation trait value */
public int getReferenceOrientation() { if (referenceOrientation != null) { return referenceOrientation.getValue(); } else { return 0; } }
{@inheritDoc}
/** * {@inheritDoc} */
public Direction getInlineProgressionDirection() { if (writingModeTraits != null) { return writingModeTraits.getInlineProgressionDirection(); } else { return Direction.LR; } }
{@inheritDoc}
/** * {@inheritDoc} */
public Direction getBlockProgressionDirection() { if (writingModeTraits != null) { return writingModeTraits.getBlockProgressionDirection(); } else { return Direction.TB; } }
{@inheritDoc}
/** * {@inheritDoc} */
public Direction getColumnProgressionDirection() { if (writingModeTraits != null) { return writingModeTraits.getColumnProgressionDirection(); } else { return Direction.LR; } }
{@inheritDoc}
/** * {@inheritDoc} */
public Direction getRowProgressionDirection() { if (writingModeTraits != null) { return writingModeTraits.getRowProgressionDirection(); } else { return Direction.TB; } }
{@inheritDoc}
/** * {@inheritDoc} */
public Direction getShiftDirection() { if (writingModeTraits != null) { return writingModeTraits.getShiftDirection(); } else { return Direction.TB; } }
{@inheritDoc}
/** * {@inheritDoc} */
public WritingMode getWritingMode() { if (writingModeTraits != null) { return writingModeTraits.getWritingMode(); } else { return WritingMode.LR_TB; } }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean getExplicitWritingMode() { if (writingModeTraits != null) { return writingModeTraits.getExplicitWritingMode(); } else { return false; } } @Override protected Stack<DelimitedTextRange> collectDelimitedTextRanges(Stack<DelimitedTextRange> ranges, DelimitedTextRange currentRange) { // collect ranges from static content flows Map<String, FONode> flows = getFlowMap(); if (flows != null) { for (FONode fn : flows.values()) { if (fn instanceof StaticContent) { ranges = ((StaticContent) fn).collectDelimitedTextRanges(ranges); } } } // collect ranges in main flow Flow main = getMainFlow(); if (main != null) { ranges = main.collectDelimitedTextRanges(ranges); } return ranges; } @Override protected boolean isBidiBoundary(boolean propagate) { return true; }
Releases a page-sequence's children after the page-sequence has been fully processed.
/** * Releases a page-sequence's children after the page-sequence has been fully processed. */
public void releasePageSequence() { this.mainFlow = null; this.flowMap.clear(); } public SimplePageMaster getLastSimplePageMaster(int page, boolean isFirstPage, boolean isBlank) { boolean isOddPage = ((page % 2) != 0); // please findbugs... log.debug("getNextSimplePageMaster(page=" + page + " isOdd=" + isOddPage + " isFirst=" + isFirstPage + " isLast=true" + " isBlank=" + isBlank + ")"); if (pageSequenceMaster == null) { return simplePageMaster; } return pageSequenceMaster.getLastSimplePageMaster(isOddPage, isFirstPage, isBlank, getMainFlow() .getFlowName()); } }