blob: 3a08809dc9ec65795ba4b22cf5e3413afba2d4ea [file] [log] [blame]
/*
* 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.area;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import org.apache.fop.datatypes.FODimension;
import org.apache.fop.datatypes.LengthBase;
import org.apache.fop.datatypes.SimplePercentBaseContext;
import org.apache.fop.fo.pagination.Region;
import org.apache.fop.fo.pagination.RegionBody;
import org.apache.fop.fo.pagination.SimplePageMaster;
import org.apache.fop.fo.properties.CommonMarginBlock;
import org.apache.fop.layoutmgr.TraitSetter;
import org.apache.fop.traits.WritingModeTraitsGetter;
import static org.apache.fop.fo.Constants.FO_REGION_AFTER;
import static org.apache.fop.fo.Constants.FO_REGION_BEFORE;
import static org.apache.fop.fo.Constants.FO_REGION_BODY;
import static org.apache.fop.fo.Constants.FO_REGION_END;
import static org.apache.fop.fo.Constants.FO_REGION_START;
import static org.apache.fop.fo.Constants.EN_ERROR_IF_OVERFLOW;
import static org.apache.fop.fo.Constants.EN_HIDDEN;
/**
* The page.
* This holds the contents of the page. Each region is added.
* The unresolved references area added so that if the page is
* serialized then it will handle the resolving properly after
* being reloaded.
* This is serializable so it can be saved to cache to save
* memory if there are forward references.
* The page is cloneable so the page master can make copies of
* the top level page and regions.
*/
public class Page extends AreaTreeObject implements Serializable, Cloneable {
private static final long serialVersionUID = 6272157047421543866L;
// contains before, start, body, end and after regions
private RegionViewport regionBefore = null;
private RegionViewport regionStart = null;
private RegionViewport regionBody = null;
private RegionViewport regionEnd = null;
private RegionViewport regionAfter = null;
// temporary map of unresolved objects used when serializing the page
private Map<String, List<Resolvable>> unresolved = null;
/** Set to true to make this page behave as if it were not empty. */
private boolean fakeNonEmpty = false;
/**
* Empty constructor, for cloning
*/
public Page() {
}
/**
* Constructor
* @param spm SimplePageMaster containing the dimensions for this
* page-reference-area
*/
public Page(SimplePageMaster spm) {
// Width and Height of the page view port
FODimension pageViewPortDims = new FODimension(spm.getPageWidth().getValue()
, spm.getPageHeight().getValue());
// Get absolute margin properties (top, left, bottom, right)
CommonMarginBlock mProps = spm.getCommonMarginBlock();
/*
* Create the page reference area rectangle (0,0 is at top left
* of the "page media" and y increases
* when moving towards the bottom of the page.
* The media rectangle itself is (0,0,pageWidth,pageHeight).
*/
/* Special rules apply to resolving margins in the page context.
* Contrary to normal margins in this case top and bottom margin
* are resolved relative to the height. In the property subsystem
* all margin properties are configured to using BLOCK_WIDTH.
* That's why we 'cheat' here and setup a context for the height but
* use the LengthBase.BLOCK_WIDTH.
*/
SimplePercentBaseContext pageWidthContext
= new SimplePercentBaseContext(null, LengthBase.CONTAINING_BLOCK_WIDTH
, pageViewPortDims.ipd);
SimplePercentBaseContext pageHeightContext
= new SimplePercentBaseContext(null, LengthBase.CONTAINING_BLOCK_WIDTH
, pageViewPortDims.bpd);
Rectangle pageRefRect
= new Rectangle(mProps.marginLeft.getValue(pageWidthContext)
, mProps.marginTop.getValue(pageHeightContext)
, pageViewPortDims.ipd
- mProps.marginLeft.getValue(pageWidthContext)
- mProps.marginRight.getValue(pageWidthContext)
, pageViewPortDims.bpd
- mProps.marginTop.getValue(pageHeightContext)
- mProps.marginBottom.getValue(pageHeightContext));
// Set up the CTM on the page reference area based on writing-mode
// and reference-orientation
FODimension reldims = new FODimension(0, 0);
CTM pageCTM = CTM.getCTMandRelDims(spm.getReferenceOrientation(),
spm.getWritingMode(), pageRefRect, reldims);
// Create a RegionViewport/ reference area pair for each page region
RegionReference rr;
for (Region r : spm.getRegions().values()) {
RegionViewport rvp = makeRegionViewport(r, reldims, pageCTM);
if (r.getNameId() == FO_REGION_BODY) {
rr = new BodyRegion((RegionBody) r, rvp);
} else {
rr = new RegionReference(r, rvp);
}
// set borders and padding traits
// (a little extensions wrt what prescribed by the specs at 6.4.14)
TraitSetter.addBorders(rr, r.getCommonBorderPaddingBackground(),
false, false, false, false, null);
TraitSetter.addPadding(rr, r.getCommonBorderPaddingBackground(),
false, false, false, false, null);
setRegionReferencePosition(rr, r, rvp.getViewArea());
rvp.setRegionReference(rr);
setRegionViewport(r.getNameId(), rvp);
}
}
/**
* Call this method to force this page to pretend not to be empty.
*/
public void fakeNonEmpty() {
this.fakeNonEmpty = true;
}
/**
* Creates a RegionViewport Area object for this pagination Region.
* @param r the region the viewport is to be created for
* @param reldims relative dimensions
* @param pageCTM page coordinate transformation matrix
* @return the new region viewport
*/
private static RegionViewport makeRegionViewport(Region r, FODimension reldims, CTM pageCTM) {
Rectangle2D relRegionRect = r.getViewportRectangle(reldims);
Rectangle2D absRegionRect = pageCTM.transform(relRegionRect);
// Get the region viewport rectangle in absolute coords by
// transforming it using the page CTM
RegionViewport rv = new RegionViewport(absRegionRect);
rv.setBPD((int)relRegionRect.getHeight());
rv.setIPD((int)relRegionRect.getWidth());
TraitSetter.addBackground(rv, r.getCommonBorderPaddingBackground(), null);
rv.setClip(r.getOverflow() == EN_HIDDEN
|| r.getOverflow() == EN_ERROR_IF_OVERFLOW);
return rv;
}
/**
* Set the region reference position within the region viewport.
* This sets the transform that is used to place the contents of
* the region reference.
*
* @param rr the region reference area
* @param r the region-xxx formatting object
* @param absRegVPRect The region viewport rectangle in "absolute" coordinates
* where x=distance from left, y=distance from bottom, width=right-left
* height=top-bottom
*/
private static void setRegionReferencePosition(RegionReference rr, Region r,
Rectangle2D absRegVPRect) {
FODimension reldims = new FODimension(0, 0);
rr.setCTM(CTM.getCTMandRelDims(r.getReferenceOrientation(),
r.getWritingMode(), absRegVPRect, reldims));
rr.setIPD(reldims.ipd
- rr.getBorderAndPaddingWidthStart()
- rr.getBorderAndPaddingWidthEnd());
rr.setBPD(reldims.bpd
- rr.getBorderAndPaddingWidthBefore()
- rr.getBorderAndPaddingWidthAfter());
}
/**
* Set the region on this page.
*
* @param areaclass the area class of the region to set
* @param port the region viewport to set
*/
public void setRegionViewport(int areaclass, RegionViewport port) {
if (areaclass == FO_REGION_BEFORE) {
regionBefore = port;
} else if (areaclass == FO_REGION_START) {
regionStart = port;
} else if (areaclass == FO_REGION_BODY) {
regionBody = port;
} else if (areaclass == FO_REGION_END) {
regionEnd = port;
} else if (areaclass == FO_REGION_AFTER) {
regionAfter = port;
}
}
/**
* Get the region from this page.
*
* @param areaClass the region area class
* @return the region viewport or null if none
*/
public RegionViewport getRegionViewport(int areaClass) {
switch (areaClass) {
case FO_REGION_BEFORE:
return regionBefore;
case FO_REGION_START:
return regionStart;
case FO_REGION_BODY:
return regionBody;
case FO_REGION_END:
return regionEnd;
case FO_REGION_AFTER:
return regionAfter;
default:
throw new IllegalArgumentException("No such area class with ID = " + areaClass);
}
}
/**
* Indicates whether any FOs have been added to the body region
*
* @return whether any FOs have been added to the body region
*/
public boolean isEmpty() {
if (fakeNonEmpty) {
return false;
} else if (regionBody == null) {
return true;
} else {
BodyRegion body = (BodyRegion)regionBody.getRegionReference();
return body.isEmpty();
}
}
/**
* Clone this page.
* This returns a new page with a clone of all the regions.
*
* @return a new clone of this page
*/
public Object clone() {
Page p = new Page();
if (regionBefore != null) {
p.regionBefore = (RegionViewport)regionBefore.clone();
}
if (regionStart != null) {
p.regionStart = (RegionViewport)regionStart.clone();
}
if (regionBody != null) {
p.regionBody = (RegionViewport)regionBody.clone();
}
if (regionEnd != null) {
p.regionEnd = (RegionViewport)regionEnd.clone();
}
if (regionAfter != null) {
p.regionAfter = (RegionViewport)regionAfter.clone();
}
return p;
}
/**
* Set the unresolved references on this page for serializing.
*
* @param unres the Map of unresolved objects
*/
public void setUnresolvedReferences(Map<String, List<Resolvable>> unres) {
unresolved = unres;
}
/**
* Get the map unresolved references from this page.
* This should be called after deserializing to retrieve
* the map of unresolved references that were serialized.
*
* @return the de-serialized HashMap of unresolved objects
*/
public Map<String, List<Resolvable>> getUnresolvedReferences() {
return unresolved;
}
/**
* Sets the writing mode traits for the region viewports of
* this page.
* @param wmtg a WM traits getter
*/
public void setWritingModeTraits(WritingModeTraitsGetter wmtg) {
if (regionBefore != null) {
regionBefore.setWritingModeTraits(wmtg);
}
if (regionStart != null) {
regionStart.setWritingModeTraits(wmtg);
}
if (regionBody != null) {
regionBody.setWritingModeTraits(wmtg);
}
if (regionEnd != null) {
regionEnd.setWritingModeTraits(wmtg);
}
if (regionAfter != null) {
regionAfter.setWritingModeTraits(wmtg);
}
}
}