suppressLowLevelAreas = Boolean.TRUE;
} else if (args[i].equals("-d")) {
setLogLevel("debug");
+ } else if (args[i].equals("-r")) {
+ foUserAgent.setStrictValidation(false);
} else if (args[i].equals("-q") || args[i].equals("--quiet")) {
setLogLevel("error");
} else if (args[i].equals("-fo")) {
+ " -q quiet mode \n"
+ " -c cfg.xml use additional configuration file cfg.xml\n"
+ " -l lang the language to use for user information \n"
+ + " -r relaxed/less strict validation (where available)\n"
+ " -s for area tree XML, down to block areas only\n"
+ " -v to show FOP version being used\n\n"
+ " [INPUT] \n"
* behavior for FOP. However, this flag, if set, provides the user the
* ability for FOP to halt on all content model violations if desired.
*/
- private boolean strictValidation = false;
+ private boolean strictValidation = true;
/* Additional fo.ElementMapping subclasses set by user */
private ArrayList additionalElementMappings = null;
// If no main flow, nothing to layout!
if (pageSequence.getMainFlow() != null) {
PageSequenceLayoutManager pageSLM;
- try {
- pageSLM = (PageSequenceLayoutManager)
- getLayoutManagerMaker().makeLayoutManager(pageSequence);
- } catch (FOPException e) {
- log.error("Failed to create a PageSequenceLayoutManager; "
- + "no pages will be laid out:");
- log.error(e.getMessage());
- return;
- }
- pageSLM.setAreaTreeHandler(this);
+ pageSLM =
+ getLayoutManagerMaker().makePageSequenceLayoutManager(this,
+ pageSequence);
pageSLM.activateLayout();
}
}
package org.apache.fop.area;
import org.apache.fop.fo.Constants;
-import org.apache.fop.fo.pagination.RegionBody;
/**
- * This class is a container for all areas that may be generated by
+ * This class is a container for the areas that may be generated by
* an fo:region-body. It extends the RegionReference that is used
* directly by the other region classes.
* See fo:region-body definition in the XSL Rec for more information.
private int columnGap;
private int columnCount;
- /**
- * Create a new body region area.
- * This sets the region reference area class to BODY.
- */
- public BodyRegion() {
- super(Constants.FO_REGION_BODY);
- addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
- mainReference = new MainReference();
- }
-
/**
* Constructor which can read traits directly
* from an fo:region-body formatting object.
*/
- public BodyRegion(RegionBody rb) {
- this();
- columnCount = rb.getColumnCount();
- columnGap = rb.getColumnGap();
- if ((columnCount > 1) && (rb.getOverflow() == Constants.EN_SCROLL)) {
- // recover by setting 'column-count' to 1. This is allowed but
- // not required by the spec.
- log.error("Setting 'column-count' to 1 because "
- + "'overflow' is set to 'scroll'");
- columnCount = 1;
- }
+ public BodyRegion(int columnCount, int columnGap, RegionViewport parent) {
+ super(Constants.FO_REGION_BODY, parent);
+ this.columnCount = columnCount;
+ this.columnGap = columnGap;
+ mainReference = new MainReference(this);
}
/**
this.columnGap = colGap;
}
- /**
- * Set the before float area.
- *
- * @param bf the before float area
- */
- public void setBeforeFloat(BeforeFloat bf) {
- beforeFloat = bf;
- }
-
- /**
- * Set the main reference area.
- *
- * @param mr the main reference area
- */
- public void setMainReference(MainReference mr) {
- mainReference = mr;
- }
-
- /**
- * Set the footnote area.
- *
- * @param foot the footnote area
- */
- public void setFootnote(Footnote foot) {
- footnote = foot;
+ /** @return the column-gap value */
+ public int getColumnGap() {
+ return this.columnGap;
}
-
- /**
- * Get the before float area.
- *
- * @return the before float area
- */
- public BeforeFloat getBeforeFloat() {
- return beforeFloat;
- }
-
+
/**
* Get the main reference area.
*
}
+ /**
+ * Get the before float area.
+ *
+ * @return the before float area
+ */
+ public BeforeFloat getBeforeFloat() {
+ if (beforeFloat == null) {
+ beforeFloat = new BeforeFloat();
+ }
+ return beforeFloat;
+ }
+
/**
* Get the footnote area.
*
* @return the footnote area
*/
public Footnote getFootnote() {
+ if (footnote == null) {
+ footnote = new Footnote();
+ }
return footnote;
}
* @return a shallow copy of this object
*/
public Object clone() {
- BodyRegion br = new BodyRegion();
+ BodyRegion br = new BodyRegion(columnCount, columnGap, regionViewport);
br.setCTM(getCTM());
br.setIPD(getIPD());
- br.columnGap = columnGap;
- br.columnCount = columnCount;
br.beforeFloat = beforeFloat;
br.mainReference = mainReference;
br.footnote = footnote;
* See fo:region-body definition in the XSL Rec for more information.
*/
public class MainReference extends Area {
+
+ private BodyRegion parent;
private List spanAreas = new java.util.ArrayList();
- private int columnGap;
private int width;
private boolean isEmpty = true;
/**
* Constructor
*/
- public MainReference() {
+ public MainReference(BodyRegion parent) {
+ this.parent = parent;
addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
}
*
* @param span the span area to add
*/
- public void addSpan(Span span) {
- spanAreas.add(span);
+ public Span createSpan(boolean spanAll) {
+ RegionViewport rv = parent.getRegionViewport();
+ int ipdWidth = (int) parent.getIPD() -
+ rv.getBorderAndPaddingWidthStart() - rv.getBorderAndPaddingWidthEnd();
+
+ Span newSpan = new Span(((spanAll) ? 1 : getColumnCount()),
+ getColumnGap(), ipdWidth);
+ spanAreas.add(newSpan);
+ return getCurrentSpan();
}
/**
return spanAreas;
}
+ /**
+ * Get the span area currently being filled (i.e., the last span created)
+ *
+ * @return the active span
+ */
+ public Span getCurrentSpan() {
+ return (Span) spanAreas.get(spanAreas.size()-1);
+ }
+
/**
* indicates whether any child areas have been added to this reference area
* this is achieved by looping through each span
return isEmpty;
}
- /**
- * Get the column gap in millipoints.
- *
- * @return the column gap in millipoints
- */
+ /** @return the number of columns */
+ public int getColumnCount() {
+ return parent.getColumnCount();
+ }
+
+ /** @return the column gap in millipoints */
public int getColumnGap() {
- return columnGap;
+ return parent.getColumnGap();
}
/**
public class NormalFlow extends BlockParent {
/**
* Constructor.
+ * @param ipd of Normal flow object
*/
- public NormalFlow() {
+ public NormalFlow(int ipd) {
addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
+ setIPD(ipd);
}
}
package org.apache.fop.area;
+import java.awt.Rectangle;
+import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.HashMap;
+import java.util.Iterator;
+import org.apache.fop.datatypes.FODimension;
+import org.apache.fop.datatypes.PercentBase;
import org.apache.fop.fo.Constants;
+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;
/**
* The page.
// temporary map of unresolved objects used when serializing the page
private HashMap unresolved = null;
+ /**
+ * Empty constructor, for cloning
+ */
+ public Page() {
+ }
+
+ /**
+ * Constructor
+ * @param spm SimplePageMaster containing the dimensions for this
+ * page-reference-area
+ */
+ public Page(SimplePageMaster spm) {
+ int pageWidth = spm.getPageWidth().getValue();
+ int pageHeight = 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).
+ */
+ Rectangle pageRefRect =
+ new Rectangle(mProps.marginLeft.getValue(), mProps.marginTop.getValue(),
+ pageWidth - mProps.marginLeft.getValue() - mProps.marginRight.getValue(),
+ pageHeight - mProps.marginTop.getValue() - mProps.marginBottom.getValue());
+
+ // 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 = null;
+ for (Iterator regenum = spm.getRegions().values().iterator();
+ regenum.hasNext();) {
+ Region r = (Region)regenum.next();
+ RegionViewport rvp = makeRegionViewport(r, reldims, pageCTM);
+ r.setLayoutDimension(PercentBase.BLOCK_IPD, rvp.getIPD());
+ r.setLayoutDimension(PercentBase.BLOCK_BPD, rvp.getBPD());
+ if (r.getNameId() == Constants.FO_REGION_BODY) {
+ RegionBody rb = (RegionBody) r;
+ rr = new BodyRegion(rb.getColumnCount(), rb.getColumnGap(),
+ rvp);
+ } else {
+ rr = new RegionReference(r.getNameId(), rvp);
+ }
+ setRegionReferencePosition(rr, r, rvp.getViewArea());
+ rvp.setRegionReference(rr);
+ setRegionViewport(r.getNameId(), rvp);
+ }
+ }
+
+ /**
+ * Creates a RegionViewport Area object for this pagination Region.
+ * @param reldims relative dimensions
+ * @param pageCTM page coordinate transformation matrix
+ * @return the new region viewport
+ */
+ private 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());
+ rv.setClip(r.getOverflow() == Constants.EN_HIDDEN
+ || r.getOverflow() == Constants.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 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.setBPD(reldims.bpd);
+ }
+
/**
* 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) {
+ private void setRegionViewport(int areaclass, RegionViewport port) {
if (areaclass == Constants.FO_REGION_BEFORE) {
regionBefore = port;
} else if (areaclass == Constants.FO_REGION_START) {
return true;
}
else {
- BodyRegion body = (BodyRegion)regionBody.getRegion();
+ BodyRegion body = (BodyRegion)regionBody.getRegionReference();
return body.isEmpty();
}
}
package org.apache.fop.area;
+import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import org.apache.commons.logging.LogFactory;
import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.pagination.SimplePageMaster;
/**
* Page viewport that specifies the viewport area and holds the page contents.
private Rectangle2D viewArea;
private boolean clip = false;
private String pageNumberString = null;
+ private SimplePageMaster spm = null;
// list of id references and the rectangle on the page
private Map idReferences = null;
/**
* Create a page viewport.
- * @param p the page reference area that holds the contents
- * @param bounds the bounds of this viewport
+ * @param spm SimplePageMaster indicating the page and region dimensions
*/
- public PageViewport(Page p, Rectangle2D bounds) {
- page = p;
- viewArea = bounds;
+ public PageViewport(SimplePageMaster spm) {
+ this.spm = spm;
+ int pageWidth = spm.getPageWidth().getValue();
+ int pageHeight = spm.getPageHeight().getValue();
+ viewArea = new Rectangle(0, 0, pageWidth, pageHeight);
+ page = new Page(spm);
+ }
+
+ /**
+ * Create a page viewport
+ * @param spm SimplePageMaster indicating the page and region dimensions
+ * @param p Page Reference Area
+ * @param bounds Page Viewport dimensions
+ */
+ public PageViewport(SimplePageMaster spm, Page p, Rectangle2D bounds) {
+ this.spm = spm;
+ this.page = p;
+ this.viewArea = bounds;
}
/**
* @return BodyRegion object
*/
public BodyRegion getBodyRegion() {
- return (BodyRegion)
- getPage().getRegionViewport(Constants.FO_REGION_BODY).getRegion();
+ return (BodyRegion) getPage().getRegionViewport(
+ Constants.FO_REGION_BODY).getRegionReference();
+ }
+
+ /**
+ * Convenience method to create a new Span for this
+ * this PageViewport.
+ *
+ * @param spanAll whether this is a single-column span
+ * @return Span object created
+ */
+ public Span createSpan(boolean spanAll) {
+ return getBodyRegion().getMainReference().createSpan(spanAll);
+ }
+
+ /**
+ * Convenience method to get the span-reference-area currently
+ * being processed
+ *
+ * @return span currently being processed.
+ */
+ public Span getCurrentSpan() {
+ return getBodyRegion().getMainReference().getCurrentSpan();
}
/**
*/
public void addMarkers(Map marks, boolean starting,
boolean isfirst, boolean islast) {
+
+ if (marks == null) {
+ return;
+ }
+
// at the start of the area, register is-first and any areas
if (starting) {
if (isfirst) {
*/
public Object clone() {
Page p = (Page)page.clone();
- PageViewport ret = new PageViewport(p, (Rectangle2D)viewArea.clone());
+ PageViewport ret = new PageViewport(spm, p, (Rectangle2D)viewArea.clone());
return ret;
}
sb.append(getPageNumberString());
return sb.toString();
}
+ /**
+ * @return Returns the spm.
+ */
+ public SimplePageMaster getSPM() {
+ return spm;
+ }
}
\ No newline at end of file
import org.apache.fop.fo.Constants;
/**
- * This is a region reference area for the page regions.
- * This area represents a region on the page. It is cloneable
+ * This is a region reference area for a page regions.
+ * This area is the direct child of a region-viewport-area. It is cloneable
* so the page master can make copies from the original page and regions.
*/
public class RegionReference extends Area implements Cloneable {
private int regionClass = Constants.FO_REGION_BEFORE;
private CTM ctm;
+
// the list of block areas from the static flow
private List blocks = new ArrayList();
-
- private int bpd;
+
+ // the parent RegionViewport for this object
+ protected RegionViewport regionViewport;
/**
* Create a new region reference area.
*
* @param type the region class type
*/
- public RegionReference(int type) {
+ public RegionReference(int type, RegionViewport parent) {
regionClass = type;
addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
+ regionViewport = parent;
}
/**
public void setCTM(CTM ctm) {
this.ctm = ctm;
}
+
+ /**
+ * @return Returns the parent RegionViewport.
+ */
+ public RegionViewport getRegionViewport() {
+ return regionViewport;
+ }
/**
* Get the current transform of this region.
blocks.add(block);
}
- /**
- * Set the block-progression-dimension.
- *
- * @return the footnote area
- */
- public void setBPD(int bpd) {
- this.bpd = bpd;
- }
-
- /**
- * Set the block-progression-dimension.
- *
- * @return the footnote area
- */
- public int getBPD() {
- return bpd;
- }
-
/**
* Clone this region.
* This is used when cloning the page by the page master.
* @return a copy of this region reference area
*/
public Object clone() {
- RegionReference rr = new RegionReference(regionClass);
+ RegionReference rr = new RegionReference(regionClass, regionViewport);
rr.ctm = ctm;
rr.setIPD(getIPD());
return rr;
import java.util.HashMap;
/**
- * Region Viewport reference area.
- * This area is the viewport for a region and contains a region area.
+ * Region Viewport area.
+ * This object represents the region-viewport-area. It has a
+ * region-reference-area as its child. These areas are described
+ * in the fo:region-body description in the XSL Recommendation.
*/
public class RegionViewport extends Area implements Cloneable {
// this rectangle is relative to the page
- private RegionReference region;
+ private RegionReference regionReference;
private Rectangle2D viewArea;
private boolean clip = false;
/**
- * Create a new region viewport.
+ * Create a new region-viewport-area
*
* @param viewArea the view area of this viewport
*/
}
/**
- * Set the region for this region viewport.
+ * Set the region-reference-area for this region viewport.
*
- * @param reg the child region inside this viewport
+ * @param reg the child region-reference-area inside this viewport
*/
- public void setRegion(RegionReference reg) {
- region = reg;
+ public void setRegionReference(RegionReference reg) {
+ regionReference = reg;
}
/**
- * Get the region for this region viewport.
+ * Get the region-reference-area for this region viewport.
*
- * @return the child region inside this viewport
+ * @return the child region-reference-area inside this viewport
*/
- public RegionReference getRegion() {
- return region;
+ public RegionReference getRegionReference() {
+ return regionReference;
}
/**
clip = c;
}
+ /** @return true if the viewport should be clipped. */
+ public boolean isClip() {
+ return this.clip;
+ }
+
/**
* Get the view area of this viewport.
*
out.writeFloat((float) viewArea.getHeight());
out.writeBoolean(clip);
out.writeObject(props);
- out.writeObject(region);
+ out.writeObject(regionReference);
}
private void readObject(java.io.ObjectInputStream in)
in.readFloat(), in.readFloat());
clip = in.readBoolean();
props = (HashMap)in.readObject();
- setRegion((RegionReference) in.readObject());
+ setRegionReference((RegionReference) in.readObject());
}
/**
*/
public Object clone() {
RegionViewport rv = new RegionViewport((Rectangle2D)viewArea.clone());
- rv.region = (RegionReference)region.clone();
+ rv.regionReference = (RegionReference)regionReference.clone();
if (props != null) {
rv.props = (HashMap)props.clone();
}
// the list of flow reference areas in this span area
private List flowAreas;
private int height;
- private int columnCount;
+ private int colCount;
+ private int colGap;
+ private int colWidth; // width for each normal flow, calculated value
/**
* Create a span area with the number of columns for this span area.
*
- * @param cols the number of columns in the span
- * @param ipd the ipd of the span
+ * @param colCount the number of columns in the span
+ * @param colGap the column gap between each column
+ * @param ipd the total ipd of the span
*/
- public Span(int cols, int ipd) {
+ public Span(int colCount, int colGap, int ipd) {
addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
- columnCount = cols;
+ this.colCount = colCount;
+ this.colGap = colGap;
this.ipd = ipd;
- flowAreas = new java.util.ArrayList(cols);
- addAdditionalNormalFlow(); // one normal flow is required
+ createNormalFlows();
}
/**
- * Create a new normal flow and add it to this span area
- *
- * @return the newly made NormalFlow object
+ * Create the normal flows for this Span
*/
- public NormalFlow addAdditionalNormalFlow() {
- if (flowAreas.size() >= columnCount) { // internal error
- throw new IllegalStateException("Maximum number of flow areas (" +
- columnCount + ") for this span reached.");
+ private void createNormalFlows() {
+ flowAreas = new java.util.ArrayList(colCount);
+ colWidth = (ipd - ((colCount - 1) * colGap)) / colCount;
+
+ for (int i=0; i< colCount; i++) {
+ NormalFlow newFlow = new NormalFlow(colWidth);
+ newFlow.setIPD(getIPD());
+ flowAreas.add(newFlow);
}
- NormalFlow newFlow = new NormalFlow();
- newFlow.setIPD(getIPD());
- flowAreas.add(newFlow);
- return newFlow;
}
/**
* @return the number of columns defined for this span area
*/
public int getColumnCount() {
- return columnCount;
+ return colCount;
}
/**
- * Get the count of normal flows for this span area.
+ * Get the width of a single column within this Span
*
- * @return the number of normal flows attached to this span
+ * @return the width of a single column
*/
- public int getNormalFlowCount() {
- return flowAreas.size();
+ public int getColumnWidth() {
+ return colWidth;
}
/**
return height;
}
+
/**
* Get the normal flow area for a particular column.
*
- * @param count the column number for the flow
+ * @param colRequested the zero-based column number of the flow
* @return the flow area for the requested column
*/
- public NormalFlow getNormalFlow(int columnNumber) {
- return (NormalFlow) flowAreas.get(columnNumber);
+ public NormalFlow getNormalFlow(int colRequested) {
+ if (colRequested >= 0 && colRequested < colCount) {
+ return (NormalFlow) flowAreas.get(colRequested);
+ } else { // internal error
+ throw new IllegalArgumentException("Invalid column number " +
+ colRequested + " requested; only 0-" + (colCount-1) +
+ " available.");
+ }
}
-
}
int PR_INTRUSION_DISPLACE = 247;
int PR_INDEX_CLASS = 248; // XSL 1.1
int PR_INDEX_KEY = 249; // XSL 1.1
- int PROPERTY_COUNT = 249;
+ int PR_X_BLOCK_PROGRESSION_UNIT = 250; //Custom extension
+ int PROPERTY_COUNT = 250;
// compound property constants
int EN_VISIBLE = 159;
int EN_WIDER = 160;
int EN_WRAP = 161;
- int ENUM_COUNT = 161;
+ int EN_X_FILL = 162; //non-standard for display-align
+ int EN_X_DISTRIBUTE = 163; //non-standard for display-align
+ int ENUM_COUNT = 163;
}
m.addEnum("after", getEnumProperty(EN_AFTER, "AFTER"));
m.addEnum("center", getEnumProperty(EN_CENTER, "CENTER"));
m.addEnum("auto", getEnumProperty(EN_AUTO, "AUTO"));
+/*LF*/ m.addEnum("distribute", getEnumProperty(EN_X_DISTRIBUTE, "DISTRIBUTE"));
+/*LF*/ m.addEnum("fill", getEnumProperty(EN_X_FILL, "FILL"));
m.setDefault("auto");
addPropertyMaker("display-align", m);
l.setPercentBase(LengthBase.BLOCK_WIDTH);
l.setDefault("auto");
addPropertyMaker("width", l);
+
+/*LF*/ // block-progression-unit (**CUSTOM EXTENSION**)
+/*LF*/ l = new LengthProperty.Maker(PR_X_BLOCK_PROGRESSION_UNIT);
+/*LF*/ l.setInherited(false);
+/*LF*/ l.setDefault("0pt");
+/*LF*/ addPropertyMaker("block-progression-unit", l);
}
private void createBlockAndLineProperties() {
package org.apache.fop.fo;
-import java.util.ArrayList;
-import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
public static PropertyMaker[] propertyListTable = null;
/** The immediate child nodes of this node. */
- public ArrayList childNodes = null;
+ public List childNodes = null;
/** Used to indicate if this FO is either an Out Of Line FO (see rec)
or a descendant of one. Used during validateChildNode() FO
addMarker((Marker) child);
} else {
if (childNodes == null) {
- childNodes = new ArrayList();
+ childNodes = new java.util.ArrayList();
}
childNodes.add(child);
}
*/
public void setLayoutDimension(PercentBase.LayoutDimension key, int dimension) {
if (layoutDimension == null) {
- layoutDimension = new HashMap();
+ layoutDimension = new java.util.HashMap();
}
layoutDimension.put(key, new Integer(dimension));
}
*/
public void setLayoutDimension(PercentBase.LayoutDimension key, float dimension) {
if (layoutDimension == null) {
- layoutDimension = new HashMap();
+ layoutDimension = new java.util.HashMap();
}
layoutDimension.put(key, new Float(dimension));
}
}
}
if (markers == null) {
- markers = new HashMap();
+ markers = new java.util.HashMap();
}
if (!markers.containsKey(mcname)) {
markers.put(mcname, marker);
elem.addProperties(CommonBorderPaddingBackgroundProperties);
elem.addProperties(CommonMarginPropertiesBlock);
elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
+ elem.addProperty(Constants.PR_X_BLOCK_PROGRESSION_UNIT);
elem.addProperty(Constants.PR_PAGE_BREAK_AFTER);
elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE);
elem.addProperty(Constants.PR_BREAK_AFTER);
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.ValidationException;
import org.apache.fop.fo.properties.CommonRelativePosition;
-import org.apache.fop.fo.properties.CommonTextDecoration;
import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.fo.properties.LengthRangeProperty;
import java.util.HashMap;
import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
import org.xml.sax.Locator;
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FOEventHandler;
import org.apache.fop.fo.FONode;
-import org.apache.fop.fo.FOText;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.FObjMixed;
import org.apache.fop.fo.PropertyList;
* @see org.apache.fop.fo.FObj#bind(PropertyList)
*/
public void bind(PropertyList pList) throws FOPException {
+ if (findAncestor(FO_FLOW) < 0) {
+ invalidChildError(locator, FO_URI, "marker",
+ "An fo:marker is permitted only as the descendant " +
+ "of an fo:flow");
+ }
+
markerClassName = pList.get(PR_MARKER_CLASS_NAME).getString();
+
+ if (markerClassName == null || markerClassName.equals("")) {
+ missingPropertyError("marker-class-name");
+ }
}
/**
import java.util.HashMap;
import java.util.Iterator;
import java.util.ArrayList;
-import java.util.List;
import org.xml.sax.Locator;
* @see org.apache.fop.fo.FObj#bind(PropertyList)
*/
public void bind(PropertyList pList) throws FOPException {
+ if (findAncestor(FO_STATIC_CONTENT) < 0) {
+ invalidChildError(locator, FO_URI, "retrieve-marker",
+ "An fo:retrieve-marker is permitted only as the " +
+ " descendant of an fo:static-content.");
+ }
+
retrieveClassName = pList.get(PR_RETRIEVE_CLASS_NAME).getString();
retrievePosition = pList.get(PR_RETRIEVE_POSITION).getEnum();
retrieveBoundary = pList.get(PR_RETRIEVE_BOUNDARY).getEnum();
+
+ if (retrieveClassName == null || retrieveClassName.equals("")) {
+ missingPropertyError("retrieve-class-name");
+ }
}
/**
public List getColumns() {
return columns;
}
+
+ /**
+ * @param index index of the table-body element.
+ * @return the requested table-body element
+ */
+ public TableBody getBody(int index) {
+ return (TableBody)childNodes.get(index);
+ }
public TableBody getTableHeader() {
return tableHeader;
return commonBorderPaddingBackground;
}
- /**
- * @return the "break-after" property.
- */
+ /** @return the "break-after" property. */
public int getBreakAfter() {
return breakAfter;
}
- /**
- * @return the "break-before" property.
- */
+ /** @return the "break-before" property. */
public int getBreakBefore() {
return breakBefore;
}
-
+
+ /** @return the "keep-with-next" property. */
+ public KeepProperty getKeepWithNext() {
+ return keepWithNext;
+ }
+
+ /** @return the "keep-with-previous" property. */
+ public KeepProperty getKeepWithPrevious() {
+ return keepWithPrevious;
+ }
+
+ /** @return the "keep-together" property. */
+ public KeepProperty getKeepTogether() {
+ return keepTogether;
+ }
+
/** @return the "border-collapse" property. */
public int getBorderCollapse() {
return borderCollapse;
getParent().removeChild(this);
}
}
+ /*
if (tableCellsFound) {
convertCellsToRows();
- }
+ }*/
savedPropertyList = null; //Release reference
}
*/
private void convertCellsToRows() throws FOPException {
//getLogger().debug("Converting cells to rows...");
- List cells = (List)childNodes.clone();
+ List cells = new java.util.ArrayList(childNodes);
childNodes.clear();
Iterator i = cells.iterator();
TableRow row = null;
if (!blockItemFound) {
missingChildElementError("marker* (%block;)+");
}
+ //TODO Complain about startsRow|endsRow=true if parent is a table-row
getFOEventHandler().endCell(this);
}
return columnNumber.getValue();
}
- /**
- * @return value for number of columns repeated
- */
+ /** @return value for number-columns-repeated. */
public int getNumberColumnsRepeated() {
return numberColumnsRepeated.getValue();
}
+ /** @return value for number-columns-spanned. */
+ public int getNumberColumnsSpanned() {
+ return numberColumnsSpanned.getValue();
+ }
+
/** @see org.apache.fop.fo.FONode#getName() */
public String getName() {
return "fo:table-column";
public int getNameId() {
return FO_TABLE_COLUMN;
}
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ StringBuffer sb = new StringBuffer("fo:table-column");
+ if (hasColumnNumber()) {
+ sb.append(" column-number=").append(getColumnNumber());
+ }
+ if (getNumberColumnsRepeated() > 1) {
+ sb.append(" number-columns-repeated=").append(getNumberColumnsRepeated());
+ }
+ if (getNumberColumnsSpanned() > 1) {
+ sb.append(" number-columns-spanned=").append(getNumberColumnsSpanned());
+ }
+ sb.append(" column-width=").append(getColumnWidth());
+ return sb.toString();
+ }
}
/*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package org.apache.fop.fo.flow;
// Java
-import java.util.List;
-import java.util.ListIterator;
-
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FObjMixed;
import org.apache.fop.fo.PropertyList;
+import org.apache.fop.fo.ValidationException;
+import org.xml.sax.Locator;
/**
* Implementation for fo:wrapper formatting object.
* The wrapper object serves as
* a property holder for its child node objects.
- *
- * Content: (#PCDATA|%inline;|%block;)*
- * Properties: id
- * @todo implement validateChildNode()
*/
public class Wrapper extends FObjMixed {
// The value of properties relevant for fo:wrapper.
private String id;
// End of property values
+ // used for FO validation
+ private boolean blockOrInlineItemFound = false;
+
/**
* @param parent FONode that is the parent of this object
*/
checkId(id);
}
+ /**
+ * @see org.apache.fop.fo.FONode#validateChildNode(Locator, String, String)
+ * XSL Content Model: marker* (#PCDATA|%inline;|%block;)*
+ * Additionally (unimplemented): "An fo:wrapper that is a child of an
+ * fo:multi-properties is only permitted to have children that would
+ * be permitted in place of the fo:multi-properties."
+ */
+ protected void validateChildNode(Locator loc, String nsURI, String localName)
+ throws ValidationException {
+ if (nsURI == FO_URI && localName.equals("marker")) {
+ if (blockOrInlineItemFound) {
+ nodesOutOfOrderError(loc, "fo:marker",
+ "(#PCDATA|%inline;|%block;)");
+ }
+ } else if (isBlockOrInlineItem(nsURI, localName)) {
+ blockOrInlineItemFound = true;
+ } else {
+ invalidChildError(loc, nsURI, localName);
+ }
+ }
+
/**
* Return the "id" property.
*/
/*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import java.awt.Rectangle;
import org.apache.fop.apps.FOPException;
-import org.apache.fop.datatypes.Length;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.PropertyList;
/**
* Abstract base class for fo:region-before and fo:region-after.
*/
-public abstract class RegionBA extends Region {
+public abstract class RegionBA extends SideRegion {
// The value of properties relevant for fo:region-[before|after].
- private Length extent;
private int precedence;
// End of property values
*/
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
- extent = pList.get(PR_EXTENT).getLength();
precedence = pList.get(PR_PRECEDENCE).getEnum();
}
/**
- * Return the "extent" property.
- */
- public Length getExtent() {
- return extent;
- }
-
- /**
- * Return the "precedence" property.
+ * @return the "precedence" property.
*/
public int getPrecedence() {
return precedence;
import org.apache.fop.datatypes.FODimension;
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.Numeric;
+import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.PropertyList;
import org.apache.fop.fo.properties.CommonMarginBlock;
commonMarginBlock = pList.getMarginBlockProps();
columnCount = pList.get(PR_COLUMN_COUNT).getNumeric();
columnGap = pList.get(PR_COLUMN_GAP).getLength();
+
+ if ((getColumnCount() > 1) && (getOverflow() == EN_SCROLL)) {
+ /* This is an error (See XSL Rec, fo:region-body description).
+ * The Rec allows for acting as if "1" is chosen in
+ * these cases, but we will need to be able to change Numeric
+ * values in order to do this.
+ */
+ attributeError("If overflow property is set to \"scroll\"," +
+ " a column-count other than \"1\" may not be specified.");
+ }
}
/**
/*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import java.awt.Rectangle;
import org.apache.fop.apps.FOPException;
-import org.apache.fop.datatypes.Length;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.PropertyList;
/**
* Abstract base class for fo:region-start and fo:region-end.
*/
-public abstract class RegionSE extends Region {
+public abstract class RegionSE extends SideRegion {
// The value of properties relevant for fo:region-[start|end].
- private Length extent;
// End of property values
/**
*/
public void bind(PropertyList pList) throws FOPException {
super.bind(pList);
- extent = pList.get(PR_EXTENT).getLength();
}
- /**
- * Return the "extent" property.
- */
- public Length getExtent() {
- return extent;
- }
-
/**
* Adjust the viewport reference rectangle for a region as a function
* of precedence.
--- /dev/null
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.fo.pagination;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.datatypes.Length;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.PropertyList;
+
+/**
+ * Common base class for side regions (before, after, start, end).
+ */
+public abstract class SideRegion extends Region {
+
+ private Length extent;
+
+ /** @see org.apache.fop.fo.FONode#FONode(FONode) */
+ protected SideRegion(FONode parent) {
+ super(parent);
+ }
+
+ /** @see org.apache.fop.fo.FObj#bind(PropertyList) */
+ public void bind(PropertyList pList) throws FOPException {
+ super.bind(pList);
+ extent = pList.get(PR_EXTENT).getLength();
+ }
+
+ /** @return the "extent" property. */
+ public Length getExtent() {
+ return extent;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.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.fo.Constants;
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * Abstract base class for breakers (page breakers, static region handlers etc.).
+ */
+public abstract class AbstractBreaker {
+
+ /** logging instance */
+ protected static Log log = LogFactory.getLog(AbstractBreaker.class);
+
+ /*LF*/
+ public static class PageBreakPosition extends LeafPosition {
+ double bpdAdjust; // Percentage to adjust (stretch or shrink)
+ int difference;
+
+ PageBreakPosition(LayoutManager lm, int iBreakIndex,
+ double bpdA, int diff) {
+ super(lm, iBreakIndex);
+ bpdAdjust = bpdA;
+ difference = diff;
+ }
+ }
+
+ public class BlockSequence extends KnuthSequence {
+
+ /**
+ * startOn represents where on the page/which page layout
+ * should start for this BlockSequence. Acceptable values:
+ * Constants.EN_ANY (can continue from finished location
+ * of previous BlockSequence?), EN_COLUMN, EN_ODD_PAGE,
+ * EN_EVEN_PAGE.
+ */
+ private int startOn;
+
+ public BlockSequence(int iStartOn) {
+ super();
+ startOn = iStartOn;
+ }
+
+ public int getStartOn() {
+ return this.startOn;
+ }
+
+ public BlockSequence endBlockSequence() {
+ KnuthSequence temp = super.endSequence();
+ if (temp != null) {
+ BlockSequence returnSequence = new BlockSequence(startOn);
+ returnSequence.addAll(temp);
+ returnSequence.ignoreAtEnd = this.ignoreAtEnd;
+ return returnSequence;
+ } else {
+ return null;
+ }
+ }
+ }
+
+ /** blockListIndex of the current BlockSequence in blockLists */
+ private int blockListIndex = 0;
+/*LF*/
+ /*LF*/
+ private List blockLists = null;
+
+ private int alignment;
+ private int alignmentLast;
+ /*LF*/
+
+ protected abstract int getCurrentDisplayAlign();
+ protected abstract boolean hasMoreContent();
+ protected abstract void addAreas(PositionIterator posIter, LayoutContext context);
+ protected abstract LayoutManager getTopLevelLM();
+ protected abstract LayoutManager getCurrentChildLM();
+ protected abstract LinkedList getNextKnuthElements(LayoutContext context, int alignment);
+
+ /** @return true if there's no content that could be handled. */
+ public boolean isEmpty() {
+ return (blockLists.size() == 0);
+ }
+
+ protected void startPart(BlockSequence list, boolean bIsFirstPage) {
+ //nop
+ }
+
+ protected abstract void finishPart();
+
+ protected LayoutContext createLayoutContext() {
+ return new LayoutContext(0);
+ }
+
+ public void doLayout(int flowBPD) {
+ LayoutContext childLC = createLayoutContext();
+ childLC.setStackLimit(new MinOptMax(flowBPD));
+
+ //System.err.println("Vertical alignment: " +
+ // currentSimplePageMaster.getRegion(FO_REGION_BODY).getDisplayAlign());
+ if (getCurrentDisplayAlign() == Constants.EN_X_FILL) {
+ //EN_FILL is non-standard (by LF)
+ alignment = Constants.EN_JUSTIFY;
+ } else {
+ alignment = Constants.EN_START;
+ }
+ alignmentLast = Constants.EN_START;
+
+ BlockSequence blockList;
+ blockLists = new java.util.ArrayList();
+
+ System.out.println("PLM> flow BPD =" + flowBPD);
+
+ //*** Phase 1: Get Knuth elements ***
+ int nextSequenceStartsOn = Constants.EN_ANY;
+ while (hasMoreContent()) {
+ nextSequenceStartsOn = getNextBlockList(childLC, nextSequenceStartsOn, blockLists);
+ }
+
+ //*** Phase 2: Alignment and breaking ***
+ System.out.println("PLM> blockLists.size() = " + blockLists.size());
+ for (blockListIndex = 0; blockListIndex < blockLists.size(); blockListIndex++) {
+ blockList = (BlockSequence) blockLists.get(blockListIndex);
+
+ //debug code start
+ System.err.println(" blockListIndex = " + blockListIndex);
+ String pagina = (blockList.startOn == Constants.EN_ANY) ? "any page"
+ : (blockList.startOn == Constants.EN_ODD_PAGE) ? "odd page"
+ : "even page";
+ System.err.println(" sequence starts on " + pagina);
+ logBlocklist(blockList);
+ //debug code end
+
+ System.out.println("PLM> start of algorithm (" + this.getClass().getName()
+ + "), flow BPD =" + flowBPD);
+ PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(),
+ alignment, alignmentLast);
+ int iOptPageNumber;
+
+ BlockSequence effectiveList;
+ if (alignment == Constants.EN_JUSTIFY) {
+ /* justification */
+ effectiveList = justifyBoxes(blockList, alg, flowBPD);
+ } else {
+ /* no justification */
+ effectiveList = blockList;
+ }
+
+ //iOptPageNumber = alg.firstFit(effectiveList, flowBPD, 1, true);
+ iOptPageNumber = alg.findBreakingPoints(effectiveList, flowBPD, 1,
+ true, true);
+ System.out.println("PLM> iOptPageNumber= " + iOptPageNumber
+ + " pageBreaks.size()= " + alg.getPageBreaks().size());
+
+
+ //*** Phase 3: Add areas ***
+ doPhase3(alg, iOptPageNumber, blockList, effectiveList);
+ }
+ }
+
+ /**
+ * Phase 3 of Knuth algorithm: Adds the areas
+ * @param alg PageBreakingAlgorithm instance which determined the breaks
+ * @param partCount number of parts (pages) to be rendered
+ * @param originalList original Knuth element list
+ * @param effectiveList effective Knuth element list (after adjustments)
+ */
+ protected abstract void doPhase3(PageBreakingAlgorithm alg, int partCount,
+ BlockSequence originalList, BlockSequence effectiveList);
+
+ /**
+ * Phase 3 of Knuth algorithm: Adds the areas
+ * @param alg PageBreakingAlgorithm instance which determined the breaks
+ * @param partCount number of parts (pages) to be rendered
+ * @param originalList original Knuth element list
+ * @param effectiveList effective Knuth element list (after adjustments)
+ */
+ protected void addAreas(PageBreakingAlgorithm alg, int partCount,
+ BlockSequence originalList, BlockSequence effectiveList) {
+ LayoutContext childLC;
+ // add areas
+ ListIterator effectiveListIterator = effectiveList.listIterator();
+ int startElementIndex = 0;
+ int endElementIndex = 0;
+ for (int p = 0; p < partCount; p++) {
+ PageBreakPosition pbp = (PageBreakPosition) alg.getPageBreaks().get(p);
+ endElementIndex = pbp.getLeafPos();
+ System.out.println("PLM> part: " + (p + 1)
+ + ", break at position " + endElementIndex);
+
+ startPart(effectiveList, (p == 0));
+
+ int displayAlign = getCurrentDisplayAlign();
+
+ // ignore the first elements added by the
+ // PageSequenceLayoutManager
+ startElementIndex += (startElementIndex == 0)
+ ? effectiveList.ignoreAtStart
+ : 0;
+
+ // ignore the last elements added by the
+ // PageSequenceLayoutManager
+ endElementIndex -= (endElementIndex == (originalList.size() - 1))
+ ? effectiveList.ignoreAtEnd
+ : 0;
+
+ // ignore the last element in the page if it is a KnuthGlue
+ // object
+ if (((KnuthElement) effectiveList.get(endElementIndex))
+ .isGlue()) {
+ endElementIndex--;
+ }
+
+ // ignore KnuthGlue and KnuthPenalty objects
+ // at the beginning of the line
+ effectiveListIterator = effectiveList
+ .listIterator(startElementIndex);
+ while (effectiveListIterator.hasNext()
+ && !((KnuthElement) effectiveListIterator.next())
+ .isBox()) {
+ startElementIndex++;
+ }
+
+ if (startElementIndex <= endElementIndex) {
+ System.out.println(" addAreas da " + startElementIndex
+ + " a " + endElementIndex);
+ childLC = new LayoutContext(0);
+ // add space before if display-align is center or bottom
+ // add space after if display-align is distribute and
+ // this is not the last page
+ if (pbp.difference != 0 && displayAlign == Constants.EN_CENTER) {
+ childLC.setSpaceBefore(pbp.difference / 2);
+ } else if (pbp.difference != 0 && displayAlign == Constants.EN_AFTER) {
+ childLC.setSpaceBefore(pbp.difference);
+ } else if (pbp.difference != 0 && displayAlign == Constants.EN_X_DISTRIBUTE
+ && p < (partCount - 1)) {
+ // count the boxes whose width is not 0
+ int boxCount = 0;
+ effectiveListIterator = effectiveList
+ .listIterator(startElementIndex);
+ while (effectiveListIterator.nextIndex() <= endElementIndex) {
+ KnuthElement tempEl = (KnuthElement)effectiveListIterator.next();
+ if (tempEl.isBox() && tempEl.getW() > 0) {
+ boxCount++;
+ }
+ }
+ // split the difference
+ if (boxCount >= 2) {
+ childLC.setSpaceAfter(pbp.difference / (boxCount - 1));
+ }
+ }
+
+ /* *** *** non-standard extension *** *** */
+ if (displayAlign == Constants.EN_X_FILL) {
+ int averageLineLength = optimizeLineLength(effectiveList, startElementIndex, endElementIndex);
+ if (averageLineLength != 0) {
+ childLC.setStackLimit(new MinOptMax(averageLineLength));
+ }
+ }
+ /* *** *** non-standard extension *** *** */
+
+ addAreas(new KnuthPossPosIter(effectiveList,
+ startElementIndex, endElementIndex + 1), childLC);
+ }
+
+ finishPart();
+
+ startElementIndex = pbp.getLeafPos() + 1;
+ }
+ }
+
+ /**
+ * Gets the next block list (sequence) and adds it to a list of block lists if it's not empty.
+ * @param childLC LayoutContext to use
+ * @param nextSequenceStartsOn indicates on what page the next sequence should start
+ * @param blockLists list of block lists (sequences)
+ * @return the page on which the next content should appear after a hard break
+ */
+ private int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn, List blockLists) {
+ LinkedList returnedList;
+ BlockSequence blockList;
+ if ((returnedList = getNextKnuthElements(childLC, alignment)) != null) {
+ if (returnedList.size() == 0) {
+ return nextSequenceStartsOn;
+ }
+ blockList = new BlockSequence(nextSequenceStartsOn);
+ if (((KnuthElement) returnedList.getLast()).isPenalty()
+ && ((KnuthPenalty) returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+ KnuthPenalty breakPenalty = (KnuthPenalty) returnedList
+ .removeLast();
+ switch (breakPenalty.getBreakClass()) {
+ case Constants.EN_PAGE:
+ System.err.println("PLM> break - PAGE");
+ nextSequenceStartsOn = Constants.EN_ANY;
+ break;
+ case Constants.EN_COLUMN:
+ System.err.println("PLM> break - COLUMN");
+ //TODO Fix this when implementing multi-column layout
+ nextSequenceStartsOn = Constants.EN_COLUMN;
+ break;
+ case Constants.EN_ODD_PAGE:
+ System.err.println("PLM> break - ODD PAGE");
+ nextSequenceStartsOn = Constants.EN_ODD_PAGE;
+ break;
+ case Constants.EN_EVEN_PAGE:
+ System.err.println("PLM> break - EVEN PAGE");
+ nextSequenceStartsOn = Constants.EN_EVEN_PAGE;
+ break;
+ default:
+ throw new IllegalStateException("Invalid break class: "
+ + breakPenalty.getBreakClass());
+ }
+ }
+ blockList.addAll(returnedList);
+ BlockSequence seq = null;
+ seq = blockList.endBlockSequence();
+ if (seq != null) {
+ blockLists.add(seq);
+ }
+ }
+ return nextSequenceStartsOn;
+ }
+
+ /**
+ * @param effectiveList effective block list to work on
+ * @param startElementIndex
+ * @param endElementIndex
+ * @return the average line length, 0 if there's no content
+ */
+ private int optimizeLineLength(KnuthSequence effectiveList, int startElementIndex, int endElementIndex) {
+ ListIterator effectiveListIterator;
+ // optimize line length
+ //System.out.println(" ");
+ int boxCount = 0;
+ int accumulatedLineLength = 0;
+ int greatestMinimumLength = 0;
+ effectiveListIterator = effectiveList
+ .listIterator(startElementIndex);
+ while (effectiveListIterator.nextIndex() <= endElementIndex) {
+ KnuthElement tempEl = (KnuthElement) effectiveListIterator
+ .next();
+ if (tempEl instanceof KnuthBlockBox) {
+ KnuthBlockBox blockBox = (KnuthBlockBox) tempEl;
+ if (blockBox.getBPD() > 0) {
+ log.debug("PSLM> nominal length of line = " + blockBox.getBPD());
+ log.debug(" range = "
+ + blockBox.getIPDRange());
+ boxCount++;
+ accumulatedLineLength += ((KnuthBlockBox) tempEl)
+ .getBPD();
+ }
+ if (blockBox.getIPDRange().min > greatestMinimumLength) {
+ greatestMinimumLength = blockBox
+ .getIPDRange().min;
+ }
+ }
+ }
+ int averageLineLength = 0;
+ if (accumulatedLineLength > 0 && boxCount > 0) {
+ averageLineLength = (int) (accumulatedLineLength / boxCount);
+ //System.out.println("PSLM> lunghezza media = " + averageLineLength);
+ if (averageLineLength < greatestMinimumLength) {
+ averageLineLength = greatestMinimumLength;
+ //System.out.println(" correzione, ora e' = " + averageLineLength);
+ }
+ }
+ return averageLineLength;
+ }
+
+ /**
+ * Justifies the boxes and returns them as a new KnuthSequence.
+ * @param blockList block list to justify
+ * @param alg reference to the algorithm instance
+ * @param availableBPD the available BPD
+ * @return the effective list
+ */
+ private BlockSequence justifyBoxes(BlockSequence blockList, PageBreakingAlgorithm alg, int availableBPD) {
+ int iOptPageNumber;
+ iOptPageNumber = alg.findBreakingPoints(blockList, availableBPD, 1,
+ true, true);
+ System.out.println("PLM> iOptPageNumber= " + iOptPageNumber);
+
+ //
+ ListIterator sequenceIterator = blockList.listIterator();
+ ListIterator breakIterator = alg.getPageBreaks().listIterator();
+ KnuthElement thisElement = null;
+ PageBreakPosition thisBreak;
+ int accumulatedS; // accumulated stretch or shrink
+ int adjustedDiff; // difference already adjusted
+ int firstElementIndex;
+
+ while (breakIterator.hasNext()) {
+ thisBreak = (PageBreakPosition) breakIterator.next();
+ System.out.println("| first page: break= "
+ + thisBreak.getLeafPos() + " difference= "
+ + thisBreak.difference + " ratio= "
+ + thisBreak.bpdAdjust);
+ accumulatedS = 0;
+ adjustedDiff = 0;
+
+ // glue and penalty items at the beginning of the page must
+ // be ignored:
+ // the first element returned by sequenceIterator.next()
+ // inside the
+ // while loop must be a box
+ KnuthElement firstElement;
+ while (!(firstElement = (KnuthElement) sequenceIterator
+ .next()).isBox()) {
+ //
+ System.out.println("PLM> ignoring glue or penalty element "
+ + "at the beginning of the sequence");
+ if (firstElement.isGlue()) {
+ ((BlockLevelLayoutManager) firstElement
+ .getLayoutManager())
+ .discardSpace((KnuthGlue) firstElement);
+ }
+ }
+ firstElementIndex = sequenceIterator.previousIndex();
+ sequenceIterator.previous();
+
+ // scan the sub-sequence representing a page,
+ // collecting information about potential adjustments
+ MinOptMax lineNumberMaxAdjustment = new MinOptMax(0);
+ MinOptMax spaceMaxAdjustment = new MinOptMax(0);
+ double spaceAdjustmentRatio = 0.0;
+ LinkedList blockSpacesList = new LinkedList();
+ LinkedList unconfirmedList = new LinkedList();
+ LinkedList adjustableLinesList = new LinkedList();
+ boolean bBoxSeen = false;
+ while (sequenceIterator.hasNext()
+ && sequenceIterator.nextIndex() <= thisBreak
+ .getLeafPos()) {
+ thisElement = (KnuthElement) sequenceIterator.next();
+ if (thisElement.isGlue()) {
+ // glue elements are used to represent adjustable
+ // lines
+ // and adjustable spaces between blocks
+ switch (((KnuthGlue) thisElement)
+ .getAdjustmentClass()) {
+ case BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT:
+ // fall through
+ case BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT:
+ // potential space adjustment
+ // glue items before the first box or after the
+ // last one
+ // must be ignored
+ unconfirmedList.add(thisElement);
+ break;
+ case BlockLevelLayoutManager.LINE_NUMBER_ADJUSTMENT:
+ // potential line number adjustment
+ lineNumberMaxAdjustment.max += ((KnuthGlue) thisElement)
+ .getY();
+ lineNumberMaxAdjustment.min -= ((KnuthGlue) thisElement)
+ .getZ();
+ adjustableLinesList.add(thisElement);
+ break;
+ case BlockLevelLayoutManager.LINE_HEIGHT_ADJUSTMENT:
+ // potential line height adjustment
+ break;
+ default:
+ // nothing
+ }
+ } else if (thisElement.isBox()) {
+ if (!bBoxSeen) {
+ // this is the first box met in this page
+ bBoxSeen = true;
+ } else if (unconfirmedList.size() > 0) {
+ // glue items in unconfirmedList were not after
+ // the last box
+ // in this page; they must be added to
+ // blockSpaceList
+ while (unconfirmedList.size() > 0) {
+ KnuthGlue blockSpace = (KnuthGlue) unconfirmedList
+ .removeFirst();
+ spaceMaxAdjustment.max += ((KnuthGlue) blockSpace)
+ .getY();
+ spaceMaxAdjustment.min -= ((KnuthGlue) blockSpace)
+ .getZ();
+ blockSpacesList.add(blockSpace);
+ }
+ }
+ }
+ }
+ System.out.println("| line number adj= "
+ + lineNumberMaxAdjustment);
+ System.out.println("| space adj = "
+ + spaceMaxAdjustment);
+
+ if (thisElement.isPenalty() && thisElement.getW() > 0) {
+ System.out
+ .println(" mandatory variation to the number of lines!");
+ ((BlockLevelLayoutManager) thisElement
+ .getLayoutManager()).negotiateBPDAdjustment(
+ thisElement.getW(), thisElement);
+ }
+
+ if (thisBreak.bpdAdjust != 0
+ && (thisBreak.difference > 0 && thisBreak.difference <= spaceMaxAdjustment.max)
+ || (thisBreak.difference < 0 && thisBreak.difference >= spaceMaxAdjustment.min)) {
+ // modify only the spaces between blocks
+ spaceAdjustmentRatio = ((double) thisBreak.difference / (thisBreak.difference > 0 ? spaceMaxAdjustment.max
+ : spaceMaxAdjustment.min));
+ adjustedDiff += adjustBlockSpaces(
+ blockSpacesList,
+ thisBreak.difference,
+ (thisBreak.difference > 0 ? spaceMaxAdjustment.max
+ : -spaceMaxAdjustment.min));
+ System.out.println("single space: "
+ + (adjustedDiff == thisBreak.difference
+ || thisBreak.bpdAdjust == 0 ? "ok"
+ : "ERROR"));
+ } else if (thisBreak.bpdAdjust != 0) {
+ adjustedDiff += adjustLineNumbers(
+ adjustableLinesList,
+ thisBreak.difference,
+ (thisBreak.difference > 0 ? lineNumberMaxAdjustment.max
+ : -lineNumberMaxAdjustment.min));
+ adjustedDiff += adjustBlockSpaces(
+ blockSpacesList,
+ thisBreak.difference - adjustedDiff,
+ ((thisBreak.difference - adjustedDiff) > 0 ? spaceMaxAdjustment.max
+ : -spaceMaxAdjustment.min));
+ System.out.println("lines and space: "
+ + (adjustedDiff == thisBreak.difference
+ || thisBreak.bpdAdjust == 0 ? "ok"
+ : "ERROR"));
+
+ }
+ }
+
+ // create a new sequence: the new elements will contain the
+ // Positions
+ // which will be used in the addAreas() phase
+ BlockSequence effectiveList = new BlockSequence(blockList.getStartOn());
+ effectiveList.addAll(getCurrentChildLM().getChangedKnuthElements(
+ blockList.subList(0, blockList.size() - blockList.ignoreAtEnd),
+ /* 0, */0));
+ //effectiveList.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
+ // false, new Position(this), false));
+ effectiveList.endSequence();
+
+ logEffectiveList(effectiveList);
+
+ alg.getPageBreaks().clear(); //Why this?
+ return effectiveList;
+ }
+
+ /**
+ * Logs the contents of a block list for debugging purposes
+ * @param blockList block list to log
+ */
+ private void logBlocklist(KnuthSequence blockList) {
+ ListIterator tempIter = blockList.listIterator();
+
+ KnuthElement temp;
+ System.out.println(" ");
+ while (tempIter.hasNext()) {
+ temp = (KnuthElement) tempIter.next();
+ if (temp.isBox()) {
+ System.out.println(tempIter.previousIndex()
+ + ") " + temp);
+ } else if (temp.isGlue()) {
+ System.out.println(tempIter.previousIndex()
+ + ") " + temp);
+ } else {
+ System.out.println(tempIter.previousIndex()
+ + ") " + temp);
+ }
+ if (temp.getPosition() != null) {
+ System.out.println(" " + temp.getPosition());
+ }
+ }
+ System.out.println(" ");
+ }
+
+ /**
+ * Logs the contents of an effective block list for debugging purposes
+ * @param effectiveList block list to log
+ */
+ private void logEffectiveList(KnuthSequence effectiveList) {
+ System.out.println("Effective list");
+ logBlocklist(effectiveList);
+ }
+
+ private int adjustBlockSpaces(LinkedList spaceList, int difference, int total) {
+ /*LF*/ System.out.println("AdjustBlockSpaces: difference " + difference + " / " + total + " on " + spaceList.size() + " spaces in block");
+ ListIterator spaceListIterator = spaceList.listIterator();
+ int adjustedDiff = 0;
+ int partial = 0;
+ while (spaceListIterator.hasNext()) {
+ KnuthGlue blockSpace = (KnuthGlue)spaceListIterator.next();
+ partial += (difference > 0 ? blockSpace.getY() : blockSpace.getZ());
+ System.out.println("available = " + partial + " / " + total);
+ System.out.println("competenza = " + (((int) ((float) partial * difference / total)) - adjustedDiff) + " / " + difference);
+ int newAdjust = ((BlockLevelLayoutManager) blockSpace.getLayoutManager()).negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, blockSpace);
+ adjustedDiff += newAdjust;
+ }
+ return adjustedDiff;
+ }
+
+ private int adjustLineNumbers(LinkedList lineList, int difference, int total) {
+ /*LF*/ System.out.println("AdjustLineNumbers: difference " + difference + " / " + total + " on " + lineList.size() + " elements");
+
+// int adjustedDiff = 0;
+// int partial = 0;
+// KnuthGlue prevLine = null;
+// KnuthGlue currLine = null;
+// ListIterator lineListIterator = lineList.listIterator();
+// while (lineListIterator.hasNext()) {
+// currLine = (KnuthGlue)lineListIterator.next();
+// if (prevLine != null
+// && prevLine.getLayoutManager() != currLine.getLayoutManager()) {
+// int newAdjust = ((BlockLevelLayoutManager) prevLine.getLayoutManager())
+// .negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, prevLine);
+// adjustedDiff += newAdjust;
+// }
+// partial += (difference > 0 ? currLine.getY() : currLine.getZ());
+// prevLine = currLine;
+// }
+// if (currLine != null) {
+// int newAdjust = ((BlockLevelLayoutManager) currLine.getLayoutManager())
+// .negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, currLine);
+// adjustedDiff += newAdjust;
+// }
+// return adjustedDiff;
+
+ ListIterator lineListIterator = lineList.listIterator();
+ int adjustedDiff = 0;
+ int partial = 0;
+ while (lineListIterator.hasNext()) {
+ KnuthGlue line = (KnuthGlue)lineListIterator.next();
+ partial += (difference > 0 ? line.getY() : line.getZ());
+ int newAdjust = ((BlockLevelLayoutManager) line.getLayoutManager()).negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, line);
+ adjustedDiff += newAdjust;
+ }
+ return adjustedDiff;
+ }
+
+
+}
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.FONode;
import org.apache.fop.area.Area;
-import org.apache.fop.area.Resolvable;
import org.apache.fop.area.PageViewport;
-import org.apache.fop.area.AreaTreeHandler;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.flow.RetrieveMarker;
-import org.apache.fop.fo.flow.Marker;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import java.util.LinkedList;
import java.util.List;
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.Map;
/**
- * The base class for all LayoutManagers.
+ * The base class for most LayoutManagers.
*/
public abstract class AbstractLayoutManager implements LayoutManager, Constants {
protected LayoutManager parentLM = null;
/**
* Used during addAreas(): signals that a BreakPoss is not generating areas
- * and therefore doesn't add IDs and markers to the current page.
+ * and therefore shouldn't add IDs and markers to the current page.
* @see org.apache.fop.layoutmgr.AbstractLayoutManager#isBogus
*/
protected boolean bBogus = false;
if (fo == null) {
throw new IllegalStateException("Null formatting object found.");
}
- setFObj(fo);
- }
-
- /**
- * Set the FO object for this layout manager
- *
- * @param fo the formatting object for this layout manager
- */
- public void setFObj(FObj fo) {
markers = fo.getMarkers();
fobjIter = fo.getChildNodes();
childLMiter = new LMiter(this);
return this.parentLM;
}
- // /**
- // * Ask the parent LayoutManager to add the current (full) area to the
- // * appropriate parent area.
- // * @param bFinished If true, this area is finished, either because it's
- // * completely full or because there is no more content to put in it.
- // * If false, we are in the middle of this area. This can happen,
- // * for example, if we find floats in a line. We stop the current area,
- // * and add it (temporarily) to its parent so that we can see if there
- // * is enough space to place the float(s) anchored in the line.
- // */
- // protected void flush(Area area, boolean bFinished) {
- // if (area != null) {
- // // area.setFinished(true);
- // parentLM.addChildArea(area, bFinished); // ????
- // if (bFinished) {
- // setCurrentArea(null);
- // }
- // }
- // }
-
- /**
- * Return an Area which can contain the passed childArea. The childArea
- * may not yet have any content, but it has essential traits set.
- * In general, if the LayoutManager already has an Area it simply returns
- * it. Otherwise, it makes a new Area of the appropriate class.
- * It gets a parent area for its area by calling its parent LM.
- * Finally, based on the dimensions of the parent area, it initializes
- * its own area. This includes setting the content IPD and the maximum
- * BPD.
- */
-
-
/** @see org.apache.fop.layoutmgr.LayoutManager#generatesInlineAreas() */
public boolean generatesInlineAreas() {
return false;
* interface which are declared abstract in AbstractLayoutManager.
* ---------------------------------------------------------*/
- /**
- * @see org.apache.fop.layoutmgr.LayoutManager#getParentArea(org.apache.fop.area.Area)
- */
- public Area getParentArea(Area childArea) {
+ public LinkedList getNextKnuthElements(LayoutContext context,
+ int alignment) {
+ log.warn("null implementation of getNextKnuthElements() called!");
+ setFinished(true);
return null;
}
- protected void flush() {
+ public KnuthElement addALetterSpaceTo(KnuthElement element) {
+ log.warn("null implementation of addALetterSpaceTo() called!");
+ return element;
}
- public void addChildArea(Area childArea) {
+ public void getWordChars(StringBuffer sbChars, Position pos) {
+ log.warn("null implementation of getWordChars() called!");
}
- /**
- * Delegate getting the current page number to the parent layout manager.
- *
- * @see org.apache.fop.layoutmgr.LayoutManager
- */
- public String getCurrentPageNumberString() {
- return parentLM.getCurrentPageNumberString();
+ public void hyphenate(Position pos, HyphContext hc) {
+ log.warn("null implementation of hyphenate called!");
}
- /**
- * Delegate resolving the id reference to the parent layout manager.
- *
- * @see org.apache.fop.layoutmgr.LayoutManager
- */
- public PageViewport resolveRefID(String ref) {
- return parentLM.resolveRefID(ref);
- }
-
- /**
- * Add the id to the page.
- * If the id string is not null then add the id to the current page.
- */
- protected void addID(String foID) {
- if (foID != null && foID.length() > 0) {
- addIDToPage(foID);
- }
- }
-
- /**
- * Delegate adding id reference to the parent layout manager.
- *
- * @see org.apache.fop.layoutmgr.LayoutManager
- */
- public void addIDToPage(String id) {
- parentLM.addIDToPage(id);
- }
-
- /**
- * Delegate adding unresolved area to the parent layout manager.
- *
- * @see org.apache.fop.layoutmgr.LayoutManager
- */
- public void addUnresolvedArea(String id, Resolvable res) {
- parentLM.addUnresolvedArea(id, res);
+ public boolean applyChanges(List oldList) {
+ log.warn("null implementation of applyChanges() called!");
+ return false;
}
- /**
- * Add the markers when adding an area.
- */
- protected void addMarkers(boolean starting, boolean isfirst, boolean islast) {
- // add markers
- if (markers != null) {
- addMarkerMap(markers, starting, isfirst, islast);
- }
+ public LinkedList getChangedKnuthElements(List oldList,
+ /*int flaggedPenalty,*/
+ int alignment) {
+ log.warn("null implementation of getChangeKnuthElement() called!");
+ return null;
}
- /**
- * Delegate adding marker to the parent layout manager.
- *
- * @see org.apache.fop.layoutmgr.LayoutManager
- */
- public void addMarkerMap(Map marks, boolean starting, boolean isfirst, boolean islast) {
- parentLM.addMarkerMap(marks, starting, isfirst, islast);
+ public int getWordSpaceIPD() {
+ log.warn("null implementation of getWordSpaceIPD() called!");
+ return 0;
}
/**
- * Delegate retrieve marker to the parent layout manager.
- *
- * @see org.apache.fop.layoutmgr.LayoutManager
+ * Return an Area which can contain the passed childArea. The childArea
+ * may not yet have any content, but it has essential traits set.
+ * In general, if the LayoutManager already has an Area it simply returns
+ * it. Otherwise, it makes a new Area of the appropriate class.
+ * It gets a parent area for its area by calling its parent LM.
+ * Finally, based on the dimensions of the parent area, it initializes
+ * its own area. This includes setting the content IPD and the maximum
+ * BPD.
*/
- public Marker retrieveMarker(String name, int pos, int boundary) {
- return parentLM.retrieveMarker(name, pos, boundary);
+ public Area getParentArea(Area childArea) {
+ return null;
}
- /**
- * Delegate getAreaTreeHandler to the parent layout manager.
- *
- * @see org.apache.fop.layoutmgr.LayoutManager
- * @return the AreaTreeHandler object.
- */
- public AreaTreeHandler getAreaTreeHandler() {
- return parentLM.getAreaTreeHandler();
+ public void addChildArea(Area childArea) {
}
- /**
- * Handles retrieve-marker nodes as they occur.
- * @param foNode FO node to check
- * @return the original foNode or in case of a retrieve-marker the replaced
- * FO node. null if the the replacement results in no nodes to be
- * processed.
- */
- private FONode handleRetrieveMarker(FONode foNode) {
- if (foNode instanceof RetrieveMarker) {
- RetrieveMarker rm = (RetrieveMarker) foNode;
- Marker marker = retrieveMarker(rm.getRetrieveClassName(),
- rm.getRetrievePosition(),
- rm.getRetrieveBoundary());
- if (marker == null) {
- return null;
- }
- rm.bindMarker(marker);
- return rm;
- } else {
- return foNode;
- }
- }
-
/**
* Convenience method: preload a number of child LMs
* @param size the requested number of child LMs
Object theobj = fobjIter.next();
if (theobj instanceof FONode) {
FONode foNode = (FONode) theobj;
- foNode = handleRetrieveMarker(foNode);
+ if (foNode instanceof RetrieveMarker) {
+ foNode = getPSLM().resolveRetrieveMarker(
+ (RetrieveMarker) foNode);
+ }
if (foNode != null) {
- getAreaTreeHandler().getLayoutManagerMaker().
+ getPSLM().getLayoutManagerMaker().
makeLayoutManagers(foNode, newLMs);
}
}
return newLMs;
}
+ /**
+ * @see org.apache.fop.layoutmgr.PageSequenceLayoutManager#getPSLM
+ */
+ public PageSequenceLayoutManager getPSLM() {
+ return parentLM.getPSLM();
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.PageSequenceLayoutManager#getCurrentPV
+ */
+ public PageViewport getCurrentPV() {
+ return getPSLM().getCurrentPV();
+ }
+
/**
* @see org.apache.fop.layoutmgr.LayoutManager#preLoadNext
*/
addChildLM(lm);
}
}
-
}
-
--- /dev/null
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.Iterator;
+import java.util.LinkedList;
+
+public class AreaAdditionUtil {
+
+ private static class StackingIter extends PositionIterator {
+ StackingIter(Iterator parentIter) {
+ super(parentIter);
+ }
+
+ protected LayoutManager getLM(Object nextObj) {
+ return ((Position) nextObj).getLM();
+ }
+
+ protected Position getPos(Object nextObj) {
+ return ((Position) nextObj);
+ }
+ }
+
+ public static void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
+ LayoutManager childLM = null;
+ LayoutContext lc = new LayoutContext(0);
+ LayoutManager firstLM = null;
+ LayoutManager lastLM = null;
+
+ // "unwrap" the NonLeafPositions stored in parentIter
+ // and put them in a new list;
+ LinkedList positionList = new LinkedList();
+ Position pos;
+ while (parentIter.hasNext()) {
+ pos = (Position)parentIter.next();
+ if (pos instanceof NonLeafPosition) {
+ // pos was created by a child of this FlowLM
+ positionList.add(((NonLeafPosition) pos).getPosition());
+ lastLM = ((NonLeafPosition) pos).getPosition().getLM();
+ if (firstLM == null) {
+ firstLM = lastLM;
+ }
+ } else {
+ // pos was created by this LM, so it must be ignored
+ }
+ }
+
+ StackingIter childPosIter = new StackingIter(positionList.listIterator());
+ while ((childLM = childPosIter.getNextChildLM()) != null) {
+ // Add the block areas to Area
+ lc.setFlags(LayoutContext.FIRST_AREA, childLM == firstLM);
+ lc.setFlags(LayoutContext.LAST_AREA, childLM == lastLM);
+ // set space before for the first LM, in order to implement
+ // display-align = center or after
+ lc.setSpaceBefore((childLM == firstLM ? layoutContext.getSpaceBefore() : 0));
+ // set space after for each LM, in order to implement
+ // display-align = distribute
+ lc.setSpaceAfter(layoutContext.getSpaceAfter());
+ lc.setStackLimit(layoutContext.getStackLimit());
+ childLM.addAreas(childPosIter, lc);
+ }
+ }
+
+}
if (fobj.getExternalDestination() != null) {
area.addTrait(Trait.EXTERNAL_LINK, fobj.getExternalDestination());
} else {
- String link = fobj.getInternalDestination();
- PageViewport page = parentLM.resolveRefID(link);
+ String idref = fobj.getInternalDestination();
+ PageViewport page = getPSLM().getFirstPVWithID(idref);
if (page != null) {
area.addTrait(Trait.INTERNAL_LINK, page.getKey());
} else {
- LinkResolver res = new LinkResolver(link, area);
- parentLM.addUnresolvedArea(link, res);
+ LinkResolver res = new LinkResolver(idref, area);
+ getPSLM().addUnresolvedArea(idref, res);
}
}
}
public BidiLayoutManager(BidiOverride node, InlineLayoutManager cLM) {
super(node);
children = new ArrayList();
- setFObj(node);
/*
for (int count = cLM.size() - 1; count >= 0; count--) {
InlineArea ia = cLM.get(count);
package org.apache.fop.layoutmgr;
+import java.util.LinkedList;
import java.util.List;
+import java.util.ListIterator;
import java.awt.Point;
import java.awt.geom.Rectangle2D;
import org.apache.fop.area.Area;
import org.apache.fop.area.BlockViewport;
import org.apache.fop.area.Block;
-import org.apache.fop.area.PageViewport;
import org.apache.fop.area.Trait;
import org.apache.fop.fo.flow.BlockContainer;
import org.apache.fop.fo.properties.CommonAbsolutePosition;
* LayoutManager for a block-container FO.
*/
public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
- private BlockContainer fobj;
private BlockViewport viewportBlockArea;
private Block referenceArea;
private int vpContentIPD;
private int vpContentBPD;
private int usedBPD;
-
+
// When viewport should grow with the content.
private boolean autoHeight = true;
//TODO space-before|after: handle space-resolution rules
private MinOptMax foBlockSpaceBefore;
private MinOptMax foBlockSpaceAfter;
+
+ private boolean bBreakBeforeServed = false;
+ private boolean bSpaceBeforeServed = false;
+
+ /*LF*/
+ /** Only used to store the original list when createUnitElements is called */
+ //TODO Maybe pull up as protected member if also used in this class (JM)
+ private LinkedList storedList = null;
+
/**
* Create a new block container layout manager.
*/
public BlockContainerLayoutManager(BlockContainer node) {
super(node);
- fobj = node;
}
- /**
- * @return the currently applicable page viewport
- */
- protected PageViewport getPageViewport() {
- LayoutManager lm = this;
- while (lm != null && !(lm instanceof PageSequenceLayoutManager)) {
- lm = lm.getParent();
- }
- if (lm == null) {
- return null;
- } else {
- return ((PageSequenceLayoutManager)lm).getCurrentPageViewport();
- }
- }
-
/**
* @see org.apache.fop.layoutmgr.AbstractLayoutManager#initProperties()
*/
protected void initProperties() {
- abProps = fobj.getCommonAbsolutePosition();
- foBlockSpaceBefore = new SpaceVal(fobj.getCommonMarginBlock().spaceBefore).getSpace();
- foBlockSpaceAfter = new SpaceVal(fobj.getCommonMarginBlock().spaceAfter).getSpace();
+ abProps = getBlockContainerFO().getCommonAbsolutePosition();
+ foBlockSpaceBefore = new SpaceVal(getBlockContainerFO().getCommonMarginBlock().spaceBefore).getSpace();
+ foBlockSpaceAfter = new SpaceVal(getBlockContainerFO().getCommonMarginBlock().spaceAfter).getSpace();
- boolean rotated = (fobj.getReferenceOrientation() % 180 != 0);
+ boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0);
if (rotated) {
- height = fobj.getInlineProgressionDimension().getOptimum().getLength();
- width = fobj.getBlockProgressionDimension().getOptimum().getLength();
+ height = getBlockContainerFO().getInlineProgressionDimension().getOptimum().getLength();
+ width = getBlockContainerFO().getBlockProgressionDimension().getOptimum().getLength();
} else {
- height = fobj.getBlockProgressionDimension().getOptimum().getLength();
- width = fobj.getInlineProgressionDimension().getOptimum().getLength();
+ height = getBlockContainerFO().getBlockProgressionDimension().getOptimum().getLength();
+ width = getBlockContainerFO().getInlineProgressionDimension().getOptimum().getLength();
}
+
+/*LF*/ bpUnit = 0; //layoutProps.blockProgressionUnit;
+/*LF*/ if (bpUnit == 0) {
+/*LF*/ // use optimum space values
+/*LF*/ adjustedSpaceBefore = getBlockContainerFO().getCommonMarginBlock().spaceBefore.getSpace().getOptimum().getLength().getValue();
+/*LF*/ adjustedSpaceAfter = getBlockContainerFO().getCommonMarginBlock().spaceAfter.getSpace().getOptimum().getLength().getValue();
+/*LF*/ } else {
+/*LF*/ // use minimum space values
+/*LF*/ adjustedSpaceBefore = getBlockContainerFO().getCommonMarginBlock().spaceBefore.getSpace().getMinimum().getLength().getValue();
+/*LF*/ adjustedSpaceAfter = getBlockContainerFO().getCommonMarginBlock().spaceAfter.getSpace().getMinimum().getLength().getValue();
+/*LF*/ }
}
/** @return the content IPD */
protected int getRotatedIPD() {
- return fobj.getInlineProgressionDimension().getOptimum().getLength().getValue();
+ return getBlockContainerFO().getInlineProgressionDimension().getOptimum().getLength().getValue();
}
private int getSpaceBefore() {
private int getBPIndents() {
int indents = 0;
- indents += fobj.getCommonMarginBlock().spaceBefore.getOptimum().getLength().getValue();
- indents += fobj.getCommonMarginBlock().spaceAfter.getOptimum().getLength().getValue();
- indents += fobj.getCommonBorderPaddingBackground().getBPPaddingAndBorder(false);
+ indents += getBlockContainerFO().getCommonMarginBlock().spaceBefore.getOptimum().getLength().getValue();
+ indents += getBlockContainerFO().getCommonMarginBlock().spaceAfter.getOptimum().getLength().getValue();
+ indents += getBlockContainerFO().getCommonBorderPaddingBackground().getBPPaddingAndBorder(false);
return indents;
}
private int getIPIndents() {
int iIndents = 0;
- iIndents += fobj.getCommonMarginBlock().startIndent.getValue();
- iIndents += fobj.getCommonMarginBlock().endIndent.getValue();
+ iIndents += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
+ iIndents += getBlockContainerFO().getCommonMarginBlock().endIndent.getValue();
return iIndents;
}
return (abProps.absolutePosition == EN_FIXED);
}
+ /**
+ * @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(org.apache.fop.layoutmgr.LayoutContext, int)
+ */
+ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+ if (isAbsoluteOrFixed()) {
+ return getNextKnuthElementsAbsolute(context, alignment);
+ }
+
+ autoHeight = false;
+ boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0); //vals[0] == 0.0;
+ referenceIPD = context.getRefIPD();
+ int maxbpd = context.getStackLimit().opt;
+ int allocBPD, allocIPD;
+ if (height.getEnum() != EN_AUTO) {
+ allocBPD = height.getValue(); //this is the content-height
+ allocBPD += getBPIndents();
+ } else {
+ allocBPD = maxbpd;
+ autoHeight = true;
+ }
+ if (width.getEnum() != EN_AUTO) {
+ allocIPD = width.getValue(); //this is the content-width
+ allocIPD += getIPIndents();
+ } else {
+ allocIPD = referenceIPD;
+ }
+
+ vpContentBPD = allocBPD - getBPIndents();
+ vpContentIPD = allocIPD - getIPIndents();
+
+ double contentRectOffsetX = 0;
+ contentRectOffsetX += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
+ double contentRectOffsetY = 0;
+ //contentRectOffsetY += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
+ //contentRectOffsetY += getSpaceBefore();
+ contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
+ contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getPaddingBefore(false);
+
+ Rectangle2D rect = new Rectangle2D.Double(
+ contentRectOffsetX, contentRectOffsetY,
+ vpContentIPD, vpContentBPD);
+ relDims = new FODimension(0, 0);
+ absoluteCTM = CTM.getCTMandRelDims(getBlockContainerFO().getReferenceOrientation(),
+ getBlockContainerFO().getWritingMode(), rect, relDims);
+
+ MinOptMax stackLimit = new MinOptMax(relDims.bpd);
+
+ LinkedList returnedList = null;
+ LinkedList contentList = new LinkedList();
+ LinkedList returnList = new LinkedList();
+ Position returnPosition = new NonLeafPosition(this, null);
+
+ if (!bBreakBeforeServed) {
+ try {
+ if (addKnuthElementsForBreakBefore(returnList, returnPosition)) {
+ return returnList;
+ }
+ } finally {
+ bBreakBeforeServed = true;
+ }
+ }
+
+ if (!bSpaceBeforeServed) {
+ addKnuthElementsForSpaceBefore(returnList, returnPosition, alignment);
+ bSpaceBeforeServed = true;
+ }
+
+ addKnuthElementsForBorderPaddingBefore(returnList, returnPosition);
+
+ if (autoHeight) {
+ BlockLevelLayoutManager curLM; // currently active LM
+ BlockLevelLayoutManager prevLM = null; // previously active LM
+ while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
+ LayoutContext childLC = new LayoutContext(0);
+ // curLM is a ?
+ childLC.setStackLimit(MinOptMax.subtract(context
+ .getStackLimit(), stackLimit));
+ childLC.setRefIPD(relDims.ipd);
+
+ // get elements from curLM
+ returnedList = curLM.getNextKnuthElements(childLC, alignment);
+ if (returnedList.size() == 1
+ && ((KnuthElement) returnedList.getFirst()).isPenalty()
+ && ((KnuthPenalty) returnedList.getFirst()).getP() == -KnuthElement.INFINITE) {
+ // a descendant of this block has break-before
+ if (returnList.size() == 0) {
+ // the first child (or its first child ...) has
+ // break-before;
+ // all this block, including space before, will be put in
+ // the
+ // following page
+ bSpaceBeforeServed = false;
+ }
+ contentList.addAll(returnedList);
+
+ // "wrap" the Position inside each element
+ // moving the elements from contentList to returnList
+ returnedList = new LinkedList();
+ wrapPositionElements(contentList, returnList);
+
+ return returnList;
+ } else {
+ if (prevLM != null) {
+ // there is a block handled by prevLM
+ // before the one handled by curLM
+ if (mustKeepTogether()
+ || prevLM.mustKeepWithNext()
+ || curLM.mustKeepWithPrevious()) {
+ // add an infinite penalty to forbid a break between
+ // blocks
+ contentList.add(new KnuthPenalty(0,
+ KnuthElement.INFINITE, false,
+ new Position(this), false));
+ } else if (!((KnuthElement) contentList.getLast()).isGlue()) {
+ // add a null penalty to allow a break between blocks
+ contentList.add(new KnuthPenalty(0, 0, false,
+ new Position(this), false));
+ } else {
+ // the last element in contentList is a glue;
+ // it is a feasible breakpoint, there is no need to add
+ // a penalty
+ }
+ }
+ contentList.addAll(returnedList);
+ if (returnedList.size() == 0) {
+ //Avoid NoSuchElementException below (happens with empty blocks)
+ continue;
+ }
+ if (((KnuthElement) returnedList.getLast()).isPenalty()
+ && ((KnuthPenalty) returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+ // a descendant of this block has break-after
+ if (curLM.isFinished()) {
+ // there is no other content in this block;
+ // it's useless to add space after before a page break
+ setFinished(true);
+ }
+
+ returnedList = new LinkedList();
+ wrapPositionElements(contentList, returnList);
+
+ return returnList;
+ }
+ }
+ prevLM = curLM;
+ }
+
+ returnedList = new LinkedList();
+ wrapPositionElements(contentList, returnList);
+
+ } else {
+ MinOptMax range = new MinOptMax(relDims.ipd);
+ BlockContainerBreaker breaker = new BlockContainerBreaker(this, range);
+ breaker.doLayout(relDims.bpd);
+ boolean contentOverflows = false;
+ if (!breaker.isEmpty()) {
+ contentOverflows = (breaker.deferredAlg.getPageBreaks().size() > 1);
+ }
+
+ Position bcPosition = new BlockContainerPosition(this, breaker);
+ returnList.add(new KnuthBox(vpContentBPD, bcPosition, false));
+ //TODO Handle min/opt/max for block-progression-dimension
+ /* These two elements will be used to add stretchability to the above box
+ returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, returnPosition, false));
+ returnList.add(new KnuthGlue(0, 1 * constantLineHeight, 0,
+ LINE_NUMBER_ADJUSTMENT, returnPosition, false));
+ */
+
+ if (contentOverflows) {
+ log.warn("Contents overflow block-container viewport: clipping");
+ if (getBlockContainerFO().getOverflow() == EN_HIDDEN) {
+ clip = true;
+ } else if (getBlockContainerFO().getOverflow() == EN_ERROR_IF_OVERFLOW) {
+ //TODO Throw layout exception
+ clip = true;
+ }
+ }
+ }
+ addKnuthElementsForBorderPaddingAfter(returnList, returnPosition);
+ addKnuthElementsForSpaceAfter(returnList, returnPosition, alignment);
+ addKnuthElementsForBreakAfter(returnList, returnPosition);
+
+ setFinished(true);
+ return returnList;
+ }
+
+ private LinkedList getNextKnuthElementsAbsolute(LayoutContext context, int alignment) {
+ MinOptMax stackSize = new MinOptMax();
+ autoHeight = false;
+
+ Point offset = getAbsOffset();
+ int allocBPD, allocIPD;
+ if (height.getEnum() != EN_AUTO) {
+ allocBPD = height.getValue(); //this is the content-height
+ allocBPD += getBPIndents();
+ } else {
+ allocBPD = 0;
+ if (abProps.bottom.getEnum() != EN_AUTO) {
+ if (isFixed()) {
+ allocBPD = (int)getCurrentPV().getViewArea().getHeight();
+ } else {
+ allocBPD = context.getStackLimit().opt;
+ }
+ allocBPD -= offset.y;
+ if (abProps.bottom.getEnum() != EN_AUTO) {
+ allocBPD -= abProps.bottom.getValue();
+ }
+ } else {
+ autoHeight = true;
+ }
+ }
+ if (width.getEnum() != EN_AUTO) {
+ allocIPD = width.getValue(); //this is the content-width
+ allocIPD += getIPIndents();
+ } else {
+ if (isFixed()) {
+ allocIPD = (int)getCurrentPV().getViewArea().getWidth();
+ } else {
+ allocIPD = context.getRefIPD();
+ }
+ if (abProps.left.getEnum() != EN_AUTO) {
+ allocIPD -= abProps.left.getValue();
+ }
+ if (abProps.right.getEnum() != EN_AUTO) {
+ allocIPD -= abProps.right.getValue();
+ }
+ }
+
+ vpContentBPD = allocBPD - getBPIndents();
+ vpContentIPD = allocIPD - getIPIndents();
+
+ double contentRectOffsetX = offset.getX();
+ contentRectOffsetX += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
+ double contentRectOffsetY = offset.getY();
+ contentRectOffsetY += getSpaceBefore();
+ contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
+ contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getPaddingBefore(false);
+
+ Rectangle2D rect = new Rectangle2D.Double(
+ contentRectOffsetX, contentRectOffsetY,
+ vpContentIPD, vpContentBPD);
+ relDims = new FODimension(0, 0);
+ absoluteCTM = CTM.getCTMandRelDims(
+ getBlockContainerFO().getReferenceOrientation(),
+ getBlockContainerFO().getWritingMode(),
+ rect, relDims);
+
+ MinOptMax range = new MinOptMax(relDims.ipd);
+ BlockContainerBreaker breaker = new BlockContainerBreaker(this, range);
+ breaker.doLayout(relDims.bpd);
+ boolean contentOverflows = breaker.isOverflow();
+ LinkedList returnList = new LinkedList();
+ if (!breaker.isEmpty()) {
+ usedBPD = relDims.bpd - breaker.getDifferenceOfFirstPart();
+
+ Position bcPosition = new BlockContainerPosition(this, breaker);
+ returnList.add(new KnuthBox(0, bcPosition, false));
+
+ //TODO Maybe check for page overflow when autoHeight=true
+ if (!autoHeight & (contentOverflows/*usedBPD > relDims.bpd*/)) {
+ log.warn("Contents overflow block-container viewport: clipping");
+ if (getBlockContainerFO().getOverflow() == EN_HIDDEN) {
+ clip = true;
+ } else if (getBlockContainerFO().getOverflow() == EN_ERROR_IF_OVERFLOW) {
+ //TODO Throw layout exception
+ clip = true;
+ }
+ }
+ }
+
+ setFinished(true);
+ return returnList;
+ }
+
+ private class BlockContainerPosition extends NonLeafPosition {
+
+ private BlockContainerBreaker breaker;
+
+ public BlockContainerPosition(LayoutManager lm, BlockContainerBreaker breaker) {
+ super(lm, null);
+ this.breaker = breaker;
+ }
+
+ public BlockContainerBreaker getBreaker() {
+ return this.breaker;
+ }
+
+ }
+
+ private class BlockContainerBreaker extends AbstractBreaker {
+
+ private BlockContainerLayoutManager bclm;
+ private MinOptMax ipd;
+
+ //Info for deferred adding of areas
+ private PageBreakingAlgorithm deferredAlg;
+ private BlockSequence deferredOriginalList;
+ private BlockSequence deferredEffectiveList;
+
+ public BlockContainerBreaker(BlockContainerLayoutManager bclm, MinOptMax ipd) {
+ this.bclm = bclm;
+ this.ipd = ipd;
+ }
+
+ public int getDifferenceOfFirstPart() {
+ PageBreakPosition pbp = (PageBreakPosition)this.deferredAlg.getPageBreaks().getFirst();
+ return pbp.difference;
+ }
+
+ public boolean isOverflow() {
+ if (isEmpty()) {
+ return false;
+ } else {
+ return (deferredAlg.getPageBreaks().size() > 1);
+ }
+ }
+
+ protected LayoutManager getTopLevelLM() {
+ return bclm;
+ }
+
+ protected LayoutContext createLayoutContext() {
+ LayoutContext lc = super.createLayoutContext();
+ lc.setRefIPD(ipd.opt);
+ return lc;
+ }
+
+ protected LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+ LayoutManager curLM; // currently active LM
+ LinkedList returnList = new LinkedList();
+
+ while ((curLM = getChildLM()) != null) {
+ LayoutContext childLC = new LayoutContext(0);
+ childLC.setStackLimit(context.getStackLimit());
+ childLC.setRefIPD(context.getRefIPD());
+
+ LinkedList returnedList = null;
+ if (!curLM.isFinished()) {
+ returnedList = curLM.getNextKnuthElements(childLC, alignment);
+ }
+ if (returnedList != null) {
+ bclm.wrapPositionElements(returnedList, returnList);
+ //returnList.addAll(returnedList);
+ }
+ }
+ setFinished(true);
+ return returnList;
+ }
+
+ protected int getCurrentDisplayAlign() {
+ return getBlockContainerFO().getDisplayAlign();
+ }
+
+ protected boolean hasMoreContent() {
+ return !isFinished();
+ }
+
+ protected void addAreas(PositionIterator posIter, LayoutContext context) {
+ AreaAdditionUtil.addAreas(posIter, context);
+ }
+
+ protected void doPhase3(PageBreakingAlgorithm alg, int partCount,
+ BlockSequence originalList, BlockSequence effectiveList) {
+ //Defer adding of areas until addAreas is called by the parent LM
+ this.deferredAlg = alg;
+ this.deferredOriginalList = originalList;
+ this.deferredEffectiveList = effectiveList;
+ }
+
+ protected void finishPart() {
+ //nop for bclm
+ }
+
+ protected LayoutManager getCurrentChildLM() {
+ return curChildLM;
+ }
+
+ public void addContainedAreas() {
+ if (isEmpty()) {
+ return;
+ }
+ //Rendering all parts (not just the first) at once for the case where the parts that
+ //overflow should be visible.
+ //TODO Check if this has any unwanted side-effects. Feels a bit like a hack.
+ addAreas(this.deferredAlg,
+ /*1*/ this.deferredAlg.getPageBreaks().size(),
+ this.deferredOriginalList, this.deferredEffectiveList);
+ }
+
+ }
+
/**
* @see org.apache.fop.layoutmgr.LayoutManager#getNextBreakPoss(org.apache.fop.layoutmgr.LayoutContext)
*/
}
autoHeight = false;
- boolean rotated = (fobj.getReferenceOrientation() % 180 != 0); //vals[0] == 0.0;
+ boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0); //vals[0] == 0.0;
referenceIPD = context.getRefIPD();
int maxbpd = context.getStackLimit().opt;
int allocBPD, allocIPD;
vpContentIPD = allocIPD - getIPIndents();
double contentRectOffsetX = 0;
- contentRectOffsetX += fobj.getCommonMarginBlock().startIndent.getValue();
+ contentRectOffsetX += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
double contentRectOffsetY = 0;
- //contentRectOffsetY += fobj.getCommonMarginBlock().startIndent.getValue();
+ //contentRectOffsetY += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
//contentRectOffsetY += getSpaceBefore();
- contentRectOffsetY += fobj.getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
- contentRectOffsetY += fobj.getCommonBorderPaddingBackground().getPaddingBefore(false);
+ contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
+ contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getPaddingBefore(false);
Rectangle2D rect = new Rectangle2D.Double(
contentRectOffsetX, contentRectOffsetY,
vpContentIPD, vpContentBPD);
relDims = new FODimension(0, 0);
- absoluteCTM = CTM.getCTMandRelDims(fobj.getReferenceOrientation(),
- fobj.getWritingMode(), rect, relDims);
+ absoluteCTM = CTM.getCTMandRelDims(getBlockContainerFO().getReferenceOrientation(),
+ getBlockContainerFO().getWritingMode(), rect, relDims);
//double[] vals = absoluteCTM.toArray();
MinOptMax stackLimit;
BreakPoss lastPos = null;
//TODO fix layout dimensions!
- fobj.setLayoutDimension(PercentBase.BLOCK_IPD, allocIPD);
- fobj.setLayoutDimension(PercentBase.BLOCK_BPD, allocBPD);
- fobj.setLayoutDimension(PercentBase.REFERENCE_AREA_IPD, relDims.ipd);
- fobj.setLayoutDimension(PercentBase.REFERENCE_AREA_BPD, relDims.bpd);
+ getBlockContainerFO().setLayoutDimension(PercentBase.BLOCK_IPD, allocIPD);
+ getBlockContainerFO().setLayoutDimension(PercentBase.BLOCK_BPD, allocBPD);
+ getBlockContainerFO().setLayoutDimension(PercentBase.REFERENCE_AREA_IPD, relDims.ipd);
+ getBlockContainerFO().setLayoutDimension(PercentBase.REFERENCE_AREA_BPD, relDims.bpd);
while ((curLM = getChildLM()) != null) {
//Treat bc with fixed BPD as non-breakable
allocBPD = 0;
if (abProps.bottom.getEnum() != EN_AUTO) {
if (isFixed()) {
- allocBPD = (int)getPageViewport().getViewArea().getHeight();
+ allocBPD = (int)getCurrentPV().getViewArea().getHeight();
} else {
allocBPD = context.getStackLimit().opt;
}
allocIPD += getIPIndents();
} else {
if (isFixed()) {
- allocIPD = (int)getPageViewport().getViewArea().getWidth();
+ allocIPD = (int)getCurrentPV().getViewArea().getWidth();
} else {
allocIPD = context.getRefIPD();
}
vpContentIPD = allocIPD - getIPIndents();
double contentRectOffsetX = offset.getX();
- contentRectOffsetX += fobj.getCommonMarginBlock().startIndent.getValue();
+ contentRectOffsetX += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
double contentRectOffsetY = offset.getY();
contentRectOffsetY += getSpaceBefore();
- contentRectOffsetY += fobj.getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
- contentRectOffsetY += fobj.getCommonBorderPaddingBackground().getPaddingBefore(false);
+ contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
+ contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getPaddingBefore(false);
Rectangle2D rect = new Rectangle2D.Double(
contentRectOffsetX, contentRectOffsetY,
vpContentIPD, vpContentBPD);
relDims = new FODimension(0, 0);
absoluteCTM = CTM.getCTMandRelDims(
- fobj.getReferenceOrientation(),
- fobj.getWritingMode(),
+ getBlockContainerFO().getReferenceOrientation(),
+ getBlockContainerFO().getWritingMode(),
rect, relDims);
while ((curLM = getChildLM()) != null) {
//TODO Maybe check for page overflow when autoHeight=true
if (!autoHeight & (usedBPD > relDims.bpd)) {
log.warn("Contents overflow block-container viewport: clipping");
- if (fobj.getOverflow() == EN_HIDDEN) {
+ if (getBlockContainerFO().getOverflow() == EN_HIDDEN) {
clip = true;
- } else if (fobj.getOverflow() == EN_ERROR_IF_OVERFLOW) {
+ } else if (getBlockContainerFO().getOverflow() == EN_ERROR_IF_OVERFLOW) {
//TODO Throw layout exception
clip = true;
}
* @see org.apache.fop.layoutmgr.LayoutManager#addAreas(org.apache.fop.layoutmgr.PositionIterator, org.apache.fop.layoutmgr.LayoutContext)
*/
public void addAreas(PositionIterator parentIter,
+ LayoutContext layoutContext) {
+ getParentArea(null);
+
+ // if this will create the first block area in a page
+ // and display-align is bottom or center, add space before
+ if (layoutContext.getSpaceBefore() > 0) {
+ addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore()));
+ }
+
+ getPSLM().addIDToPage(getBlockContainerFO().getId());
+ //addMarkersToPV(true, bp1.isFirstArea(), bp1.isLastArea());
+ getCurrentPV().addMarkers(markers, true, true, false);
+
+ LayoutManager childLM = null;
+ LayoutManager lastLM = null;
+ LayoutContext lc = new LayoutContext(0);
+ // set space after in the LayoutContext for children
+ if (layoutContext.getSpaceAfter() > 0) {
+ lc.setSpaceAfter(layoutContext.getSpaceAfter());
+ }
+ BlockContainerPosition bcpos = null;
+ PositionIterator childPosIter;
+
+ // "unwrap" the NonLeafPositions stored in parentIter
+ // and put them in a new list;
+ LinkedList positionList = new LinkedList();
+ Position pos;
+ boolean bSpaceBefore = false;
+ boolean bSpaceAfter = false;
+ while (parentIter.hasNext()) {
+ pos = (Position) parentIter.next();
+ /* LF *///System.out.println("pos = " + pos.getClass().getName());
+ Position innerPosition = ((NonLeafPosition) pos).getPosition();
+ if (pos instanceof BlockContainerPosition) {
+ if (bcpos != null) {
+ throw new IllegalStateException("Only one BlockContainerPosition allowed");
+ }
+ bcpos = (BlockContainerPosition)pos;
+ //Add child areas inside the reference area
+ //bcpos.getBreaker().addContainedAreas();
+ } else if (innerPosition == null) {
+ // pos was created by this BCLM and was inside an element
+ // representing space before or after
+ // this means the space was not discarded
+ if (positionList.size() == 0) {
+ // pos was in the element representing space-before
+ bSpaceBefore = true;
+ /* LF *///System.out.println(" space-before");
+ } else {
+ // pos was in the element representing space-after
+ bSpaceAfter = true;
+ /* LF *///System.out.println(" space-after");
+ }
+ } else if (innerPosition.getLM() == this
+ && !(innerPosition instanceof MappingPosition)) {
+ // pos was created by this BlockLM and was inside a penalty
+ // allowing or forbidding a page break
+ // nothing to do
+ /* LF *///System.out.println(" penalty");
+ } else {
+ // innerPosition was created by another LM
+ positionList.add(innerPosition);
+ lastLM = innerPosition.getLM();
+ /* LF *///System.out.println(" " +
+ // innerPosition.getClass().getName());
+ }
+ }
+
+ if (bcpos == null) {
+
+ if (bpUnit == 0) {
+ // the Positions in positionList were inside the elements
+ // created by the LineLM
+ childPosIter = new StackingIter(positionList.listIterator());
+ } else {
+ // the Positions in positionList were inside the elements
+ // created by the BCLM in the createUnitElements() method
+ //if (((Position) positionList.getLast()) instanceof
+ // LeafPosition) {
+ // // the last item inside positionList is a LeafPosition
+ // // (a LineBreakPosition, more precisely); this means that
+ // // the whole paragraph is on the same page
+ // System.out.println("paragrafo intero");
+ // childPosIter = new KnuthPossPosIter(storedList, 0,
+ // storedList.size());
+ //} else {
+ // // the last item inside positionList is a Position;
+ // // this means that the paragraph has been split
+ // // between consecutive pages
+ LinkedList splitList = new LinkedList();
+ int splitLength = 0;
+ int iFirst = ((MappingPosition) positionList.getFirst()).getFirstIndex();
+ int iLast = ((MappingPosition) positionList.getLast()).getLastIndex();
+ // copy from storedList to splitList all the elements from
+ // iFirst to iLast
+ ListIterator storedListIterator = storedList.listIterator(iFirst);
+ while (storedListIterator.nextIndex() <= iLast) {
+ KnuthElement element = (KnuthElement) storedListIterator
+ .next();
+ // some elements in storedList (i.e. penalty items) were created
+ // by this BlockLM, and must be ignored
+ if (element.getLayoutManager() != this) {
+ splitList.add(element);
+ splitLength += element.getW();
+ lastLM = element.getLayoutManager();
+ }
+ }
+ //System.out.println("addAreas riferito a storedList da " +
+ // iFirst + " a " + iLast);
+ //System.out.println("splitLength= " + splitLength
+ // + " (" + neededUnits(splitLength) + " unita') "
+ // + (neededUnits(splitLength) * bpUnit - splitLength) + " spazi");
+ // add space before and / or after the paragraph
+ // to reach a multiple of bpUnit
+ if (bSpaceBefore && bSpaceAfter) {
+ foBlockSpaceBefore = new SpaceVal(getBlockContainerFO().getCommonMarginBlock().spaceBefore).getSpace();
+ foBlockSpaceAfter = new SpaceVal(getBlockContainerFO().getCommonMarginBlock().spaceAfter).getSpace();
+ adjustedSpaceBefore = (neededUnits(splitLength
+ + foBlockSpaceBefore.min
+ + foBlockSpaceAfter.min)
+ * bpUnit - splitLength) / 2;
+ adjustedSpaceAfter = neededUnits(splitLength
+ + foBlockSpaceBefore.min
+ + foBlockSpaceAfter.min)
+ * bpUnit - splitLength - adjustedSpaceBefore;
+ } else if (bSpaceBefore) {
+ adjustedSpaceBefore = neededUnits(splitLength
+ + foBlockSpaceBefore.min)
+ * bpUnit - splitLength;
+ } else {
+ adjustedSpaceAfter = neededUnits(splitLength
+ + foBlockSpaceAfter.min)
+ * bpUnit - splitLength;
+ }
+ //System.out.println("spazio prima = " + adjustedSpaceBefore
+ // + " spazio dopo = " + adjustedSpaceAfter + " totale = " +
+ // (adjustedSpaceBefore + adjustedSpaceAfter + splitLength));
+ childPosIter = new KnuthPossPosIter(splitList, 0, splitList
+ .size());
+ //}
+ }
+
+ // if adjusted space before
+ if (bSpaceBefore) {
+ addBlockSpacing(0, new MinOptMax(adjustedSpaceBefore));
+ }
+
+ while ((childLM = childPosIter.getNextChildLM()) != null) {
+ // set last area flag
+ lc.setFlags(LayoutContext.LAST_AREA,
+ (layoutContext.isLastArea() && childLM == lastLM));
+ /*LF*/lc.setStackLimit(layoutContext.getStackLimit());
+ // Add the line areas to Area
+ childLM.addAreas(childPosIter, lc);
+ }
+ } else {
+ // if adjusted space before
+ if (bSpaceBefore) {
+ addBlockSpacing(0, new MinOptMax(adjustedSpaceBefore));
+ }
+ //Add child areas inside the reference area
+ bcpos.getBreaker().addContainedAreas();
+ }
+
+ int bIndents = getBlockContainerFO().getCommonBorderPaddingBackground().getBPPaddingAndBorder(false);
+
+ getCurrentPV().addMarkers(markers, false, false, true);
+
+ flush();
+
+ // if adjusted space after
+ if (bSpaceAfter) {
+ addBlockSpacing(0, new MinOptMax(adjustedSpaceAfter));
+ }
+
+ viewportBlockArea = null;
+ referenceArea = null;
+ }
+
+ public void addAreasOLDOLDOLD(PositionIterator parentIter,
LayoutContext layoutContext) {
getParentArea(null);
}*/
BreakPoss bp1 = (BreakPoss)parentIter.peekNext();
- addID(fobj.getId());
- addMarkers(true, bp1.isFirstArea(), bp1.isLastArea());
+ getPSLM().addIDToPage(getBlockContainerFO().getId());
+ getCurrentPV().addMarkers(markers, true, bp1.isFirstArea(), bp1.isLastArea());
LayoutManager childLM;
int iStartPos = 0;
}
flush();
- addMarkers(true, bp1.isFirstArea(), bp1.isLastArea());
+ getCurrentPV().addMarkers(markers, true, bp1.isFirstArea(), bp1.isLastArea());
/*
if (!isAbsoluteOrFixed()) {
// if adjusted space after
- foBlockSpaceAfter = new SpaceVal(fobj.getCommonMarginBlock().spaceAfter).getSpace();
+ foBlockSpaceAfter = new SpaceVal(getBlockContainerFO().getCommonMarginBlock().spaceAfter).getSpace();
addBlockSpacing(adjust, foBlockSpaceAfter);
}*/
viewportBlockArea.setBPD(vpContentBPD);
}
- TraitSetter.addBorders(viewportBlockArea, fobj.getCommonBorderPaddingBackground());
- TraitSetter.addBackground(viewportBlockArea, fobj.getCommonBorderPaddingBackground());
+ TraitSetter.addBorders(viewportBlockArea, getBlockContainerFO().getCommonBorderPaddingBackground());
+ TraitSetter.addBackground(viewportBlockArea, getBlockContainerFO().getCommonBorderPaddingBackground());
TraitSetter.addMargins(viewportBlockArea,
- fobj.getCommonBorderPaddingBackground(),
- fobj.getCommonMarginBlock());
+ getBlockContainerFO().getCommonBorderPaddingBackground(),
+ getBlockContainerFO().getCommonMarginBlock());
viewportBlockArea.setCTM(absoluteCTM);
viewportBlockArea.setClip(clip);
//Handle display-align now that the used BPD can be determined
usedBPD = referenceArea.getAllocBPD();
+ /* done by the breaker now by inserting additional boxes
if (!autoHeight & (usedBPD > 0)) {
- if (fobj.getDisplayAlign() == EN_CENTER) {
+ if (getBlockContainerFO().getDisplayAlign() == EN_CENTER) {
viewportBlockArea.setCTM(viewportBlockArea.getCTM().multiply(
new CTM().translate(0, (relDims.bpd - usedBPD) / 2)));
- } else if (fobj.getDisplayAlign() == EN_AFTER) {
+ } else if (getBlockContainerFO().getDisplayAlign() == EN_AFTER) {
viewportBlockArea.setCTM(viewportBlockArea.getCTM().multiply(
new CTM().translate(0, (relDims.bpd - usedBPD))));
}
- }
+ }*/
// Fake a 0 height for absolute positioned blocks.
int saveBPD = viewportBlockArea.getBPD();
viewportBlockArea.setBPD(saveBPD);
}
}
-
+
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#negotiateBPDAdjustment(int, org.apache.fop.layoutmgr.KnuthElement)
+ */
+ public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#discardSpace(org.apache.fop.layoutmgr.KnuthGlue)
+ */
+ public void discardSpace(KnuthGlue spaceGlue) {
+ // TODO Auto-generated method stub
+
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
+ */
+ public boolean mustKeepTogether() {
+ //TODO Keeps will have to be more sophisticated sooner or later
+ return ((BlockLevelLayoutManager)getParent()).mustKeepTogether()
+ || !getBlockContainerFO().getKeepTogether().getWithinPage().isAuto()
+ || !getBlockContainerFO().getKeepTogether().getWithinColumn().isAuto();
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
+ */
+ public boolean mustKeepWithPrevious() {
+ return !getBlockContainerFO().getKeepWithPrevious().getWithinPage().isAuto()
+ || !getBlockContainerFO().getKeepWithPrevious().getWithinColumn().isAuto();
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
+ */
+ public boolean mustKeepWithNext() {
+ return !getBlockContainerFO().getKeepWithNext().getWithinPage().isAuto()
+ || !getBlockContainerFO().getKeepWithNext().getWithinColumn().isAuto();
+ }
+
+ /**
+ * convenience method that returns the BlockContainer node
+ */
+ protected BlockContainer getBlockContainerFO() {
+ return (BlockContainer) fobj;
+ }
}
package org.apache.fop.layoutmgr;
+import java.util.LinkedList;
import java.util.ListIterator;
import java.util.List;
public class BlockLayoutManager extends BlockStackingLayoutManager {
private static final int FINISHED_LEAF_POS = -2;
-
- private org.apache.fop.fo.flow.Block fobj;
-
+
private Block curBlockArea;
/** Iterator over the child layout managers. */
private int iStartPos = 0;
- private int referenceIPD = 0;
//private int contentIPD = 0;
/** The list of child BreakPoss instances. */
protected List childBreaks = new java.util.ArrayList();
private boolean isfirst = true;
+
+ private LineLayoutManager childLLM = null;
/**
* Creates a new BlockLayoutManager.
*/
public BlockLayoutManager(org.apache.fop.fo.flow.Block inBlock) {
super(inBlock);
- fobj = inBlock;
proxyLMiter = new ProxyLMiter();
- Font fs = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo());
+ Font fs = getBlockFO().getCommonFont().getFontState(
+ getBlockFO().getFOEventHandler().getFontInfo());
lead = fs.getAscender();
follow = -fs.getDescender();
middleShift = -fs.getXHeight() / 2;
- lineHeight = fobj.getLineHeight().getOptimum().getLength().getValue();
+ lineHeight = getBlockFO().getLineHeight().getOptimum().getLength().getValue();
}
/**
* if defined for the block.
*/
protected void initProperties() {
- foBlockSpaceBefore = new SpaceVal(fobj.getCommonMarginBlock().spaceBefore).getSpace();
+ foBlockSpaceBefore = new SpaceVal(getBlockFO().getCommonMarginBlock().spaceBefore).getSpace();
prevFoBlockSpaceAfter = foBlockSpaceAfter;
+/*LF*/ bpUnit = 0; //layoutProps.blockProgressionUnit;
+/*LF*/ if (bpUnit == 0) {
+/*LF*/ // use optimum space values
+/*LF*/ adjustedSpaceBefore = getBlockFO().getCommonMarginBlock().spaceBefore.getSpace().getOptimum().getLength().getValue();
+/*LF*/ adjustedSpaceAfter = getBlockFO().getCommonMarginBlock().spaceAfter.getSpace().getOptimum().getLength().getValue();
+/*LF*/ } else {
+/*LF*/ // use minimum space values
+/*LF*/ adjustedSpaceBefore = getBlockFO().getCommonMarginBlock().spaceBefore.getSpace().getMinimum().getLength().getValue();
+/*LF*/ adjustedSpaceAfter = getBlockFO().getCommonMarginBlock().spaceAfter.getSpace().getMinimum().getLength().getValue();
+/*LF*/ }
}
/**
*/
private LineLayoutManager createLineManager(LayoutManager firstlm) {
LineLayoutManager llm;
- llm = new LineLayoutManager(fobj, lineHeight, lead, follow, middleShift);
+ llm = new LineLayoutManager(getBlockFO(), lineHeight, lead, follow, middleShift);
List inlines = new java.util.ArrayList();
inlines.add(firstlm);
while (proxyLMiter.hasNext()) {
private int getIPIndents() {
int iIndents = 0;
- iIndents += fobj.getCommonMarginBlock().startIndent.getValue();
- iIndents += fobj.getCommonMarginBlock().endIndent.getValue();
+ iIndents += getBlockFO().getCommonMarginBlock().startIndent.getValue();
+ iIndents += getBlockFO().getCommonMarginBlock().endIndent.getValue();
return iIndents;
}
/**
* @see org.apache.fop.layoutmgr.LayoutManager#getNextBreakPoss(org.apache.fop.layoutmgr.LayoutContext)
*/
- public BreakPoss getNextBreakPoss(LayoutContext context) {
+ public BreakPoss getNextBreakPossOLDOLDOLD(LayoutContext context) {
LayoutManager curLM; // currently active LM
//int refipd = context.getRefIPD();
BreakPoss lastPos = null;
// Set context for percentage property values.
- fobj.setLayoutDimension(PercentBase.BLOCK_IPD, contentipd);
- fobj.setLayoutDimension(PercentBase.BLOCK_BPD, -1);
+ getBlockFO().setLayoutDimension(PercentBase.BLOCK_IPD, contentipd);
+ getBlockFO().setLayoutDimension(PercentBase.BLOCK_BPD, -1);
while ((curLM = getChildLM()) != null) {
// Make break positions and return blocks!
if (getChildLM() == null || over) {
if (getChildLM() == null) {
setFinished(true);
- stackSize.add(new SpaceVal(fobj.getCommonMarginBlock().spaceAfter).getSpace());
+ stackSize.add(new SpaceVal(getBlockFO().getCommonMarginBlock().spaceAfter).getSpace());
}
BreakPoss breakPoss = new BreakPoss(
new LeafPosition(this, childBreaks.size() - 1));
return breakPoss;
}
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
+ */
+ public boolean mustKeepTogether() {
+ //TODO Keeps will have to be more sophisticated sooner or later
+ return ((BlockLevelLayoutManager)getParent()).mustKeepTogether()
+ || !getBlockFO().getKeepTogether().getWithinPage().isAuto()
+ || !getBlockFO().getKeepTogether().getWithinColumn().isAuto();
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
+ */
+ public boolean mustKeepWithPrevious() {
+ return !getBlockFO().getKeepWithPrevious().getWithinPage().isAuto()
+ || !getBlockFO().getKeepWithPrevious().getWithinColumn().isAuto();
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
+ */
+ public boolean mustKeepWithNext() {
+ return !getBlockFO().getKeepWithNext().getWithinPage().isAuto()
+ || !getBlockFO().getKeepWithNext().getWithinColumn().isAuto();
+ }
+
+ //TODO this method is no longer used
+ public BreakPoss getNextBreakPoss(LayoutContext context) {
+ setFinished(true);
+ return null;
+ }
+
/**
* @see org.apache.fop.layoutmgr.LayoutManager#addAreas(org.apache.fop.layoutmgr.PositionIterator, org.apache.fop.layoutmgr.LayoutContext)
*/
- public void addAreas(PositionIterator parentIter,
+ public void addAreasOLDOLDOLD(PositionIterator parentIter,
LayoutContext layoutContext) {
getParentArea(null);
foBlockSpaceBefore = null;
if (!isBogus()) {
- addID(fobj.getId());
- addMarkers(true, bp1.isFirstArea(), bp1.isLastArea());
+ getPSLM().addIDToPage(getBlockFO().getId());
+ getCurrentPV().addMarkers(markers, true, bp1.isFirstArea(),
+ bp1.isLastArea());
}
try {
}
} finally {
if (!isBogus()) {
- addMarkers(false, bp1.isFirstArea(), bp1.isLastArea());
+ getCurrentPV().addMarkers(markers, false, bp1.isFirstArea(),
+ bp1.isLastArea());
}
flush();
// if adjusted space after
- foBlockSpaceAfter = new SpaceVal(fobj.getCommonMarginBlock().spaceAfter).getSpace();
+ foBlockSpaceAfter = new SpaceVal(getBlockFO().getCommonMarginBlock().spaceAfter).getSpace();
addBlockSpacing(adjust, foBlockSpaceAfter);
curBlockArea = null;
}
}
+ public void addAreas(PositionIterator parentIter,
+ LayoutContext layoutContext) {
+ /* LF *///System.out.println(" BLM.addAreas>");
+ getParentArea(null);
+
+ // if this will create the first block area in a page
+ // and display-align is bottom or center, add space before
+ if (layoutContext.getSpaceBefore() > 0) {
+ addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore()));
+ }
+
+ getPSLM().addIDToPage(getBlockFO().getId());
+ //addMarkersToPV(true, bp1.isFirstArea(), bp1.isLastArea());
+ getCurrentPV().addMarkers(markers, true, true, false);
+
+ LayoutManager childLM = null;
+ LayoutManager lastLM = null;
+ LayoutContext lc = new LayoutContext(0);
+ /* LF */// set space after in the LayoutContext for children
+ /* LF */if (layoutContext.getSpaceAfter() > 0) {
+ /* LF */lc.setSpaceAfter(layoutContext.getSpaceAfter());
+ /* LF */}
+ /* LF */PositionIterator childPosIter;
+
+ // "unwrap" the NonLeafPositions stored in parentIter
+ // and put them in a new list;
+ LinkedList positionList = new LinkedList();
+ Position pos;
+ boolean bSpaceBefore = false;
+ boolean bSpaceAfter = false;
+ while (parentIter.hasNext()) {
+ pos = (Position) parentIter.next();
+ //log.trace("pos = " + pos.getClass().getName() + "; " + pos);
+ Position innerPosition = pos;
+ if (pos instanceof NonLeafPosition) {
+ //Not all elements are wrapped
+ innerPosition = ((NonLeafPosition) pos).getPosition();
+ }
+ if (innerPosition == null) {
+ // pos was created by this BlockLM and was inside an element
+ // representing space before or after
+ // this means the space was not discarded
+ if (positionList.size() == 0) {
+ // pos was in the element representing space-before
+ bSpaceBefore = true;
+ //log.trace(" space before");
+ } else {
+ // pos was in the element representing space-after
+ bSpaceAfter = true;
+ //log.trace(" space-after");
+ }
+ } else if (innerPosition.getLM() == this
+ && !(innerPosition instanceof MappingPosition)) {
+ // pos was created by this BlockLM and was inside a penalty
+ // allowing or forbidding a page break
+ // nothing to do
+ //log.trace(" penalty");
+ } else {
+ // innerPosition was created by another LM
+ positionList.add(innerPosition);
+ lastLM = innerPosition.getLM();
+ //log.trace(" " + innerPosition.getClass().getName());
+ }
+ }
+
+ if (bpUnit == 0) {
+ // the Positions in positionList were inside the elements
+ // created by the LineLM
+ childPosIter = new StackingIter(positionList.listIterator());
+ } else {
+ // the Positions in positionList were inside the elements
+ // created by the BlockLM in the createUnitElements() method
+ //if (((Position) positionList.getLast()) instanceof
+ // LeafPosition) {
+ // // the last item inside positionList is a LeafPosition
+ // // (a LineBreakPosition, more precisely); this means that
+ // // the whole paragraph is on the same page
+ // System.out.println("paragrafo intero");
+ // childPosIter = new KnuthPossPosIter(storedList, 0,
+ // storedList.size());
+ //} else {
+ // // the last item inside positionList is a Position;
+ // // this means that the paragraph has been split
+ // // between consecutive pages
+ LinkedList splitList = new LinkedList();
+ int splitLength = 0;
+ int iFirst = ((MappingPosition) positionList.getFirst()).getFirstIndex();
+ int iLast = ((MappingPosition) positionList.getLast()).getLastIndex();
+ // copy from storedList to splitList all the elements from
+ // iFirst to iLast
+ ListIterator storedListIterator = storedList.listIterator(iFirst);
+ while (storedListIterator.nextIndex() <= iLast) {
+ KnuthElement element = (KnuthElement) storedListIterator
+ .next();
+ // some elements in storedList (i.e. penalty items) were created
+ // by this BlockLM, and must be ignored
+ if (element.getLayoutManager() != this) {
+ splitList.add(element);
+ splitLength += element.getW();
+ lastLM = element.getLayoutManager();
+ }
+ }
+ //System.out.println("addAreas riferito a storedList da " +
+ // iFirst + " a " + iLast);
+ //System.out.println("splitLength= " + splitLength
+ // + " (" + neededUnits(splitLength) + " unita') "
+ // + (neededUnits(splitLength) * bpUnit - splitLength) + " spazi");
+ // add space before and / or after the paragraph
+ // to reach a multiple of bpUnit
+ if (bSpaceBefore && bSpaceAfter) {
+ foBlockSpaceBefore = new SpaceVal(getBlockFO().getCommonMarginBlock().spaceBefore).getSpace();
+ foBlockSpaceAfter = new SpaceVal(getBlockFO().getCommonMarginBlock().spaceAfter).getSpace();
+ adjustedSpaceBefore = (neededUnits(splitLength
+ + foBlockSpaceBefore.min
+ + foBlockSpaceAfter.min)
+ * bpUnit - splitLength) / 2;
+ adjustedSpaceAfter = neededUnits(splitLength
+ + foBlockSpaceBefore.min
+ + foBlockSpaceAfter.min)
+ * bpUnit - splitLength - adjustedSpaceBefore;
+ } else if (bSpaceBefore) {
+ adjustedSpaceBefore = neededUnits(splitLength
+ + foBlockSpaceBefore.min)
+ * bpUnit - splitLength;
+ } else {
+ adjustedSpaceAfter = neededUnits(splitLength
+ + foBlockSpaceAfter.min)
+ * bpUnit - splitLength;
+ }
+ //System.out.println("spazio prima = " + adjustedSpaceBefore
+ // + " spazio dopo = " + adjustedSpaceAfter + " totale = " +
+ // (adjustedSpaceBefore + adjustedSpaceAfter + splitLength));
+ childPosIter = new KnuthPossPosIter(splitList, 0, splitList
+ .size());
+ //}
+ }
+
+ // if adjusted space before
+ if (bSpaceBefore) {
+ addBlockSpacing(0, new MinOptMax(adjustedSpaceBefore));
+ }
+
+ while ((childLM = childPosIter.getNextChildLM()) != null) {
+ // set last area flag
+ lc.setFlags(LayoutContext.LAST_AREA,
+ (layoutContext.isLastArea() && childLM == lastLM));
+ /*LF*/lc.setStackLimit(layoutContext.getStackLimit());
+ // Add the line areas to Area
+ childLM.addAreas(childPosIter, lc);
+ }
+
+ int bIndents = getBlockFO().getCommonBorderPaddingBackground().getBPPaddingAndBorder(false);
+
+ getCurrentPV().addMarkers(markers, false, false, true);
+
+ flush();
+
+ // if adjusted space after
+ if (bSpaceAfter) {
+ addBlockSpacing(0, new MinOptMax(adjustedSpaceAfter));
+ }
+
+ curBlockArea = null;
+ }
+
/**
* Return an Area which can contain the passed childArea. The childArea
* may not yet have any content, but it has essential traits set.
if (curBlockArea == null) {
curBlockArea = new Block();
+ TraitSetter.addBreaks(curBlockArea,
+ getBlockFO().getBreakBefore(), getBlockFO().getBreakAfter());
+
// Must get dimensions from parent area
//Don't optimize this line away. It can have ugly side-effects.
/*Area parentArea =*/ parentLM.getParentArea(curBlockArea);
// set traits
TraitSetter.addBorders(curBlockArea,
- fobj.getCommonBorderPaddingBackground());
+ getBlockFO().getCommonBorderPaddingBackground());
TraitSetter.addBackground(curBlockArea,
- fobj.getCommonBorderPaddingBackground());
+ getBlockFO().getCommonBorderPaddingBackground());
TraitSetter.addMargins(curBlockArea,
- fobj.getCommonBorderPaddingBackground(),
- fobj.getCommonMarginBlock());
- TraitSetter.addBreaks(curBlockArea,
- fobj.getBreakBefore(), fobj.getBreakAfter());
+ getBlockFO().getCommonBorderPaddingBackground(),
+ getBlockFO().getCommonMarginBlock());
// Set up dimensions
// Get reference IPD from parentArea
LayoutManager lm = resetPos.getLM();
}
}
+
+ /**
+ * convenience method that returns the Block node
+ */
+ protected org.apache.fop.fo.flow.Block getBlockFO() {
+ return (org.apache.fop.fo.flow.Block) fobj;
+ }
}
--- /dev/null
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ * Licensed 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;
+
+/**
+ * The interface for LayoutManagers which generate block areas
+ */
+public interface BlockLevelLayoutManager extends LayoutManager {
+
+ static final int NO_ADJUSTMENT = -1;
+ static final int SPACE_BEFORE_ADJUSTMENT = 0;
+ static final int SPACE_AFTER_ADJUSTMENT = 1;
+ static final int LINE_NUMBER_ADJUSTMENT = 2;
+ static final int LINE_HEIGHT_ADJUSTMENT = 3;
+
+ int negotiateBPDAdjustment(int adj, KnuthElement lastElement);
+
+ void discardSpace(KnuthGlue spaceGlue);
+
+ /**
+ * @return true if this element must be kept together
+ */
+ boolean mustKeepTogether();
+
+ /**
+ * @return true if this element must be kept with the previous element.
+ */
+ boolean mustKeepWithPrevious();
+
+ /**
+ * @return true if this element must be kept with the next element.
+ */
+ boolean mustKeepWithNext();
+
+}
package org.apache.fop.layoutmgr;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
import org.apache.fop.area.Area;
import org.apache.fop.area.BlockParent;
import org.apache.fop.area.Block;
+import org.apache.fop.datatypes.PercentBase;
+import org.apache.fop.fo.Constants;
import org.apache.fop.fo.FObj;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
+import org.apache.fop.fo.properties.CommonMarginBlock;
+import org.apache.fop.fo.properties.SpaceProperty;
import org.apache.fop.traits.MinOptMax;
/**
* Base LayoutManager class for all areas which stack their child
* areas in the block-progression direction, such as Flow, Block, ListBlock.
*/
-public abstract class BlockStackingLayoutManager extends AbstractLayoutManager {
+public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
+ implements BlockLevelLayoutManager {
/**
* Reference to FO whose areas it's managing or to the traits
* of the FO.
//protected LayoutManager curChildLM = null; AbstractLayoutManager also defines this!
protected BlockParent parentArea = null;
+ /*LF*/
+ /** Value of the block-progression-unit (non-standard property) */
+ protected int bpUnit = 0;
+ /** space-before value adjusted for block-progression-unit handling */
+ protected int adjustedSpaceBefore = 0;
+ /** space-after value adjusted for block-progression-unit handling */
+ protected int adjustedSpaceAfter = 0;
+ /** Only used to store the original list when createUnitElements is called */
+ protected LinkedList storedList = null;
+ protected FObj fobj;
+ private boolean bBreakBeforeServed = false;
+ private boolean bSpaceBeforeServed = false;
+ protected int referenceIPD = 0;
+ /*LF*/
+
public BlockStackingLayoutManager(FObj node) {
super(node);
+ fobj = node;
}
private BreakCost evaluateBreakCost(Area parent, Area child) {
}
}
+ /**
+ * @param len length in millipoints to span with bp units
+ * @return the minimum integer n such that n * bpUnit >= len
+ */
+ protected int neededUnits(int len) {
+ return (int) Math.ceil((float)len / bpUnit);
+ }
+
+ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+ /* LF *///System.err.println("BLM.getNextKnuthElements> keep-together = "
+ // + layoutProps.keepTogether.getType());
+ /* LF *///System.err.println(" keep-with-previous = " +
+ // layoutProps.keepWithPrevious.getType());
+ /* LF *///System.err.println(" keep-with-next = " +
+ // layoutProps.keepWithNext.getType());
+ BlockLevelLayoutManager curLM; // currently active LM
+ BlockLevelLayoutManager prevLM = null; // previously active LM
+
+ referenceIPD = context.getRefIPD();
+ int iIndents = 0;
+ int bIndents = 0;
+ if (fobj instanceof org.apache.fop.fo.flow.Block) {
+ iIndents = ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().startIndent.getValue()
+ + ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().endIndent.getValue();
+ bIndents = ((org.apache.fop.fo.flow.Block) fobj).getCommonBorderPaddingBackground().getBPPaddingAndBorder(false);
+ }
+ int ipd = referenceIPD - iIndents;
+
+ MinOptMax stackSize = new MinOptMax();
+
+ // Set context for percentage property values.
+ fobj.setLayoutDimension(PercentBase.BLOCK_IPD, ipd);
+ fobj.setLayoutDimension(PercentBase.BLOCK_BPD, -1);
+
+ LinkedList returnedList = null;
+ LinkedList contentList = new LinkedList();
+ LinkedList returnList = new LinkedList();
+ Position returnPosition = new NonLeafPosition(this, null);
+
+ if (!bBreakBeforeServed) {
+ try {
+ if (addKnuthElementsForBreakBefore(returnList, returnPosition)) {
+ return returnList;
+ }
+ } finally {
+ bBreakBeforeServed = true;
+ }
+ }
+
+ if (!bSpaceBeforeServed) {
+ addKnuthElementsForSpaceBefore(returnList, returnPosition, alignment);
+ bSpaceBeforeServed = true;
+ }
+
+ addKnuthElementsForBorderPaddingBefore(returnList, returnPosition);
+
+ while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
+ LayoutContext childLC = new LayoutContext(0);
+ if (curLM instanceof LineLayoutManager) {
+ // curLM is a LineLayoutManager
+ // set stackLimit for lines
+ childLC.setStackLimit(new MinOptMax(ipd/*
+ * - iIndents -
+ * iTextIndent
+ */));
+ childLC.setRefIPD(ipd);
+ } else {
+ // curLM is a ?
+ childLC.setStackLimit(MinOptMax.subtract(context
+ .getStackLimit(), stackSize));
+ childLC.setRefIPD(referenceIPD);
+ }
+
+ // get elements from curLM
+ returnedList = curLM.getNextKnuthElements(childLC, alignment);
+ if (returnedList.size() == 1
+ && ((KnuthElement) returnedList.getFirst()).isPenalty()
+ && ((KnuthPenalty) returnedList.getFirst()).getP() == -KnuthElement.INFINITE) {
+ // a descendant of this block has break-before
+ if (returnList.size() == 0) {
+ // the first child (or its first child ...) has
+ // break-before;
+ // all this block, including space before, will be put in
+ // the
+ // following page
+ bSpaceBeforeServed = false;
+ }
+ contentList.addAll(returnedList);
+
+ /* extension: conversione di tutta la sequenza fin'ora ottenuta */
+ if (bpUnit > 0) {
+ storedList = contentList;
+ contentList = createUnitElements(contentList);
+ }
+ /* end of extension */
+
+ // "wrap" the Position inside each element
+ // moving the elements from contentList to returnList
+ returnedList = new LinkedList();
+ wrapPositionElements(contentList, returnList);
+
+ return returnList;
+ } else {
+ if (prevLM != null) {
+ // there is a block handled by prevLM
+ // before the one handled by curLM
+ if (mustKeepTogether()
+ || prevLM.mustKeepWithNext()
+ || curLM.mustKeepWithPrevious()) {
+ // add an infinite penalty to forbid a break between
+ // blocks
+ contentList.add(new KnuthPenalty(0,
+ KnuthElement.INFINITE, false,
+ new Position(this), false));
+ } else if (!((KnuthElement) contentList.getLast()).isGlue()) {
+ // add a null penalty to allow a break between blocks
+ contentList.add(new KnuthPenalty(0, 0, false,
+ new Position(this), false));
+ } else {
+ // the last element in contentList is a glue;
+ // it is a feasible breakpoint, there is no need to add
+ // a penalty
+ }
+ }
+ if (returnedList.size() == 0) {
+ //Avoid NoSuchElementException below (happens with empty blocks)
+ continue;
+ }
+ contentList.addAll(returnedList);
+ if (((KnuthElement) returnedList.getLast()).isPenalty()
+ && ((KnuthPenalty) returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+ // a descendant of this block has break-after
+ if (curLM.isFinished()) {
+ // there is no other content in this block;
+ // it's useless to add space after before a page break
+ setFinished(true);
+ }
+
+ /* extension: conversione di tutta la sequenza fin'ora ottenuta */
+ if (bpUnit > 0) {
+ storedList = contentList;
+ contentList = createUnitElements(contentList);
+ }
+ /* end of extension */
+
+ returnedList = new LinkedList();
+ wrapPositionElements(contentList, returnList);
+
+ return returnList;
+ }
+ /*
+ if (allocatedSpace.min > context.getStackLimit().max) {
+ log.debug("Allocated space exceeds stack limit, returning early.");
+ return returnList;
+ }*/
+ }
+ prevLM = curLM;
+ }
+
+ /* Extension: conversione di tutta la sequenza fin'ora ottenuta */
+ if (bpUnit > 0) {
+ storedList = contentList;
+ contentList = createUnitElements(contentList);
+ }
+ /* end of extension */
+
+ returnedList = new LinkedList();
+ wrapPositionElements(contentList, returnList);
+
+ addKnuthElementsForBorderPaddingAfter(returnList, returnPosition);
+ addKnuthElementsForSpaceAfter(returnList, returnPosition, alignment);
+ addKnuthElementsForBreakAfter(returnList, returnPosition);
+
+ setFinished(true);
+
+ return returnList;
+ }
+
+ public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
+/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> " + adj);
+/*LF*/ //System.out.println(" lastElement e' " + (lastElement.isPenalty() ? "penalty" : (lastElement.isGlue() ? "glue" : "box" )));
+/*LF*/ //System.out.println(" position e' " + lastElement.getPosition().getClass().getName());
+/*LF*/ //System.out.println(" " + (bpUnit > 0 ? "unit" : ""));
+ Position innerPosition = ((NonLeafPosition) lastElement.getPosition()).getPosition();
+
+ if (innerPosition == null && lastElement.isGlue()) {
+ // this adjustment applies to space-before or space-after of this block
+ if (((KnuthGlue) lastElement).getAdjustmentClass() == SPACE_BEFORE_ADJUSTMENT) {
+ // this adjustment applies to space-before
+ adjustedSpaceBefore += adj;
+/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> spazio prima: " + adj);
+ } else {
+ // this adjustment applies to space-after
+ adjustedSpaceAfter += adj;
+/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> spazio dopo: " + adj);
+ }
+ return adj;
+ } else if (innerPosition instanceof MappingPosition) {
+ // this block has block-progression-unit > 0: the adjustment can concern
+ // - the space-before or space-after of this block,
+ // - the line number of a descendant of this block
+ if (lastElement.isGlue()) {
+ // lastElement is a glue
+/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> bpunit con glue");
+ ListIterator storedListIterator = storedList.listIterator(((MappingPosition) innerPosition).getFirstIndex());
+ int newAdjustment = 0;
+ while (storedListIterator.nextIndex() <= ((MappingPosition) innerPosition).getLastIndex()) {
+ KnuthElement storedElement = (KnuthElement) storedListIterator.next();
+ if (storedElement.isGlue()) {
+ newAdjustment += ((BlockLevelLayoutManager) storedElement.getLayoutManager()).negotiateBPDAdjustment(adj - newAdjustment, storedElement);
+/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> (progressivo) righe: " + newAdjustment);
+ }
+ }
+ newAdjustment = (newAdjustment > 0 ? bpUnit * neededUnits(newAdjustment)
+ : - bpUnit * neededUnits(- newAdjustment));
+ return newAdjustment;
+ } else {
+ // lastElement is a penalty: this means that the paragraph
+ // has been split between consecutive pages:
+ // this may involve a change in the number of lines
+/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> bpunit con penalty");
+ KnuthPenalty storedPenalty = (KnuthPenalty)
+ storedList.get(((MappingPosition) innerPosition).getLastIndex());
+ if (storedPenalty.getW() > 0) {
+ // the original penalty has width > 0
+/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> chiamata passata");
+ return ((BlockLevelLayoutManager) storedPenalty.getLayoutManager())
+ .negotiateBPDAdjustment(storedPenalty.getW(), (KnuthElement) storedPenalty);
+ } else {
+ // the original penalty has width = 0
+ // the adjustment involves only the spaces before and after
+/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> chiamata gestita");
+ return adj;
+ }
+ }
+ } else if (innerPosition.getLM() != this) {
+ // this adjustment concerns another LM
+ NonLeafPosition savedPos = (NonLeafPosition) lastElement.getPosition();
+ lastElement.setPosition(innerPosition);
+ int returnValue = ((BlockLevelLayoutManager) lastElement.getLayoutManager()).negotiateBPDAdjustment(adj, lastElement);
+ lastElement.setPosition(savedPos);
+/*LF*/ //System.out.println(" BLM.negotiateBPDAdjustment> righe: " + returnValue);
+ return returnValue;
+ } else {
+ // this should never happen
+ System.err.println("BlockLayoutManager.negotiateBPDAdjustment(): unexpected Position");
+ return 0;
+ }
+ }
+
+ public void discardSpace(KnuthGlue spaceGlue) {
+/*LF*/ //System.out.println(" BLM.discardSpace> " + spaceGlue.getPosition().getClass().getName());
+ Position innerPosition = ((NonLeafPosition) spaceGlue.getPosition()).getPosition();
+
+/*LF*/ if (innerPosition == null || innerPosition.getLM() == this) {
+ // if this block has block-progression-unit > 0, innerPosition can be
+ // a MappingPosition
+ // spaceGlue represents space before or space after of this block
+ if (spaceGlue.getAdjustmentClass() == SPACE_BEFORE_ADJUSTMENT) {
+ // space-before must be discarded
+ adjustedSpaceBefore = 0;
+ } else {
+ // space-after must be discarded
+ adjustedSpaceAfter = 0;
+ //TODO Why are both cases handled in the same way?
+ }
+/*LF*/ } else {
+ // this element was not created by this BlockLM
+ NonLeafPosition savedPos = (NonLeafPosition)spaceGlue.getPosition();
+ spaceGlue.setPosition(innerPosition);
+ ((BlockLevelLayoutManager) spaceGlue.getLayoutManager()).discardSpace(spaceGlue);
+ spaceGlue.setPosition(savedPos);
+ }
+ }
+
+ public LinkedList getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/ int alignment) {
+/*LF*/ //System.out.println("");
+/*LF*/ //System.out.println(" BLM.getChangedKnuthElements> inizio: oldList.size() = " + oldList.size());
+ ListIterator oldListIterator = oldList.listIterator();
+ KnuthElement returnedElement;
+ KnuthElement currElement = null;
+ KnuthElement prevElement = null;
+ LinkedList returnedList = new LinkedList();
+ LinkedList returnList = new LinkedList();
+ int fromIndex = 0;
+
+ // "unwrap" the Positions stored in the elements
+ KnuthElement oldElement = null;
+ while (oldListIterator.hasNext()) {
+ oldElement = (KnuthElement)oldListIterator.next();
+ Position innerPosition = ((NonLeafPosition) oldElement.getPosition()).getPosition();
+/*LF*/ //System.out.println(" BLM> unwrapping: " + (oldElement.isBox() ? "box " : (oldElement.isGlue() ? "glue " : "penalty")) + " creato da " + oldElement.getLayoutManager().getClass().getName());
+/*LF*/ //System.out.println(" BLM> unwrapping: " + oldElement.getPosition().getClass().getName());
+ if (innerPosition != null) {
+ // oldElement was created by a descendant of this BlockLM
+ oldElement.setPosition(innerPosition);
+ } else {
+ // thisElement was created by this BlockLM
+ // modify its position in order to recognize it was not created
+ // by a child
+ oldElement.setPosition(new Position(this));
+ }
+ }
+
+ // create the iterator
+ List workList;
+ if (bpUnit == 0) {
+ workList = oldList;
+ } else {
+ // the storedList must be used instead of oldList;
+ // find the index of the first element of returnedList
+ // corresponding to the first element of oldList
+ oldListIterator = oldList.listIterator();
+ KnuthElement el = (KnuthElement) oldListIterator.next();
+ while (!(el.getPosition() instanceof MappingPosition)) {
+ el = (KnuthElement) oldListIterator.next();
+ }
+ int iFirst = ((MappingPosition) el.getPosition()).getFirstIndex();
+
+ // find the index of the last element of returnedList
+ // corresponding to the last element of oldList
+ oldListIterator = oldList.listIterator(oldList.size());
+ el = (KnuthElement) oldListIterator.previous();
+ while (!(el.getPosition() instanceof MappingPosition)) {
+ el = (KnuthElement) oldListIterator.previous();
+ }
+ int iLast = ((MappingPosition) el.getPosition()).getLastIndex();
+
+/*LF*/ //System.out.println(" si usa storedList da " + iFirst + " a " + iLast + " compresi su " + storedList.size() + " elementi totali");
+ workList = storedList.subList(iFirst, iLast + 1);
+ }
+ ListIterator workListIterator = workList.listIterator();
+
+/*LF*/ //System.out.println("");
+/*LF*/ //System.out.println(" BLM.getChangedKnuthElements> workList.size() = " + workList.size() + " da 0 a " + (workList.size() - 1));
+
+ while (workListIterator.hasNext()) {
+ currElement = (KnuthElement) workListIterator.next();
+/*LF*/ //System.out.println("elemento n. " + workListIterator.previousIndex() + " nella workList");
+ if (prevElement != null
+ && prevElement.getLayoutManager() != currElement.getLayoutManager()) {
+ // prevElement is the last element generated by the same LM
+ BlockLevelLayoutManager prevLM = (BlockLevelLayoutManager)
+ prevElement.getLayoutManager();
+ BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
+ currElement.getLayoutManager();
+ boolean bSomethingAdded = false;
+ if (prevLM != this) {
+/*LF*/ //System.out.println(" BLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + workListIterator.previousIndex() + " su " + prevLM.getClass().getName());
+ returnedList.addAll(prevLM.getChangedKnuthElements(workList.subList(fromIndex, workListIterator.previousIndex()),
+ /*flaggedPenalty,*/ alignment));
+ bSomethingAdded = true;
+ } else {
+ // prevLM == this
+ // do nothing
+/*LF*/ //System.out.println(" BLM.getChangedKnuthElements> elementi propri, ignorati, da " + fromIndex + " a " + workListIterator.previousIndex() + " su " + prevLM.getClass().getName());
+ }
+ fromIndex = workListIterator.previousIndex();
+
+ // there is another block after this one
+ if (bSomethingAdded
+ && (this.mustKeepTogether()
+ || prevLM.mustKeepWithNext()
+ || currLM.mustKeepWithPrevious())) {
+ // add an infinite penalty to forbid a break between blocks
+ returnedList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, new Position(this), false));
+ } else if (bSomethingAdded && !((KnuthElement) returnedList.getLast()).isGlue()) {
+ // add a null penalty to allow a break between blocks
+ returnedList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+ }
+ }
+ prevElement = currElement;
+ }
+ if (currElement != null) {
+ BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
+ currElement.getLayoutManager();
+ if (currLM != this) {
+/*LF*/ //System.out.println(" BLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + oldList.size() + " su " + currLM.getClass().getName());
+ returnedList.addAll(currLM.getChangedKnuthElements(workList.subList(fromIndex, workList.size()),
+ /*flaggedPenalty,*/ alignment));
+ } else {
+ // currLM == this
+ // there are no more elements to add
+ // remove the last penalty added to returnedList
+ if (returnedList.size() > 0) {
+ returnedList.removeLast();
+ }
+/*LF*/ //System.out.println(" BLM.getChangedKnuthElements> elementi propri, ignorati, da " + fromIndex + " a " + workList.size());
+ }
+ }
+
+ // append elements representing space-before
+ boolean spaceBeforeIsConditional = true;
+ if (fobj instanceof org.apache.fop.fo.flow.Block) {
+ spaceBeforeIsConditional =
+ ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceBefore.getSpace().isDiscard();
+ }
+ if (bpUnit > 0
+ || adjustedSpaceBefore != 0) {
+ if (!spaceBeforeIsConditional) {
+ // add elements to prevent the glue to be discarded
+ returnList.add(new KnuthBox(0,
+ new NonLeafPosition(this, null), false));
+ returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
+ new NonLeafPosition(this, null), false));
+ }
+ if (bpUnit > 0) {
+ returnList.add(new KnuthGlue(0, 0, 0,
+ SPACE_BEFORE_ADJUSTMENT, new NonLeafPosition(this, null), true));
+ } else {
+ returnList.add(new KnuthGlue(adjustedSpaceBefore, 0, 0,
+ SPACE_BEFORE_ADJUSTMENT, new NonLeafPosition(this, null), true));
+ }
+ }
+
+/*LF*/ //System.out.println(" BLM.getChangedKnuthElements> intermedio: returnedList.size() = " + returnedList.size());
+
+/* estensione: conversione complessiva */
+/*LF*/ if (bpUnit > 0) {
+/*LF*/ storedList = returnedList;
+/*LF*/ returnedList = createUnitElements(returnedList);
+/*LF*/ }
+/* estensione */
+
+ // "wrap" the Position stored in each element of returnedList
+ // and add elements to returnList
+ ListIterator listIter = returnedList.listIterator();
+ while (listIter.hasNext()) {
+ returnedElement = (KnuthElement)listIter.next();
+ returnedElement.setPosition(new NonLeafPosition(this, returnedElement.getPosition()));
+ returnList.add(returnedElement);
+ }
+
+ // append elements representing space-after
+ boolean spaceAfterIsConditional = true;
+ if (fobj instanceof org.apache.fop.fo.flow.Block) {
+ spaceAfterIsConditional =
+ ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceAfter.getSpace().isDiscard();
+ }
+ if (bpUnit > 0
+ || adjustedSpaceAfter != 0) {
+ if (!spaceAfterIsConditional) {
+ returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
+ new NonLeafPosition(this, null), false));
+ }
+ if (bpUnit > 0) {
+ returnList.add(new KnuthGlue(0, 0, 0,
+ SPACE_AFTER_ADJUSTMENT, new NonLeafPosition(this, null),
+ (!spaceAfterIsConditional) ? false : true));
+ } else {
+ returnList.add(new KnuthGlue(adjustedSpaceAfter, 0, 0,
+ SPACE_AFTER_ADJUSTMENT, new NonLeafPosition(this, null),
+ (!spaceAfterIsConditional) ? false : true));
+ }
+ if (!spaceAfterIsConditional) {
+ returnList.add(new KnuthBox(0,
+ new NonLeafPosition(this, null), true));
+ }
+ }
+
+/*LF*/ //System.out.println(" BLM.getChangedKnuthElements> fine: returnList.size() = " + returnList.size());
+ return returnList;
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
+ */
+ public boolean mustKeepTogether() {
+ return false;
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
+ */
+ public boolean mustKeepWithPrevious() {
+ return false;
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
+ */
+ public boolean mustKeepWithNext() {
+ return false;
+ }
+
+ /**
+ * Creates Knuth elements for before border padding and adds them to the return list.
+ * @param returnList return list to add the additional elements to
+ * @param returnPosition applicable return position
+ */
+ protected void addKnuthElementsForBorderPaddingBefore(LinkedList returnList,
+ Position returnPosition) {
+ //Border and Padding (before)
+ CommonBorderPaddingBackground borderAndPadding = null;
+ if (fobj instanceof org.apache.fop.fo.flow.Block) {
+ borderAndPadding =
+ ((org.apache.fop.fo.flow.Block) fobj).getCommonBorderPaddingBackground();
+ } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
+ borderAndPadding =
+ ((org.apache.fop.fo.flow.BlockContainer) fobj).getCommonBorderPaddingBackground();
+ }
+ if (borderAndPadding != null) {
+ //TODO Handle conditionality
+ int bpBefore = borderAndPadding.getBorderBeforeWidth(false)
+ + borderAndPadding.getPaddingBefore(false);
+ if (bpBefore > 0) {
+ returnList.add(new KnuthBox(bpBefore, returnPosition, true));
+ }
+ }
+ }
+
+ /**
+ * Creates Knuth elements for after border padding and adds them to the return list.
+ * @param returnList return list to add the additional elements to
+ * @param returnPosition applicable return position
+ */
+ protected void addKnuthElementsForBorderPaddingAfter(LinkedList returnList,
+ Position returnPosition) {
+ //Border and Padding (after)
+ CommonBorderPaddingBackground borderAndPadding = null;
+ if (fobj instanceof org.apache.fop.fo.flow.Block) {
+ borderAndPadding =
+ ((org.apache.fop.fo.flow.Block) fobj).getCommonBorderPaddingBackground();
+ } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
+ borderAndPadding =
+ ((org.apache.fop.fo.flow.BlockContainer) fobj).getCommonBorderPaddingBackground();
+ }
+ if (borderAndPadding != null) {
+ //TODO Handle conditionality
+ int bpAfter = borderAndPadding.getBorderAfterWidth(false)
+ + borderAndPadding.getPaddingAfter(false);
+ if (bpAfter > 0) {
+ returnList.add(new KnuthBox(bpAfter, returnPosition, true));
+ }
+ }
+ }
+
+ /**
+ * Creates Knuth elements for break-before and adds them to the return list.
+ * @param returnList return list to add the additional elements to
+ * @param returnPosition applicable return position
+ * @return true if an element has been added due to a break-before.
+ */
+ protected boolean addKnuthElementsForBreakBefore(LinkedList returnList,
+ Position returnPosition) {
+ int breakBefore = -1;
+ if (fobj instanceof org.apache.fop.fo.flow.Block) {
+ breakBefore = ((org.apache.fop.fo.flow.Block) fobj).getBreakBefore();
+ } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
+ breakBefore = ((org.apache.fop.fo.flow.BlockContainer) fobj).getBreakBefore();
+ }
+ if (breakBefore == EN_PAGE
+ || breakBefore == EN_COLUMN
+ || breakBefore == EN_EVEN_PAGE
+ || breakBefore == EN_ODD_PAGE) {
+ // return a penalty element, representing a forced page break
+ returnList.add(new KnuthPenalty(0, -KnuthElement.INFINITE, false,
+ breakBefore, returnPosition, false));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Creates Knuth elements for break-after and adds them to the return list.
+ * @param returnList return list to add the additional elements to
+ * @param returnPosition applicable return position
+ * @return true if an element has been added due to a break-after.
+ */
+ protected boolean addKnuthElementsForBreakAfter(LinkedList returnList,
+ Position returnPosition) {
+ int breakAfter = -1;
+ if (fobj instanceof org.apache.fop.fo.flow.Block) {
+ breakAfter = ((org.apache.fop.fo.flow.Block) fobj).getBreakAfter();
+ } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
+ breakAfter = ((org.apache.fop.fo.flow.BlockContainer) fobj).getBreakAfter();
+ }
+ if (breakAfter == EN_PAGE
+ || breakAfter == EN_COLUMN
+ || breakAfter == EN_EVEN_PAGE
+ || breakAfter == EN_ODD_PAGE) {
+ // add a penalty element, representing a forced page break
+ returnList.add(new KnuthPenalty(0, -KnuthElement.INFINITE, false,
+ breakAfter, returnPosition, false));
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Creates Knuth elements for space-before and adds them to the return list.
+ * @param returnList return list to add the additional elements to
+ * @param returnPosition applicable return position
+ * @param alignment vertical alignment
+ */
+ protected void addKnuthElementsForSpaceBefore(LinkedList returnList,
+ Position returnPosition, int alignment) {
+ SpaceProperty spaceBefore = null;
+ if (fobj instanceof org.apache.fop.fo.flow.Block) {
+ spaceBefore = ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceBefore;
+ } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
+ spaceBefore = ((org.apache.fop.fo.flow.BlockContainer) fobj).getCommonMarginBlock().spaceBefore;
+ }
+ // append elements representing space-before
+ if (bpUnit > 0
+ || spaceBefore != null
+ && !(spaceBefore.getMinimum().getLength().getValue() == 0
+ && spaceBefore.getMaximum().getLength().getValue() == 0)) {
+ if (spaceBefore != null && !spaceBefore.getSpace().isDiscard()) {
+ // add elements to prevent the glue to be discarded
+ returnList.add(new KnuthBox(0, returnPosition, false));
+ returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE,
+ false, returnPosition, false));
+ }
+ if (bpUnit > 0) {
+ returnList.add(new KnuthGlue(0, 0, 0,
+ BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT,
+ returnPosition, true));
+ } else /*if (alignment == EN_JUSTIFY)*/ {
+ returnList.add(new KnuthGlue(
+ spaceBefore.getOptimum().getLength().getValue(),
+ spaceBefore.getMaximum().getLength().getValue()
+ - spaceBefore.getOptimum().getLength().getValue(),
+ spaceBefore.getOptimum().getLength().getValue()
+ - spaceBefore.getMinimum().getLength().getValue(),
+ BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT,
+ returnPosition, true));
+ } /*else {
+ returnList.add(new KnuthGlue(
+ spaceBefore.getOptimum().getLength().getValue(),
+ 0, 0, BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT,
+ returnPosition, true));
+ }*/
+ }
+ }
+
+ /**
+ * Creates Knuth elements for space-after and adds them to the return list.
+ * @param returnList return list to add the additional elements to
+ * @param returnPosition applicable return position
+ * @param alignment vertical alignment
+ */
+ protected void addKnuthElementsForSpaceAfter(LinkedList returnList, Position returnPosition,
+ int alignment) {
+ SpaceProperty spaceAfter = null;
+ if (fobj instanceof org.apache.fop.fo.flow.Block) {
+ spaceAfter = ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceAfter;
+ } else if (fobj instanceof org.apache.fop.fo.flow.Block) {
+ spaceAfter = ((org.apache.fop.fo.flow.BlockContainer) fobj).getCommonMarginBlock().spaceAfter;
+ }
+ // append elements representing space-after
+ if (bpUnit > 0
+ || spaceAfter != null
+ && !(spaceAfter.getMinimum().getLength().getValue() == 0
+ && spaceAfter.getMaximum().getLength().getValue() == 0)) {
+ if (spaceAfter != null && !spaceAfter.getSpace().isDiscard()) {
+ returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE,
+ false, returnPosition, false));
+ }
+ if (bpUnit > 0) {
+ returnList.add(new KnuthGlue(0, 0, 0,
+ BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT,
+ returnPosition, true));
+ } else /*if (alignment == EN_JUSTIFY)*/ {
+ returnList.add(new KnuthGlue(
+ spaceAfter.getOptimum().getLength().getValue(),
+ spaceAfter.getMaximum().getLength().getValue()
+ - spaceAfter.getOptimum().getLength().getValue(),
+ spaceAfter.getOptimum().getLength().getValue()
+ - spaceAfter.getMinimum().getLength().getValue(),
+ BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT, returnPosition,
+ (!spaceAfter.getSpace().isDiscard()) ? false : true));
+ } /*else {
+ returnList.add(new KnuthGlue(
+ spaceAfter.getOptimum().getLength().getValue(), 0, 0,
+ BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT, returnPosition,
+ (!spaceAfter.getSpace().isDiscard()) ? false : true));
+ }*/
+ if (spaceAfter != null && !spaceAfter.getSpace().isDiscard()) {
+ returnList.add(new KnuthBox(0, returnPosition, true));
+ }
+ }
+ }
+
+ protected LinkedList createUnitElements(LinkedList oldList) {
+ //System.out.println(" ");
+ //System.out.println("Inizio conversione: " + oldList.size() + " elementi, spazio minimo prima= " + layoutProps.spaceBefore.getSpace().min
+ // + " spazio minimo dopo= " + layoutProps.spaceAfter.getSpace().min);
+ // add elements at the beginning and at the end of oldList
+ // representing minimum spaces
+ LayoutManager lm = ((KnuthElement)oldList.getFirst()).getLayoutManager();
+ boolean bAddedBoxBefore = false;
+ boolean bAddedBoxAfter = false;
+ if (adjustedSpaceBefore > 0) {
+ oldList.addFirst(new KnuthBox(adjustedSpaceBefore,
+ new Position(lm), true));
+ bAddedBoxBefore = true;
+ }
+ if (adjustedSpaceAfter > 0) {
+ oldList.addLast(new KnuthBox(adjustedSpaceAfter,
+ new Position(lm), true));
+ bAddedBoxAfter = true;
+ }
+
+ MinOptMax totalLength = new MinOptMax(0);
+ MinOptMax totalUnits = new MinOptMax(0);
+ LinkedList newList = new LinkedList();
+
+ //System.out.println(" ");
+ //System.out.println(" Prima scansione");
+ // scan the list once to compute total min, opt and max length
+ ListIterator oldListIterator = oldList.listIterator();
+ while (oldListIterator.hasNext()) {
+ KnuthElement element = (KnuthElement) oldListIterator.next();
+ if (element.isBox()) {
+/*LF*/ totalLength.add(new MinOptMax(element.getW()));
+/*LF*/ //System.out.println("box " + element.getW());
+/*LF*/ } else if (element.isGlue()) {
+/*LF*/ totalLength.min -= ((KnuthGlue) element).getZ();
+/*LF*/ totalLength.max += ((KnuthGlue) element).getY();
+/*LF*/ //leafValue = ((LeafPosition) element.getPosition()).getLeafPos();
+/*LF*/ //System.out.println("glue " + element.getW() + " + " + ((KnuthGlue) element).getY() + " - " + ((KnuthGlue) element).getZ());
+/*LF*/ } else {
+/*LF*/ //System.out.println((((KnuthPenalty)element).getP() == KnuthElement.INFINITE ? "PENALTY " : "penalty ") + element.getW());
+ }
+ }
+ // compute the total amount of "units"
+ totalUnits = new MinOptMax(neededUnits(totalLength.min),
+ neededUnits(totalLength.opt),
+ neededUnits(totalLength.max));
+ //System.out.println(" totalLength= " + totalLength);
+ //System.out.println(" unita'= " + totalUnits);
+
+ //System.out.println(" ");
+ //System.out.println(" Seconda scansione");
+ // scan the list once more, stopping at every breaking point
+ // in order to compute partial min, opt and max length
+ // and create the new elements
+ oldListIterator = oldList.listIterator();
+ boolean bPrevIsBox = false;
+ MinOptMax lengthBeforeBreak = new MinOptMax(0);
+ MinOptMax lengthAfterBreak = (MinOptMax) totalLength.clone();
+ MinOptMax unitsBeforeBreak;
+ MinOptMax unitsAfterBreak;
+ MinOptMax unsuppressibleUnits = new MinOptMax(0);
+ int firstIndex = 0;
+ int lastIndex = -1;
+ while (oldListIterator.hasNext()) {
+ KnuthElement element = (KnuthElement) oldListIterator.next();
+ lastIndex ++;
+ if (element.isBox()) {
+ lengthBeforeBreak.add(new MinOptMax(element.getW()));
+ lengthAfterBreak.subtract(new MinOptMax(element.getW()));
+ bPrevIsBox = true;
+ } else if (element.isGlue()) {
+ lengthBeforeBreak.min -= ((KnuthGlue) element).getZ();
+ lengthAfterBreak.min += ((KnuthGlue) element).getZ();
+ lengthBeforeBreak.max += ((KnuthGlue) element).getY();
+ lengthAfterBreak.max -= ((KnuthGlue) element).getY();
+ bPrevIsBox = false;
+ } else {
+ lengthBeforeBreak.add(new MinOptMax(element.getW()));
+ bPrevIsBox = false;
+ }
+
+ // create the new elements
+ if (element.isPenalty() && ((KnuthPenalty) element).getP() < KnuthElement.INFINITE
+ || element.isGlue() && bPrevIsBox
+ || !oldListIterator.hasNext()) {
+ // suppress elements after the breaking point
+ int iStepsForward = 0;
+ while (oldListIterator.hasNext()) {
+ KnuthElement el = (KnuthElement) oldListIterator.next();
+ iStepsForward++;
+ if (el.isGlue()) {
+ // suppressed glue
+ lengthAfterBreak.min += ((KnuthGlue) el).getZ();
+ lengthAfterBreak.max -= ((KnuthGlue) el).getY();
+ } else if (el.isPenalty()) {
+ // suppressed penalty, do nothing
+ } else {
+ // box, end of suppressions
+ break;
+ }
+ }
+ // compute the partial amount of "units" before and after the break
+ unitsBeforeBreak = new MinOptMax(neededUnits(lengthBeforeBreak.min),
+ neededUnits(lengthBeforeBreak.opt),
+ neededUnits(lengthBeforeBreak.max));
+ unitsAfterBreak = new MinOptMax(neededUnits(lengthAfterBreak.min),
+ neededUnits(lengthAfterBreak.opt),
+ neededUnits(lengthAfterBreak.max));
+
+ // rewind the iterator and lengthAfterBreak
+ for (int i = 0; i < iStepsForward; i++) {
+ KnuthElement el = (KnuthElement) oldListIterator.previous();
+ if (el.isGlue()) {
+ lengthAfterBreak.min -= ((KnuthGlue) el).getZ();
+ lengthAfterBreak.max += ((KnuthGlue) el).getY();
+ }
+ }
+
+ // compute changes in length, stretch and shrink
+ int uLengthChange = unitsBeforeBreak.opt + unitsAfterBreak.opt - totalUnits.opt;
+ int uStretchChange = (unitsBeforeBreak.max + unitsAfterBreak.max - totalUnits.max)
+ - (unitsBeforeBreak.opt + unitsAfterBreak.opt - totalUnits.opt);
+ int uShrinkChange = (unitsBeforeBreak.opt + unitsAfterBreak.opt - totalUnits.opt)
+ - (unitsBeforeBreak.min + unitsAfterBreak.min - totalUnits.min);
+
+ // compute the number of normal, stretch and shrink unit
+ // that must be added to the new sequence
+ int uNewNormal = unitsBeforeBreak.opt - unsuppressibleUnits.opt;
+ int uNewStretch = (unitsBeforeBreak.max - unitsBeforeBreak.opt)
+ - (unsuppressibleUnits.max - unsuppressibleUnits.opt);
+ int uNewShrink = (unitsBeforeBreak.opt - unitsBeforeBreak.min)
+ - (unsuppressibleUnits.opt - unsuppressibleUnits.min);
+
+/*LF*/ //System.out.println("(" + unsuppressibleUnits.min + "-" + unsuppressibleUnits.opt + "-" + unsuppressibleUnits.max + ") "
+/*LF*/ // + " -> " + unitsBeforeBreak.min + "-" + unitsBeforeBreak.opt + "-" + unitsBeforeBreak.max
+/*LF*/ // + " + " + unitsAfterBreak.min + "-" + unitsAfterBreak.opt + "-" + unitsAfterBreak.max
+/*LF*/ // + (uLengthChange != 0 ? " [length " + uLengthChange + "] " : "")
+/*LF*/ // + (uStretchChange != 0 ? " [stretch " + uStretchChange + "] " : "")
+/*LF*/ // + (uShrinkChange != 0 ? " [shrink " + uShrinkChange + "]" : "")
+/*LF*/ // );
+
+ // create the MappingPosition which will be stored in the new elements
+ // correct firstIndex and lastIndex
+ int firstIndexCorrection = 0;
+ int lastIndexCorrection = 0;
+ if (bAddedBoxBefore) {
+ if (firstIndex != 0) {
+ firstIndexCorrection ++;
+ }
+ lastIndexCorrection ++;
+ }
+ if (bAddedBoxAfter && lastIndex == (oldList.size() - 1)) {
+ lastIndexCorrection ++;
+ }
+ MappingPosition mappingPos = new MappingPosition(this,
+ firstIndex - firstIndexCorrection,
+ lastIndex - lastIndexCorrection);
+
+ // new box
+ newList.add(new KnuthBox((uNewNormal - uLengthChange) * bpUnit,
+ mappingPos,
+ false));
+ unsuppressibleUnits.add(new MinOptMax(uNewNormal - uLengthChange));
+ //System.out.println(" box " + (uNewNormal - uLengthChange));
+
+ // new infinite penalty, glue and box, if necessary
+ if (uNewStretch - uStretchChange > 0
+ || uNewShrink - uShrinkChange > 0) {
+ int iStretchUnits = (uNewStretch - uStretchChange > 0 ? (uNewStretch - uStretchChange) : 0);
+ int iShrinkUnits = (uNewShrink - uShrinkChange > 0 ? (uNewShrink - uShrinkChange) : 0);
+ newList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
+ mappingPos,
+ false));
+ newList.add(new KnuthGlue(0,
+ iStretchUnits * bpUnit,
+ iShrinkUnits * bpUnit,
+ LINE_NUMBER_ADJUSTMENT,
+ mappingPos,
+ false));
+ //System.out.println(" PENALTY");
+ //System.out.println(" glue 0 " + iStretchUnits + " " + iShrinkUnits);
+ unsuppressibleUnits.max += iStretchUnits;
+ unsuppressibleUnits.min -= iShrinkUnits;
+ if (!oldListIterator.hasNext()) {
+ newList.add(new KnuthBox(0,
+ mappingPos,
+ false));
+ //System.out.println(" box 0");
+ }
+ }
+
+ // new breaking sequence
+ if (uStretchChange != 0
+ || uShrinkChange != 0) {
+ // new infinite penalty, glue, penalty and glue
+ newList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
+ mappingPos,
+ false));
+ newList.add(new KnuthGlue(0,
+ uStretchChange * bpUnit,
+ uShrinkChange * bpUnit,
+ LINE_NUMBER_ADJUSTMENT,
+ mappingPos,
+ false));
+ newList.add(new KnuthPenalty(uLengthChange * bpUnit,
+ 0, false, element.getPosition(), false));
+ newList.add(new KnuthGlue(0,
+ - uStretchChange * bpUnit,
+ - uShrinkChange * bpUnit,
+ LINE_NUMBER_ADJUSTMENT,
+ mappingPos,
+ false));
+ //System.out.println(" PENALTY");
+ //System.out.println(" glue 0 " + uStretchChange + " " + uShrinkChange);
+ //System.out.println(" penalty " + uLengthChange + " * unit");
+ //System.out.println(" glue 0 " + (- uStretchChange) + " " + (- uShrinkChange));
+ } else if (oldListIterator.hasNext()){
+ // new penalty
+ newList.add(new KnuthPenalty(uLengthChange * bpUnit,
+ 0, false,
+ mappingPos,
+ false));
+ //System.out.println(" penalty " + uLengthChange + " * unit");
+ }
+ // update firstIndex
+ firstIndex = lastIndex + 1;
+ }
+
+ if (element.isPenalty()) {
+ lengthBeforeBreak.add(new MinOptMax(-element.getW()));
+ }
+
+ }
+
+ // remove elements at the beginning and at the end of oldList
+ // representing minimum spaces
+ if (adjustedSpaceBefore > 0) {
+ oldList.removeFirst();
+ }
+ if (adjustedSpaceAfter > 0) {
+ oldList.removeLast();
+ }
+
+ // if space-before.conditionality is "discard", correct newList
+ boolean correctFirstElement = false;
+ if (fobj instanceof org.apache.fop.fo.flow.Block) {
+ correctFirstElement =
+ ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceBefore.getSpace().isDiscard();
+ }
+ if (correctFirstElement) {
+ // remove the wrong element
+ KnuthBox wrongBox = (KnuthBox) newList.removeFirst();
+ // if this paragraph is at the top of a page, the space before
+ // must be ignored; compute the length change
+ int decreasedLength = (neededUnits(totalLength.opt)
+ - neededUnits(totalLength.opt - adjustedSpaceBefore))
+ * bpUnit;
+ // insert the correct elements
+ newList.addFirst(new KnuthBox(wrongBox.getW() - decreasedLength,
+ wrongBox.getPosition(), false));
+ newList.addFirst(new KnuthGlue(decreasedLength, 0, 0, SPACE_BEFORE_ADJUSTMENT,
+ wrongBox.getPosition(), false));
+ //System.out.println(" rimosso box " + neededUnits(wrongBox.getW()));
+ //System.out.println(" aggiunto glue " + neededUnits(decreasedLength) + " 0 0");
+ //System.out.println(" aggiunto box " + neededUnits(wrongBox.getW() - decreasedLength));
+ }
+
+ // if space-after.conditionality is "discard", correct newList
+ boolean correctLastElement = false;
+ if (fobj instanceof org.apache.fop.fo.flow.Block) {
+ correctLastElement =
+ ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceAfter.getSpace().isDiscard();
+ }
+ if (correctLastElement) {
+ // remove the wrong element
+ KnuthBox wrongBox = (KnuthBox) newList.removeLast();
+ // if the old sequence is box(h) penalty(inf) glue(x,y,z) box(0)
+ // (it cannot be parted and has some stretch or shrink)
+ // the wrong box is the first one, not the last one
+ LinkedList preserveList = new LinkedList();
+ if (wrongBox.getW() == 0) {
+ preserveList.add(wrongBox);
+ preserveList.addFirst((KnuthGlue) newList.removeLast());
+ preserveList.addFirst((KnuthPenalty) newList.removeLast());
+ wrongBox = (KnuthBox) newList.removeLast();
+ }
+
+ // if this paragraph is at the bottom of a page, the space after
+ // must be ignored; compute the length change
+ int decreasedLength = (neededUnits(totalLength.opt)
+ - neededUnits(totalLength.opt - adjustedSpaceAfter))
+ * bpUnit;
+ // insert the correct box
+ newList.addLast(new KnuthBox(wrongBox.getW() - decreasedLength,
+ wrongBox.getPosition(), false));
+ // add preserved elements
+ if (preserveList.size() > 0) {
+ newList.addAll(preserveList);
+ }
+ // insert the correct glue
+ newList.addLast(new KnuthGlue(decreasedLength, 0, 0, SPACE_AFTER_ADJUSTMENT,
+ wrongBox.getPosition(), false));
+ //System.out.println(" rimosso box " + neededUnits(wrongBox.getW()));
+ //System.out.println(" aggiunto box " + neededUnits(wrongBox.getW() - decreasedLength));
+ //System.out.println(" aggiunto glue " + neededUnits(decreasedLength) + " 0 0");
+ }
+
+ return newList;
+ }
+
+ protected static class StackingIter extends PositionIterator {
+ StackingIter(Iterator parentIter) {
+ super(parentIter);
+ }
+
+ protected LayoutManager getLM(Object nextObj) {
+ return ((Position) nextObj).getLM();
+ }
+
+ protected Position getPos(Object nextObj) {
+ return ((Position) nextObj);
+ }
+ }
+
+ protected static class MappingPosition extends Position {
+ private int iFirstIndex;
+ private int iLastIndex;
+
+ public MappingPosition(LayoutManager lm, int first, int last) {
+ super(lm);
+ iFirstIndex = first;
+ iLastIndex = last;
+ }
+
+ public int getFirstIndex() {
+ return iFirstIndex;
+ }
+
+ public int getLastIndex() {
+ return iLastIndex;
+ }
+ }
+
+ /**
+ * "wrap" the Position inside each element moving the elements from
+ * SourceList to targetList
+ * @param sourceList source list
+ * @param targetList target list receiving the wrapped position elements
+ */
+ protected void wrapPositionElements(List sourceList, List targetList) {
+ ListIterator listIter = sourceList.listIterator();
+ while (listIter.hasNext()) {
+ KnuthElement tempElement;
+ tempElement = (KnuthElement) listIter.next();
+ if (tempElement.getLayoutManager() != this) {
+ tempElement.setPosition(new NonLeafPosition(this,
+ tempElement.getPosition()));
+ }
+ targetList.add(tempElement);
+ }
+ }
+
}
--- /dev/null
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ * Licensed 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 org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * The set of nodes is sorted into lines indexed into activeLines.
+ * The nodes in each line are linked together in a single linked list by the
+ * KnuthNode.next field. The activeLines array contains a link to the head of
+ * the linked list in index 'line*2' and a link to the tail at index 'line*2+1'.
+ * <p>
+ * The set of active nodes can be traversed by
+ * <pre>
+ * for (int line = startLine; line < endLine; line++) {
+ * for (KnuthNode node = getNode(line); node != null; node = node.next) {
+ * // Do something with 'node'
+ * }
+ * }
+ * </pre>
+ */
+public abstract class BreakingAlgorithm {
+ // parameters of Knuth's algorithm:
+ // penalty value for flagged penalties
+ private int flaggedPenalty = 50;
+ // demerit for consecutive lines ending at flagged penalties
+ private int repeatedFlaggedDemerit = 50;
+ // demerit for consecutive lines belonging to incompatible fitness classes
+ private int incompatibleFitnessDemerit = 50;
+ // suggested modification to the "optimum" number of lines
+ private int looseness = 0;
+
+ /**
+ * The threshold for considering breaks to be acceptable.
+ */
+ private double threshold;
+
+ /**
+ * The paragraph of KnuthElements.
+ */
+ private KnuthSequence par;
+
+ /**
+ * The width of a line.
+ */
+ private int lineWidth = 0;
+ private boolean force = false;
+
+ protected KnuthNode lastDeactivatedNode = null;
+ private KnuthNode lastTooLong;
+ private KnuthNode lastTooShort;
+ private KnuthNode lastDeactivated;
+
+ protected int alignment;
+ protected int alignmentLast;
+ protected boolean bFirst;
+
+ /**
+ * The set of active nodes.
+ */
+ private KnuthNode[] activeLines;
+
+ /**
+ * The number of active nodes.
+ */
+ protected int activeNodeCount;
+
+ /**
+ * The lowest available line in the set of active nodes.
+ */
+ protected int startLine = 0;
+
+ /**
+ * The highest + 1 available line in the set of active nodes.
+ */
+ protected int endLine = 0;
+
+ /**
+ * The total width of all elements handled so far.
+ */
+ private int totalWidth;
+
+ /**
+ * The total stretch of all elements handled so far.
+ */
+ private int totalStretch = 0;
+
+ /**
+ * The total shrink of all elements handled so far.
+ */
+ private int totalShrink = 0;
+
+ private BestRecords best;
+ private KnuthNode[] positions;
+
+ private static final int INFINITE_RATIO = 1000;
+
+ protected static Log log = LogFactory.getLog(KnuthParagraph.class);
+
+ public BreakingAlgorithm(int align, int alignLast,
+ boolean first) {
+ alignment = align;
+ alignmentLast = alignLast;
+ bFirst = first;
+ this.best = new BestRecords();
+ }
+
+
+ // this class represent a feasible breaking point
+ public class KnuthNode {
+ // index of the breakpoint represented by this node
+ public int position;
+
+ // number of the line ending at this breakpoint
+ public int line;
+
+ // fitness class of the line ending at his breakpoint
+ public int fitness;
+
+ // accumulated width of the KnuthElements
+ public int totalWidth;
+
+ // accumulated stretchability of the KnuthElements
+ public int totalStretch;
+
+ // accumulated shrinkability of the KnuthElements
+ public int totalShrink;
+
+ // adjustment ratio if the line ends at this breakpoint
+ public double adjustRatio;
+
+ // available stretch of the line ending at this breakpoint
+ public int availableShrink;
+
+ // available shrink of the line ending at this breakpoint
+ public int availableStretch;
+
+ // difference between target and actual line width
+ public int difference;
+
+ // minimum total demerits up to this breakpoint
+ public double totalDemerits;
+
+ // best node for the preceding breakpoint
+ public KnuthNode previous;
+
+ // next possible node in the same line
+ public KnuthNode next;
+
+
+ public KnuthNode(int position, int line, int fitness,
+ int totalWidth, int totalStretch, int totalShrink,
+ double adjustRatio, int availableShrink, int availableStretch, int difference,
+ double totalDemerits, KnuthNode previous) {
+ this.position = position;
+ this.line = line;
+ this.fitness = fitness;
+ this.totalWidth = totalWidth;
+ this.totalStretch = totalStretch;
+ this.totalShrink = totalShrink;
+ this.adjustRatio = adjustRatio;
+ this.availableShrink = availableShrink;
+ this.availableStretch = availableStretch;
+ this.difference = difference;
+ this.totalDemerits = totalDemerits;
+ this.previous = previous;
+ }
+
+ public String toString() {
+ return "<KnuthNode at " + position + " " +
+ totalWidth + "+" + totalStretch + "-" + totalShrink +
+ " line:" + line +
+ " prev:" + (previous != null ? previous.position : -1) +
+ " dem:" + totalDemerits +
+ ">";
+ }
+ }
+
+ // this class stores information about how the nodes
+ // which could start a line
+ // ending at the current element
+ private class BestRecords {
+ private static final double INFINITE_DEMERITS = Double.POSITIVE_INFINITY;
+ //private static final double INFINITE_DEMERITS = 1E11;
+
+ private double bestDemerits[] = new double[4];
+ private KnuthNode bestNode[] = new KnuthNode[4];
+ private double bestAdjust[] = new double[4];
+ private int bestDifference[] = new int[4];
+ private int bestAvailableShrink[] = new int[4];
+ private int bestAvailableStretch[] = new int[4];
+ private int bestIndex = -1;
+
+ public BestRecords() {
+ reset();
+ }
+
+ public void addRecord(double demerits, KnuthNode node, double adjust,
+ int availableShrink, int availableStretch, int difference, int fitness) {
+ if (demerits > bestDemerits[fitness]) {
+ log.error("New demerits value greter than the old one");
+ }
+ bestDemerits[fitness] = demerits;
+ bestNode[fitness] = node;
+ bestAdjust[fitness] = adjust;
+ bestAvailableShrink[fitness] = availableShrink;
+ bestAvailableStretch[fitness] = availableStretch;
+ bestDifference[fitness] = difference;
+ if (bestIndex == -1 || demerits < bestDemerits[bestIndex]) {
+ bestIndex = fitness;
+ }
+ }
+
+ public boolean hasRecords() {
+ return (bestIndex != -1);
+ }
+
+ public boolean notInfiniteDemerits(int fitness) {
+ return (bestDemerits[fitness] != INFINITE_DEMERITS);
+ }
+
+ public double getDemerits(int fitness) {
+ return bestDemerits[fitness];
+ }
+
+ public KnuthNode getNode(int fitness) {
+ return bestNode[fitness];
+ }
+
+ public double getAdjust(int fitness) {
+ return bestAdjust[fitness];
+ }
+
+ public int getAvailableShrink(int fitness) {
+ return bestAvailableShrink[fitness];
+ }
+
+ public int getAvailableStretch(int fitness) {
+ return bestAvailableStretch[fitness];
+ }
+
+ public int getDifference(int fitness) {
+ return bestDifference[fitness];
+ }
+
+ public double getMinDemerits() {
+ if (bestIndex != -1) {
+ return getDemerits(bestIndex);
+ } else {
+ // anyway, this should never happen
+ return INFINITE_DEMERITS;
+ }
+ }
+
+ public void reset() {
+ for (int i = 0; i < 4; i ++) {
+ bestDemerits[i] = INFINITE_DEMERITS;
+ bestNode[i] = null;
+ bestAdjust[i] = 0.0;
+ bestDifference[i] = 0;
+ bestAvailableShrink[i] = 0;
+ bestAvailableStretch[i] = 0;
+ }
+ bestIndex = -1;
+ }
+ }
+
+
+
+ public abstract void updateData1(int total, double demerits) ;
+
+ public abstract void updateData2(KnuthNode bestActiveNode,
+ KnuthSequence sequence,
+ int total) ;
+
+ public int findBreakingPoints(KnuthSequence par, int lineWidth,
+ double threshold, boolean force,
+ boolean hyphenationAllowed) {
+ this.par = par;
+ this.threshold = threshold;
+ this.force = force;
+ this.lineWidth = lineWidth;
+ this.totalWidth = 0;
+ this.totalStretch = 0;
+ this.totalShrink = 0;
+
+ activeLines = new KnuthNode[20];
+
+ // reset lastTooShort and lastTooLong, as they could be not null
+ // because of previous calls to findBreakingPoints
+ lastTooShort = lastTooLong = null;
+ // reset startLine and endLine
+ startLine = endLine = 0;
+ // current element in the paragraph
+ KnuthElement thisElement = null;
+ // previous element in the paragraph is a KnuthBox?
+ boolean previousIsBox = false;
+
+ // index of the first KnuthBox in the sequence
+ int firstBoxIndex = 0;
+ while (alignment != org.apache.fop.fo.Constants.EN_CENTER
+ && ! ((KnuthElement) par.get(firstBoxIndex)).isBox()) {
+ firstBoxIndex++;
+ }
+
+ // create an active node representing the starting point
+ activeLines = new KnuthNode[20];
+ addNode(0, new KnuthNode(firstBoxIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null));
+
+ if (log.isTraceEnabled()) {
+ log.trace("Looping over " + par.size() + " box objects");
+ }
+
+ KnuthNode lastForced = getNode(0);
+
+ // main loop
+ for (int i = 0; i < par.size(); i++) {
+ thisElement = getElement(i);
+ if (thisElement.isBox()) {
+ // a KnuthBox object is not a legal line break
+ totalWidth += thisElement.getW();
+ previousIsBox = true;
+ } else if (thisElement.isGlue()) {
+ // a KnuthGlue object is a legal line break
+ // only if the previous object is a KnuthBox
+ if (previousIsBox) {
+ considerLegalBreak(thisElement, i);
+ }
+ totalWidth += thisElement.getW();
+ totalStretch += thisElement.getY();
+ totalShrink += thisElement.getZ();
+ previousIsBox = false;
+ } else {
+ // a KnuthPenalty is a legal line break
+ // only if its penalty is not infinite;
+ // if hyphenationAllowed is false, ignore flagged penalties
+ if (((KnuthPenalty) thisElement).getP()
+ < KnuthElement.INFINITE
+ && (hyphenationAllowed || !((KnuthPenalty) thisElement).isFlagged())) {
+ considerLegalBreak(thisElement, i);
+ }
+ previousIsBox = false;
+ }
+ if (activeNodeCount == 0) {
+ if (!force) {
+ log.debug("Could not find a set of breaking points " + threshold);
+ return 0;
+ }
+ if (lastTooShort == null || lastForced.position == lastTooShort.position) {
+ lastForced = lastTooLong;
+ } else {
+ lastForced = lastTooShort;
+ }
+
+ log.debug("Restarting at node " + lastForced);
+ lastForced.totalDemerits = 0;
+ addNode(lastForced.line, lastForced);
+ i = lastForced.position;
+ startLine = lastForced.line;
+ endLine = startLine + 1;
+ totalWidth = lastForced.totalWidth;
+ totalStretch = lastForced.totalStretch;
+ totalShrink = lastForced.totalShrink;
+ lastTooShort = lastTooLong = null;
+ }
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("Main loop completed " + activeNodeCount);
+ log.trace("Active nodes=" + toString(""));
+ }
+
+ // there is at least one set of breaking points
+ // select one or more active nodes, removing the others from the list
+ int line = filterActiveNodes();
+
+ // for each active node, create a set of breaking points
+ for (int i = startLine; i < endLine; i++) {
+ for (KnuthNode node = getNode(line); node != null; node = node.next) {
+ updateData1(node.line, node.totalDemerits);
+ calculateBreakPoints(node, par, node.line);
+ }
+ }
+
+ activeLines = null;
+ return line;
+ }
+
+ private void considerLegalBreak(KnuthElement element, int elementIdx) {
+
+ if (log.isTraceEnabled()) {
+ log.trace("Feasible breakpoint at " + par.indexOf(element) + " " + totalWidth + "+" + totalStretch + "-" + totalShrink);
+ log.trace("\tCurrent active node list: " + activeNodeCount + " " + this.toString("\t"));
+ }
+
+ lastDeactivated = null;
+ lastTooLong = null;
+ for (int line = startLine; line < endLine; line++) {
+ for (KnuthNode node = getNode(line); node != null; node = node.next) {
+ if (node.position == elementIdx) {
+ continue;
+ }
+ int difference = computeDifference(node, element);
+ double r = computeAdjustmentRatio(node, difference);
+ int availableShrink = totalShrink - node.totalShrink;
+ int availableStretch = totalStretch - node.totalStretch;
+ if (log.isTraceEnabled()) {
+ log.trace("\tr=" + r);
+ log.trace("\tline=" + line);
+ }
+
+ // The line would be too long.
+ if (r < -1 || element.isForcedBreak()) {
+ // Deactivate node.
+ if (log.isTraceEnabled()) {
+ log.trace("Removing " + node);
+ }
+ removeNode(line, node);
+ lastDeactivated = compareNodes(lastDeactivated, node);
+ }
+
+ // The line is within the available shrink and the threshold.
+ if (r >= -1 && r <= threshold) {
+ int fitnessClass = computeFitness(r);
+ double demerits = computeDemerits(node, element, fitnessClass, r);
+
+ if (log.isTraceEnabled()) {
+ log.trace("\tDemerits=" + demerits);
+ log.trace("\tFitness class=" + fitnessClass);
+ }
+
+ if (demerits < best.getDemerits(fitnessClass)) {
+ // updates best demerits data
+ best.addRecord(demerits, node, r, availableShrink, availableStretch,
+ difference, fitnessClass);
+ lastTooShort = null;
+ }
+ }
+
+ // The line is way too short, but we are in forcing mode, so a node is
+ // calculated and stored in lastValidNode.
+ if (force && (r <= -1 || r > threshold)) {
+ int fitnessClass = computeFitness(r);
+ double demerits = computeDemerits(node, element, fitnessClass, r);
+ if (r <= -1) {
+ if (lastTooLong == null || demerits < lastTooLong.totalDemerits) {
+ lastTooLong = new KnuthNode(elementIdx, line + 1, fitnessClass,
+ totalWidth, totalStretch, totalShrink,
+ r, availableShrink, availableStretch,
+ difference, demerits, node);
+ if (log.isTraceEnabled()) {
+ log.trace("Picking tooLong " + lastTooLong);
+ }
+ }
+ } else {
+ if (lastTooShort == null || demerits <= lastTooShort.totalDemerits) {
+ lastTooShort = new KnuthNode(elementIdx, line + 1, fitnessClass,
+ totalWidth, totalStretch, totalShrink,
+ r, availableShrink, availableStretch,
+ difference, demerits, node);
+ if (log.isTraceEnabled()) {
+ log.trace("Picking tooShort " + lastTooShort);
+ }
+ }
+ }
+ }
+ }
+ addBreaks(line, elementIdx);
+ }
+ }
+
+ private void addBreaks(int line, int elementIdx) {
+ if (!best.hasRecords()) {
+ return;
+ }
+
+ int newWidth = totalWidth;
+ int newStretch = totalStretch;
+ int newShrink = totalShrink;
+
+ for (int i = elementIdx; i < par.size(); i++) {
+ KnuthElement tempElement = getElement(i);
+ if (tempElement.isBox()) {
+ break;
+ } else if (tempElement.isGlue()) {
+ newWidth += tempElement.getW();
+ newStretch += tempElement.getY();
+ newShrink += tempElement.getZ();
+ } else if (tempElement.isForcedBreak() && i != elementIdx) {
+ break;
+ }
+ }
+
+ // add nodes to the active nodes list
+ double minimumDemerits = best.getMinDemerits() + incompatibleFitnessDemerit;
+ for (int i = 0; i <= 3; i++) {
+ if (best.notInfiniteDemerits(i) && best.getDemerits(i) <= minimumDemerits) {
+ // the nodes in activeList must be ordered
+ // by line number and position;
+ if (log.isTraceEnabled()) {
+ log.trace("\tInsert new break in list of " + activeNodeCount);
+ }
+ KnuthNode newNode = new KnuthNode(elementIdx, line + 1, i,
+ newWidth, newStretch, newShrink,
+ best.getAdjust(i),
+ best.getAvailableShrink(i),
+ best.getAvailableStretch(i),
+ best.getDifference(i),
+ best.getDemerits(i),
+ best.getNode(i));
+ addNode(line + 1, newNode);
+ }
+ }
+ best.reset();
+ }
+
+ /**
+ * Return the difference between the line width and the width of the break that
+ * ends in 'element'.
+ * @param activeNode
+ * @param element
+ * @return The difference in width. Positive numbers mean extra space in the line,
+ * negative number that the line overflows.
+ */
+ private int computeDifference(KnuthNode activeNode, KnuthElement element) {
+ // compute the adjustment ratio
+ int actualWidth = totalWidth - activeNode.totalWidth;
+ if (element.isPenalty()) {
+ actualWidth += element.getW();
+ }
+ return lineWidth - actualWidth;
+ }
+
+ /**
+ * Return the adjust ration needed to make up for the difference. A ration of
+ * <ul>
+ * <li>0 means that the break has the exact right width</li>
+ * <li>>= -1 && < 0 means that the break is to wider than the line,
+ * but within the minimim values of the glues.</li>
+ * <li>>0 && < 1 means that the break is smaller than the line width,
+ * but within the maximum values of the glues.</li>
+ * <li>> 1 means that the break is too small to make up for the glues.</li>
+ * </ul>
+ * @param activeNode
+ * @param difference
+ * @return The ration.
+ */
+ private double computeAdjustmentRatio(KnuthNode activeNode, int difference) {
+ // compute the adjustment ratio
+ if (difference > 0) {
+ int maxAdjustment = totalStretch - activeNode.totalStretch;
+ if (maxAdjustment > 0) {
+ return (double) difference / maxAdjustment;
+ } else {
+ return INFINITE_RATIO;
+ }
+ } else if (difference < 0) {
+ int maxAdjustment = totalShrink - activeNode.totalShrink;
+ if (maxAdjustment > 0) {
+ return (double) difference / maxAdjustment;
+ } else {
+ return -INFINITE_RATIO;
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Figure out the fitness class of this line (tight, loose,
+ * very tight or very loose).
+ * @param r
+ * @return
+ */
+ private int computeFitness(double r) {
+ int newFitnessClass;
+ if (r < -0.5) {
+ return 0;
+ } else if (r <= 0.5) {
+ return 1;
+ } else if (r <= 1) {
+ return 2;
+ } else {
+ return 3;
+ }
+ }
+
+ private double computeDemerits(KnuthNode activeNode, KnuthElement element,
+ int fitnessClass, double r) {
+ double demerits = 0;
+ // compute demerits
+ double f = Math.abs(r);
+ f = 1 + 100 * f * f * f;
+ if (element.isPenalty() && element.getP() >= 0) {
+ f += element.getP();
+ demerits = f * f;
+ } else if (element.isPenalty() && !element.isForcedBreak()) {
+ double penalty = element.getP();
+ demerits = f * f - penalty * penalty;
+ } else {
+ demerits = f * f;
+ }
+
+ if (element.isPenalty() && ((KnuthPenalty) element).isFlagged()
+ && getElement(activeNode.position).isPenalty()
+ && ((KnuthPenalty) getElement(activeNode.position)).isFlagged()) {
+ // add demerit for consecutive breaks at flagged penalties
+ demerits += repeatedFlaggedDemerit;
+ }
+ if (Math.abs(fitnessClass - activeNode.fitness) > 1) {
+ // add demerit for consecutive breaks
+ // with very different fitness classes
+ demerits += incompatibleFitnessDemerit;
+ }
+ demerits += activeNode.totalDemerits;
+ return demerits;
+ }
+
+ /**
+ * Return the element at index idx in the paragraph.
+ * @param idx index of the element.
+ * @return
+ */
+ private KnuthElement getElement(int idx) {
+ return (KnuthElement) par.get(idx);
+ }
+
+ /**
+ * Compare two KnuthNodes and return the node with the least demerit.
+ * @param node1 The first knuth node.
+ * @param node2 The other knuth node.
+ * @return
+ */
+ protected KnuthNode compareNodes(KnuthNode node1, KnuthNode node2) {
+ if (node1 == null || node2.position > node1.position) {
+ return node2;
+ }
+ if (node2.position == node1.position) {
+ if (node2.totalDemerits < node1.totalDemerits) {
+ return node2;
+ }
+ }
+ return node1;
+ }
+
+ /**
+ * Add a KnuthNode at the end of line 'line'.
+ * If this is the first node in the line, adjust endLine accordingly.
+ * @param line
+ * @param node
+ */
+ private void addNode(int line, KnuthNode node) {
+ int headIdx = line * 2;
+ if (headIdx >= activeLines.length) {
+ KnuthNode[] oldList = activeLines;
+ activeLines = new KnuthNode[headIdx + headIdx];
+ System.arraycopy(oldList, 0, activeLines, 0, oldList.length);
+ }
+ node.next = null;
+ if (activeLines[headIdx + 1] != null) {
+ activeLines[headIdx + 1].next = node;
+ } else {
+ activeLines[headIdx] = node;
+ endLine = line+1;
+ }
+ activeLines[headIdx + 1] = node;
+ activeNodeCount++;
+ }
+
+ /**
+ * Remove the first node in line 'line'. If the line then becomes empty, adjust the
+ * startLine accordingly.
+ * @param line
+ * @param node
+ */
+ protected void removeNode(int line, KnuthNode node) {
+ KnuthNode n = getNode(line);
+ if (n != node) {
+ log.error("Should be first");
+ } else {
+ activeLines[line*2] = node.next;
+ if (node.next == null) {
+ activeLines[line*2+1] = null;
+ }
+ while (startLine < endLine && getNode(startLine) == null) {
+ startLine++;
+ }
+ }
+ activeNodeCount--;
+ }
+
+ protected KnuthNode getNode(int line) {
+ return activeLines[line * 2];
+ }
+
+ /**
+ * Return true if the position 'idx' is a legal breakpoint.
+ * @param idx
+ * @return
+ */
+ private boolean isLegalBreakpoint(int idx) {
+ KnuthElement elm = getElement(idx);
+ if (elm.isPenalty() && elm.getP() != KnuthElement.INFINITE) {
+ return true;
+ } else if (idx > 0 && elm.isGlue() && getElement(idx-1).isBox()) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public int getDifference(int line) {
+ return positions[line].difference;
+ }
+
+ public double getAdjustRatio(int line) {
+ return positions[line].adjustRatio;
+ }
+
+ public int getStart(int line) {
+ KnuthNode previous = positions[line].previous;
+ return line == 0 ? 0 : previous.position + 1;
+ }
+
+ public int getEnd(int line) {
+ return positions[line].position;
+ }
+
+ /**
+ * Return a string representation of a MinOptMax in the form of a
+ * "width+stretch-shrink". Useful only for debugging.
+ * @param mom
+ * @return
+ */
+ private static String width(MinOptMax mom) {
+ return mom.opt + "+" + (mom.max - mom.opt) + "-" + (mom.opt - mom.min);
+
+ }
+
+ public String toString(String prepend) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("[\n");
+ for (int i = startLine; i < endLine; i++) {
+ for (KnuthNode node = getNode(i); node != null; node = node.next) {
+ sb.append(prepend + "\t" + node + ",\n");
+ }
+ }
+ sb.append(prepend + "]");
+ return sb.toString();
+ }
+
+ protected abstract int filterActiveNodes() ;
+
+ private void calculateBreakPoints(KnuthNode node, KnuthSequence par,
+ int total) {
+ KnuthNode bestActiveNode = node;
+ // use bestActiveNode to determine the optimum breakpoints
+ for (int i = node.line; i > 0; i--) {
+ updateData2(bestActiveNode, par, total);
+ bestActiveNode = bestActiveNode.previous;
+ }
+ }
+}
// node is a fo:Character
if (letterSpaceIPD.min == letterSpaceIPD.max) {
// constant letter space, only return a box
- returnList.add(new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
+ returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
areaInfo.total, areaInfo.middle,
new LeafPosition(this, 0), false));
} else {
// at the moment the character is supposed to have no letter spaces,
// but returning this sequence allows us to change only one element
// if addALetterSpaceTo() is called
- returnList.add(new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
+ returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
areaInfo.total, areaInfo.middle,
new LeafPosition(this, 0), false));
returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
new LeafPosition(this, -1), true));
returnList.add(new KnuthGlue(0, 0, 0,
new LeafPosition(this, -1), true));
- returnList.add(new KnuthBox(0, 0, 0, 0,
+ returnList.add(new KnuthInlineBox(0, 0, 0, 0,
new LeafPosition(this, -1), true));
}
if (letterSpaceIPD.min == letterSpaceIPD.max) {
// constant letter space, return a new box
- return new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
+ return new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
areaInfo.total, areaInfo.middle,
new LeafPosition(this, 0), false);
} else {
if (letterSpaceIPD.min == letterSpaceIPD.max
|| areaInfo.iLScount == 0) {
// constant letter space, or no letter space
- returnList.add(new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
+ returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
areaInfo.total, areaInfo.middle,
new LeafPosition(this, 0), false));
if (areaInfo.bHyphenated) {
} else {
// adjustable letter space
returnList.add
- (new KnuthBox(areaInfo.ipdArea.opt
+ (new KnuthInlineBox(areaInfo.ipdArea.opt
- areaInfo.iLScount * letterSpaceIPD.opt,
areaInfo.lead, areaInfo.total, areaInfo.middle,
new LeafPosition(this, 0), false));
areaInfo.iLScount * letterSpaceIPD.max - letterSpaceIPD.opt,
areaInfo.iLScount * letterSpaceIPD.opt - letterSpaceIPD.min,
new LeafPosition(this, -1), true));
- returnList.add(new KnuthBox(0, 0, 0, 0,
+ returnList.add(new KnuthInlineBox(0, 0, 0, 0,
new LeafPosition(this, -1), true));
if (areaInfo.bHyphenated) {
returnList.add
}
protected void addId() {
- addID(fobj.getId());
+ getPSLM().addIDToPage(fobj.getId());
}
}
package org.apache.fop.layoutmgr;
import org.apache.fop.apps.FOUserAgent;
-import org.apache.fop.fo.FObj;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.pagination.Title;
-import org.apache.fop.fo.flow.Marker;
import org.apache.fop.area.Area;
-import org.apache.fop.area.AreaTreeHandler;
import org.apache.fop.area.LineArea;
import org.apache.fop.area.inline.InlineArea;
-import org.apache.fop.area.Resolvable;
-import org.apache.fop.area.PageViewport;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
-import java.util.Map;
import java.util.ArrayList;
import org.apache.fop.traits.MinOptMax;
*/
public class ContentLayoutManager implements InlineLevelLayoutManager {
private FOUserAgent userAgent;
- private AreaTreeHandler areaTreeHandler;
private Area holder;
private int stackSize;
private LayoutManager parentLM;
while (contentIter.hasNext()) {
KnuthElement element = (KnuthElement) contentIter.next();
if (element.isBox()) {
- KnuthBox box = (KnuthBox) element;
+ KnuthInlineBox box = (KnuthInlineBox) element;
if (box.getLead() > lineLead) {
lineLead = box.getLead();
}
return userAgent;
}
- /** @see org.apache.fop.layoutmgr.LayoutManager */
- public void setFObj(FObj fobj) {
- }
-
/** @see org.apache.fop.layoutmgr.LayoutManager */
public void setParent(LayoutManager lm) {
parentLM = lm;
public void getWordChars(StringBuffer sbChars, Position bp1,
Position bp2) { }
- /** @see org.apache.fop.layoutmgr.LayoutManager */
- public String getCurrentPageNumberString() {
- return parentLM.getCurrentPageNumberString();
- }
-
- /** @see org.apache.fop.layoutmgr.LayoutManager */
- public PageViewport resolveRefID(String ref) {
- return parentLM.resolveRefID(ref);
- }
-
- /** @see org.apache.fop.layoutmgr.LayoutManager */
- public void addIDToPage(String id) {
- parentLM.addIDToPage(id);
- }
-
- /** @see org.apache.fop.layoutmgr.LayoutManager */
- public void addUnresolvedArea(String id, Resolvable res) {
- parentLM.addUnresolvedArea(id, res);
- }
-
- /** @see org.apache.fop.layoutmgr.LayoutManager */
- public void addMarkerMap(Map marks, boolean starting, boolean isfirst, boolean islast) {
- parentLM.addMarkerMap(marks, starting, isfirst, islast);
- }
-
- /** @see org.apache.fop.layoutmgr.LayoutManager */
- public Marker retrieveMarker(String name, int pos, int boundary) {
- return parentLM.retrieveMarker(name, pos, boundary);
- }
-
- /**
- * @see org.apache.fop.layoutmgr.LayoutManager
- * @return the AreaTreeHandler object.
- */
- public AreaTreeHandler getAreaTreeHandler() {
- return parentLM.getAreaTreeHandler();
- }
-
/**
* @see org.apache.fop.layoutmgr.LayoutManager#preLoadNext
*/
return contentList;
}
- public KnuthElement addALetterSpaceTo(KnuthElement element) {
- return element;
+ public List addALetterSpaceTo(List oldList) {
+ return oldList;
}
public void getWordChars(StringBuffer sbChars, Position pos) {
}
public LinkedList getChangedKnuthElements(List oldList,
- int flaggedPenalty,
+ /*int flaggedPenalty,*/
int alignment) {
return null;
}
-
+
+ public PageSequenceLayoutManager getPSLM() {
+ return parentLM.getPSLM();
+ }
}
--- /dev/null
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * Utilities for Knuth element lists.
+ */
+public class ElementListUtils {
+
+ /**
+ * Removes all legal breaks in an element list.
+ * @param elements the element list
+ */
+ public static void removeLegalBreaks(LinkedList elements) {
+ ListIterator i = elements.listIterator();
+ while (i.hasNext()) {
+ KnuthElement el = (KnuthElement)i.next();
+ if (el.isPenalty()) {
+ KnuthPenalty penalty = (KnuthPenalty)el;
+ //Convert all penalties no break inhibitors
+ if (penalty.getP() < KnuthPenalty.INFINITE) {
+ i.set(new KnuthPenalty(penalty.getW(), KnuthPenalty.INFINITE,
+ penalty.isFlagged(), penalty.getPosition(), penalty.isAuxiliary()));
+ }
+ } else if (el.isGlue()) {
+ i.previous();
+ if (el.isBox()) {
+ i.next();
+ i.add(new KnuthPenalty(0, KnuthPenalty.INFINITE, false,
+ /*new Position(getTableLM())*/null, false));
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes all legal breaks in an element list. A constraint can be specified to limit the
+ * range in which the breaks are removed. Legal breaks occuring before at least
+ * constraint.opt space is filled will be removed.
+ * @param elements the element list
+ * @param constraint min/opt/max value to restrict the range in which the breaks are removed.
+ * @return true if the opt constraint is bigger than the list contents
+ */
+ public static boolean removeLegalBreaks(LinkedList elements, MinOptMax constraint) {
+ int len = 0;
+ ListIterator i = elements.listIterator();
+ while (i.hasNext()) {
+ KnuthElement el = (KnuthElement)i.next();
+ if (el.isPenalty()) {
+ KnuthPenalty penalty = (KnuthPenalty)el;
+ //Convert all penalties no break inhibitors
+ if (penalty.getP() < KnuthPenalty.INFINITE) {
+ i.set(new KnuthPenalty(penalty.getW(), KnuthPenalty.INFINITE,
+ penalty.isFlagged(), penalty.getPosition(), penalty.isAuxiliary()));
+ }
+ } else if (el.isGlue()) {
+ len += el.getW();
+ i.previous();
+ if (el.isBox()) {
+ i.next();
+ i.add(new KnuthPenalty(0, KnuthPenalty.INFINITE, false,
+ /*new Position(getTableLM())*/null, false));
+ }
+ } else {
+ len += el.getW();
+ }
+ if (len > constraint.opt) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Calculates the content length of the given element list. Warning: It doesn't take any
+ * stretch and shrink possibilities into account.
+ * @param elems the element list
+ * @param start element at which to start
+ * @param end element at which to stop
+ * @return the content length
+ */
+ public static int calcContentLength(List elems, int start, int end) {
+ ListIterator iter = elems.listIterator(start);
+ int count = end - start + 1;
+ int len = 0;
+ while (iter.hasNext()) {
+ KnuthElement el = (KnuthElement)iter.next();
+ if (el.isBox()) {
+ len += el.getW();
+ } else if (el.isGlue()) {
+ len += el.getW();
+ } else {
+ //log.debug("Ignoring penalty: " + el);
+ //ignore penalties
+ }
+ count--;
+ if (count == 0) {
+ break;
+ }
+ }
+ return len;
+ }
+
+ /**
+ * Calculates the content length of the given element list. Warning: It doesn't take any
+ * stretch and shrink possibilities into account.
+ * @param elems the element list
+ * @return the content length
+ */
+ public static int calcContentLength(List elems) {
+ return calcContentLength(elems, 0, elems.size() - 1);
+ }
+
+}
}
protected void addId() {
- addID(fobj.getId());
+ getPSLM().addIDToPage(fobj.getId());
}
}
package org.apache.fop.layoutmgr;
import org.apache.fop.datatypes.PercentBase;
-import org.apache.fop.fo.flow.Marker;
import org.apache.fop.fo.pagination.Flow;
import org.apache.fop.area.Area;
import org.apache.fop.area.BlockParent;
+import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
-import org.apache.fop.traits.MinOptMax;
+import java.util.ListIterator;
/**
* LayoutManager for an fo:flow object.
* This LM is responsible for getting columns of the appropriate size
* and filling them with block-level areas generated by its children.
*/
-public class FlowLayoutManager extends BlockStackingLayoutManager {
-
+public class FlowLayoutManager extends BlockStackingLayoutManager
+ implements BlockLevelLayoutManager {
private Flow fobj;
/** List of break possibilities */
*/
private int numSubsequentOverflows = 0;
+/*LF*/
+ private static class StackingIter extends PositionIterator {
+ StackingIter(Iterator parentIter) {
+ super(parentIter);
+ }
+
+ protected LayoutManager getLM(Object nextObj) {
+ return ((Position) nextObj).getLM();
+ }
+
+ protected Position getPos(Object nextObj) {
+ return ((Position) nextObj);
+ }
+ }
+/*LF*/
+
/**
* This is the top level layout manager.
* It is created by the PageSequence FO.
fobj = node;
}
- /**
- * @see org.apache.fop.layoutmgr.LayoutManager#getNextBreakPoss(LayoutContext)
- */
+ /*
public BreakPoss getNextBreakPoss(LayoutContext context) {
// currently active LM
new LeafPosition(this, blockBreaks.size() - 1));
}
return null;
+ }*/
+
+
+ /**
+ * "wrap" the Position inside each element moving the elements from
+ * SourceList to targetList
+ * @param sourceList source list
+ * @param targetList target list receiving the wrapped position elements
+ */
+ protected void wrapPositionElements(List sourceList, List targetList) {
+ ListIterator listIter = sourceList.listIterator();
+ while (listIter.hasNext()) {
+ KnuthElement tempElement;
+ tempElement = (KnuthElement) listIter.next();
+ //if (tempElement.getLayoutManager() != this) {
+ tempElement.setPosition(new NonLeafPosition(this,
+ tempElement.getPosition()));
+ //}
+ targetList.add(tempElement);
+ }
+ }
+
+
+//TODO Reintroduce emergency counter (generate error to avoid endless loop)
+ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+ // set layout dimensions
+ fobj.setLayoutDimension(PercentBase.BLOCK_IPD, context.getRefIPD());
+ fobj.setLayoutDimension(PercentBase.BLOCK_BPD, context.getStackLimit().opt);
+
+ // currently active LM
+ BlockLevelLayoutManager curLM;
+ BlockLevelLayoutManager prevLM = null;
+ //MinOptMax stackSize = new MinOptMax();
+ LinkedList returnedList;
+ LinkedList returnList = new LinkedList();
+
+ while ((curLM = ((BlockLevelLayoutManager) getChildLM())) != null) {
+ if (curLM.generatesInlineAreas()) {
+ log.error("inline area not allowed under flow - ignoring");
+ curLM.setFinished(true);
+ continue;
+ }
+
+ // Set up a LayoutContext
+ //MinOptMax bpd = context.getStackLimit();
+
+ LayoutContext childLC = new LayoutContext(0);
+ childLC.setStackLimit(context.getStackLimit());
+ childLC.setRefIPD(context.getRefIPD());
+
+ // get elements from curLM
+ returnedList = curLM.getNextKnuthElements(childLC, alignment);
+ //log.debug("FLM.getNextKnuthElements> returnedList.size() = " + returnedList.size());
+
+ // "wrap" the Position inside each element
+ LinkedList tempList = returnedList;
+ returnedList = new LinkedList();
+ wrapPositionElements(tempList, returnedList);
+
+ if (returnedList.size() == 1
+ && ((KnuthElement)returnedList.getFirst()).isPenalty()
+ && ((KnuthPenalty)returnedList.getFirst()).getP() == -KnuthElement.INFINITE) {
+ // a descendant of this flow has break-before
+ returnList.addAll(returnedList);
+ return returnList;
+ } else {
+ if (returnList.size() > 0) {
+ // there is a block before this one
+ if (prevLM.mustKeepWithNext()
+ || curLM.mustKeepWithPrevious()) {
+ // add an infinite penalty to forbid a break between blocks
+ returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, new Position(this), false));
+ } else if (!((KnuthElement) returnList.getLast()).isGlue()) {
+ // add a null penalty to allow a break between blocks
+ returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+ }
+ }
+/*LF*/ if (returnedList.size() > 0) { // controllare!
+ returnList.addAll(returnedList);
+ if (((KnuthElement)returnedList.getLast()).isPenalty()
+ && ((KnuthPenalty)returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+ // a descendant of this flow has break-after
+/*LF*/ //System.out.println("FLM - break after!!");
+ return returnList;
+ }
+/*LF*/ }
+ }
+ prevLM = curLM;
+ }
+
+ setFinished(true);
+
+ if (returnList.size() > 0) {
+ return returnList;
+ } else {
+ return null;
+ }
+ }
+
+ public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
+ log.debug(" FLM.negotiateBPDAdjustment> " + adj);
+
+ if (lastElement.getPosition() instanceof NonLeafPosition) {
+ // this element was not created by this FlowLM
+ NonLeafPosition savedPos = (NonLeafPosition)lastElement.getPosition();
+ lastElement.setPosition(savedPos.getPosition());
+ int returnValue = ((BlockLevelLayoutManager) lastElement.getLayoutManager()).negotiateBPDAdjustment(adj, lastElement);
+ lastElement.setPosition(savedPos);
+ log.debug(" FLM.negotiateBPDAdjustment> result " + returnValue);
+ return returnValue;
+ } else {
+ return 0;
+ }
+ }
+
+ public void discardSpace(KnuthGlue spaceGlue) {
+ log.debug(" FLM.discardSpace> ");
+
+ if (spaceGlue.getPosition() instanceof NonLeafPosition) {
+ // this element was not created by this FlowLM
+ NonLeafPosition savedPos = (NonLeafPosition)spaceGlue.getPosition();
+ spaceGlue.setPosition(savedPos.getPosition());
+ ((BlockLevelLayoutManager) spaceGlue.getLayoutManager()).discardSpace(spaceGlue);
+ spaceGlue.setPosition(savedPos);
+ }
+ }
+
+ public boolean mustKeepTogether() {
+ return false;
+ }
+
+ public boolean mustKeepWithPrevious() {
+ return false;
+ }
+
+ public boolean mustKeepWithNext() {
+ return false;
+ }
+
+ public LinkedList getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/ int alignment) {
+ ListIterator oldListIterator = oldList.listIterator();
+ KnuthElement returnedElement;
+ LinkedList returnedList = new LinkedList();
+ LinkedList returnList = new LinkedList();
+ KnuthElement prevElement = null;
+ KnuthElement currElement = null;
+ int fromIndex = 0;
+
+/*LF*/ //System.out.println("");
+/*LF*/ //System.out.println("FLM.getChangedKnuthElements> prima dell'unwrap, oldList.size() = " + oldList.size() + " da 0 a " + (oldList.size() - 1));
+ // "unwrap" the Positions stored in the elements
+ KnuthElement oldElement;
+ while (oldListIterator.hasNext()) {
+ oldElement = (KnuthElement)oldListIterator.next();
+ if (oldElement.getPosition() instanceof NonLeafPosition) {
+ // oldElement was created by a descendant of this FlowLM
+ oldElement.setPosition(((NonLeafPosition)oldElement.getPosition()).getPosition());
+ } else {
+ // thisElement was created by this FlowLM, remove it
+ oldListIterator.remove();
+ }
+ }
+ // reset the iterator
+ oldListIterator = oldList.listIterator();
+
+/*LF*/ //System.out.println("FLM.getChangedKnuthElements> dopo l'unwrap, oldList.size() = " + oldList.size() + " da 0 a " + (oldList.size() - 1));
+
+ while (oldListIterator.hasNext()) {
+ currElement = (KnuthElement) oldListIterator.next();
+/*LF*/ //System.out.println("elemento n. " + oldListIterator.previousIndex() + " nella oldList");
+ if (prevElement != null
+ && prevElement.getLayoutManager() != currElement.getLayoutManager()) {
+ // prevElement is the last element generated by the same LM
+ BlockLevelLayoutManager prevLM = (BlockLevelLayoutManager)
+ prevElement.getLayoutManager();
+ BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
+ currElement.getLayoutManager();
+/*LF*/ //System.out.println("FLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + oldListIterator.previousIndex());
+ returnedList.addAll(prevLM.getChangedKnuthElements(oldList.subList(fromIndex, oldListIterator.previousIndex()),
+ /*flaggedPenalty,*/ alignment));
+ fromIndex = oldListIterator.previousIndex();
+
+ // there is another block after this one
+ if (prevLM.mustKeepWithNext()
+ || currLM.mustKeepWithPrevious()) {
+ // add an infinite penalty to forbid a break between blocks
+ returnedList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, new Position(this), false));
+ } else if (!((KnuthElement) returnedList.getLast()).isGlue()) {
+ // add a null penalty to allow a break between blocks
+ returnedList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+ }
+ }
+ prevElement = currElement;
+ }
+ if (currElement != null) {
+ BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
+ currElement.getLayoutManager();
+/*LF*/ //System.out.println("FLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + oldList.size());
+ returnedList.addAll(currLM.getChangedKnuthElements(oldList.subList(fromIndex, oldList.size()),
+ /*flaggedPenalty,*/ alignment));
+ }
+
+ // "wrap" the Position stored in each element of returnedList
+ // and add elements to returnList
+ ListIterator listIter = returnedList.listIterator();
+ while (listIter.hasNext()) {
+ returnedElement = (KnuthElement)listIter.next();
+ if (returnedElement.getLayoutManager() != this) {
+ returnedElement.setPosition(new NonLeafPosition(this, returnedElement.getPosition()));
+ }
+ returnList.add(returnedElement);
+ }
+
+ return returnList;
}
/**
* @see org.apache.fop.layoutmgr.LayoutManager#addAreas(PositionIterator, LayoutContext)
*/
public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
-
- LayoutManager childLM;
+ AreaAdditionUtil.addAreas(parentIter, layoutContext);
+ /*
+ LayoutManager childLM = null;
LayoutContext lc = new LayoutContext(0);
+ LayoutManager firstLM = null;
+ LayoutManager lastLM = null;
+
+ // "unwrap" the NonLeafPositions stored in parentIter
+ // and put them in a new list;
+ LinkedList positionList = new LinkedList();
+ Position pos;
while (parentIter.hasNext()) {
- LeafPosition lfp = (LeafPosition) parentIter.next();
- // Add the block areas to Area
- PositionIterator breakPosIter = new BreakPossPosIter(
- blockBreaks, iStartPos, lfp.getLeafPos() + 1);
- iStartPos = lfp.getLeafPos() + 1;
- while ((childLM = breakPosIter.getNextChildLM()) != null) {
- childLM.addAreas(breakPosIter, lc);
+ pos = (Position)parentIter.next();
+ if (pos instanceof NonLeafPosition) {
+ // pos was created by a child of this FlowLM
+ positionList.add(((NonLeafPosition) pos).getPosition());
+ lastLM = ((NonLeafPosition) pos).getPosition().getLM();
+ if (firstLM == null) {
+ firstLM = lastLM;
+ }
+ } else {
+ // pos was created by this FlowLM, so it must be ignored
}
}
+ StackingIter childPosIter = new StackingIter(positionList.listIterator());
+ while ((childLM = childPosIter.getNextChildLM()) != null) {
+ // Add the block areas to Area
+ lc.setFlags(LayoutContext.FIRST_AREA, childLM == firstLM);
+ lc.setFlags(LayoutContext.LAST_AREA, childLM == lastLM);
+ // set space before for the first LM, in order to implement
+ // display-align = center or after
+ lc.setSpaceBefore((childLM == firstLM ? layoutContext.getSpaceBefore() : 0));
+ // set space after for each LM, in order to implement
+ // display-align = distribute
+ lc.setSpaceAfter(layoutContext.getSpaceAfter());
+ lc.setStackLimit(layoutContext.getStackLimit());
+ childLM.addAreas(childPosIter, lc);
+ }*/
+
flush();
}
reset(null);
}
}
-
- /**
- * Retrieve marker is not allowed in the flow so this reports an
- * error and returns null.
- *
- * @see org.apache.fop.layoutmgr.LayoutManager
- */
- public Marker retrieveMarker(String name, int pos, int boundary) {
- // error cannot retrieve markers in flow
- log.error("Cannot retrieve a marker from the flow");
- return null;
- }
}
}
protected void addId() {
- addID(fobj.getId());
+ getPSLM().addIDToPage(fobj.getId());
}
}
return null;
}
+ /*
public KnuthElement addALetterSpaceTo(KnuthElement element) {
NonLeafPosition savedPos = (NonLeafPosition) element.getPosition();
element.setPosition(savedPos.getPosition());
returnList.add(returnedElement);
}
return returnList;
- }
+ }*/
}
/*
- * Copyright 2004 The Apache Software Foundation.
+ * Copyright 2004-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package org.apache.fop.layoutmgr;
-import java.util.LinkedList;
import java.util.List;
/**
*/
public interface InlineLevelLayoutManager extends LayoutManager {
- /**
- * Get a sequence of KnuthElements representing the content
- * of the node assigned to the LM
- *
- * @param context the LayoutContext used to store layout information
- * @param alignment the desired text alignement
- * @return the list of KnuthElements
- */
- LinkedList getNextKnuthElements(LayoutContext context, int alignment);
-
/**
* Tell the LM to modify its data, adding a letter space
- * to the word fragment represented by the given element,
- * and returning a corrected element
+ * to the word fragment represented by the given elements,
+ * and returning the corrected elements
*
- * @param element the element which must be given one more letter space
- * @return the new element replacing the old one
+ * @param oldList the elements which must be given one more letter space
+ * @return the new elements replacing the old ones
*/
- KnuthElement addALetterSpaceTo(KnuthElement element);
+ List addALetterSpaceTo(List oldList);
/**
* Get the word chars corresponding to the given position
*/
boolean applyChanges(List oldList);
- /**
- * Get a sequence of KnuthElements representing the content
- * of the node assigned to the LM, after changes have been applied
- *
- * @param oldList the elements to replace
- * @param flaggedPenalty the penalty value for hyphenated lines
- * @param alignment the desired text alignment
- * @return the updated list of KnuthElements
- */
- LinkedList getChangedKnuthElements(List oldList, int flaggedPenalty,
- int alignment);
}
import java.util.LinkedList;
import java.util.Iterator;
+import java.util.List;
import java.util.ListIterator;
import java.util.HashMap;
import org.apache.fop.fo.FObj;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.SpaceProperty;
+import org.apache.fop.traits.InlineProps;
import org.apache.fop.traits.SpaceVal;
import org.apache.fop.area.Area;
import org.apache.fop.area.inline.InlineArea;
* which stack children in the inline direction, such as Inline or
* Line. It should not be instantiated directly.
*/
-public class InlineStackingLayoutManager extends AbstractLayoutManager {
+public class InlineStackingLayoutManager extends AbstractLayoutManager
+ implements InlineLevelLayoutManager {
private static class StackingIter extends PositionIterator {
private Area currentArea; // LineArea or InlineParent
private BreakPoss prevBP;
- protected LayoutContext childLC ;
+ protected LayoutContext childLC;
private LayoutManager lastChildLM = null; // Set when return last breakposs
private boolean bAreaCreated = false;
// Current child layout context
protected LayoutContext getContext() {
- return childLC ;
+ return childLC;
}
protected void addSpace(Area parentArea, MinOptMax spaceRange,
}
}
}
+
+ public LinkedList getNextKnuthElements(LayoutContext lc, int alignment) {
+ InlineLevelLayoutManager curLM;
+
+ // the list returned by child LM
+ LinkedList returnedList;
+ KnuthElement returnedElement;
+
+ // the list which will be returned to the parent LM
+ LinkedList returnList = new LinkedList();
+
+ SpaceSpecifier leadingSpace = lc.getLeadingSpace();
+
+ if (lc.startsNewArea()) {
+ // First call to this LM in new parent "area", but this may
+ // not be the first area created by this inline
+ childLC = new LayoutContext(lc);
+ lc.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart()));
+
+ // Check for "fence"
+ if (hasLeadingFence(!lc.isFirstArea())) {
+ // Reset leading space sequence for child areas
+ leadingSpace = new SpaceSpecifier(false);
+ }
+ // Reset state variables
+ clearPrevIPD(); // Clear stored prev content dimensions
+ }
+
+ while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
+ // get KnuthElements from curLM
+ returnedList = curLM.getNextKnuthElements(lc, alignment);
+ if (returnedList != null) {
+ // "wrap" the Position stored in each element of returnedList
+ ListIterator listIter = returnedList.listIterator();
+ while (listIter.hasNext()) {
+ returnedElement = (KnuthElement) listIter.next();
+ returnedElement.setPosition
+ (new NonLeafPosition(this,
+ returnedElement.getPosition()));
+ returnList.add(returnedElement);
+ }
+ return returnList;
+ } else {
+ // curLM returned null because it finished;
+ // just iterate once more to see if there is another child
+ }
+ }
+ setFinished(true);
+ return null;
+ }
+
+ public List addALetterSpaceTo(List oldList) {
+ // old list contains only a box, or the sequence: box penalty glue box
+
+ ListIterator oldListIterator = oldList.listIterator();
+ KnuthElement element = null;
+ // "unwrap" the Position stored in each element of oldList
+ while (oldListIterator.hasNext()) {
+ element = (KnuthElement) oldListIterator.next();
+ element.setPosition(((NonLeafPosition)element.getPosition()).getPosition());
+ }
+
+ oldList = ((InlineLevelLayoutManager)
+ element.getLayoutManager()).addALetterSpaceTo(oldList);
+
+ // "wrap" againg the Position stored in each element of oldList
+ oldListIterator = oldList.listIterator();
+ while (oldListIterator.hasNext()) {
+ element = (KnuthElement) oldListIterator.next();
+ element.setPosition(new NonLeafPosition(this, element.getPosition()));
+ }
+
+ return oldList;
+ }
+
+ public void getWordChars(StringBuffer sbChars, Position pos) {
+ Position newPos = ((NonLeafPosition) pos).getPosition();
+ ((InlineLevelLayoutManager)
+ newPos.getLM()).getWordChars(sbChars, newPos);
+ }
+
+ public void hyphenate(Position pos, HyphContext hc) {
+ Position newPos = ((NonLeafPosition) pos).getPosition();
+ ((InlineLevelLayoutManager)
+ newPos.getLM()).hyphenate(newPos, hc);
+ }
+
+ public boolean applyChanges(List oldList) {
+ // "unwrap" the Positions stored in the elements
+ ListIterator oldListIterator = oldList.listIterator();
+ KnuthElement oldElement;
+ while (oldListIterator.hasNext()) {
+ oldElement = (KnuthElement) oldListIterator.next();
+ oldElement.setPosition
+ (((NonLeafPosition) oldElement.getPosition()).getPosition());
+ }
+ // reset the iterator
+ oldListIterator = oldList.listIterator();
+
+ InlineLevelLayoutManager prevLM = null;
+ InlineLevelLayoutManager currLM;
+ int fromIndex = 0;
+
+ boolean bSomethingChanged = false;
+ while(oldListIterator.hasNext()) {
+ oldElement = (KnuthElement) oldListIterator.next();
+ currLM = (InlineLevelLayoutManager) oldElement.getLayoutManager();
+ // initialize prevLM
+ if (prevLM == null) {
+ prevLM = currLM;
+ }
+
+ if (currLM != prevLM || !oldListIterator.hasNext()) {
+ if (oldListIterator.hasNext()) {
+ bSomethingChanged
+ = prevLM.applyChanges(oldList.subList(fromIndex, oldListIterator.previousIndex()))
+ || bSomethingChanged;
+ prevLM = currLM;
+ fromIndex = oldListIterator.previousIndex();
+ } else if (currLM == prevLM) {
+ bSomethingChanged
+ = prevLM.applyChanges(oldList.subList(fromIndex, oldList.size()))
+ || bSomethingChanged;
+ } else {
+ bSomethingChanged
+ = prevLM.applyChanges(oldList.subList(fromIndex, oldListIterator.previousIndex()))
+ || bSomethingChanged;
+ bSomethingChanged
+ = currLM.applyChanges(oldList.subList(oldListIterator.previousIndex(), oldList.size()))
+ || bSomethingChanged;
+ }
+ }
+ }
+
+ // "wrap" again the Positions stored in the elements
+ oldListIterator = oldList.listIterator();
+ while (oldListIterator.hasNext()) {
+ oldElement = (KnuthElement) oldListIterator.next();
+ oldElement.setPosition
+ (new NonLeafPosition(this, oldElement.getPosition()));
+ }
+ return bSomethingChanged;
+ }
+
+ public LinkedList getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/ int alignment) {
+ // "unwrap" the Positions stored in the elements
+ ListIterator oldListIterator = oldList.listIterator();
+ KnuthElement oldElement;
+ while (oldListIterator.hasNext()) {
+ oldElement = (KnuthElement) oldListIterator.next();
+ oldElement.setPosition
+ (((NonLeafPosition) oldElement.getPosition()).getPosition());
+ }
+ // reset the iterator
+ oldListIterator = oldList.listIterator();
+
+ KnuthElement returnedElement;
+ LinkedList returnedList = new LinkedList();
+ LinkedList returnList = new LinkedList();
+ InlineLevelLayoutManager prevLM = null;
+ InlineLevelLayoutManager currLM;
+ int fromIndex = 0;
+
+ while(oldListIterator.hasNext()) {
+ oldElement = (KnuthElement) oldListIterator.next();
+ currLM = (InlineLevelLayoutManager) oldElement.getLayoutManager();
+ if (prevLM == null) {
+ prevLM = currLM;
+ }
+
+ if (currLM != prevLM || !oldListIterator.hasNext()) {
+ if (oldListIterator.hasNext()) {
+ returnedList.addAll
+ (prevLM.getChangedKnuthElements
+ (oldList.subList(fromIndex,
+ oldListIterator.previousIndex()),
+ /*flaggedPenalty,*/ alignment));
+ prevLM = currLM;
+ fromIndex = oldListIterator.previousIndex();
+ } else if (currLM == prevLM) {
+ returnedList.addAll
+ (prevLM.getChangedKnuthElements
+ (oldList.subList(fromIndex, oldList.size()),
+ /*flaggedPenalty,*/ alignment));
+ } else {
+ returnedList.addAll
+ (prevLM.getChangedKnuthElements
+ (oldList.subList(fromIndex,
+ oldListIterator.previousIndex()),
+ /*flaggedPenalty,*/ alignment));
+ returnedList.addAll
+ (currLM.getChangedKnuthElements
+ (oldList.subList(oldListIterator.previousIndex(),
+ oldList.size()),
+ /*flaggedPenalty,*/ alignment));
+ }
+ }
+ }
+
+ // "wrap" the Position stored in each element of returnedList
+ ListIterator listIter = returnedList.listIterator();
+ while (listIter.hasNext()) {
+ returnedElement = (KnuthElement) listIter.next();
+ returnedElement.setPosition
+ (new NonLeafPosition(this, returnedElement.getPosition()));
+ returnList.add(returnedElement);
+ }
+ return returnList;
+ }
}
package org.apache.fop.layoutmgr;
// Java
-import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
// FOP
* @see org.apache.fop.layoutmgr.LeafNodeLayoutManager#addId()
*/
protected void addId() {
- addID(fobj.getId());
+ getPSLM().addIDToPage(fobj.getId());
}
}
--- /dev/null
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ * Licensed 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 org.apache.fop.traits.MinOptMax;
+
+public class KnuthBlockBox extends KnuthBox {
+
+ private MinOptMax ipdRange;
+ private int bpd;
+
+ public KnuthBlockBox(int w, MinOptMax range, int bpdim, Position pos, boolean bAux) {
+ super(w, pos, bAux);
+ ipdRange = (MinOptMax) range.clone();
+ bpd = bpdim;
+ }
+
+ public MinOptMax getIPDRange() {
+ return (MinOptMax) ipdRange.clone();
+ }
+
+ public int getBPD() {
+ return bpd;
+ }
+}
\ No newline at end of file
* positioning, and the methods used to get them.
*/
public class KnuthBox extends KnuthElement {
- private int lead;
- private int total;
- private int middle;
/**
* Create a new KnuthBox.
*
* @param w the width of this box
- * @param l the height of this box above the main baseline
- * @param t the total height of this box
- * @param m the height of this box above and below the middle baseline
* @param pos the Position stored in this box
* @param bAux is this box auxiliary?
*/
- public KnuthBox(int w, int l, int t, int m, Position pos, boolean bAux) {
+ public KnuthBox(int w, Position pos, boolean bAux) {
super(w, pos, bAux);
- lead = l;
- total = t;
- middle = m;
}
+ /** @see org.apache.fop.layoutmgr.KnuthElement#isBox() */
public boolean isBox() {
return true;
}
- /**
- * Return the height of this box above the main baseline.
- */
- public int getLead() {
- return lead;
- }
-
- /**
- * Return the total height of this box.
- */
- public int getTotal() {
- return total;
- }
-
- /**
- * Return the height of this box above and below the middle baseline.
- */
- public int getMiddle() {
- return middle;
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ StringBuffer sb = new StringBuffer(64);
+ if (isAuxiliary()) {
+ sb.append("aux. ");
+ }
+ sb.append("box");
+ sb.append(" w=");
+ sb.append(getW());
+ return sb.toString();
}
+
}
* to get these values.
*/
public class KnuthGlue extends KnuthElement {
+
private int stretchability;
private int shrinkability;
+ private int adjustmentClass = -1;
/**
* Create a new KnuthGlue.
shrinkability = z;
}
+ public KnuthGlue(int w, int y, int z,
+ int iAdjClass, Position pos, boolean bAux) {
+ super(w, pos, bAux);
+ stretchability = y;
+ shrinkability = z;
+ adjustmentClass = iAdjClass;
+ }
+
+ /** @see org.apache.fop.layoutmgr.KnuthElement#isGlue() */
public boolean isGlue() {
return true;
}
- /**
- * Return the stretchability of this glue.
- */
+ /** @return the stretchability of this glue. */
public int getY() {
return stretchability;
}
- /**
- * Return the shrinkability of this glue.
- */
+ /** @return the shrinkability of this glue. */
public int getZ() {
return shrinkability;
}
+
+ /** @return the adjustment class (or role) of this glue. */
+ public int getAdjustmentClass() {
+ return adjustmentClass;
+ }
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ StringBuffer sb = new StringBuffer(64);
+ if (isAuxiliary()) {
+ sb.append("aux. ");
+ }
+ sb.append("glue");
+ sb.append(" w=").append(getW());
+ sb.append(" stretch=").append(getY());
+ sb.append(" shrink=").append(getZ());
+ if (getAdjustmentClass() >= 0) {
+ sb.append(" adj-class=").append(getAdjustmentClass());
+ }
+ return sb.toString();
+ }
+
}
--- /dev/null
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ * Licensed 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;
+
+public class KnuthInlineBox extends KnuthBox {
+
+ private int lead;
+ private int total;
+ private int middle;
+
+ /**
+ * Create a new KnuthBox.
+ *
+ * @param w the width of this box
+ * @param l the height of this box above the main baseline
+ * @param t the total height of this box
+ * @param m the height of this box above and below the middle baseline
+ * @param pos the Position stored in this box
+ * @param bAux is this box auxiliary?
+ */
+ public KnuthInlineBox(int w, int l, int t, int m, Position pos, boolean bAux) {
+ super(w, pos, bAux);
+ lead = l;
+ total = t;
+ middle = m;
+ }
+
+ /**
+ * @return the height of this box above the main baseline.
+ */
+ public int getLead() {
+ return lead;
+ }
+
+ /**
+ * @return the total height of this box.
+ */
+ public int getTotal() {
+ return total;
+ }
+
+ /**
+ * @return the height of this box above and below the middle baseline.
+ */
+ public int getMiddle() {
+ return middle;
+ }
+}
\ No newline at end of file
*/
/* $Id$ */
+
package org.apache.fop.layoutmgr;
-import java.util.ArrayList;
+import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* The paragraph of KnuthElements.
*/
- private ArrayList par;
+ private List par;
/**
* The width of a line.
protected static Log log = LogFactory.getLog(KnuthParagraph.class);
- public KnuthParagraph(ArrayList par) {
+ public KnuthParagraph(List par) {
this.best = new BestRecords();
this.par = par;
}
* be chosen as breaking points for consecutive lines.
*/
public class KnuthPenalty extends KnuthElement {
+
+ public static final int FLAGGED_PENALTY = 50;
+
private int penalty;
private boolean bFlagged;
+ private int breakClass = -1;
/**
* Create a new KnuthPenalty.
bFlagged = f;
}
+ public KnuthPenalty(int w, int p, boolean f,
+ int iBreakClass, Position pos, boolean bAux) {
+ super(w, pos, bAux);
+ penalty = p;
+ bFlagged = f;
+ breakClass = iBreakClass;
+ }
+
public boolean isPenalty() {
return true;
}
public boolean isForcedBreak() {
return penalty == -KnuthElement.INFINITE;
}
+
+ public int getBreakClass() {
+ return breakClass;
+ }
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ StringBuffer sb = new StringBuffer(64);
+ if (isAuxiliary()) {
+ sb.append("aux. ");
+ }
+ sb.append("penalty");
+ sb.append(" p=");
+ if (getP() < 0) {
+ sb.append("-");
+ }
+ if (Math.abs(getP()) == INFINITE) {
+ sb.append("INFINITE");
+ } else {
+ sb.append(getP());
+ }
+ if (isFlagged()) {
+ sb.append(" [flagged]");
+ }
+ sb.append(" w=");
+ sb.append(getW());
+ if (isForcedBreak()) {
+ sb.append(" (forced break)");
+ }
+ return sb.toString();
+ }
+
}
/*
- * Copyright 2004 The Apache Software Foundation.
+ * Copyright 2004-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
/**
* Main constructor
- * @param bpList List of break possibilities
+ * @param elementList List of Knuth elements
* @param startPos starting position
* @param endPos ending position
*/
- public KnuthPossPosIter(List bpList, int startPos, int endPos) {
- super(bpList.listIterator(startPos));
+ public KnuthPossPosIter(List elementList, int startPos, int endPos) {
+ super(elementList.listIterator(startPos));
iterCount = endPos - startPos;
}
+
+ /**
+ * Auxiliary constructor
+ * @param elementList List of Knuth elements
+ */
+ public KnuthPossPosIter(List elementList) {
+ this(elementList, 0, elementList.size());
+ }
// Check position < endPos
--- /dev/null
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ * Licensed 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;
+
+/**
+ * Represents a list of Knuth elements.
+ */
+public class KnuthSequence extends ArrayList {
+ /** Number of elements to ignore at the beginning of the list. */
+ public int ignoreAtStart = 0;
+ /** Number of elements to ignore at the end of the list. */
+ public int ignoreAtEnd = 0;
+
+ /**
+ * Creates a new and empty list.
+ */
+ public KnuthSequence() {
+ super();
+ }
+
+ /**
+ * Marks the start of the sequence.
+ */
+ public void startSequence() {
+ }
+
+ /**
+ * @return a finalized sequence.
+ */
+ public KnuthSequence endSequence() {
+ // remove glue and penalty item at the end of the paragraph
+ while (this.size() > ignoreAtStart
+ && !((KnuthElement)this.get(this.size() - 1)).isBox()) {
+ this.remove(this.size() - 1);
+ }
+ if (this.size() > ignoreAtStart) {
+ // add the elements representing the space at the end of the last line
+ // and the forced break
+ this.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, null, false));
+ this.add(new KnuthGlue(0, 10000000, 0, null, false));
+ this.add(new KnuthPenalty(0, -KnuthElement.INFINITE, false, null, false));
+ ignoreAtEnd = 3;
+ return this;
+ } else {
+ this.clear();
+ return null;
+ }
+ }
+
+ public KnuthElement getLast() {
+ int idx = size();
+ if (idx == 0) {
+ return null;
+ }
+ return (KnuthElement) get(idx - 1);
+ }
+
+ public KnuthElement removeLast() {
+ int idx = size();
+ if (idx == 0) {
+ return null;
+ }
+ return (KnuthElement) remove(idx - 1);
+ }
+}
private int iLineHeight;
private int iBaseline;
private int iMiddleShift;
+ private int iTopShift; /*LF*/
+ private int iBottomShift; /*LF*/
+ private int iSpaceBefore; /*LF*/
+ private int iSpaceAfter; /*LF*/
public LayoutContext(LayoutContext parentLC) {
this.flags = parentLC.flags;
this.iLineHeight = parentLC.iLineHeight;
this.iBaseline = parentLC.iBaseline;
this.iMiddleShift = parentLC.iMiddleShift;
+/*LF*/ this.iTopShift = parentLC.iTopShift;
+/*LF*/ this.iBottomShift = parentLC.iBottomShift;
+/*LF*/ this.iSpaceBefore = parentLC.iSpaceBefore;
+/*LF*/ this.iSpaceAfter = parentLC.iSpaceAfter;
// Copy other fields as necessary. Use clone???
}
return iBaseline + iMiddleShift;
}
+ public void setTopShift(int ts) {
+ iTopShift = ts;
+ }
+
+ public int getTopBaseline() {
+ return iBaseline + iTopShift;
+ }
+
+ public void setBottomShift(int bs) {
+ iBottomShift = bs;
+ }
+
+ public int getBottomBaseline() {
+ return iBaseline + iBottomShift;
+ }
+
+ public int getSpaceBefore() {
+ return iSpaceBefore;
+ }
+
+ public void setSpaceBefore(int sp) {
+ iSpaceBefore = sp;
+ }
+
+ public int getSpaceAfter() {
+ return iSpaceAfter;
+ }
+
+ public void setSpaceAfter(int sp) {
+ iSpaceAfter = sp;
+ }
+
public String toString() {
return "Layout Context:" +
"\nStack Limit: \t" + (getStackLimit() == null ? "null" : getStackLimit().toString()) +
package org.apache.fop.layoutmgr;
+import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
-
-import org.apache.fop.fo.flow.Marker;
import org.apache.fop.area.Area;
-import org.apache.fop.area.Resolvable;
-import org.apache.fop.area.PageViewport;
-import org.apache.fop.area.AreaTreeHandler;
-import org.apache.fop.fo.FObj;
/**
* The interface for all LayoutManagers.
*/
public interface LayoutManager {
- /**
- * Set the FO object for this layout manager.
- * For layout managers that are created without an FO
- * this may not be called.
- *
- * @param obj the FO object for this layout manager
- */
- void setFObj(FObj obj);
-
/**
* Set the parent layout manager.
* The parent layout manager is required for adding areas.
*/
void initialize();
+ /**
+ * Get the active PageSequenceLayoutManager instance for this
+ * layout process.
+ */
+ PageSequenceLayoutManager getPSLM();
+
/**
* Generates inline areas.
* This is used to check if the layout manager generates inline
*/
void addAreas(PositionIterator posIter, LayoutContext context);
- /**
- * Get the string of the current page number.
- *
- * @return the string for the current page number
- */
- String getCurrentPageNumberString();
-
- /**
- * Resolve the id reference.
- * This is called by an area looking for an id reference.
- * If the id reference is not found then it should add a resolvable object.
- *
- * @param ref the id reference
- * @return the page containing the id reference or null if not found
- */
- PageViewport resolveRefID(String ref);
-
- /**
- * Add an id to the page.
- * (todo) add the location of the area on the page
- *
- * @param id the id reference to add.
- */
- void addIDToPage(String id);
-
- /**
- * Add an unresolved area.
- * The is used to add a resolvable object to the page for a given id.
- *
- * @param id the id reference this object needs for resolving
- * @param res the resolvable object
- */
- void addUnresolvedArea(String id, Resolvable res);
-
- /**
- * Add the marker.
- * A number of formatting objects may contain markers. This
- * method is used to add those markers to the page.
- *
- * @param name the marker class name
- * @param starting if the area being added is starting or ending
- * @param isfirst if the area being added has is-first trait
- * @param islast if the area being added has is-last trait
- */
- void addMarkerMap(Map marks, boolean starting, boolean isfirst, boolean islast);
-
- /**
- * Retrieve a marker.
- * This method is used when retrieve a marker.
- *
- * @param name the class name of the marker
- * @param pos the retrieve position
- * @param boundary the boundary for retrieving the marker
- * @return the layout manaager of the retrieved marker if any
- */
- Marker retrieveMarker(String name, int pos, int boundary);
-
- /**
- * @return the AreaTreeHandler object.
- */
- AreaTreeHandler getAreaTreeHandler();
-
/**
* Load next child LMs, up to child LM index pos
* @param pos index up to which child LMs are requested
* @param newLMs the list of LMs to be added
*/
void addChildLMs(List newLMs);
+
+ /**
+ * Get a sequence of KnuthElements representing the content
+ * of the node assigned to the LM
+ *
+ * @param context the LayoutContext used to store layout information
+ * @param alignment the desired text alignement
+ * @return the list of KnuthElements
+ */
+ LinkedList getNextKnuthElements(LayoutContext context, int alignment);
+
+ /**
+ * Get a sequence of KnuthElements representing the content
+ * of the node assigned to the LM, after changes have been applied
+ *
+ * @param oldList the elements to replace
+ * @param flaggedPenalty the penalty value for hyphenated lines
+ * @param alignment the desired text alignment
+ * @return the updated list of KnuthElements
+ */
+ LinkedList getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/
+ int alignment);
+
}
/*
- * Copyright 2004 The Apache Software Foundation.
+ * Copyright 2004-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
import java.util.List;
import org.apache.fop.fo.FONode;
-import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.pagination.PageSequence;
+import org.apache.fop.area.AreaTreeHandler;
+
/**
* The interface for all LayoutManager makers
public void makeLayoutManagers(FONode node, List lms);
/**
- * Make the LayoutManager for the node.
- * If not exactly one LayoutManagers is made,
- * a FOPException is thrown.
+ * Make a specific LayoutManager for the node.
+ * If not exactly one LayoutManagers is available,
+ * an IllegalStateException is thrown.
* @param node the FO node for which the LayoutManagers are made
* @return The created LayoutManager
+ * @throws IllegalStateException if not exactly one
+ * LayoutManager is available for the requested node
+ */
+ public LayoutManager makeLayoutManager(FONode node);
+
+ /**
+ * Make a PageSequenceLayoutManager object.
+ * @param ath the AreaTreeHandler object the PSLM interacts with
+ * @param ps the fo:page-sequence object this PSLM will process
+ * @return The created PageSequenceLayoutManager object
*/
- public LayoutManager makeLayoutManager(FONode node)
- throws FOPException;
+ public PageSequenceLayoutManager makePageSequenceLayoutManager(
+ AreaTreeHandler ath, PageSequence ps);
}
import java.util.Map;
import java.util.HashMap;
import java.util.List;
-import java.util.ListIterator;
import java.util.Iterator;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-import org.apache.fop.apps.FOPException;
-
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.FOText;
import org.apache.fop.fo.FObjMixed;
import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.pagination.StaticContent;
import org.apache.fop.fo.pagination.Title;
+import org.apache.fop.area.AreaTreeHandler;
import org.apache.fop.layoutmgr.list.ListBlockLayoutManager;
import org.apache.fop.layoutmgr.list.ListItemLayoutManager;
-import org.apache.fop.layoutmgr.table.Body;
+/*import org.apache.fop.layoutmgr.table.Body;
import org.apache.fop.layoutmgr.table.Cell;
import org.apache.fop.layoutmgr.table.Column;
-import org.apache.fop.layoutmgr.table.Row;
+import org.apache.fop.layoutmgr.table.Row;*/
import org.apache.fop.layoutmgr.table.TableLayoutManager;
/**
/**
* Initializes the set of maker objects associated with this LayoutManagerMapping
*/
- private void initialize() {
+ protected void initialize() {
makers.put(FOText.class, new FOTextLayoutManagerMaker());
makers.put(FObjMixed.class, new Maker());
makers.put(BidiOverride.class, new BidiOverrideLayoutManagerMaker());
makers.put(PageNumber.class, new PageNumberLayoutManagerMaker());
makers.put(PageNumberCitation.class,
new PageNumberCitationLayoutManagerMaker());
- makers.put(PageSequence.class, new PageSequenceLayoutManagerMaker());
makers.put(Table.class, new TableLayoutManagerMaker());
- makers.put(TableBody.class, new TableBodyLayoutManagerMaker());
- makers.put(TableColumn.class, new TableColumnLayoutManagerMaker());
- makers.put(TableRow.class, new TableRowLayoutManagerMaker());
- makers.put(TableCell.class, new TableCellLayoutManagerMaker());
- makers.put(TableFooter.class, new TableBodyLayoutManagerMaker());
- makers.put(TableHeader.class, new TableBodyLayoutManagerMaker());
+ makers.put(TableBody.class, new /*TableBodyLayoutManager*/Maker());
+ makers.put(TableColumn.class, new /*TableColumnLayoutManager*/Maker());
+ makers.put(TableRow.class, new /*TableRowLayoutManager*/Maker());
+ makers.put(TableCell.class, new /*TableCellLayoutManager*/Maker());
+ makers.put(TableFooter.class, new /*TableBodyLayoutManager*/Maker());
+ makers.put(TableHeader.class, new /*TableBodyLayoutManager*/Maker());
makers.put(Flow.class, new FlowLayoutManagerMaker());
makers.put(StaticContent.class, new StaticContentLayoutManagerMaker());
makers.put(Wrapper.class, new WrapperLayoutManagerMaker());
/**
* @see org.apache.fop.layoutmgr.LayoutManagerMaker#makeLayoutManager(FONode)
*/
- public LayoutManager makeLayoutManager(FONode node)
- throws FOPException {
+ public LayoutManager makeLayoutManager(FONode node) {
List lms = new ArrayList();
makeLayoutManagers(node, lms);
if (lms.size() == 0) {
- throw new FOPException("No LayoutManager for class "
+ throw new IllegalStateException("LayoutManager for class "
+ node.getClass()
- + "; 1 was required");
+ + " is missing.");
} else if (lms.size() > 1) {
- throw new FOPException("More than 1 LayoutManager for class "
+ throw new IllegalStateException("Duplicate LayoutManagers for class "
+ node.getClass()
- + "; 1 was required");
+ + " found, only one may be declared.");
}
return (LayoutManager) lms.get(0);
}
+ public PageSequenceLayoutManager makePageSequenceLayoutManager(
+ AreaTreeHandler ath, PageSequence ps) {
+ return new PageSequenceLayoutManager(ath, ps);
+ }
+
public static class Maker {
public void make(FONode node, List lms) {
// no layout manager
}
}
- public static class PageSequenceLayoutManagerMaker extends Maker {
- public void make(FONode node, List lms) {
- lms.add(new PageSequenceLayoutManager((PageSequence) node));
- }
- }
-
public static class TableLayoutManagerMaker extends Maker {
+ /*
private List getColumnLayoutManagerList(Table table, TableLayoutManager tlm) {
List columnLMs = null;
List columns = table.getColumns();
}
}
return columnLMs;
- }
+ }*/
public void make(FONode node, List lms) {
Table table = (Table) node;
TableLayoutManager tlm = new TableLayoutManager(table);
+ /*
List columnLMs = getColumnLayoutManagerList(table, tlm);
if (columnLMs != null) {
tlm.setColumns(columnLMs);
- }
+ }*/
lms.add(tlm);
}
}
-
+
+ /*
public static class TableBodyLayoutManagerMaker extends Maker {
public void make(FONode node, List lms) {
lms.add(new Body((TableBody) node));
public void make(FONode node, List lms) {
lms.add(new Cell((TableCell) node));
}
- }
+ }*/
public static class FlowLayoutManagerMaker extends Maker {
public void make(FONode node, List lms) {
lead, total, middle);
// node is a fo:Leader
- returnList.add(new KnuthBox(0, areaInfo.lead, areaInfo.total,
+ returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
areaInfo.middle,
new LeafPosition(this, -1), true));
returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
areaInfo.ipdArea.max - areaInfo.ipdArea.opt,
areaInfo.ipdArea.opt - areaInfo.ipdArea.min,
new LeafPosition(this, 0), false));
- returnList.add(new KnuthBox(0, areaInfo.lead, areaInfo.total,
+ returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
areaInfo.middle,
new LeafPosition(this, -1), true));
LinkedList returnList = new LinkedList();
- returnList.add(new KnuthBox(0, areaInfo.lead, areaInfo.total,
+ returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
areaInfo.middle,
new LeafPosition(this, -1), true));
returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
areaInfo.ipdArea.max - areaInfo.ipdArea.opt,
areaInfo.ipdArea.opt - areaInfo.ipdArea.min,
new LeafPosition(this, 0), false));
- returnList.add(new KnuthBox(0, areaInfo.lead, areaInfo.total,
+ returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
areaInfo.middle,
new LeafPosition(this, -1), true));
}
protected void addId() {
- addID(fobj.getId());
+ getPSLM().addIDToPage(fobj.getId());
}
}
super(node);
}
+ /**
+ * Create a Leaf node layout mananger.
+ */
+ public LeafNodeLayoutManager() {
+ }
+
/**
* get the inline area.
* @param context the context used to create the area
curArea.setOffset(context.getMiddleBaseline() - bpd / 2);
break;
case EN_TOP:
- //curArea.setOffset(0);
+ curArea.setOffset(context.getTopBaseline());
break;
case EN_BOTTOM:
- curArea.setOffset(context.getLineHeight() - bpd);
+ curArea.setOffset(context.getBottomBaseline() - bpd);
break;
case EN_BASELINE:
default:
// node is a fo:ExternalGraphic, fo:InstreamForeignObject,
// fo:PageNumber or fo:PageNumberCitation
- returnList.add(new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
+ returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
areaInfo.total, areaInfo.middle,
new LeafPosition(this, 0), false));
setFinished(true);
return returnList;
}
- public void getWordChars(StringBuffer sbChars, Position bp) {
+ public List addALetterSpaceTo(List oldList) {
+ // return the unchanged elements
+ return oldList;
}
- public KnuthElement addALetterSpaceTo(KnuthElement element) {
- // return the unchanged box object
- return new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
- areaInfo.total, areaInfo.middle,
- new LeafPosition(this, 0), false);
+ public void getWordChars(StringBuffer sbChars, Position pos) {
}
public void hyphenate(Position pos, HyphContext hc) {
}
public LinkedList getChangedKnuthElements(List oldList,
- int flaggedPenalty,
+ /*int flaggedPenalty,*/
int alignment) {
if (isFinished()) {
return null;
// fobj is a fo:ExternalGraphic, fo:InstreamForeignObject,
// fo:PageNumber or fo:PageNumberCitation
- returnList.add(new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
- areaInfo.total, areaInfo.middle,
- new LeafPosition(this, 0), true));
+ returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
+ areaInfo.total, areaInfo.middle,
+ new LeafPosition(this, 0), true));
setFinished(true);
return returnList;
import org.apache.fop.hyphenation.Hyphenation;
import org.apache.fop.hyphenation.Hyphenator;
import org.apache.fop.area.LineArea;
-import org.apache.fop.area.Resolvable;
import java.util.ListIterator;
import java.util.Iterator;
* creates a line area to contain the inline areas added by the
* child layout managers.
*/
-public class LineLayoutManager extends InlineStackingLayoutManager {
+public class LineLayoutManager extends InlineStackingLayoutManager
+ implements BlockLevelLayoutManager {
+
private Block fobj;
- /**
- * Create a new Line Layout Manager.
- * This is used by the block layout manager to create
- * line managers for handling inline areas flowing into line areas.
- *
- * @param lh the default line height
- * @param l the default lead, from top to baseline
- * @param f the default follow, from baseline to bottom
- */
- public LineLayoutManager(Block node, int lh, int l, int f, int ms) {
- super(node);
- fobj = node;
- // the child FObj are owned by the parent BlockLM
- // this LM has all its childLMs preloaded
- fobjIter = null;
- lineHeight = lh;
- lead = l;
- follow = f;
- middleShift = ms;
- initialize(); // Normally done when started by parent!
- }
-
/**
* @see org.apache.fop.layoutmgr.AbstractLayoutManager#initProperties()
*/
private static class LineBreakPosition extends LeafPosition {
// int iPos;
int iParIndex; // index of the Paragraph this Position refers to
+ int availableShrink;
+ int availableStretch;
+ int difference;
double dAdjust; // Percentage to adjust (stretch or shrink)
double ipdAdjust; // Percentage to adjust (stretch or shrink)
int startIndent;
int lineHeight;
+ int lineWidth;
int baseline;
+ int topShift;
+ int bottomShift;
LineBreakPosition(LayoutManager lm, int index, int iBreakIndex,
- double ipdA, double adjust, int ind, int lh, int bl) {
+ int shrink, int stretch, int diff,
+ double ipdA, double adjust, int ind,
+ int lh, int lw, int bl, int ts, int bs) {
super(lm, iBreakIndex);
- // iPos = iBreakIndex;
+ availableShrink = shrink;
+ availableStretch = stretch;
+ difference = diff;
iParIndex = index;
ipdAdjust = ipdA;
dAdjust = adjust;
startIndent = ind;
lineHeight = lh;
+ lineWidth = lw;
baseline = bl;
+ topShift = ts;
+ bottomShift = bs;
}
}
/** Break positions returned by inline content. */
- private List vecInlineBreaks = new ArrayList();
+ private List vecInlineBreaks = new java.util.ArrayList();
private BreakPoss prevBP = null; // Last confirmed break position
private int bTextAlignment = EN_JUSTIFY;
private int bTextAlignmentLast;
private int effectiveAlignment;
private Length textIndent;
+ private int iIndents = 0;
private CommonHyphenation hyphProps;
+ //private LayoutProps layoutProps;
private int lineHeight;
private int lead;
// offset of the middle baseline with respect to the main baseline
private int middleShift;
- private ArrayList knuthParagraphs = null;
- private ArrayList breakpoints = null;
+ private List knuthParagraphs = null;
+ private List breakpoints = null;
private int iReturnedLBP = 0;
private int iStartElement = 0;
private int iEndElement = 0;
// penalty value for flagged penalties
private int flaggedPenalty = 50;
+ private LineLayoutPossibilities lineLayouts;
+ private List lineLayoutsList;
+ private int iLineWidth = 0;
+
// this constant is used to create elements when text-align is center:
// every TextLM descendant of LineLM must use the same value,
// otherwise the line breaking algorithm does not find the right
}
// this class represents a paragraph
- public class Paragraph extends ArrayList {
- // number of KnuthElements added by the LineLayoutManager
- public int ignoreAtStart = 0;
- public int ignoreAtEnd = 0;
- // minimum space at the end of the last line (in millipoints)
- public int lineFillerWidth;
-
- public void startParagraph(int lineWidth) {
+ private class Paragraph extends KnuthSequence {
+ // space at the end of the last line (in millipoints)
+ private MinOptMax lineFiller;
+ private int textAlignment;
+ private int textAlignmentLast;
+ private int textIndent;
+ private int lineWidth;
+ // the LM which created the paragraph
+ private LineLayoutManager layoutManager;
+
+ public Paragraph(LineLayoutManager llm, int alignment, int alignmentLast,
+ int indent) {
+ super();
+ layoutManager = llm;
+ textAlignment = alignment;
+ textAlignmentLast = alignmentLast;
+ textIndent = indent;
+ }
+
+ public void startParagraph(int lw) {
+ lineWidth = lw;
+ startSequence();
+ }
+
+ public void startSequence() {
// set the minimum amount of empty space at the end of the
// last line
if (bTextAlignment == EN_CENTER) {
- lineFillerWidth = 0;
+ lineFiller = new MinOptMax(0);
} else {
- lineFillerWidth = (int)(lineWidth / 12);
+ lineFiller = new MinOptMax(0, (int)(lineWidth / 12), lineWidth);
}
// add auxiliary elements at the beginning of the paragraph
// add the element representing text indentation
// at the beginning of the first paragraph
if (knuthParagraphs.size() == 0
- && textIndent.getValue() != 0) {
- this.add(new KnuthBox(textIndent.getValue(), 0, 0, 0,
+ && fobj.getTextIndent().getValue() != 0) {
+ this.add(new KnuthInlineBox(fobj.getTextIndent().getValue(), 0, 0, 0,
null, false));
ignoreAtStart ++;
}
}
public void endParagraph() {
+ KnuthSequence finishedPar = this.endSequence();
+ if (finishedPar != null) {
+ knuthParagraphs.add(finishedPar);
+ }
+ }
+
+ public KnuthSequence endSequence() {
// remove glue and penalty item at the end of the paragraph
while (this.size() > ignoreAtStart
- && !((KnuthElement) this.get(this.size() - 1)).isBox()) {
+ && !((KnuthElement)this.get(this.size() - 1)).isBox()) {
this.remove(this.size() - 1);
}
if (this.size() > ignoreAtStart) {
// add the elements representing the space
// at the end of the last line
// and the forced break
- this.add(new KnuthPenalty(0, KnuthElement.INFINITE,
+ this.add(new KnuthPenalty(0, KnuthElement.INFINITE,
false, null, false));
- this.add(new KnuthGlue(lineFillerWidth, 10000000, 0,
- null, false));
+ this.add(new KnuthGlue(lineFiller.opt,
+ lineFiller.max - lineFiller.opt,
+ lineFiller.opt - lineFiller.min, null, false));
this.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
false, null, false));
ignoreAtEnd = 3;
false, null, false));
ignoreAtEnd = 1;
}
- knuthParagraphs.add(this);
+ return this;
+ } else {
+ this.clear();
+ return null;
}
}
- public KnuthElement getLast() {
- int idx = size();
- if (idx == 0) {
- return null;
+ }
+
+ private class LineBreakingAlgorithm extends BreakingAlgorithm {
+ private LineLayoutManager thisLLM;
+ private int pageAlignment;
+ private int activePossibility;
+ private int addedPositions;
+ private int textIndent;
+ private int fillerMinWidth;
+ private int lineHeight;
+ private int lead;
+ private int follow;
+ private int middleshift;
+ private int maxDiff;
+ private static final double MAX_DEMERITS = 10e6;
+
+ public LineBreakingAlgorithm (int pageAlign,
+ int textAlign, int textAlignLast,
+ int indent, int fillerWidth,
+ int lh, int ld, int fl, int ms, boolean first,
+ LineLayoutManager llm) {
+ super(textAlign, textAlignLast, first);
+ pageAlignment = pageAlign;
+ textIndent = indent;
+ fillerMinWidth = fillerWidth;
+ lineHeight = lh;
+ lead = ld;
+ follow = fl;
+ middleshift = ms;
+ thisLLM = llm;
+ activePossibility = -1;
+ maxDiff = fobj.getWidows() >= fobj.getOrphans()
+ ? fobj.getWidows()
+ : fobj.getOrphans();
+ }
+
+ public void updateData1(int lineCount, double demerits) {
+ lineLayouts.addPossibility(lineCount, demerits);
+ log.trace("Layout possibility in " + lineCount + " lines; break at position:");
+ }
+
+ public void updateData2(KnuthNode bestActiveNode,
+ KnuthSequence par,
+ int total) {
+ // compute indent and adjustment ratio, according to
+ // the value of text-align and text-align-last
+ int indent = 0;
+ int difference = (bestActiveNode.line < total) ? bestActiveNode.difference : bestActiveNode.difference + fillerMinWidth;
+ int textAlign = (bestActiveNode.line < total) ? alignment : alignmentLast;
+ indent += (textAlign == Constants.EN_CENTER) ?
+ difference / 2 :
+ (textAlign == Constants.EN_END) ? difference : 0;
+ indent += (bestActiveNode.line == 1 && bFirst) ?
+ textIndent : 0;
+ double ratio = (textAlign == Constants.EN_JUSTIFY
+ || bestActiveNode.adjustRatio < 0) ? bestActiveNode.adjustRatio : 0;
+
+ // add nodes at the beginning of the list, as they are found
+ // backwards, from the last one to the first one
+
+ // the first time this method is called, initialize activePossibility
+ if (activePossibility == -1) {
+ activePossibility = 0;
+ addedPositions = 0;
+ }
+
+ if (addedPositions == lineLayouts.getLineCount(activePossibility)) {
+ activePossibility ++;
+ addedPositions = 0;
+ //System.out.println(" ");
+ }
+
+ //System.out.println("LLM> (" + (lineLayouts.getLineNumber(activePossibility) - addedPositions) + ") difference = " + difference + " ratio = " + ratio);
+ lineLayouts.addBreakPosition(makeLineBreakPosition(par,
+ (bestActiveNode.line > 1 ? bestActiveNode.previous.position + 1: 0),
+ bestActiveNode.position,
+ bestActiveNode.availableShrink - (addedPositions > 0 ? 0 : ((Paragraph)par).lineFiller.opt - ((Paragraph)par).lineFiller.min), bestActiveNode.availableStretch, difference, ratio, indent),
+ activePossibility);
+ addedPositions ++;
+ }
+
+ /* reset activePossibility, as if breakpoints have not yet been computed
+ */
+ public void resetAlgorithm() {
+ activePossibility = -1;
+ }
+
+ private LineBreakPosition makeLineBreakPosition(KnuthSequence par,
+ int firstElementIndex,
+ int lastElementIndex,
+ int availableShrink, int availableStretch, int difference,
+ double ratio,
+ int indent) {
+ // line height calculation
+
+ int halfLeading = (lineHeight - lead - follow) / 2;
+ // height before the main baseline
+ int lineLead = lead;
+ // maximum size of top and bottom alignment
+ int maxtb = follow;
+ // max size of middle alignment before and after the middle baseline
+ int middlefollow = maxtb;
+
+ // if line-stacking-strategy is "font-height", the line height
+ // is not affected by its content
+ if (fobj.getLineStackingStrategy() != EN_FONT_HEIGHT) {
+ ListIterator inlineIterator
+ = par.listIterator(firstElementIndex);
+ for (int j = firstElementIndex;
+ j <= lastElementIndex;
+ j++) {
+ KnuthElement element = (KnuthElement) inlineIterator.next();
+ if (element.isBox()) {
+ if (((KnuthInlineBox) element).getLead() > lineLead) {
+ lineLead = ((KnuthInlineBox) element).getLead();
+ }
+ if (((KnuthInlineBox) element).getTotal() > maxtb) {
+ maxtb = ((KnuthInlineBox) element).getTotal();
+ }
+ if (((KnuthInlineBox) element).getMiddle() > lineLead + middleShift) {
+ lineLead += ((KnuthInlineBox) element).getMiddle()
+ - lineLead - middleShift;
+ }
+ if (((KnuthInlineBox) element).getMiddle() > middlefollow - middleShift) {
+ middlefollow += ((KnuthInlineBox) element).getMiddle()
+ - middlefollow + middleShift;
+ }
+ }
+ }
+
+ if (maxtb - lineLead > middlefollow) {
+ middlefollow = maxtb - lineLead;
+ }
}
- return (KnuthElement) get(idx - 1);
+
+ //lineLead += halfLeading;
+ //middlefollow += lineHeight - lead - follow - halfLeading;
+
+ constantLineHeight = lineLead + middlefollow + (lineHeight - lead - follow);
+ //System.out.println("desired height: " + lineHeight + " actual height: " + (lineLead + middlefollow + (lineHeight - lead - follow)) + " halfleading = " + halfLeading + " and " + (lineHeight - lead - follow - halfLeading));
+
+ return new LineBreakPosition(thisLLM,
+ knuthParagraphs.indexOf(par),
+ lastElementIndex,
+ availableShrink, availableStretch, difference, ratio, 0, indent,
+ lineLead + middlefollow + (lineHeight - lead - follow), iLineWidth,
+ lineLead + halfLeading,
+ - lineLead, middlefollow);
+ }
+
+ public int findBreakingPoints(Paragraph par, int lineWidth,
+ double threshold, boolean force,
+ boolean hyphenationAllowed) {
+ return super.findBreakingPoints(par, lineWidth, threshold, force, hyphenationAllowed);
}
- public KnuthElement removeLast() {
- int idx = size();
- if (idx == 0) {
- return null;
+ protected int filterActiveNodes() {
+ KnuthNode bestActiveNode = null;
+
+ if (pageAlignment == EN_JUSTIFY) {
+ // leave all active nodes and find the optimum line number
+ //System.out.println("LBA.filterActiveNodes> " + activeNodeCount + " layouts");
+ for (int i = startLine; i < endLine; i++) {
+ for (KnuthNode node = getNode(i); node != null; node = node.next) {
+ //System.out.println(" + lines = " + node.line + " demerits = " + node.totalDemerits);
+ bestActiveNode = compareNodes(bestActiveNode, node);
+ }
+ }
+
+ // scan the node set once again and remove some nodes
+ //System.out.println("LBA.filterActiveList> layout selection");
+ for (int i = startLine; i < endLine; i++) {
+ for (KnuthNode node = getNode(i); node != null; node = node.next) {
+ //if (Math.abs(node.line - bestActiveNode.line) > maxDiff) {
+ //if (false) {
+ if (node.line != bestActiveNode.line
+ && node.totalDemerits > MAX_DEMERITS) {
+ //System.out.println(" XXX lines = " + node.line + " demerits = " + node.totalDemerits);
+ removeNode(i, node);
+ } else {
+ //System.out.println(" ok lines = " + node.line + " demerits = " + node.totalDemerits);
+ }
+ }
+ }
+ } else {
+ // leave only the active node with fewest total demerits
+ for (int i = startLine; i < endLine; i++) {
+ for (KnuthNode node = getNode(i); node != null; node = node.next) {
+ bestActiveNode = compareNodes(bestActiveNode, node);
+ if (node != bestActiveNode) {
+ removeNode(i, node);
+ }
+ }
+ }
}
- return (KnuthElement) remove(idx - 1);
+ return bestActiveNode.line;
}
}
+
+ private int constantLineHeight = 12000;
+
+
+ /**
+ * Create a new Line Layout Manager.
+ * This is used by the block layout manager to create
+ * line managers for handling inline areas flowing into line areas.
+ *
+ * @param lh the default line height
+ * @param l the default lead, from top to baseline
+ * @param f the default follow, from baseline to bottom
+ */
+ public LineLayoutManager(Block block, int lh, int l, int f, int ms) {
+ super(block);
+ fobj = block;
+ // the child FObj are owned by the parent BlockLM
+ // this LM has all its childLMs preloaded
+ fobjIter = null;
+ lineHeight = lh;
+ lead = l;
+ follow = f;
+ middleShift = ms;
+ initialize(); // Normally done when started by parent!
+ }
/**
* Call child layout managers to generate content.
* @param context the layout context for finding breaks
* @return the next break position
*/
+ // this method is no longer used
public BreakPoss getNextBreakPoss(LayoutContext context) {
+ setFinished(true);
+ return null;
+ }
+
+ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
// Get a break from currently active child LM
// Set up constraints for inline level managers
InlineLevelLayoutManager curLM ; // currently active LM
// IPD remaining in line
MinOptMax availIPD = context.getStackLimit();
- LayoutContext inlineLC = new LayoutContext(context);
-
clearPrevIPD();
int iPrevLineEnd = vecInlineBreaks.size();
prevBP = null;
//PHASE 1: Create Knuth elements
-
if (knuthParagraphs == null) {
// it's the first time this method is called
knuthParagraphs = new ArrayList();
// here starts Knuth's algorithm
- KnuthElement thisElement = null;
- LinkedList returnedList = null;
-
- // convert all the text in a sequence of paragraphs made
- // of KnuthBox, KnuthGlue and KnuthPenalty objects
- boolean bPrevWasKnuthBox = false;
- KnuthBox prevBox = null;
-
- Paragraph knuthPar = new Paragraph();
- knuthPar.startParagraph(availIPD.opt);
- while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
- if ((returnedList
- = curLM.getNextKnuthElements(inlineLC,
- effectiveAlignment))
- != null) {
- // look at the first element
- thisElement = (KnuthElement) returnedList.getFirst();
- if (thisElement.isBox() && !thisElement.isAuxiliary()
- && bPrevWasKnuthBox) {
- prevBox = (KnuthBox) knuthPar.removeLast();
- // if there are two consecutive KnuthBoxes the
- // first one does not represent a whole word,
- // so it must be given one more letter space
- if (!prevBox.isAuxiliary()) {
- // if letter spacing is constant,
- // only prevBox needs to be replaced;
- knuthPar.add(((InlineLevelLayoutManager)
- prevBox.getLayoutManager())
- .addALetterSpaceTo(prevBox));
- } else {
- // prevBox is the last element
- // in the sub-sequence
- // <box> <aux penalty> <aux glue> <aux box>
- // the letter space is added to <aux glue>,
- // while the other elements are not changed
- KnuthBox auxBox = prevBox;
- KnuthGlue auxGlue
- = (KnuthGlue) knuthPar.removeLast();
- KnuthPenalty auxPenalty
- = (KnuthPenalty) knuthPar.removeLast();
- prevBox = (KnuthBox) knuthPar.getLast();
- knuthPar.add(auxPenalty);
- knuthPar.add(((InlineLevelLayoutManager)
- prevBox.getLayoutManager())
- .addALetterSpaceTo(prevBox));
- knuthPar.add(auxBox);
- }
- }
-
- // look at the last element
- KnuthElement lastElement = (KnuthElement) returnedList.getLast();
- boolean bForceLinefeed = false;
- if (lastElement.isBox()) {
- bPrevWasKnuthBox = true;
- } else {
- bPrevWasKnuthBox = false;
- if (lastElement.isPenalty()
- && ((KnuthPenalty) lastElement).getP()
- == -KnuthPenalty.INFINITE) {
- // a penalty item whose value is -inf
- // represents a preserved linefeed,
- // wich forces a line break
- bForceLinefeed = true;
- returnedList.removeLast();
- }
- }
-
- // add the new elements to the paragraph
- knuthPar.addAll(returnedList);
- if (bForceLinefeed) {
- if (knuthPar.size() == 0) {
- //only a forced linefeed on this line
- //-> compensate with a zero width box
- knuthPar.add(new KnuthBox(0, 0, 0, 0,
- null, false));
- }
- knuthPar.endParagraph();
- knuthPar = new Paragraph();
- knuthPar.startParagraph(availIPD.opt);
- bPrevWasKnuthBox = false;
- }
- } else {
- // curLM returned null; this can happen
- // if it has nothing more to layout,
- // so just iterate once more to see
- // if there are other children
- }
- }
- knuthPar.endParagraph();
+ //TODO availIPD should not really be used here, so we can later support custom line
+ //widths for for each line (side-floats, differing available IPD after page break)
+ collectInlineKnuthElements(context, availIPD);
} else {
// this method has been called before
// all line breaks are already calculated
}
//PHASE 2: Create line breaks
-
+ return findOptimalLineBreakingPoints(alignment);
+ /*
LineBreakPosition lbp = null;
if (breakpoints == null) {
// find the optimal line breaking points for each paragraph
currPar = (Paragraph) paragraphsIterator.previous();
findBreakingPoints(currPar, context.getStackLimit().opt);
}
- }
+ }*/
//PHASE 3: Return lines
+ /*
// get a break point from the list
lbp = (LineBreakPosition) breakpoints.get(iReturnedLBP ++);
if (iReturnedLBP == breakpoints.size()) {
curLineBP.setFlag(BreakPoss.ISLAST, isFinished());
curLineBP.setStackingSize(new MinOptMax(lbp.lineHeight));
return curLineBP;
+ */
+ }
+
+ /**
+ * Phase 1 of Knuth algorithm: Collect all inline Knuth elements before determining line breaks.
+ * @param context the LayoutContext
+ * @param availIPD available IPD for line (should be removed!)
+ */
+ private void collectInlineKnuthElements(LayoutContext context, MinOptMax availIPD) {
+ LayoutContext inlineLC = new LayoutContext(context);
+
+ InlineLevelLayoutManager curLM;
+ KnuthElement thisElement = null;
+ LinkedList returnedList = null;
+ iLineWidth = context.getStackLimit().opt;
+
+ // convert all the text in a sequence of paragraphs made
+ // of KnuthBox, KnuthGlue and KnuthPenalty objects
+ boolean bPrevWasKnuthBox = false;
+ KnuthBox prevBox = null;
+
+ Paragraph knuthPar = new Paragraph(this,
+ bTextAlignment, bTextAlignmentLast,
+ textIndent.getValue());
+ knuthPar.startParagraph(availIPD.opt);
+ while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
+ if ((returnedList
+ = curLM.getNextKnuthElements(inlineLC,
+ effectiveAlignment))
+ != null) {
+ // look at the first element
+ thisElement = (KnuthElement) returnedList.getFirst();
+ if (thisElement.isBox() && !thisElement.isAuxiliary()
+ && bPrevWasKnuthBox) {
+ prevBox = (KnuthBox) knuthPar.removeLast();
+ LinkedList oldList = new LinkedList();
+ // if there are two consecutive KnuthBoxes the
+ // first one does not represent a whole word,
+ // so it must be given one more letter space
+ if (!prevBox.isAuxiliary()) {
+ // if letter spacing is constant,
+ // only prevBox needs to be replaced;
+ oldList.add(prevBox);
+ } else {
+ // prevBox is the last element
+ // in the sub-sequence
+ // <box> <aux penalty> <aux glue> <aux box>
+ // the letter space is added to <aux glue>,
+ // while the other elements are not changed
+ oldList.add(prevBox);
+ oldList.addFirst((KnuthGlue) knuthPar.removeLast());
+ oldList.addFirst((KnuthPenalty) knuthPar.removeLast());
+ }
+ // adding a letter space could involve, according to the text
+ // represented by oldList, replacing a glue element or adding
+ // new elements
+ knuthPar.addAll(((InlineLevelLayoutManager)
+ prevBox.getLayoutManager())
+ .addALetterSpaceTo(oldList));
+ }
+
+ // look at the last element
+ KnuthElement lastElement = (KnuthElement) returnedList.getLast();
+ boolean bForceLinefeed = false;
+ if (lastElement.isBox()) {
+ bPrevWasKnuthBox = true;
+ } else {
+ bPrevWasKnuthBox = false;
+ if (lastElement.isPenalty()
+ && ((KnuthPenalty) lastElement).getP()
+ == -KnuthPenalty.INFINITE) {
+ // a penalty item whose value is -inf
+ // represents a preserved linefeed,
+ // wich forces a line break
+ bForceLinefeed = true;
+ returnedList.removeLast();
+ }
+ }
+
+ // add the new elements to the paragraph
+ knuthPar.addAll(returnedList);
+ if (bForceLinefeed) {
+ if (knuthPar.size() == 0) {
+ //only a forced linefeed on this line
+ //-> compensate with a zero width box
+ knuthPar.add(new KnuthInlineBox(0, 0, 0, 0,
+ null, false));
+ }
+ knuthPar.endParagraph();
+ knuthPar = new Paragraph(this,
+ bTextAlignment, bTextAlignmentLast,
+ textIndent.getValue());
+ knuthPar.startParagraph(availIPD.opt);
+ bPrevWasKnuthBox = false;
+ }
+ } else {
+ // curLM returned null; this can happen
+ // if it has nothing more to layout,
+ // so just iterate once more to see
+ // if there are other children
+ }
+ }
+ knuthPar.endParagraph();
}
/**
* into lines
* @param lineWidth the desired length ot the lines
*/
+ /*
private void findBreakingPoints(Paragraph par, int lineWidth) {
// maximum adjustment ratio permitted
float maxAdjustment = 1;
j++) {
KnuthElement element = (KnuthElement) inlineIterator.next();
if (element.isBox()) {
- if (((KnuthBox) element).getLead() > lineLead) {
- lineLead = ((KnuthBox) element).getLead();
+ KnuthInlineBox box = (KnuthInlineBox)element;
+ if (box.getLead() > lineLead) {
+ lineLead = box.getLead();
}
- if (((KnuthBox) element).getTotal() > maxtb) {
- maxtb = ((KnuthBox) element).getTotal();
+ if (box.getTotal() > maxtb) {
+ maxtb = box.getTotal();
}
- if (((KnuthBox) element).getMiddle() > lineLead + middleShift) {
- lineLead += ((KnuthBox) element).getMiddle()
+ if (box.getMiddle() > lineLead + middleShift) {
+ lineLead += box.getMiddle()
- lineLead - middleShift;
}
- if (((KnuthBox) element).getMiddle() > middlefollow - middleShift) {
- middlefollow += ((KnuthBox) element).getMiddle()
+ if (box.getMiddle() > middlefollow - middleShift) {
+ middlefollow += box.getMiddle()
- middlefollow + middleShift;
}
}
ratio, 0, indent,
lineLead + middlefollow,
lineLead));
+ }*/
+
+
+ /**
+ * Phase 2 of Knuth algorithm: find optimal break points.
+ * @param alignment alignmenr of the paragraph
+ * @return a list of Knuth elements representing broken lines
+ */
+ private LinkedList findOptimalLineBreakingPoints(int alignment) {
+
+ // find the optimal line breaking points for each paragraph
+ ListIterator paragraphsIterator
+ = knuthParagraphs.listIterator(knuthParagraphs.size());
+ Paragraph currPar = null;
+ LineBreakingAlgorithm alg;
+ lineLayoutsList = new ArrayList(knuthParagraphs.size());
+ while (paragraphsIterator.hasPrevious()) {
+ lineLayouts = new LineLayoutPossibilities();
+ currPar = (Paragraph) paragraphsIterator.previous();
+ double maxAdjustment = 1;
+ int iBPcount = 0;
+ alg = new LineBreakingAlgorithm(alignment,
+ bTextAlignment, bTextAlignmentLast,
+ textIndent.getValue(), currPar.lineFiller.opt,
+ lineHeight, lead, follow, middleShift,
+ (knuthParagraphs.indexOf(currPar) == 0),
+ this);
+
+ if (hyphProps.hyphenate == EN_TRUE) {
+ findHyphenationPoints(currPar);
+ }
+
+ // first try
+ boolean bHyphenationAllowed = false;
+ iBPcount = alg.findBreakingPoints(currPar,
+ iLineWidth,
+ maxAdjustment, false, bHyphenationAllowed);
+ if (iBPcount == 0 || alignment == EN_JUSTIFY) {
+ // if the first try found a set of breaking points, save them
+ if (iBPcount > 0) {
+ alg.resetAlgorithm();
+ lineLayouts.savePossibilities(false);
+ } else {
+ // the first try failed
+ log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment);
+ }
+
+ // now try something different
+ log.debug("Hyphenation possible? " + (hyphProps.hyphenate == EN_TRUE));
+ if (hyphProps.hyphenate == EN_TRUE) {
+ // consider every hyphenation point as a legal break
+ bHyphenationAllowed = true;
+ } else {
+ // try with a higher threshold
+ maxAdjustment = 5;
+ }
+
+ if ((iBPcount
+ = alg.findBreakingPoints(currPar,
+ iLineWidth,
+ maxAdjustment, false, bHyphenationAllowed)) == 0) {
+ // the second try failed too, try with a huge threshold
+ // and force the algorithm to find
+ // a set of breaking points
+ log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment
+ + (hyphProps.hyphenate == EN_TRUE ? " and hyphenation" : ""));
+ maxAdjustment = 20;
+ iBPcount
+ = alg.findBreakingPoints(currPar,
+ iLineWidth,
+ maxAdjustment, true, bHyphenationAllowed);
+ }
+
+ // use non-hyphenated breaks, when possible
+ lineLayouts.restorePossibilities();
+
+ /* extension (not in the XSL FO recommendation): if vertical alignment
+ is justify and the paragraph has only one layout, try using
+ shorter or longer lines */
+ //TODO This code snippet is disabled. Reenable?
+ if (false && alignment == EN_JUSTIFY && bTextAlignment == EN_JUSTIFY) {
+ //System.out.println("LLM.getNextKnuthElements> layouts with more lines? " + lineLayouts.canUseMoreLines());
+ //System.out.println(" layouts with fewer lines? " + lineLayouts.canUseLessLines());
+ if (!lineLayouts.canUseMoreLines()) {
+ alg.resetAlgorithm();
+ lineLayouts.savePossibilities(true);
+ // try with shorter lines
+ int savedLineWidth = iLineWidth;
+ iLineWidth = (int) (iLineWidth * 0.95);
+ iBPcount
+ = alg.findBreakingPoints(currPar,
+ iLineWidth,
+ maxAdjustment, true, bHyphenationAllowed);
+ // use normal lines, when possible
+ lineLayouts.restorePossibilities();
+ iLineWidth = savedLineWidth;
+ }
+ if (!lineLayouts.canUseLessLines()) {
+ alg.resetAlgorithm();
+ lineLayouts.savePossibilities(true);
+ // try with longer lines
+ int savedLineWidth = iLineWidth;
+ iLineWidth = (int) (iLineWidth * 1.05);
+ iBPcount
+ = alg.findBreakingPoints(currPar,
+ iLineWidth,
+ maxAdjustment, true, bHyphenationAllowed);
+ // use normal lines, when possible
+ lineLayouts.restorePossibilities();
+ iLineWidth = savedLineWidth;
+ }
+ //System.out.println("LLM.getNextKnuthElements> now, layouts with more lines? " + lineLayouts.canUseMoreLines());
+ //System.out.println(" now, layouts with fewer lines? " + lineLayouts.canUseLessLines());
+ }
+ }
+ lineLayoutsList.add(0, lineLayouts);
+ }
+
+
+ setFinished(true);
+
+ //Post-process the line breaks found
+ return postProcessLineBreaks(alignment);
+ }
+
+ private LinkedList postProcessLineBreaks(int alignment) {
+
+ LinkedList returnList = new LinkedList();
+
+ for (int p = 0; p < knuthParagraphs.size(); p ++) {
+ // null penalty between paragraphs
+ if (p > 0
+ && !((BlockLevelLayoutManager) parentLM).mustKeepTogether()) {
+ returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+ }
+
+ lineLayouts = (LineLayoutPossibilities)lineLayoutsList.get(p);
+
+ if (alignment == EN_JUSTIFY) {
+ /* justified vertical alignment (not in the XSL FO recommendation):
+ create a multi-layout sequence whose elements will contain
+ a conventional Position */
+ Position returnPosition = new LeafPosition(this, p);
+ createElements(returnList, lineLayouts, returnPosition);
+ } else {
+ /* "normal" vertical alignment: create a sequence whose boxes
+ represent effective lines, and contain LineBreakPositions */
+ Position returnPosition = new LeafPosition(this, p);
+ for (int i = 0;
+ i < lineLayouts.getChosenLineCount();
+ i++) {
+ if (!((BlockLevelLayoutManager) parentLM).mustKeepTogether()
+ && i >= fobj.getOrphans()
+ && i <= lineLayouts.getChosenLineCount() - fobj.getWidows()
+ && returnList.size() > 0) {
+ // null penalty allowing a page break between lines
+ returnList.add(new KnuthPenalty(0, 0, false, returnPosition, false));
+ }
+ returnList.add(new KnuthBox(((LineBreakPosition) lineLayouts.getChosenPosition(i)).lineHeight,
+ lineLayouts.getChosenPosition(i), false));
+ }
+ }
+ }
+
+ return returnList;
}
+ private void createElements(List list, LineLayoutPossibilities lineLayouts,
+ Position elementPosition) {
+ /* number of normal, inner lines */
+ int nInnerLines = 0;
+ /* number of lines that can be used in order to fill more space */
+ int nOptionalLines = 0;
+ /* number of lines that can be used in order to fill more space
+ only if the paragraph is not parted */
+ int nConditionalOptionalLines = 0;
+ /* number of lines that can be omitted in order to fill less space */
+ int nEliminableLines = 0;
+ /* number of lines that can be omitted in order to fill less space
+ only if the paragraph is not parted */
+ int nConditionalEliminableLines = 0;
+ /* number of the first unbreakable lines */
+ int nFirstLines = fobj.getOrphans();
+ /* number of the last unbreakable lines */
+ int nLastLines = fobj.getWidows();
+ /* sub-sequence used to separate the elements representing different lines */
+ List breaker = new LinkedList();
+
+ /* comment out the next lines in order to test particular situations */
+ if (fobj.getOrphans() + fobj.getWidows() <= lineLayouts.getMinLineCount()) {
+ nInnerLines = lineLayouts.getMinLineCount() - (fobj.getOrphans() + fobj.getWidows());
+ nOptionalLines = lineLayouts.getMaxLineCount() - lineLayouts.getOptLineCount();
+ nEliminableLines = lineLayouts.getOptLineCount() - lineLayouts.getMinLineCount();
+ } else if (fobj.getOrphans() + fobj.getWidows() <= lineLayouts.getOptLineCount()) {
+ nOptionalLines = lineLayouts.getMaxLineCount() - lineLayouts.getOptLineCount();
+ nEliminableLines = lineLayouts.getOptLineCount() - (fobj.getOrphans() + fobj.getWidows());
+ nConditionalEliminableLines = (fobj.getOrphans() + fobj.getWidows()) - lineLayouts.getMinLineCount();
+ } else if (fobj.getOrphans() + fobj.getWidows() <= lineLayouts.getMaxLineCount()) {
+ nOptionalLines = lineLayouts.getMaxLineCount() - (fobj.getOrphans() + fobj.getWidows());
+ nConditionalOptionalLines = (fobj.getOrphans() + fobj.getWidows()) - lineLayouts.getOptLineCount();
+ nConditionalEliminableLines = lineLayouts.getOptLineCount() - lineLayouts.getMinLineCount();
+ nFirstLines -= nConditionalOptionalLines;
+ } else {
+ nConditionalOptionalLines = lineLayouts.getMaxLineCount() - lineLayouts.getOptLineCount();
+ nConditionalEliminableLines = lineLayouts.getOptLineCount() - lineLayouts.getMinLineCount();
+ nFirstLines = lineLayouts.getOptLineCount();
+ nLastLines = 0;
+ }
+ /* comment out the previous lines in order to test particular situations */
+
+ /* use these lines to test particular situations
+ nInnerLines = 0;
+ nOptionalLines = 1;
+ nConditionalOptionalLines = 2;
+ nEliminableLines = 0;
+ nConditionalEliminableLines = 0;
+ nFirstLines = 1;
+ nLastLines = 3;
+ */
+
+ if (nLastLines != 0
+ && (nConditionalOptionalLines > 0 || nConditionalEliminableLines > 0)) {
+ breaker.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
+ breaker.add(new KnuthGlue(0, -nConditionalOptionalLines * constantLineHeight,
+ -nConditionalEliminableLines * constantLineHeight,
+ LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+ breaker.add(new KnuthPenalty(nConditionalOptionalLines * constantLineHeight,
+ 0, false, elementPosition, false));
+ breaker.add(new KnuthGlue(0, nConditionalOptionalLines * constantLineHeight,
+ nConditionalEliminableLines * constantLineHeight,
+ LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+ } else if (nLastLines != 0) {
+ breaker.add(new KnuthPenalty(0, 0, false, elementPosition, false));
+ }
+
+ //System.out.println("first=" + nFirstLines + " inner=" + nInnerLines
+ // + " optional=" + nOptionalLines + " eliminable=" + nEliminableLines
+ // + " last=" + nLastLines
+ // + " (condOpt=" + nConditionalOptionalLines + " condEl=" + nConditionalEliminableLines + ")");
+
+ // creation of the elements:
+ // first group of lines
+ list.add(new KnuthBox(nFirstLines * constantLineHeight, elementPosition,
+ (nLastLines == 0
+ && nConditionalOptionalLines == 0
+ && nConditionalEliminableLines == 0 ? true : false)));
+ if (nConditionalOptionalLines > 0
+ || nConditionalEliminableLines > 0) {
+ list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
+ list.add(new KnuthGlue(0, nConditionalOptionalLines * constantLineHeight,
+ nConditionalEliminableLines * constantLineHeight,
+ LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+ list.add(new KnuthBox(0, elementPosition,
+ (nLastLines == 0 ? true : false)));
+ }
+
+ // optional lines
+ for (int i = 0; i < nOptionalLines; i++) {
+ list.addAll(breaker);
+ list.add(new KnuthBox(0, elementPosition, false));
+ list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
+ list.add(new KnuthGlue(0, 1 * constantLineHeight, 0,
+ LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+ list.add(new KnuthBox(0, elementPosition, false));
+ }
+
+ // eliminable lines
+ for (int i = 0; i < nEliminableLines; i++) {
+ list.addAll(breaker);
+ list.add(new KnuthBox(1 * constantLineHeight, elementPosition, false));
+ list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
+ list.add(new KnuthGlue(0, 0, 1 * constantLineHeight,
+ LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+ list.add(new KnuthBox(0, elementPosition, false));
+ }
+
+ // inner lines
+ for (int i = 0; i < nInnerLines; i++) {
+ list.addAll(breaker);
+ list.add(new KnuthBox(1 * constantLineHeight, elementPosition, false));
+ }
+
+ // last group of lines
+ if (nLastLines > 0) {
+ list.addAll(breaker);
+ list.add(new KnuthBox(nLastLines * constantLineHeight,
+ elementPosition, true));
+ }
+ }
+
+ public boolean mustKeepTogether() {
+ return false;
+ }
+
+ public boolean mustKeepWithPrevious() {
+ return false;
+ }
+
+ public boolean mustKeepWithNext() {
+ return false;
+ }
+
+ public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
+ LeafPosition pos = (LeafPosition)lastElement.getPosition();
+ int totalAdj = adj;
+ //if (lastElement.isPenalty()) {
+ // totalAdj += lastElement.getW();
+ //}
+ //int lineNumberDifference = (int)((double) totalAdj / constantLineHeight);
+ int lineNumberDifference = (int) Math.round((double) totalAdj / constantLineHeight + (adj > 0 ? - 0.4 : 0.4));
+ //System.out.println(" LLM> variazione calcolata = " + ((double) totalAdj / constantLineHeight) + " variazione applicata = " + lineNumberDifference);
+ lineLayouts = (LineLayoutPossibilities)lineLayoutsList.get(pos.getLeafPos());
+ lineNumberDifference = lineLayouts.applyLineCountAdjustment(lineNumberDifference);
+ return lineNumberDifference * constantLineHeight;
+ }
+
+ public void discardSpace(KnuthGlue spaceGlue) {
+ }
+
+ public LinkedList getChangedKnuthElements(List oldList, int alignment) {
+ LinkedList returnList = new LinkedList();
+ for (int p = 0;
+ p < knuthParagraphs.size();
+ p ++) {
+ lineLayouts = (LineLayoutPossibilities)lineLayoutsList.get(p);
+ //System.out.println("demerits of the chosen layout: " + lineLayouts.getChosenDemerits());
+ for (int i = 0;
+ i < lineLayouts.getChosenLineCount();
+ i ++) {
+ if (!((BlockLevelLayoutManager) parentLM).mustKeepTogether()
+ && i >= fobj.getOrphans()
+ && i <= lineLayouts.getChosenLineCount() - fobj.getWidows()) {
+ // null penalty allowing a page break between lines
+ returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+ }
+ LineBreakPosition lbp = (LineBreakPosition) lineLayouts.getChosenPosition(i);
+ //System.out.println("LLM.getChangedKnuthElements> lineWidth= " + lbp.lineWidth + " difference= " + lbp.difference);
+ //System.out.println(" shrink= " + lbp.availableShrink + " stretch= " + lbp.availableStretch);
+
+ //System.out.println("linewidth= " + lbp.lineWidth + " difference= " + lbp.difference + " indent= " + lbp.startIndent);
+ MinOptMax contentIPD;
+ if (alignment == EN_JUSTIFY) {
+ contentIPD = new MinOptMax(
+ lbp.lineWidth - lbp.difference - lbp.availableShrink,
+ lbp.lineWidth - lbp.difference,
+ lbp.lineWidth - lbp.difference + lbp.availableStretch);
+ } else if (alignment == EN_CENTER) {
+ contentIPD = new MinOptMax(lbp.lineWidth - 2 * lbp.startIndent);
+ } else if (alignment == EN_END) {
+ contentIPD = new MinOptMax(lbp.lineWidth - lbp.startIndent);
+ } else {
+ contentIPD = new MinOptMax(lbp.lineWidth - lbp.difference + lbp.startIndent);
+ }
+ returnList.add(new KnuthBlockBox(lbp.lineHeight,
+ contentIPD,
+ (lbp.ipdAdjust != 0 ? lbp.lineWidth - lbp.difference : 0),
+ lbp, false));
+ }
+ }
+ return returnList;
+ }
/**
* find hyphenation points for every word int the current paragraph
= currUpdate.inlineLM.getChangedKnuthElements
(currPar.subList(fromIndex + iAddedElements,
toIndex + iAddedElements),
- flaggedPenalty, effectiveAlignment);
+ /*flaggedPenalty,*/ effectiveAlignment);
// remove the old elements
currPar.subList(fromIndex + iAddedElements,
toIndex + iAddedElements).clear();
setFinished(false);
iReturnedLBP--;
}
- while ((LineBreakPosition) breakpoints.get(iReturnedLBP)
+ while ((LineBreakPosition) lineLayouts.getChosenPosition(iReturnedLBP)
!= (LineBreakPosition) resetPos) {
iReturnedLBP--;
}
*/
public void addAreas(PositionIterator parentIter,
LayoutContext context) {
- addAreas(parentIter, 0.0);
-
- //vecInlineBreaks.clear();
- prevBP = null;
- }
-
- // Generate and add areas to parent area
- // Set size etc
- // dSpaceAdjust should reference extra space in the BPD
- /**
- * Add the areas with the associated space adjustment.
- *
- * @param parentIter the iterator of breaks positions
- * @param dSpaceAdjust the space adjustment
- */
- public void addAreas(PositionIterator parentIter, double dSpaceAdjust) {
LayoutManager childLM;
LayoutContext lc = new LayoutContext(0);
int iCurrParIndex;
while (parentIter.hasNext()) {
- ListIterator paragraphIterator = null;
- KnuthElement tempElement = null;
- // the TLM which created the last KnuthElement in this line
- LayoutManager lastLM = null;
-
- LineBreakPosition lbp = (LineBreakPosition) parentIter.next();
- LineArea lineArea = new LineArea();
- lineArea.setStartIndent(lbp.startIndent);
- lineArea.setBPD(lbp.lineHeight);
- lc.setBaseline(lbp.baseline);
- lc.setLineHeight(lbp.lineHeight);
- lc.setMiddleShift(middleShift);
- setCurrentArea(lineArea);
-
- iCurrParIndex = lbp.iParIndex;
- Paragraph currPar = (Paragraph) knuthParagraphs.get(iCurrParIndex);
- iEndElement = lbp.getLeafPos();
-
- // ignore the first elements added by the LineLayoutManager
- iStartElement += (iStartElement == 0) ? currPar.ignoreAtStart : 0;
-
- // ignore the last elements added by the LineLayoutManager
- iEndElement -= (iEndElement == (currPar.size() - 1))
- ? currPar.ignoreAtEnd : 0;
-
- // ignore the last element in the line if it is a KnuthGlue object
- paragraphIterator = currPar.listIterator(iEndElement);
- tempElement = (KnuthElement) paragraphIterator.next();
- if (tempElement.isGlue()) {
- iEndElement --;
- // this returns the same KnuthElement
- paragraphIterator.previous();
- tempElement = (KnuthElement) paragraphIterator.previous();
- }
- lastLM = tempElement.getLayoutManager();
-
- // ignore KnuthGlue and KnuthPenalty objects
- // at the beginning of the line
- paragraphIterator = currPar.listIterator(iStartElement);
- tempElement = (KnuthElement) paragraphIterator.next();
- while (!tempElement.isBox() && paragraphIterator.hasNext()) {
+ Position pos = (Position) parentIter.next();
+ if (pos instanceof LineBreakPosition) {
+ ListIterator paragraphIterator = null;
+ KnuthElement tempElement = null;
+ // the TLM which created the last KnuthElement in this line
+ LayoutManager lastLM = null;
+
+ LineBreakPosition lbp = (LineBreakPosition) pos;
+ LineArea lineArea = new LineArea();
+ lineArea.setStartIndent(lbp.startIndent);
+ lineArea.setBPD(lbp.lineHeight);
+ lc.setBaseline(lbp.baseline);
+ lc.setLineHeight(lbp.lineHeight);
+ lc.setMiddleShift(middleShift);
+ lc.setTopShift(lbp.topShift);
+ lc.setBottomShift(lbp.bottomShift);
+
+ iCurrParIndex = lbp.iParIndex;
+ Paragraph currPar = (Paragraph) knuthParagraphs.get(iCurrParIndex);
+ iEndElement = lbp.getLeafPos();
+
+ // ignore the first elements added by the LineLayoutManager
+ iStartElement += (iStartElement == 0) ? currPar.ignoreAtStart : 0;
+
+ // ignore the last elements added by the LineLayoutManager
+ iEndElement -= (iEndElement == (currPar.size() - 1))
+ ? currPar.ignoreAtEnd : 0;
+
+ // ignore the last element in the line if it is a KnuthGlue object
+ paragraphIterator = currPar.listIterator(iEndElement);
tempElement = (KnuthElement) paragraphIterator.next();
- iStartElement ++;
- }
-
- // Add the inline areas to lineArea
- PositionIterator inlinePosIter
- = new KnuthPossPosIter(currPar, iStartElement,
- iEndElement + 1);
-
- iStartElement = lbp.getLeafPos() + 1;
- if (iStartElement == currPar.size()) {
- // advance to next paragraph
- iStartElement = 0;
- }
-
- lc.setSpaceAdjust(lbp.dAdjust);
- lc.setIPDAdjust(lbp.ipdAdjust);
- lc.setLeadingSpace(new SpaceSpecifier(true));
- lc.setTrailingSpace(new SpaceSpecifier(false));
- lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
- setChildContext(lc);
- while ((childLM = inlinePosIter.getNextChildLM()) != null) {
- lc.setFlags(LayoutContext.LAST_AREA, (childLM == lastLM));
- childLM.addAreas(inlinePosIter, lc);
- lc.setLeadingSpace(lc.getTrailingSpace());
+ if (tempElement.isGlue()) {
+ iEndElement --;
+ // this returns the same KnuthElement
+ paragraphIterator.previous();
+ tempElement = (KnuthElement) paragraphIterator.previous();
+ }
+ lastLM = tempElement.getLayoutManager();
+
+ // ignore KnuthGlue and KnuthPenalty objects
+ // at the beginning of the line
+ paragraphIterator = currPar.listIterator(iStartElement);
+ tempElement = (KnuthElement) paragraphIterator.next();
+ while (!tempElement.isBox() && paragraphIterator.hasNext()) {
+ tempElement = (KnuthElement) paragraphIterator.next();
+ iStartElement ++;
+ }
+
+ // Add the inline areas to lineArea
+ PositionIterator inlinePosIter
+ = new KnuthPossPosIter(currPar, iStartElement,
+ iEndElement + 1);
+
+ iStartElement = lbp.getLeafPos() + 1;
+ if (iStartElement == currPar.size()) {
+ // advance to next paragraph
+ iStartElement = 0;
+ }
+
+ lc.setSpaceAdjust(lbp.dAdjust);
+ lc.setIPDAdjust(lbp.ipdAdjust);
+ lc.setLeadingSpace(new SpaceSpecifier(true));
lc.setTrailingSpace(new SpaceSpecifier(false));
+ lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
+
+ /* extension (not in the XSL FO recommendation): if the left and right margins
+ have been optimized, recompute indents and / or adjust ratio, according
+ to the paragraph horizontal alignment */
+ if (false && bTextAlignment == EN_JUSTIFY) {
+ // re-compute space adjust ratio
+ int updatedDifference = context.getStackLimit().opt - lbp.lineWidth + lbp.difference;
+ double updatedRatio = 0.0;
+ if (updatedDifference > 0) {
+ updatedRatio = (float) updatedDifference / lbp.availableStretch;
+ } else if (updatedDifference < 0) {
+ updatedRatio = (float) updatedDifference / lbp.availableShrink;
+ }
+ lc.setIPDAdjust(updatedRatio);
+ //System.out.println("LLM.addAreas> old difference = " + lbp.difference + " new difference = " + updatedDifference);
+ //System.out.println(" old ratio = " + lbp.ipdAdjust + " new ratio = " + updatedRatio);
+ } else if (false && bTextAlignment == EN_CENTER) {
+ // re-compute indent
+ int updatedIndent = lbp.startIndent + (context.getStackLimit().opt - lbp.lineWidth) / 2;
+ lineArea.setStartIndent(updatedIndent);
+ } else if (false && bTextAlignment == EN_END) {
+ // re-compute indent
+ int updatedIndent = lbp.startIndent + (context.getStackLimit().opt - lbp.lineWidth);
+ lineArea.setStartIndent(updatedIndent);
+ }
+
+ setCurrentArea(lineArea);
+ setChildContext(lc);
+ while ((childLM = inlinePosIter.getNextChildLM()) != null) {
+ lc.setFlags(LayoutContext.LAST_AREA, (childLM == lastLM));
+ childLM.addAreas(inlinePosIter, lc);
+ lc.setLeadingSpace(lc.getTrailingSpace());
+ lc.setTrailingSpace(new SpaceSpecifier(false));
+ }
+
+ // when can this be null?
+ // if display-align is distribute, add space after
+ if (context.getSpaceAfter() > 0
+ && (!context.isLastArea() || parentIter.hasNext())) {
+ lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter());
+ }
+ parentLM.addChildArea(lineArea);
+ } else {
+ // pos was the Position inside a penalty item, nothing to do
}
- // when can this be null?
- if (lc.getTrailingSpace() != null) {
- addSpace(lineArea, lc.getTrailingSpace().resolve(true),
- lc.getSpaceAdjust());
- }
- parentLM.addChildArea(lineArea);
}
setCurrentArea(null); // ?? necessary
}
-
- /**
- * Add an unresolved area.
- * If a child layout manager needs to add an unresolved area
- * for page reference or linking then this intercepts it for
- * line area handling.
- * A line area may need to have the inline areas adjusted
- * to properly fill the line area. This adds a resolver that
- * resolves the inline area and can do the necessary
- * adjustments to the line and inline areas.
- *
- * @param id the id reference of the resolvable
- * @param res the resolvable object
- */
- public void addUnresolvedArea(String id, Resolvable res) {
- // create a resolvable class that handles ipd
- // adjustment for the current line
-
- parentLM.addUnresolvedArea(id, res);
- }
-
}
--- /dev/null
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class LineLayoutPossibilities {
+
+ /** logger instance */
+ protected static Log log = LogFactory.getLog(LineLayoutPossibilities.class);
+
+ private class Possibility {
+ private int lineCount;
+ private double demerits;
+ private List breakPositions;
+
+ private Possibility(int lc, double dem) {
+ lineCount = lc;
+ demerits = dem;
+ breakPositions = new java.util.ArrayList(lc);
+ }
+
+ private int getLineCount() {
+ return lineCount;
+ }
+
+ private double getDemerits() {
+ return demerits;
+ }
+
+ private void addBreakPosition(Position pos) {
+ // Positions are always added with index 0 because
+ // they are created backward, from the last one to
+ // the first one
+ breakPositions.add(0, pos);
+ }
+
+ private Position getBreakPosition(int i) {
+ return (Position)breakPositions.get(i);
+ }
+ }
+
+ private List possibilitiesList;
+ private List savedPossibilities;
+ private int minimumIndex;
+ private int optimumIndex;
+ private int maximumIndex;
+ private int chosenIndex;
+ private int savedOptLineCount;
+
+ public LineLayoutPossibilities() {
+ possibilitiesList = new java.util.ArrayList();
+ savedPossibilities = new java.util.ArrayList();
+ optimumIndex = -1;
+ }
+
+ public void addPossibility(int ln, double dem) {
+ possibilitiesList.add(new Possibility(ln, dem));
+ if (possibilitiesList.size() == 1) {
+ // first Possibility added
+ minimumIndex = 0;
+ optimumIndex = 0;
+ maximumIndex = 0;
+ chosenIndex = 0;
+ } else {
+ if (dem < ((Possibility)possibilitiesList.get(optimumIndex)).getDemerits()) {
+ optimumIndex = possibilitiesList.size() - 1;
+ chosenIndex = optimumIndex;
+ }
+ if (ln < ((Possibility)possibilitiesList.get(minimumIndex)).getLineCount()) {
+ minimumIndex = possibilitiesList.size() - 1;
+ }
+ if (ln > ((Possibility)possibilitiesList.get(maximumIndex)).getLineCount()) {
+ maximumIndex = possibilitiesList.size() - 1;
+ }
+ }
+ }
+
+ /* save in a different array the computed Possibilities,
+ * so possibilitiesList is ready to store different Possibilities
+ */
+ public void savePossibilities(boolean bSaveOptLineCount) {
+ if (bSaveOptLineCount) {
+ savedOptLineCount = getOptLineCount();
+ } else {
+ savedOptLineCount = 0;
+ }
+ savedPossibilities = possibilitiesList;
+ possibilitiesList = new java.util.ArrayList();
+ }
+
+ /* replace the Possibilities stored in possibilitiesList with
+ * the ones stored in savedPossibilities and having the same line number
+ */
+ public void restorePossibilities() {
+ int index = 0;
+ while (savedPossibilities.size() > 0) {
+ Possibility restoredPossibility = (Possibility) savedPossibilities.remove(0);
+ if (restoredPossibility.getLineCount() < getMinLineCount()) {
+ // if the line number of restoredPossibility is less than the minimum one,
+ // add restoredPossibility at the beginning of the list
+ possibilitiesList.add(0, restoredPossibility);
+ // update minimumIndex
+ minimumIndex = 0;
+ // shift the other indexes;
+ optimumIndex ++;
+ maximumIndex ++;
+ chosenIndex ++;
+ } else if (restoredPossibility.getLineCount() > getMaxLineCount()) {
+ // if the line number of restoredPossibility is greater than the maximum one,
+ // add restoredPossibility at the end of the list
+ possibilitiesList.add(possibilitiesList.size(), restoredPossibility);
+ // update maximumIndex
+ maximumIndex = possibilitiesList.size() - 1;
+ index = maximumIndex;
+ } else {
+ // find the index of the Possibility that will be replaced
+ while (index < maximumIndex
+ && getLineCount(index) < restoredPossibility.getLineCount()) {
+ index ++;
+ }
+ if (getLineCount(index) == restoredPossibility.getLineCount()) {
+ possibilitiesList.set(index, restoredPossibility);
+ } else {
+ // this should not happen
+ log.error("LineLayoutPossibilities restorePossibilities(),"
+ + " min= " + getMinLineCount()
+ + " max= " + getMaxLineCount()
+ + " restored= " + restoredPossibility.getLineCount());
+ return;
+ }
+ }
+ // update optimumIndex and chosenIndex
+ if (savedOptLineCount == 0 && getDemerits(optimumIndex) > restoredPossibility.getDemerits()
+ || savedOptLineCount != 0 && restoredPossibility.getLineCount() == savedOptLineCount) {
+ optimumIndex = index;
+ chosenIndex = optimumIndex;
+ }
+ }
+/*LF*/ //System.out.println(">> minLineCount = " + getMinLineCount() + " optLineCount = " + getOptLineCount() + " maxLineCount() = " + getMaxLineCount());
+ }
+
+ public void addBreakPosition(Position pos, int i) {
+ ((Possibility)possibilitiesList.get(i)).addBreakPosition(pos);
+ }
+
+ public boolean canUseMoreLines() {
+ return (getOptLineCount() < getMaxLineCount());
+ }
+
+ public boolean canUseLessLines() {
+ return (getMinLineCount() < getOptLineCount());
+ }
+
+ public int getMinLineCount() {
+ return getLineCount(minimumIndex);
+ }
+
+ public int getOptLineCount() {
+ return getLineCount(optimumIndex);
+ }
+
+ public int getMaxLineCount() {
+ return getLineCount(maximumIndex);
+ }
+
+ public int getChosenLineCount() {
+ return getLineCount(chosenIndex);
+ }
+
+ public int getLineCount(int i) {
+ return ((Possibility)possibilitiesList.get(i)).getLineCount();
+ }
+
+ public double getChosenDemerits() {
+ return getDemerits(chosenIndex);
+ }
+
+ public double getDemerits(int i) {
+ return ((Possibility)possibilitiesList.get(i)).getDemerits();
+ }
+
+ public int getPossibilitiesNumber() {
+ return possibilitiesList.size();
+ }
+
+ public Position getChosenPosition(int i) {
+ return ((Possibility)possibilitiesList.get(chosenIndex)).getBreakPosition(i);
+ }
+
+ public int applyLineCountAdjustment(int adj) {
+ if (adj >= (getMinLineCount() - getChosenLineCount())
+ && adj <= (getMaxLineCount() - getChosenLineCount())
+ && getLineCount(chosenIndex + adj) == getChosenLineCount() + adj) {
+ chosenIndex += adj;
+ log.debug("chosenLineCount= " + (getChosenLineCount() - adj) + " adjustment= " + adj
+ + " => chosenLineCount= " + getLineCount(chosenIndex));
+ return adj;
+ } else {
+ // this should not happen!
+ log.warn("Cannot apply the desired line count adjustment.");
+ return 0;
+ }
+ }
+
+ public void printAll() {
+ System.out.println("++++++++++");
+ System.out.println(" " + possibilitiesList.size() + " possibility':");
+ for (int i = 0; i < possibilitiesList.size(); i ++) {
+ System.out.println(" " + ((Possibility)possibilitiesList.get(i)).getLineCount()
+ + (i == optimumIndex ? " *" : "")
+ + (i == minimumIndex ? " -" : "")
+ + (i == maximumIndex ? " +" : ""));
+ }
+ System.out.println("++++++++++");
+ }
+}
}
}
+ public static void extendMinimum(MinOptMax mom, int len, boolean optToLen) {
+ if (mom.min < len) {
+ mom.min = len;
+ mom.opt = Math.max(mom.min, mom.opt);
+ if (optToLen) {
+ mom.opt = Math.min(mom.min, len);
+ }
+ mom.max = Math.max(mom.opt, mom.max);
+ }
+ }
+
/**
* After a calculation on a MinOptMax, this can be called to set opt to
* a new effective value.
}
}
+ /**
+ * Converts a LengthRangeProperty to a MinOptMax.
+ * @param prop LengthRangeProperty
+ * @return the requested MinOptMax instance
+ */
+ public static MinOptMax toMinOptMax(LengthRangeProperty prop) {
+ MinOptMax mom = new MinOptMax(
+ (prop.getMinimum().isAuto()
+ ? 0 : prop.getMinimum().getLength().getValue()),
+ (prop.getOptimum().isAuto()
+ ? 0 : prop.getOptimum().getLength().getValue()),
+ (prop.getMinimum().isAuto()
+ ? Integer.MAX_VALUE
+ : prop.getMaximum().getLength().getValue()));
+ return mom;
+ }
+
}
/*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
public Position getPosition() {
return subPos;
}
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("NonLeafPos(");
+ if (getPosition() != null) {
+ sb.append(getPosition().toString());
+ } else {
+ sb.append("null");
+ }
+ sb.append(")");
+ return sb.toString();
+ }
}
--- /dev/null
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ * Licensed 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.LinkedList;
+
+import org.apache.fop.layoutmgr.AbstractBreaker.PageBreakPosition;
+
+class PageBreakingAlgorithm extends BreakingAlgorithm {
+ private LayoutManager topLevelLM;
+ private LinkedList pageBreaks = null;
+
+ public PageBreakingAlgorithm(LayoutManager topLevelLM,
+ int alignment, int alignmentLast) {
+ super(alignment, alignmentLast, true);
+ this.topLevelLM = topLevelLM;
+ }
+
+ public LinkedList getPageBreaks() {
+ return pageBreaks;
+ }
+
+ public void insertPageBreakAsFirst(PageBreakPosition pageBreak) {
+ if (pageBreaks == null) {
+ pageBreaks = new LinkedList();
+ }
+ pageBreaks.addFirst(pageBreak);
+ }
+
+ public void updateData1(int total, double demerits) {
+ }
+
+ public void updateData2(KnuthNode bestActiveNode,
+ KnuthSequence sequence,
+ int total) {
+ //int difference = (bestActiveNode.line < total) ? bestActiveNode.difference : bestActiveNode.difference + fillerMinWidth;
+ int difference = bestActiveNode.difference;
+ int blockAlignment = (bestActiveNode.line < total) ? alignment : alignmentLast;
+ double ratio = (blockAlignment == org.apache.fop.fo.Constants.EN_JUSTIFY
+ || bestActiveNode.adjustRatio < 0) ? bestActiveNode.adjustRatio : 0;
+
+
+ // add nodes at the beginning of the list, as they are found
+ // backwards, from the last one to the first one
+ System.out.println("BBA> difference= " + difference + " ratio= " + ratio
+ + " posizione= " + bestActiveNode.position);
+ insertPageBreakAsFirst(new PageBreakPosition(this.topLevelLM,
+ bestActiveNode.position, ratio, difference));
+ }
+
+ protected int filterActiveNodes() {
+ // leave only the active node with fewest total demerits
+ KnuthNode bestActiveNode = null;
+ for (int i = startLine; i < endLine; i++) {
+ for (KnuthNode node = getNode(i); node != null; node = node.next) {
+ bestActiveNode = compareNodes(bestActiveNode, node);
+ if (node != bestActiveNode) {
+ removeNode(i, node);
+ }
+ }
+ }
+ return bestActiveNode.line;
+ }
+
+}
\ No newline at end of file
public void addAreas(PositionIterator posIter, LayoutContext context) {
super.addAreas(posIter, context);
if (!resolved) {
- parentLM.addUnresolvedArea(fobj.getRefId(), (Resolvable) curArea);
+ getPSLM().addUnresolvedArea(fobj.getRefId(), (Resolvable) curArea);
}
}
* return a resolvable area
*/
private InlineArea getPageNumberCitationInlineArea(LayoutManager parentLM) {
- PageViewport page = parentLM.resolveRefID(fobj.getRefId());
+ PageViewport page = getPSLM().getFirstPVWithID(fobj.getRefId());
InlineArea inline = null;
if (page != null) {
String str = page.getPageNumberString();
}
protected void addId() {
- addID(fobj.getId());
+ getPSLM().addIDToPage(fobj.getId());
}
}
public InlineArea get(LayoutContext context) {
// get page string from parent, build area
TextArea inline = new TextArea();
- String str = parentLM.getCurrentPageNumberString();
+ String str = getCurrentPV().getPageNumberString();
int width = 0;
for (int count = 0; count < str.length(); count++) {
width += font.getCharWidth(str.charAt(count));
}
protected void addId() {
- addID(fobj.getId());
+ getPSLM().addIDToPage(fobj.getId());
}
}
import org.apache.fop.apps.FOPException;
-import org.apache.fop.area.CTM;
import org.apache.fop.area.AreaTreeHandler;
import org.apache.fop.area.AreaTreeModel;
import org.apache.fop.area.Area;
import org.apache.fop.area.PageViewport;
-import org.apache.fop.area.NormalFlow;
import org.apache.fop.area.LineArea;
-import org.apache.fop.area.Page;
import org.apache.fop.area.RegionViewport;
-import org.apache.fop.area.RegionReference;
-import org.apache.fop.area.BodyRegion;
-import org.apache.fop.area.Span;
-import org.apache.fop.area.BeforeFloat;
-import org.apache.fop.area.Footnote;
import org.apache.fop.area.Resolvable;
-import org.apache.fop.area.Trait;
import org.apache.fop.datatypes.PercentBase;
-import org.apache.fop.datatypes.FODimension;
-import org.apache.fop.fo.FObj;
import org.apache.fop.fo.Constants;
import org.apache.fop.fo.flow.Marker;
+import org.apache.fop.fo.flow.RetrieveMarker;
import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.pagination.Region;
-import org.apache.fop.fo.pagination.RegionBody;
+import org.apache.fop.fo.pagination.SideRegion;
import org.apache.fop.fo.pagination.SimplePageMaster;
import org.apache.fop.fo.pagination.StaticContent;
-import org.apache.fop.fo.properties.CommonMarginBlock;
+import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
-import java.awt.Rectangle;
-import java.util.Iterator;
-import java.awt.geom.Rectangle2D;
-import org.apache.fop.traits.MinOptMax;
/**
- * LayoutManager for a PageSequence.
+ * LayoutManager for a PageSequence. This class is instantiated by
+ * area.AreaTreeHandler for each fo:page-sequence found in the
+ * input document.
*/
public class PageSequenceLayoutManager extends AbstractLayoutManager {
- private PageSequence pageSeq;
-
- private static class BlockBreakPosition extends LeafPosition {
- protected BreakPoss breakps;
-
- protected BlockBreakPosition(LayoutManager lm, BreakPoss bp) {
- super(lm, 0);
- breakps = bp;
- }
- }
-
- private int startPageNum = 0;
- private int currentPageNum = 0;
- private String pageNumberString;
- private boolean isFirstPage = true;
-
- /** Current page being worked on. */
- private PageViewport curPage;
-
- /** Current span being filled */
- private Span curSpan;
-
- /** Current normal-flow-reference-area being filled. */
- private NormalFlow curFlow;
-
- private int flowBPD = 0;
- private int flowIPD = 0;
/**
- * AreaTreeHandler which activates this PSLM.
+ * AreaTreeHandler which activates the PSLM and controls
+ * the rendering of its pages.
*/
private AreaTreeHandler areaTreeHandler;
/**
- * AreaTreeModel that this PSLM sends pages to.
+ * fo:page-sequence formatting object being
+ * processed by this class
*/
- private AreaTreeModel areaTreeModel;
+ private PageSequence pageSeq;
+
+ /**
+ * Current page-viewport-area being filled by
+ * the PSLM.
+ */
+ private PageViewport curPV = null;
/**
- * This is the SimplePageMaster that should be used to create the page. It
- * will be equal to the PageSequence's simplePageMaster, if it exists, or
- * to the correct member of the PageSequence's pageSequenceMaster, if that
- * is in effect instead.
+ * Zero-based index of column (Normal Flow) in span (of the PV)
+ * being filled. See XSL Rec description of fo:region-body
+ * and fop.Area package classes for more information.
*/
- private SimplePageMaster currentSimplePageMaster;
+ private int curFlowIdx = -1;
/**
- * Constructor - activated by AreaTreeHandler for each
- * fo:page-sequence in the input FO stream
- *
- * @param pageseq the page-sequence formatting object
+ * The FlowLayoutManager object, which processes
+ * the single fo:flow of the fo:page-sequence
*/
- public PageSequenceLayoutManager(PageSequence pageSeq) {
- super(pageSeq);
- this.pageSeq = pageSeq;
- }
+ private FlowLayoutManager childFLM = null;
/**
- * Set the AreaTreeHandler
- * @param areaTreeHandler the area tree handler object
+ * The collection of StaticContentLayoutManager objects that
+ * are associated with this Page Sequence, keyed by flow-name.
*/
- public void setAreaTreeHandler(AreaTreeHandler areaTreeHandler) {
- this.areaTreeHandler = areaTreeHandler;
- areaTreeModel = areaTreeHandler.getAreaTreeModel();
+ //private HashMap staticContentLMs = new HashMap(4);
+
+ private int startPageNum = 0;
+ private int currentPageNum = 0;
+
+ /**
+ * Constructor
+ *
+ * @param ath the area tree handler object
+ * @param pseq fo:page-sequence to process
+ */
+ public PageSequenceLayoutManager(AreaTreeHandler ath, PageSequence pseq) {
+ super(pseq);
+ this.areaTreeHandler = ath;
+ this.pageSeq = pseq;
}
/**
* @see org.apache.fop.layoutmgr.LayoutManager
- * @return the AreaTreeHandler object
+ * @return the LayoutManagerMaker object
*/
- public AreaTreeHandler getAreaTreeHandler() {
- return areaTreeHandler;
+ public LayoutManagerMaker getLayoutManagerMaker() {
+ return areaTreeHandler.getLayoutManagerMaker();
}
/**
*/
public void activateLayout() {
startPageNum = pageSeq.getStartingPageNumber();
- currentPageNum = startPageNum;
- pageNumberString = pageSeq.makeFormattedPageNumber(currentPageNum);
+ currentPageNum = startPageNum - 1;
LineArea title = null;
if (pageSeq.getTitleFO() != null) {
- ContentLayoutManager clm =
- new ContentLayoutManager(pageSeq.getTitleFO(), this);
- title = (LineArea) clm.getParentArea(null); // can improve
+ ContentLayoutManager clm = new ContentLayoutManager(pageSeq
+ .getTitleFO(), this);
+ title = (LineArea) clm.getParentArea(null);
}
- areaTreeModel.startPageSequence(title);
+ areaTreeHandler.getAreaTreeModel().startPageSequence(title);
log.debug("Starting layout");
- makeNewPage(false, false);
- flowIPD = curFlow.getIPD();
-
- BreakPoss bp;
- LayoutContext childLC = new LayoutContext(0);
- while (!isFinished()) {
- if ((bp = getNextBreakPoss(childLC)) != null) {
- addAreas((BlockBreakPosition)bp.getPosition());
- // add static areas and resolve any new id areas
- // finish page and add to area tree
- finishPage();
- currentPageNum++;
- pageNumberString = pageSeq.makeFormattedPageNumber(currentPageNum);
- }
- }
- // TODO: Don't decrement currentPageNum when no pages are generated
- currentPageNum--;
+ curPV = makeNewPage(false, true, false);
+
+ PageBreaker breaker = new PageBreaker(this);
+ int flowBPD = (int) curPV.getBodyRegion().getBPD();
+ breaker.doLayout(flowBPD);
+
finishPage();
- pageSeq.getRoot().notifyPageSequenceFinished(currentPageNum, (currentPageNum - startPageNum) + 1);
+ pageSeq.getRoot().notifyPageSequenceFinished(currentPageNum,
+ (currentPageNum - startPageNum) + 1);
log.debug("Ending layout");
}
+ private class PageBreaker extends AbstractBreaker {
+
+ private PageSequenceLayoutManager pslm;
+ private boolean firstPart = true;
+
+ public PageBreaker(PageSequenceLayoutManager pslm) {
+ this.pslm = pslm;
+ }
+
+ protected LayoutContext createLayoutContext() {
+ LayoutContext lc = new LayoutContext(0);
+ int flowIPD = curPV.getCurrentSpan().getColumnWidth();
+ lc.setRefIPD(flowIPD);
+ return lc;
+ }
+
+ protected LayoutManager getTopLevelLM() {
+ return pslm;
+ }
+
+ protected LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+ return pslm.getNextKnuthElements(context, alignment);
+ }
+
+ protected int getCurrentDisplayAlign() {
+ return curPV.getSPM().getRegion(Constants.FO_REGION_BODY).getDisplayAlign();
+ }
+
+ protected boolean hasMoreContent() {
+ return !isFinished();
+ }
+
+ protected void addAreas(PositionIterator posIter, LayoutContext context) {
+ getCurrentChildLM().addAreas(posIter, context);
+ }
+
+ protected void doPhase3(PageBreakingAlgorithm alg, int partCount,
+ BlockSequence originalList, BlockSequence effectiveList) {
+ //Directly add areas after finding the breaks
+ addAreas(alg, partCount, originalList, effectiveList);
+ }
+
+ protected void startPart(BlockSequence list, boolean bIsFirstPage) {
+ if (curPV == null) {
+ throw new IllegalStateException("curPV must not be null");
+ } else {
+ //firstPart is necessary because we need the first page before we start the
+ //algorithm so we have a BPD and IPD. This may subject to change later when we
+ //start handling more complex cases.
+ if (!firstPart) {
+ if (curFlowIdx < curPV.getCurrentSpan().getColumnCount()-1) {
+ curFlowIdx++;
+ } else {
+ // if this is the first page that will be created by
+ // the current BlockSequence, it could have a break
+ // condition that must be satisfied;
+ // otherwise, we may simply need a new page
+ handleBreakTrait(bIsFirstPage ? list.getStartOn() : Constants.EN_PAGE);
+ }
+ }
+ }
+ // add static areas and resolve any new id areas
+ // finish page and add to area tree
+ firstPart = false;
+ }
+
+ protected void finishPart() {
+ }
+
+ protected LayoutManager getCurrentChildLM() {
+ return childFLM;
+ }
+
+ }
+
/** @see org.apache.fop.layoutmgr.LayoutManager#isBogus() */
public boolean isBogus() {
return false;
* @param context the layout context for finding breaks
* @return the break for the page
*/
- public BreakPoss getNextBreakPoss(LayoutContext context) {
+ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
LayoutManager curLM; // currently active LM
while ((curLM = getChildLM()) != null) {
- BreakPoss bp = null;
+/*LF*/ LinkedList returnedList = null;
+/*LF*/ if (childFLM == null && (curLM instanceof FlowLayoutManager)) {
+/*LF*/ childFLM = (FlowLayoutManager)curLM;
+/*LF*/ } else {
+/*LF*/ if (curLM != childFLM) {
+/*LF*/ System.out.println("PLM> figlio sconosciuto (invalid child LM)");
+/*LF*/ }
+/*LF*/ }
LayoutContext childLC = new LayoutContext(0);
- childLC.setStackLimit(new MinOptMax(flowBPD));
- childLC.setRefIPD(flowIPD);
+ childLC.setStackLimit(context.getStackLimit());
+ childLC.setRefIPD(context.getRefIPD());
if (!curLM.isFinished()) {
+ int flowIPD = curPV.getCurrentSpan().getColumnWidth();
+ int flowBPD = (int) curPV.getBodyRegion().getBPD();
pageSeq.setLayoutDimension(PercentBase.REFERENCE_AREA_IPD, flowIPD);
pageSeq.setLayoutDimension(PercentBase.REFERENCE_AREA_BPD, flowBPD);
- bp = curLM.getNextBreakPoss(childLC);
+/*LF*/ returnedList = curLM.getNextKnuthElements(childLC, alignment);
}
- if (bp != null) {
- return new BreakPoss(
- new BlockBreakPosition(curLM, bp));
+ if (returnedList != null) {
+ return returnedList;
}
}
setFinished(true);
return null;
}
- /**
- * Get the current page number string.
- * This returns the formatted string for the current page.
- *
- * @return the formatted page number string
- */
- public String getCurrentPageNumberString() {
- return pageNumberString;
- }
-
/**
* Provides access to the current page.
* @return the current PageViewport
*/
- public PageViewport getCurrentPageViewport() {
- return this.curPage;
+ public PageViewport getCurrentPV() {
+ return curPV;
}
/**
- * Resolve a reference ID.
- * This resolves a reference ID and returns the first PageViewport
- * that contains the reference ID or null if reference not found.
+ * Provides access to this object
+ * @return this PageSequenceLayoutManager instance
+ */
+ public PageSequenceLayoutManager getPSLM() {
+ return this;
+ }
+
+ /**
+ * This returns the first PageViewport that contains an id trait
+ * matching the idref argument, or null if no such PV exists.
*
- * @param id the reference ID to lookup
- * @return the first page viewport that contains the reference
+ * @param idref the idref trait needing to be resolved
+ * @return the first PageViewport that contains the ID trait
*/
- public PageViewport resolveRefID(String id) {
- List list = areaTreeHandler.getPageViewportsContainingID(id);
+ public PageViewport getFirstPVWithID(String idref) {
+ List list = areaTreeHandler.getPageViewportsContainingID(idref);
if (list != null && list.size() > 0) {
return (PageViewport) list.get(0);
}
return null;
}
- /**
- * Add the areas to the current page.
- * Given the page break position this adds the areas to the current
- * page.
- *
- * @param bbp the block break position
- */
- public void addAreas(BlockBreakPosition bbp) {
- List list = new java.util.ArrayList();
- list.add(bbp.breakps);
- bbp.getLM().addAreas(new BreakPossPosIter(list, 0,
- 1), null);
- }
-
/**
* Add an ID reference to the current page.
* When adding areas the area adds its ID reference.
* @param id the ID reference to add
*/
public void addIDToPage(String id) {
- areaTreeHandler.associateIDWithPageViewport(id, curPage);
+ if (id != null && id.length() > 0) {
+ areaTreeHandler.associateIDWithPageViewport(id, curPV);
+ }
}
/**
- * Add an unresolved area to the layout manager.
- * The Page layout manager handles the unresolved ID
- * reference by adding to the current page and then adding
- * the page as a resolvable to the area tree.
- * This is so that the area tree can resolve the reference
- * and the page can serialize the resolvers if required.
+ * Identify an unresolved area (one needing an idref to be
+ * resolved, e.g. the internal-destination of an fo:basic-link)
+ * for both the AreaTreeHandler and PageViewport object.
+ *
+ * The AreaTreeHandler keeps a document-wide list of idref's
+ * and the PV's needing them to be resolved. It uses this to
+ * send notifications to the PV's when an id has been resolved.
+ *
+ * The PageViewport keeps lists of id's needing resolving, along
+ * with the child areas (page-number-citation, basic-link, etc.)
+ * of the PV needing their resolution.
*
* @param id the ID reference to add
* @param res the resolvable object that needs resolving
*/
public void addUnresolvedArea(String id, Resolvable res) {
- // add to the page viewport so it can serialize
- curPage.addUnresolvedIDRef(id, res);
- // add unresolved to tree
- areaTreeHandler.addUnresolvedIDRef(id, curPage);
- }
-
- /**
- * Add the marker to the page layout manager.
- *
- * @see org.apache.fop.layoutmgr.LayoutManager
- */
- public void addMarkerMap(Map marks, boolean starting, boolean isfirst, boolean islast) {
- //getLogger().debug("adding markers: " + marks + ":" + start);
- // add markers to page on area tree
- curPage.addMarkers(marks, starting, isfirst, islast);
+ curPV.addUnresolvedIDRef(id, res);
+ areaTreeHandler.addUnresolvedIDRef(id, curPV);
}
/**
- * Retrieve a marker from this layout manager.
+ * Bind the RetrieveMarker to the corresponding Marker subtree.
* If the boundary is page then it will only check the
* current page. For page-sequence and document it will
* lookup preceding pages from the area tree and try to find
* Therefore we use last-ending-within-page (Constants.EN_LEWP)
* as the position.
*
- * @param name the marker class name to lookup
- * @param pos the position to locate the marker
- * @param boundary the boundary for locating the marker
- * @return the layout manager for the marker contents
+ * @param rm the RetrieveMarker instance whose properties are to
+ * used to find the matching Marker.
+ * @return a bound RetrieveMarker instance, or null if no Marker
+ * could be found.
*/
- public Marker retrieveMarker(String name, int pos, int boundary) {
+ public RetrieveMarker resolveRetrieveMarker(RetrieveMarker rm) {
+ AreaTreeModel areaTreeModel = areaTreeHandler.getAreaTreeModel();
+ String name = rm.getRetrieveClassName();
+ int pos = rm.getRetrievePosition();
+ int boundary = rm.getRetrieveBoundary();
+
// get marker from the current markers on area tree
- Marker mark = (Marker)curPage.getMarker(name, pos);
+ Marker mark = (Marker)curPV.getMarker(name, pos);
if (mark == null && boundary != EN_PAGE) {
// go back over pages until mark found
// if document boundary then keep going
PageViewport pv = areaTreeModel.getPage(seq, page);
mark = (Marker)pv.getMarker(name, Constants.EN_LEWP);
if (mark != null) {
- return mark;
+ break;
}
page--;
if (page < 0 && doc && seq > 1) {
if (mark == null) {
log.debug("found no marker with name: " + name);
+ return null;
+ } else {
+ rm.bindMarker(mark);
+ return rm;
}
-
- return mark;
}
- /**
- * For now, only handle normal flow areas.
- * @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(org.apache.fop.area.Area)
- */
- public void addChildArea(Area childArea) {
- if (childArea == null) {
- return;
- }
- if (childArea.getAreaClass() == Area.CLASS_NORMAL) {
- getParentArea(childArea);
- } else {
- // todo: all the others!
+ private PageViewport makeNewPage(boolean bIsBlank, boolean bIsFirst, boolean bIsLast) {
+ if (curPV != null) {
+ finishPage();
}
- }
- private PageViewport makeNewPage(boolean bIsBlank, boolean bIsLast) {
- finishPage();
+ currentPageNum++;
+ String pageNumberString = pageSeq.makeFormattedPageNumber(currentPageNum);
try {
// create a new page
- currentSimplePageMaster = pageSeq.getSimplePageMasterToUse(
- currentPageNum, isFirstPage, bIsBlank);
- Region body = currentSimplePageMaster.getRegion(FO_REGION_BODY);
+ SimplePageMaster spm = pageSeq.getSimplePageMasterToUse(
+ currentPageNum, bIsFirst, bIsBlank);
+
+ Region body = spm.getRegion(FO_REGION_BODY);
if (!pageSeq.getMainFlow().getFlowName().equals(body.getRegionName())) {
+ // this is fine by the XSL Rec (fo:flow's flow-name can be mapped to
+ // any region), but we don't support it yet.
throw new FOPException("Flow '" + pageSeq.getMainFlow().getFlowName()
+ "' does not map to the region-body in page-master '"
- + currentSimplePageMaster.getMasterName() + "'");
+ + spm.getMasterName() + "'. FOP presently "
+ + "does not support this.");
}
- curPage = createPageAreas(currentSimplePageMaster);
- isFirstPage = false;
+ curPV = new PageViewport(spm);
} catch (FOPException fopex) {
throw new IllegalArgumentException("Cannot create page: " + fopex.getMessage());
}
- curPage.setPageNumberString(pageNumberString);
+ curPV.setPageNumberString(pageNumberString);
if (log.isDebugEnabled()) {
- log.debug("[" + curPage.getPageNumberString() + "]");
+ log.debug("[" + curPV.getPageNumberString() + (bIsBlank ? "*" : "") + "]");
}
- flowBPD = (int) curPage.getBodyRegion().getBPD();
- createSpan(curPage.getBodyRegion().getColumnCount());
- return curPage;
+ curPV.createSpan(false);
+ curFlowIdx = 0;
+ return curPV;
}
- private void createSpan(int numCols) {
- // get Width or Height as IPD for span
- RegionViewport rv = curPage.getPage().getRegionViewport(FO_REGION_BODY);
- int ipdWidth = (int) rv.getRegion().getIPD() -
- rv.getBorderAndPaddingWidthStart() - rv.getBorderAndPaddingWidthEnd();
-
- // currently hardcoding to one column, replace with numCols when ready
- curSpan = new Span(1 /* numCols */, ipdWidth);
-
- //curSpan.setPosition(BPD, newpos);
- curPage.getBodyRegion().getMainReference().addSpan(curSpan);
- curFlow = curSpan.getNormalFlow(0);
- }
-
- private void layoutStaticContent(int regionID) {
- Region reg = currentSimplePageMaster.getRegion(regionID);
+ /* TODO: See if can initialize the SCLM's just once for
+ * the page sequence, instead of after every page.
+ */
+ private void layoutSideRegion(int regionID) {
+ SideRegion reg = (SideRegion)curPV.getSPM().getRegion(regionID);
if (reg == null) {
return;
}
if (sc == null) {
return;
}
-
- RegionViewport rv = curPage.getPage().getRegionViewport(regionID);
+
+ RegionViewport rv = curPV.getPage().getRegionViewport(regionID);
StaticContentLayoutManager lm;
- try {
- lm = (StaticContentLayoutManager)
- areaTreeHandler.getLayoutManagerMaker().makeLayoutManager(sc);
- } catch (FOPException e) {
- log.error
- ("Failed to create a StaticContentLayoutManager for flow "
- + sc.getFlowName()
- + "; no static content will be laid out:");
- log.error(e.getMessage());
- return;
- }
- lm.initialize();
- lm.setRegionReference(rv.getRegion());
- lm.setParent(this);
- LayoutContext childLC = new LayoutContext(0);
- childLC.setStackLimit(new MinOptMax((int)curPage.getViewArea().getHeight()));
- childLC.setRefIPD(rv.getRegion().getIPD());
- while (!lm.isFinished()) {
- BreakPoss bp = lm.getNextBreakPoss(childLC);
- if (bp != null) {
- List vecBreakPoss = new java.util.ArrayList();
- vecBreakPoss.add(bp);
- lm.addAreas(new BreakPossPosIter(vecBreakPoss, 0,
- vecBreakPoss.size()), null);
- } else {
- log.error("bp==null cls=" + reg.getRegionName());
- }
- }
- //lm.flush();
+ lm = (StaticContentLayoutManager)
+ areaTreeHandler.getLayoutManagerMaker().makeLayoutManager(sc);
+ lm.setTargetRegion(rv.getRegionReference());
+ lm.setParent(this);
+ lm.doLayout(reg);
lm.reset(null);
}
private void finishPage() {
- if (curPage == null) {
- curSpan = null;
- curFlow = null;
- return;
- }
- // Layout static content into the regions
- layoutStaticContent(FO_REGION_BEFORE);
- layoutStaticContent(FO_REGION_AFTER);
- layoutStaticContent(FO_REGION_START);
- layoutStaticContent(FO_REGION_END);
+ // Layout side regions
+ layoutSideRegion(FO_REGION_BEFORE);
+ layoutSideRegion(FO_REGION_AFTER);
+ layoutSideRegion(FO_REGION_START);
+ layoutSideRegion(FO_REGION_END);
// Queue for ID resolution and rendering
- areaTreeModel.addPage(curPage);
- curPage = null;
- curSpan = null;
- curFlow = null;
+ areaTreeHandler.getAreaTreeModel().addPage(curPV);
+ log.debug("page finished: " + curPV.getPageNumberString()
+ + ", current num: " + currentPageNum);
+ curPV = null;
+ curFlowIdx = -1;
}
-
+
/**
* This is called from FlowLayoutManager when it needs to start
* a new flow container (while generating areas).
*/
public Area getParentArea(Area childArea) {
int aclass = childArea.getAreaClass();
+
if (aclass == Area.CLASS_NORMAL) {
- // todo: how to get properties from the Area???
- // Need span, break
- int breakVal = Constants.EN_AUTO;
- Integer breakBefore = (Integer)childArea.getTrait(Trait.BREAK_BEFORE);
- if (breakBefore != null) {
- breakVal = breakBefore.intValue();
- }
- if (breakVal != Constants.EN_AUTO) {
- // We may be forced to make new page
- handleBreak(breakVal);
- } else if (curPage == null) {
- log.debug("curPage is null. Making new page");
- makeNewPage(false, false);
- }
- // Now we should be on the right kind of page
- boolean bNeedNewSpan = false;
- /* Determine if a new span is needed. From the XSL
- * fo:region-body definition, if an fo:block has a span="ALL"
- * (i.e., span all columns defined for the region-body), it
- * must be placed in a span-reference-area whose
- * column-count = 1. If its span-value is "NONE",
- * place in a normal Span whose column-count is what
- * is defined for the region-body.
- */ // temporarily hardcoded to EN_NONE.
- int span = Constants.EN_NONE; // childArea.getSpan()
- int numColsNeeded;
- if (span == Constants.EN_ALL) {
- numColsNeeded = 1;
- } else { // EN_NONE
- numColsNeeded = curPage.getBodyRegion().getColumnCount();
- }
- if (curSpan == null) { // should never happen, remove?
- bNeedNewSpan = true;
- } else if (numColsNeeded != curSpan.getColumnCount()) {
- // need a new Span, with numColsNeeded columns
- if (curSpan.getColumnCount() > 1) {
- // finished with current span, so balance
- // its columns to make them the same "height"
- // balanceColumns(); // TODO: implement
- }
- bNeedNewSpan = true;
- }
- if (bNeedNewSpan) {
- createSpan(numColsNeeded);
- } else if (curFlow == null) { // should not happen
- curFlow = curSpan.addAdditionalNormalFlow();
- }
- return curFlow;
- } else {
- if (curPage == null) {
- makeNewPage(false, false);
- }
- // Now handle different kinds of areas
- if (aclass == Area.CLASS_BEFORE_FLOAT) {
- BeforeFloat bf = curPage.getBodyRegion().getBeforeFloat();
- if (bf == null) {
- bf = new BeforeFloat();
- curPage.getBodyRegion().setBeforeFloat(bf);
- }
- return bf;
- } else if (aclass == Area.CLASS_FOOTNOTE) {
- Footnote fn = curPage.getBodyRegion().getFootnote();
- if (fn == null) {
- fn = new Footnote();
- curPage.getBodyRegion().setFootnote(fn);
- }
- return fn;
- }
- // todo!!! other area classes (side-float, absolute, fixed)
- return null;
+ return curPV.getCurrentSpan().getNormalFlow(curFlowIdx);
+ } else if (aclass == Area.CLASS_BEFORE_FLOAT) {
+ return curPV.getBodyRegion().getBeforeFloat();
+ } else if (aclass == Area.CLASS_FOOTNOTE) {
+ return curPV.getBodyRegion().getFootnote();
}
+ // todo!!! other area classes (side-float, absolute, fixed)
+ return null;
}
/**
* Depending on the kind of break condition, make new column
* or page. May need to make an empty page if next page would
* not have the desired "handedness".
- *
- * @param breakVal the break value to handle
+ * @param breakVal - value of break-before or break-after trait.
*/
- private void handleBreak(int breakVal) {
+ private void handleBreakTrait(int breakVal) {
if (breakVal == Constants.EN_COLUMN) {
- if (curSpan != null
- && curSpan.getNormalFlowCount() < curSpan.getColumnCount()) {
+ if (curFlowIdx < curPV.getCurrentSpan().getColumnCount()) {
// Move to next column
- curFlow = curSpan.addAdditionalNormalFlow();
- return;
+ curFlowIdx++;
+ } else {
+ curPV = makeNewPage(false, false, false);
}
- // else need new page
- breakVal = Constants.EN_PAGE;
+ return;
}
- if (needEmptyPage(breakVal)) {
- curPage = makeNewPage(true, false);
+ log.debug("handling break-before after page " + currentPageNum
+ + " breakVal=" + breakVal);
+ if (needBlankPageBeforeNew(breakVal)) {
+ curPV = makeNewPage(true, false, false);
}
if (needNewPage(breakVal)) {
- curPage = makeNewPage(false, false);
+ curPV = makeNewPage(false, false, false);
}
}
* Note that if not all content is placed, we aren't sure whether
* it will flow onto another page or not, so we'd probably better
* block until the queue of layoutable stuff is empty!
+ * @param breakVal - value of break-before or break-after trait.
*/
- private boolean needEmptyPage(int breakValue) {
-
- if (breakValue == Constants.EN_PAGE || curPage.getPage().isEmpty()) {
+ private boolean needBlankPageBeforeNew(int breakVal) {
+ if (breakVal == Constants.EN_PAGE || (curPV.getPage().isEmpty())) {
// any page is OK or we already have an empty page
return false;
- }
- else {
+ } else {
/* IF we are on the kind of page we need, we'll need a new page. */
- if (currentPageNum%2 != 0) {
- // Current page is odd
- return (breakValue == Constants.EN_ODD_PAGE);
- }
- else {
- return (breakValue == Constants.EN_EVEN_PAGE);
+ if (currentPageNum % 2 == 0) { // even page
+ return (breakVal == Constants.EN_EVEN_PAGE);
+ } else { // odd page
+ return (breakVal == Constants.EN_ODD_PAGE);
}
}
}
/**
- * See if need to generate a new page for a forced break condition.
+ * See if need to generate a new page
+ * @param breakVal - value of break-before or break-after trait.
*/
- private boolean needNewPage(int breakValue) {
- if (curPage != null && curPage.getPage().isEmpty()) {
- if (breakValue == Constants.EN_PAGE) {
+ private boolean needNewPage(int breakVal) {
+ if (curPV.getPage().isEmpty()) {
+ if (breakVal == Constants.EN_PAGE) {
return false;
}
- else if (currentPageNum%2 != 0) {
- // Current page is odd
- return (breakValue == Constants.EN_EVEN_PAGE);
+ else if (currentPageNum % 2 == 0) { // even page
+ return (breakVal == Constants.EN_ODD_PAGE);
}
- else {
- return (breakValue == Constants.EN_ODD_PAGE);
+ else { // odd page
+ return (breakVal == Constants.EN_EVEN_PAGE);
}
- }
- else {
+ } else {
return true;
}
}
-
- private PageViewport createPageAreas(SimplePageMaster spm) {
- int pageWidth = spm.getPageWidth().getValue();
- int pageHeight = spm.getPageHeight().getValue();
-
- // Set the page dimension as the toplevel containing block for margin.
- ((FObj) pageSeq.getParent()).setLayoutDimension(PercentBase.BLOCK_IPD, pageWidth);
- ((FObj) pageSeq.getParent()).setLayoutDimension(PercentBase.BLOCK_BPD, pageHeight);
-
- // 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).
- */
- Rectangle pageRefRect =
- new Rectangle(mProps.marginLeft.getValue(), mProps.marginTop.getValue(),
- pageWidth - mProps.marginLeft.getValue() - mProps.marginRight.getValue(),
- pageHeight - mProps.marginTop.getValue() - mProps.marginBottom.getValue());
-
- Page page = new Page(); // page reference area
-
- // 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 = null;
- for (Iterator regenum = spm.getRegions().values().iterator();
- regenum.hasNext();) {
- Region r = (Region)regenum.next();
- RegionViewport rvp = makeRegionViewport(r, reldims, pageCTM);
- r.setLayoutDimension(PercentBase.BLOCK_IPD, rvp.getIPD());
- r.setLayoutDimension(PercentBase.BLOCK_BPD, rvp.getBPD());
- if (r.getNameId() == FO_REGION_BODY) {
- rr = new BodyRegion((RegionBody) r);
- } else {
- rr = new RegionReference(r.getNameId());
- }
- setRegionPosition(r, rr, rvp.getViewArea());
- rvp.setRegion(rr);
- page.setRegionViewport(r.getNameId(), rvp);
- }
-
- return new PageViewport(page, new Rectangle(0, 0, pageWidth, pageHeight));
- }
-
- /**
- * Creates a RegionViewport Area object for this pagination Region.
- * @param reldims relative dimensions
- * @param pageCTM page coordinate transformation matrix
- * @return the new region viewport
- */
- private 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());
- return rv;
- }
-
- /**
- * Set the region position inside the region viewport.
- * This sets the transform that is used to place the contents of
- * the region.
- *
- * @param r the region reference area
- * @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 void setRegionPosition(Region r, RegionReference rr,
- Rectangle2D absRegVPRect) {
- FODimension reldims = new FODimension(0, 0);
- rr.setCTM(CTM.getCTMandRelDims(r.getReferenceOrientation(),
- r.getWritingMode(), absRegVPRect, reldims));
- rr.setIPD(reldims.ipd);
- rr.setBPD(reldims.bpd);
- }
}
/*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
public Position getPosition() {
return null;
}
+
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Position");
+ if (getLM() != null) {
+ sb.append(" {");
+ sb.append(getLM());
+ sb.append("}");
+ }
+ return sb.toString();
+ }
}
private LayoutManager childLM;
private boolean bHasNext;
- PositionIterator(Iterator pIter) {
+ protected PositionIterator(Iterator pIter) {
parentIter = pIter;
lookAhead();
//checkNext();
import org.apache.fop.area.RegionReference;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
+import org.apache.fop.datatypes.PercentBase;
+import org.apache.fop.fo.pagination.Region;
+import org.apache.fop.fo.pagination.SideRegion;
import org.apache.fop.fo.pagination.StaticContent;
+import org.apache.fop.traits.MinOptMax;
import java.util.ArrayList;
+import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
+import java.util.ListIterator;
/**
* LayoutManager for an fo:flow object.
* and filling them with block-level areas generated by its children.
*/
public class StaticContentLayoutManager extends BlockStackingLayoutManager {
-
- private RegionReference region;
+ private RegionReference targetRegion;
private List blockBreaks = new ArrayList();
public StaticContentLayoutManager(StaticContent node) {
* Sets the region reference
* @param region region reference
*/
- public void setRegionReference(RegionReference region) {
- this.region = region;
+ public void setTargetRegion(RegionReference targetRegion) {
+ this.targetRegion = targetRegion;
}
+ /**
+ * @return the region-reference-area that this
+ * static content is directed to.
+ */
+ public RegionReference getTargetRegion() {
+ return targetRegion;
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(org.apache.fop.layoutmgr.LayoutContext, int)
+ */
+ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+ // set layout dimensions
+ fobj.setLayoutDimension(PercentBase.BLOCK_IPD, context.getRefIPD());
+ fobj.setLayoutDimension(PercentBase.BLOCK_BPD, context.getStackLimit().opt);
+
+ //TODO Copied from elsewhere. May be worthwhile to factor out the common parts.
+ // currently active LM
+ BlockLevelLayoutManager curLM;
+ BlockLevelLayoutManager prevLM = null;
+ MinOptMax stackSize = new MinOptMax();
+ LinkedList returnedList;
+ LinkedList returnList = new LinkedList();
+
+ while ((curLM = ((BlockLevelLayoutManager) getChildLM())) != null) {
+ if (curLM.generatesInlineAreas()) {
+ log.error("inline area not allowed under flow - ignoring");
+ curLM.setFinished(true);
+ continue;
+ }
+
+ // Set up a LayoutContext
+ MinOptMax bpd = context.getStackLimit();
+ BreakPoss bp;
+ bp = null;
+
+ LayoutContext childLC = new LayoutContext(0);
+ boolean breakPage = false;
+ childLC.setStackLimit(MinOptMax.subtract(bpd, stackSize));
+ childLC.setRefIPD(context.getRefIPD());
+
+ // get elements from curLM
+ returnedList = curLM.getNextKnuthElements(childLC, alignment);
+/*LF*/ //System.out.println("FLM.getNextKnuthElements> returnedList.size() = " + returnedList.size());
+
+ // "wrap" the Position inside each element
+ LinkedList tempList = returnedList;
+ KnuthElement tempElement;
+ returnedList = new LinkedList();
+ ListIterator listIter = tempList.listIterator();
+ while (listIter.hasNext()) {
+ tempElement = (KnuthElement)listIter.next();
+ tempElement.setPosition(new NonLeafPosition(this, tempElement.getPosition()));
+ returnedList.add(tempElement);
+ }
+
+ if (returnedList.size() == 1
+ && ((KnuthElement)returnedList.getFirst()).isPenalty()
+ && ((KnuthPenalty)returnedList.getFirst()).getP() == -KnuthElement.INFINITE) {
+ // a descendant of this flow has break-before
+ returnList.addAll(returnedList);
+ return returnList;
+ } else {
+ if (returnList.size() > 0) {
+ // there is a block before this one
+ if (prevLM.mustKeepWithNext()
+ || curLM.mustKeepWithPrevious()) {
+ // add an infinite penalty to forbid a break between blocks
+ returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, new Position(this), false));
+ } else if (!((KnuthElement) returnList.getLast()).isGlue()) {
+ // add a null penalty to allow a break between blocks
+ returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+ }
+ }
+/*LF*/ if (returnedList.size() > 0) { // controllare!
+ returnList.addAll(returnedList);
+ if (((KnuthElement)returnedList.getLast()).isPenalty()
+ && ((KnuthPenalty)returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+ // a descendant of this flow has break-after
+/*LF*/ //System.out.println("FLM - break after!!");
+ return returnList;
+ }
+/*LF*/ }
+ }
+ prevLM = curLM;
+ }
+
+ setFinished(true);
+
+ if (returnList.size() > 0) {
+ return returnList;
+ } else {
+ return null;
+ }
+ }
+
/**
* @see org.apache.fop.layoutmgr.LayoutManager#getNextBreakPoss(LayoutContext)
*/
* @see org.apache.fop.layoutmgr.LayoutManager#addAreas(PositionIterator, LayoutContext)
*/
public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
+ AreaAdditionUtil.addAreas(parentIter, layoutContext);
+ /*
LayoutManager childLM;
int iStartPos = 0;
LayoutContext lc = new LayoutContext(0);
}
blockBreaks.clear();
+ */
flush();
- region = null;
+ targetRegion = null;
}
* @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(Area)
*/
public void addChildArea(Area childArea) {
- region.addBlock((Block)childArea);
+ targetRegion.addBlock((Block)childArea);
}
/**
* @see org.apache.fop.layoutmgr.LayoutManager#getParentArea(Area)
*/
public Area getParentArea(Area childArea) {
- return region;
+ return targetRegion;
}
- /**
- * Markers are not allowed in static areas so this reports an
- * error and does nothing.
- *
- * @see org.apache.fop.layoutmgr.LayoutManager
- */
- public void addMarker(Map marks, boolean start, boolean isfirst) {
- // error markers not allowed in static
- log.error("Cannot add marker to static areas");
+ public void doLayout(SideRegion region) {
+ MinOptMax range = new MinOptMax(targetRegion.getIPD());
+ StaticContentBreaker breaker = new StaticContentBreaker(region, this, range);
+ breaker.doLayout(targetRegion.getBPD());
+ if (breaker.isOverflow()) {
+ if (region.getOverflow() == EN_ERROR_IF_OVERFLOW) {
+ //TODO throw layout exception
+ }
+ log.warn("static-content overflows the available area.");
+ }
}
+
+ private class StaticContentBreaker extends AbstractBreaker {
+
+ private Region region;
+ private StaticContentLayoutManager lm;
+ private MinOptMax ipd;
+ boolean overflow = false;
+
+ public StaticContentBreaker(Region region, StaticContentLayoutManager lm, MinOptMax ipd) {
+ this.region = region;
+ this.lm = lm;
+ this.ipd = ipd;
+ }
+
+ public boolean isOverflow() {
+ return this.overflow;
+ }
+
+ protected LayoutManager getTopLevelLM() {
+ return lm;
+ }
+
+ protected LayoutContext createLayoutContext() {
+ LayoutContext lc = super.createLayoutContext();
+ lc.setRefIPD(ipd.opt);
+ return lc;
+ }
+
+ protected LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+ LayoutManager curLM; // currently active LM
+ LinkedList returnList = new LinkedList();
+
+ while ((curLM = getChildLM()) != null) {
+ LayoutContext childLC = new LayoutContext(0);
+ childLC.setStackLimit(context.getStackLimit());
+ childLC.setRefIPD(context.getRefIPD());
+
+ LinkedList returnedList = null;
+ if (!curLM.isFinished()) {
+ returnedList = curLM.getNextKnuthElements(childLC, alignment);
+ }
+ if (returnedList != null) {
+ lm.wrapPositionElements(returnedList, returnList);
+ //returnList.addAll(returnedList);
+ }
+ }
+ setFinished(true);
+ return returnList;
+ }
+
+ protected int getCurrentDisplayAlign() {
+ return region.getDisplayAlign();
+ }
+
+ protected boolean hasMoreContent() {
+ return !lm.isFinished();
+ }
+
+ protected void addAreas(PositionIterator posIter, LayoutContext context) {
+ AreaAdditionUtil.addAreas(posIter, context);
+ }
+
+ protected void doPhase3(PageBreakingAlgorithm alg, int partCount,
+ BlockSequence originalList, BlockSequence effectiveList) {
+ //Directly add areas after finding the breaks
+ addAreas(alg, partCount, originalList, effectiveList);
+ if (partCount > 1) {
+ overflow = true;
+ }
+ }
+
+ protected void finishPart() {
+ //nop for static content
+ }
+
+ protected LayoutManager getCurrentChildLM() {
+ return null; //TODO NYI
+ }
+
+ }
}
* LayoutManager for text (a sequence of characters) which generates one
* or more inline areas.
*/
-public class TextLayoutManager extends AbstractLayoutManager
- implements InlineLevelLayoutManager {
+public class TextLayoutManager extends LeafNodeLayoutManager {
/**
* Store information about each potential text area.
* @param node The FOText object to be rendered
*/
public TextLayoutManager(FOText node) {
+ super();
foText = node;
textArray = new char[node.endIndex - node.startIndex];
textArea.setOffset(context.getMiddleBaseline() + fs.getXHeight() / 2);
break;
case EN_TOP:
- textArea.setOffset(fs.getAscender());
+ textArea.setOffset(context.getTopBaseline() + fs.getAscender());
break;
case EN_BOTTOM:
- textArea.setOffset(context.getLineHeight() - bpd + fs.getAscender());
+ textArea.setOffset(context.getBottomBaseline() - bpd + fs.getAscender());
break;
case EN_BASELINE:
default:
- 6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
new LeafPosition(this, -1), true));
returnList.add
- (new KnuthBox(0, 0, 0, 0,
+ (new KnuthInlineBox(0, 0, 0, 0,
new LeafPosition(this, -1), true));
returnList.add
(new KnuthPenalty(0, KnuthElement.INFINITE, false,
(short) 1, (short) 0,
wordSpaceIPD, false));
returnList.add
- (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
+ (new KnuthGlue(0, 3 * wordSpaceIPD.opt, 0,
new LeafPosition(this, vecAreaInfo.size() - 1), false));
returnList.add
(new KnuthPenalty(0, 0, false,
new LeafPosition(this, -1), true));
returnList.add
(new KnuthGlue(wordSpaceIPD.opt,
- - 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
+ - 3 * wordSpaceIPD.opt, 0,
new LeafPosition(this, -1), true));
iNextStart ++;
break;
for (; iTempStart < textArray.length
&& textArray[iTempStart] != SPACE
&& textArray[iTempStart] != NBSPACE
- && textArray[iTempStart] != NEWLINE;
+ && textArray[iTempStart] != NEWLINE
+ && !(iTempStart > iNextStart
+ && alignment == EN_JUSTIFY
+ && BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0);
iTempStart++) {
wordIPD.add(fs.getCharWidth(textArray[iTempStart]));
}
- wordIPD.add(MinOptMax.multiply(letterSpaceIPD, (iTempStart - iThisStart - 1)));
+ int iLetterSpaces = iTempStart - iThisStart - 1;
+ wordIPD.add(MinOptMax.multiply(letterSpaceIPD, iLetterSpaces));
vecAreaInfo.add
(new AreaInfo(iThisStart, iTempStart, (short) 0,
- (short) (iTempStart - iThisStart - 1),
+ (short) iLetterSpaces,
wordIPD, false));
if (letterSpaceIPD.min == letterSpaceIPD.max) {
// constant letter space; simply return a box
// whose width includes letter spaces
returnList.add
- (new KnuthBox(wordIPD.opt, lead, total, middle,
+ (new KnuthInlineBox(wordIPD.opt, lead, total, middle,
new LeafPosition(this, vecAreaInfo.size() - 1), false));
- iNextStart = iTempStart;
} else {
// adjustable letter space;
// some other KnuthElements are needed
returnList.add
- (new KnuthBox(wordIPD.opt - (iTempStart - iThisStart - 1) * letterSpaceIPD.opt,
+ (new KnuthInlineBox(wordIPD.opt - iLetterSpaces * letterSpaceIPD.opt,
lead, total, middle,
new LeafPosition(this, vecAreaInfo.size() - 1), false));
returnList.add
(new KnuthPenalty(0, KnuthElement.INFINITE, false,
new LeafPosition(this, -1), true));
returnList.add
- (new KnuthGlue((iTempStart - iThisStart - 1) * letterSpaceIPD.opt,
- (iTempStart - iThisStart - 1) * (letterSpaceIPD.max - letterSpaceIPD.opt),
- (iTempStart - iThisStart - 1) * (letterSpaceIPD.opt - letterSpaceIPD.min),
+ (new KnuthGlue(iLetterSpaces * letterSpaceIPD.opt,
+ iLetterSpaces * (letterSpaceIPD.max - letterSpaceIPD.opt),
+ iLetterSpaces * (letterSpaceIPD.opt - letterSpaceIPD.min),
new LeafPosition(this, -1), true));
returnList.add
- (new KnuthBox(0, lead, total, middle,
- new LeafPosition(this, -1), true));
- iNextStart = iTempStart;
+ (new KnuthInlineBox(0, lead, total, middle,
+ new LeafPosition(this, -1), true));
}
+ // if the last character is '-' or '/', it could be used as a line end;
+ // add a flagged penalty element and glue element representing a suppressible
+ // letter space if the next character is not a space
+ if (BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0
+ && iTempStart < textArray.length
+ && textArray[iTempStart] != SPACE
+ && textArray[iTempStart] != NBSPACE) {
+ returnList.add
+ (new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true,
+ new LeafPosition(this, -1), false));
+ returnList.add
+ (new KnuthGlue(letterSpaceIPD.opt,
+ letterSpaceIPD.max - letterSpaceIPD.opt,
+ letterSpaceIPD.opt - letterSpaceIPD.min,
+ new LeafPosition(this, -1), false));
+ // update the information in the AreaInfo, adding one more letter space
+ AreaInfo ai = (AreaInfo) vecAreaInfo.get(vecAreaInfo.size() - 1);
+ ai.iLScount ++;
+ }
+ iNextStart = iTempStart;
}
} // end of while
setFinished(true);
}
}
- public KnuthElement addALetterSpaceTo(KnuthElement element) {
- LeafPosition pos = (LeafPosition) element.getPosition();
+ public List addALetterSpaceTo(List oldList) {
+ // old list contains only a box, or the sequence: box penalty glue box;
+ // look at the Position stored in the first element in oldList
+ // which is always a box
+ ListIterator oldListIterator = oldList.listIterator();
+ LeafPosition pos = (LeafPosition) ((KnuthBox) oldListIterator.next()).getPosition();
AreaInfo ai = (AreaInfo) vecAreaInfo.get(pos.getLeafPos());
ai.iLScount ++;
ai.ipdArea.add(letterSpaceIPD);
- if (letterSpaceIPD.min == letterSpaceIPD.max) {
- return new KnuthBox(ai.ipdArea.opt, lead, total, middle, pos, false);
+ if (BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0) {
+ // the last character could be used as a line break
+ // append new elements to oldList
+ oldListIterator = oldList.listIterator(oldList.size());
+ oldListIterator.add(new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true,
+ new LeafPosition(this, -1), false));
+ oldListIterator.add(new KnuthGlue(letterSpaceIPD.opt,
+ letterSpaceIPD.max - letterSpaceIPD.opt,
+ letterSpaceIPD.opt - letterSpaceIPD.min,
+ new LeafPosition(this, -1), false));
+ } else if (letterSpaceIPD.min == letterSpaceIPD.max) {
+ // constant letter space: replace the box
+ oldListIterator.set(new KnuthInlineBox(ai.ipdArea.opt, lead, total, middle, pos, false));
} else {
- return new KnuthGlue(ai.iLScount * letterSpaceIPD.opt,
- ai.iLScount * (letterSpaceIPD.max - letterSpaceIPD.opt),
- ai.iLScount * (letterSpaceIPD.opt - letterSpaceIPD.min),
- new LeafPosition(this, -1), true);
+ // adjustable letter space: replace the glue
+ oldListIterator.next(); // this would return the penalty element
+ oldListIterator.next(); // this would return the glue element
+ oldListIterator.set(new KnuthGlue(ai.iLScount * letterSpaceIPD.opt,
+ ai.iLScount * (letterSpaceIPD.max - letterSpaceIPD.opt),
+ ai.iLScount * (letterSpaceIPD.opt - letterSpaceIPD.min),
+ new LeafPosition(this, -1), true));
}
+ return oldList;
}
public void hyphenate(Position pos, HyphContext hc) {
}
public LinkedList getChangedKnuthElements(List oldList,
- int flaggedPenalty,
+ /*int flaggedPenalty,*/
int alignment) {
if (isFinished()) {
return null;
AreaInfo ai = (AreaInfo) vecAreaInfo.get(iReturnedIndex);
if (ai.iWScount == 0) {
// ai refers either to a word or a word fragment
+
+ // if the last character is '-' or '/' and the next character is not a space
+ // one of the letter spaces must be represented using a penalty and a glue,
+ // and its width must be subtracted
+ if (BREAK_CHARS.indexOf(textArray[ai.iBreakIndex - 1]) >= 0
+ && ai.iLScount == (ai.iBreakIndex - ai.iStartIndex)) {
+ ai.ipdArea.add(new MinOptMax(-letterSpaceIPD.min, -letterSpaceIPD.opt, -letterSpaceIPD.max));
+ }
if (letterSpaceIPD.min == letterSpaceIPD.max) {
returnList.add
- (new KnuthBox(ai.ipdArea.opt, lead, total, middle,
+ (new KnuthInlineBox(ai.ipdArea.opt, lead, total, middle,
new LeafPosition(this, iReturnedIndex), false));
} else {
returnList.add
- (new KnuthBox(ai.ipdArea.opt
+ (new KnuthInlineBox(ai.ipdArea.opt
- ai.iLScount * letterSpaceIPD.opt,
lead, total, middle,
new LeafPosition(this, iReturnedIndex), false));
ai.iLScount * (letterSpaceIPD.opt - letterSpaceIPD.min),
new LeafPosition(this, -1), true));
returnList.add
- (new KnuthBox(0, 0, 0, 0,
+ (new KnuthInlineBox(0, 0, 0, 0,
new LeafPosition(this, -1), true));
}
if (ai.bHyphenated) {
returnList.add
- (new KnuthPenalty(hyphIPD, flaggedPenalty, true,
+ (new KnuthPenalty(hyphIPD, KnuthPenalty.FLAGGED_PENALTY, true,
new LeafPosition(this, -1), false));
}
+ // if the last character is '-' or '/', it could be used as a line end;
+ // add a flagged penalty element and a glue element representing a suppressible
+ // letter space if the next character is not a space
+ if (BREAK_CHARS.indexOf(textArray[ai.iBreakIndex - 1]) >= 0
+ && ai.iLScount == (ai.iBreakIndex - ai.iStartIndex)) {
+ returnList.add
+ (new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true,
+ new LeafPosition(this, -1), false));
+ returnList.add
+ (new KnuthGlue(letterSpaceIPD.opt,
+ letterSpaceIPD.max - letterSpaceIPD.opt,
+ letterSpaceIPD.opt - letterSpaceIPD.min,
+ new LeafPosition(this, -1), false));
+ }
iReturnedIndex ++;
} else {
// ai refers to a space
+ if (textArray[ai.iStartIndex] == NBSPACE) {
+ returnList.add
+ (new KnuthPenalty(0, KnuthElement.INFINITE, false,
+ new LeafPosition(this, -1),
+ false));
+ }
switch (alignment) {
case EN_CENTER :
returnList.add
- 6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
new LeafPosition(this, -1), true));
returnList.add
- (new KnuthBox(0, 0, 0, 0,
+ (new KnuthInlineBox(0, 0, 0, 0,
new LeafPosition(this, -1), true));
returnList.add
(new KnuthPenalty(0, KnuthElement.INFINITE, false,
import org.apache.fop.layoutmgr.BreakPoss;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.BreakPossPosIter;
import org.apache.fop.layoutmgr.Position;
+import org.apache.fop.layoutmgr.NonLeafPosition;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.traits.MinOptMax;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
+import java.util.LinkedList;
/**
* LayoutManager for a table-cell FO.
private int xoffset;
private int itemIPD;
+ private static class StackingIter extends PositionIterator {
+ StackingIter(Iterator parentIter) {
+ super(parentIter);
+ }
+
+ protected LayoutManager getLM(Object nextObj) {
+ return ((Position) nextObj).getLM();
+ }
+
+ protected Position getPos(Object nextObj) {
+ return ((Position) nextObj);
+ }
+ }
+
/**
* Create a new Cell layout manager.
*/
xoffset = off;
}
+ public LinkedList getChangedKnuthElements(List oldList, int alignment) {
+ //log.debug(" Item.getChanged>");
+ return super.getChangedKnuthElements(oldList, alignment);
+ }
+
/**
* Add the areas for the break points.
* The list item contains block stacking layout managers
int nameId = fobj.getNameId();
if (nameId == FO_LIST_ITEM_LABEL) {
- addID(((ListItemLabel) fobj).getId());
+ getPSLM().addIDToPage(((ListItemLabel) fobj).getId());
} else if (nameId == FO_LIST_ITEM_BODY) {
- addID(((ListItemBody) fobj).getId());
+ getPSLM().addIDToPage(((ListItemBody) fobj).getId());
}
- LayoutManager childLM;
- int iStartPos = 0;
+ LayoutManager childLM = null;
LayoutContext lc = new LayoutContext(0);
+ LayoutManager firstLM = null;
+ LayoutManager lastLM = null;
+
+ // "unwrap" the NonLeafPositions stored in parentIter
+ // and put them in a new list;
+ LinkedList positionList = new LinkedList();
+ Position pos;
while (parentIter.hasNext()) {
- LeafPosition lfp = (LeafPosition) parentIter.next();
- // Add the block areas to Area
- PositionIterator breakPosIter =
- new BreakPossPosIter(childBreaks, iStartPos,
- lfp.getLeafPos() + 1);
- iStartPos = lfp.getLeafPos() + 1;
- while ((childLM = breakPosIter.getNextChildLM()) != null) {
- childLM.addAreas(breakPosIter, lc);
+ pos = (Position)parentIter.next();
+ if (pos instanceof NonLeafPosition) {
+ // pos was created by a child of this ListBlockLM
+ positionList.add(((NonLeafPosition) pos).getPosition());
+ lastLM = ((NonLeafPosition) pos).getPosition().getLM();
+ if (firstLM == null) {
+ firstLM = lastLM;
+ }
+ } else {
+ // pos was created by this ListBlockLM, so it must be ignored
}
}
+ StackingIter childPosIter = new StackingIter(positionList.listIterator());
+ while ((childLM = childPosIter.getNextChildLM()) != null) {
+ // Add the block areas to Area
+ lc.setFlags(LayoutContext.FIRST_AREA, childLM == firstLM);
+ lc.setFlags(LayoutContext.LAST_AREA, childLM == lastLM);
+ lc.setStackLimit(layoutContext.getStackLimit());
+ childLM.addAreas(childPosIter, lc);
+ }
+
/*
if (borderProps != null) {
TraitSetter.addBorders(curBlockArea, borderProps);
import org.apache.fop.layoutmgr.BreakPoss;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.BreakPossPosIter;
import org.apache.fop.layoutmgr.Position;
+import org.apache.fop.layoutmgr.NonLeafPosition;
import org.apache.fop.layoutmgr.TraitSetter;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.traits.SpaceVal;
import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
/**
private Block curBlockArea;
- private int referenceIPD = 0;
-
private List bodyBreaks = new ArrayList();
//TODO space-before|after: handle space-resolution rules
private MinOptMax spaceBefore;
private MinOptMax spaceAfter;
+ private static class StackingIter extends PositionIterator {
+ StackingIter(Iterator parentIter) {
+ super(parentIter);
+ }
+
+ protected LayoutManager getLM(Object nextObj) {
+ return ((Position) nextObj).getLM();
+ }
+
+ protected Position getPos(Object nextObj) {
+ return ((Position) nextObj);
+ }
+ }
+
/*
private class SectionPosition extends LeafPosition {
protected List list;
}*/
/**
- * Create a new table layout manager.
+ * Create a new list block layout manager.
* @param node list-block to create the layout manager for
*/
public ListBlockLayoutManager(ListBlock node) {
return null;
}
+ public LinkedList getChangedKnuthElements(List oldList, int alignment) {
+ //log.debug("LBLM.getChangedKnuthElements>");
+ return super.getChangedKnuthElements(oldList, alignment);
+ }
+
/**
* The table area is a reference area that contains areas for
* columns, bodies, rows and the contents are in cells.
addBlockSpacing(adjust, spaceBefore);
spaceBefore = null;
- addID(fobj.getId());
+ getPSLM().addIDToPage(fobj.getId());
// the list block contains areas stacked from each list item
- //int listHeight = 0;
-
- LayoutManager childLM;
- int iStartPos = 0;
+ LayoutManager childLM = null;
LayoutContext lc = new LayoutContext(0);
+ LayoutManager firstLM = null;
+ LayoutManager lastLM = null;
+
+ // "unwrap" the NonLeafPositions stored in parentIter
+ // and put them in a new list;
+ LinkedList positionList = new LinkedList();
+ Position pos;
while (parentIter.hasNext()) {
- LeafPosition lfp = (LeafPosition) parentIter.next();
- // Add the block areas to Area
- PositionIterator breakPosIter = new BreakPossPosIter(
- bodyBreaks, iStartPos, lfp.getLeafPos() + 1);
- iStartPos = lfp.getLeafPos() + 1;
- while ((childLM = (LayoutManager)breakPosIter.getNextChildLM()) != null) {
- childLM.addAreas(breakPosIter, lc);
+ pos = (Position)parentIter.next();
+ if (pos instanceof NonLeafPosition
+ && ((NonLeafPosition) pos).getPosition().getLM() != this) {
+ // pos was created by a child of this ListBlockLM
+ positionList.add(((NonLeafPosition) pos).getPosition());
+ lastLM = ((NonLeafPosition) pos).getPosition().getLM();
+ if (firstLM == null) {
+ firstLM = lastLM;
+ }
}
}
+ StackingIter childPosIter = new StackingIter(positionList.listIterator());
+ while ((childLM = childPosIter.getNextChildLM()) != null) {
+ // Add the block areas to Area
+ lc.setFlags(LayoutContext.FIRST_AREA, childLM == firstLM);
+ lc.setFlags(LayoutContext.LAST_AREA, childLM == lastLM);
+ lc.setStackLimit(layoutContext.getStackLimit());
+ childLM.addAreas(childPosIter, lc);
+ }
flush();
import org.apache.fop.layoutmgr.BreakPoss;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.BreakPossPosIter;
import org.apache.fop.layoutmgr.Position;
+import org.apache.fop.layoutmgr.NonLeafPosition;
import org.apache.fop.layoutmgr.TraitSetter;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.KnuthBox;
+import org.apache.fop.layoutmgr.KnuthPenalty;
+import org.apache.fop.layoutmgr.KnuthPossPosIter;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.traits.MinOptMax;
import org.apache.fop.traits.SpaceVal;
-import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;
+import java.util.LinkedList;
+import java.util.ListIterator;
/**
* LayoutManager for a list-item FO.
private Item label;
private Item body;
- private int referenceIPD = 0;
-
private Block curBlockArea = null;
- //private List cellList = null;
+ private LinkedList labelList = null;
+ private LinkedList bodyList = null;
+
private int listItemHeight;
//TODO space-before|after: handle space-resolution rules
}
}
+ private class ListItemPosition extends Position {
+ private int iLabelFirstIndex;
+ private int iLabelLastIndex;
+ private int iBodyFirstIndex;
+ private int iBodyLastIndex;
+
+ public ListItemPosition(LayoutManager lm, int labelFirst, int labelLast,
+ int bodyFirst, int bodyLast) {
+ super(lm);
+ iLabelFirstIndex = labelFirst;
+ iLabelLastIndex = labelLast;
+ iBodyFirstIndex = bodyFirst;
+ iBodyLastIndex = bodyLast;
+ }
+
+ public int getLabelFirstIndex() {
+ return iLabelFirstIndex;
+ }
+
+ public int getLabelLastIndex() {
+ return iLabelLastIndex;
+ }
+
+ public int getBodyFirstIndex() {
+ return iBodyFirstIndex;
+ }
+
+ public int getBodyLastIndex() {
+ return iBodyLastIndex;
+ }
+ }
+
/**
* Create a new list item layout manager.
* @param node list-item to create the layout manager for
return breakPoss;
}
+ /**
+ * @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(org.apache.fop.layoutmgr.LayoutContext, int)
+ */
+ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+ referenceIPD = context.getRefIPD();
+
+ // label
+ labelList = label.getNextKnuthElements(context, alignment);
+
+ // body
+ bodyList = body.getNextKnuthElements(context, alignment);
+
+ // create a combined list
+ LinkedList returnedList = getCombinedKnuthElementsForListItem(labelList, bodyList);
+
+ // "wrap" the Position inside each element
+ LinkedList tempList = returnedList;
+ KnuthElement tempElement;
+ returnedList = new LinkedList();
+ ListIterator listIter = tempList.listIterator();
+ while (listIter.hasNext()) {
+ tempElement = (KnuthElement)listIter.next();
+ tempElement.setPosition(new NonLeafPosition(this, tempElement.getPosition()));
+ returnedList.add(tempElement);
+ }
+
+ setFinished(true);
+ return returnedList;
+ }
+
+ private LinkedList getCombinedKnuthElementsForListItem(LinkedList labelElements,
+ LinkedList bodyElements) {
+ //Copy elements to array lists to improve element access performance
+ List[] elementLists = {new ArrayList(labelElements),
+ new ArrayList(bodyElements)};
+ int[] fullHeights = {calcItemHeightFromContents(elementLists[0]),
+ calcItemHeightFromContents(elementLists[1])};
+ int[] partialHeights = {0, 0};
+ int[] start = {-1, -1};
+ int[] end = {-1, -1};
+
+ int totalHeight = Math.max(fullHeights[0], fullHeights[1]);
+ int step;
+ int addedBoxHeight = 0;
+
+ LinkedList returnList = new LinkedList();
+ while ((step = getNextStep(elementLists, start, end, partialHeights))
+ > 0) {
+ // compute penalty height and box height
+ int penaltyHeight = step
+ + getMaxRemainingHeight(fullHeights, partialHeights)
+ - totalHeight;
+ int boxHeight = step - addedBoxHeight - penaltyHeight;
+
+ // add the new elements
+ addedBoxHeight += boxHeight;
+ ListItemPosition stepPosition = new ListItemPosition(this,
+ start[0], end[0], start[1], end[1]);
+ returnList.add(new KnuthBox(boxHeight, stepPosition, false));
+ if (addedBoxHeight < totalHeight) {
+ returnList.add(new KnuthPenalty(penaltyHeight, 0, false, stepPosition, false));
+ }
+ }
+
+ return returnList;
+ }
+
+ private int calcItemHeightFromContents(List elements, int start, int end) {
+ ListIterator iter = elements.listIterator(start);
+ int count = end - start + 1;
+ int len = 0;
+ while (iter.hasNext()) {
+ KnuthElement el = (KnuthElement)iter.next();
+ if (el.isBox()) {
+ len += el.getW();
+ } else if (el.isGlue()) {
+ len += el.getW();
+ } else {
+ log.debug("Ignoring penalty: " + el);
+ //ignore penalties
+ }
+ count--;
+ if (count == 0) {
+ break;
+ }
+ }
+ return len;
+ }
+
+ private int calcItemHeightFromContents(List elements) {
+ return calcItemHeightFromContents(elements, 0, elements.size() - 1);
+ }
+
+ private int getNextStep(List[] elementLists, int[] start, int[] end, int[] partialHeights) {
+ // backup of partial heights
+ int[] backupHeights = {partialHeights[0], partialHeights[1]};
+
+ // set starting points
+ start[0] = end[0] + 1;
+ start[1] = end[1] + 1;
+
+ // get next possible sequence for label and body
+ int seqCount = 0;
+ for (int i = 0; i < start.length; i++) {
+ while (end[i] + 1 < elementLists[i].size()) {
+ end[i]++;
+ KnuthElement el = (KnuthElement)elementLists[i].get(end[i]);
+ if (el.isPenalty()) {
+ if (el.getP() < KnuthElement.INFINITE) {
+ //First legal break point
+ break;
+ }
+ } else if (el.isGlue()) {
+ KnuthElement prev = (KnuthElement)elementLists[i].get(end[i] - 1);
+ if (prev.isBox()) {
+ //Second legal break point
+ break;
+ }
+ partialHeights[i] += el.getW();
+ } else {
+ partialHeights[i] += el.getW();
+ }
+ }
+ if (end[i] < start[i]) {
+ partialHeights[i] = backupHeights[i];
+ } else {
+ seqCount++;
+ }
+ }
+ if (seqCount == 0) {
+ return 0;
+ }
+
+ // determine next step
+ int step;
+ if (backupHeights[0] == 0 && backupHeights[1] == 0) {
+ // this is the first step: choose the maximum increase, so that
+ // the smallest area in the first page will contain at least
+ // a label area and a body area
+ step = Math.max((end[0] >= start[0] ? partialHeights[0] : Integer.MIN_VALUE),
+ (end[1] >= start[1] ? partialHeights[1] : Integer.MIN_VALUE));
+ } else {
+ // this is not the first step: choose the minimum increase
+ step = Math.min((end[0] >= start[0] ? partialHeights[0] : Integer.MAX_VALUE),
+ (end[1] >= start[1] ? partialHeights[1] : Integer.MAX_VALUE));
+ }
+
+ // reset bigger-than-step sequences
+ for (int i = 0; i < partialHeights.length; i++) {
+ if (partialHeights[i] > step) {
+ partialHeights[i] = backupHeights[i];
+ end[i] = start[i] - 1;
+ }
+ }
+
+ return step;
+ }
+
+ private int getMaxRemainingHeight(int[] fullHeights, int[] partialHeights) {
+ return Math.max(fullHeights[0] - partialHeights[0],
+ fullHeights[1] - partialHeights[1]);
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.LayoutManager#getChangedKnuthElements(java.util.List, int)
+ */
+ public LinkedList getChangedKnuthElements(List oldList, int alignment) {
+/*LF*/ //log.debug(" LILM.getChanged> label");
+ // label
+ labelList = label.getChangedKnuthElements(labelList, alignment);
+
+/*LF*/ //log.debug(" LILM.getChanged> body");
+ // body
+ // "unwrap" the Positions stored in the elements
+ ListIterator oldListIterator = oldList.listIterator();
+ KnuthElement oldElement = null;
+ while (oldListIterator.hasNext()) {
+ oldElement = (KnuthElement)oldListIterator.next();
+ Position innerPosition = ((NonLeafPosition) oldElement.getPosition()).getPosition();
+/*LF*/ //System.out.println(" BLM> unwrapping: " + (oldElement.isBox() ? "box " : (oldElement.isGlue() ? "glue " : "penalty")) + " creato da " + oldElement.getLayoutManager().getClass().getName());
+/*LF*/ //System.out.println(" BLM> unwrapping: " + oldElement.getPosition().getClass().getName());
+ if (innerPosition != null) {
+ // oldElement was created by a descendant of this BlockLM
+ oldElement.setPosition(innerPosition);
+ } else {
+ // thisElement was created by this BlockLM
+ // modify its position in order to recognize it was not created
+ // by a child
+ oldElement.setPosition(new Position(this));
+ }
+ }
+
+ LinkedList returnedList = body.getChangedKnuthElements(oldList, alignment);
+ // "wrap" the Position inside each element
+ LinkedList tempList = returnedList;
+ KnuthElement tempElement;
+ returnedList = new LinkedList();
+ ListIterator listIter = tempList.listIterator();
+ while (listIter.hasNext()) {
+ tempElement = (KnuthElement)listIter.next();
+ tempElement.setPosition(new NonLeafPosition(this, tempElement.getPosition()));
+ returnedList.add(tempElement);
+ }
+
+ return returnedList;
+ }
+
/**
* Add the areas for the break points.
* This sets the offset of each cell as it is added.
addBlockSpacing(adjust, spaceBefore);
spaceBefore = null;
- addID(fobj.getId());
+ getPSLM().addIDToPage(fobj.getId());
- Item childLM;
LayoutContext lc = new LayoutContext(0);
+
+ // "unwrap" the NonLeafPositions stored in parentIter
+ LinkedList positionList = new LinkedList();
+ Position pos;
while (parentIter.hasNext()) {
- ItemPosition lfp = (ItemPosition) parentIter.next();
- // Add the block areas to Area
+ pos = (Position) parentIter.next();
+ if (pos instanceof NonLeafPosition) {
+ // pos contains a ListItemPosition created by this ListBlockLM
+ positionList.add(((NonLeafPosition) pos).getPosition());
+ }
+ }
- for (Iterator iter = lfp.cellBreaks.iterator(); iter.hasNext();) {
- List cellsbr = (List)iter.next();
- PositionIterator breakPosIter;
- breakPosIter = new BreakPossPosIter(cellsbr, 0, cellsbr.size());
+ // use the first and the last ListItemPosition to determine the
+ // corresponding indexes in the original labelList and bodyList
+ int labelFirstIndex = ((ListItemPosition) positionList.getFirst()).getLabelFirstIndex();
+ int labelLastIndex = ((ListItemPosition) positionList.getLast()).getLabelLastIndex();
+ int bodyFirstIndex = ((ListItemPosition) positionList.getFirst()).getBodyFirstIndex();
+ int bodyLastIndex = ((ListItemPosition) positionList.getLast()).getBodyLastIndex();
+
+ // add label areas
+ if (labelFirstIndex <= labelLastIndex) {
+ KnuthPossPosIter labelIter = new KnuthPossPosIter(labelList,
+ labelFirstIndex, labelLastIndex + 1);
+ lc.setFlags(LayoutContext.FIRST_AREA, layoutContext.isFirstArea());
+ lc.setFlags(LayoutContext.LAST_AREA, layoutContext.isLastArea());
+ // TO DO: use the right stack limit for the label
+ lc.setStackLimit(layoutContext.getStackLimit());
+ label.addAreas(labelIter, lc);
+ }
- while ((childLM = (Item)breakPosIter.getNextChildLM()) != null) {
- childLM.addAreas(breakPosIter, lc);
- }
- }
+ // reset the area bpd after adding the label areas and before adding the body areas
+ int savedBPD = 0;
+ if (labelFirstIndex <= labelLastIndex
+ && bodyFirstIndex <= bodyLastIndex) {
+ savedBPD = curBlockArea.getBPD();
+ curBlockArea.setBPD(0);
+ }
+
+ // add body areas
+ if (bodyFirstIndex <= bodyLastIndex) {
+ KnuthPossPosIter bodyIter = new KnuthPossPosIter(bodyList,
+ bodyFirstIndex, bodyLastIndex + 1);
+ lc.setFlags(LayoutContext.FIRST_AREA, layoutContext.isFirstArea());
+ lc.setFlags(LayoutContext.LAST_AREA, layoutContext.isLastArea());
+ // TO DO: use the right stack limit for the body
+ lc.setStackLimit(layoutContext.getStackLimit());
+ body.addAreas(bodyIter, lc);
}
- curBlockArea.setBPD(listItemHeight);
+ // after adding body areas, set the maximum area bpd
+ if (curBlockArea.getBPD() < savedBPD) {
+ curBlockArea.setBPD(savedBPD);
+ }
flush();
+++ /dev/null
-/*
- * Copyright 1999-2005 The Apache Software Foundation.
- *
- * Licensed 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.table;
-
-import java.util.List;
-
-import org.apache.fop.fo.flow.TableBody;
-import org.apache.fop.layoutmgr.LayoutManager;
-import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
-import org.apache.fop.layoutmgr.LeafPosition;
-import org.apache.fop.layoutmgr.BreakPoss;
-import org.apache.fop.layoutmgr.LayoutContext;
-import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.BreakPossPosIter;
-import org.apache.fop.layoutmgr.Position;
-import org.apache.fop.layoutmgr.TraitSetter;
-import org.apache.fop.area.Area;
-import org.apache.fop.area.Block;
-import org.apache.fop.traits.MinOptMax;
-
-/**
- * LayoutManager for a table-header, table-footer and table body FO.
- * These fo objects have either rows or cells underneath.
- * Cells are organised into rows.
- */
-public class Body extends BlockStackingLayoutManager {
- private TableBody fobj;
-
- private List columns;
-
- private int xoffset;
- private int yoffset;
- private int bodyHeight;
-
- //private Block curBlockArea;
-
- private List childBreaks = new java.util.ArrayList();
-
- /**
- * Create a new body layout manager.
- * @param node the table-body FO
- */
- public Body(TableBody node) {
- super(node);
- fobj = node;
- }
-
- /** @return the table-body|header|footer FO */
- public TableBody getFObj() {
- return this.fobj;
- }
-
- /**
- * Set the columns from the table.
- *
- * @param cols the list of columns used for this body
- */
- public void setColumns(List cols) {
- columns = cols;
- }
-
- /**
- * Breaks for this layout manager are of the form of before
- * or after a row and inside a row.
- *
- * @param context the layout context for finding the breaks
- * @return the next break possibility
- */
- public BreakPoss getNextBreakPoss(LayoutContext context) {
- Row curLM; // currently active LM
-
- MinOptMax stackSize = new MinOptMax();
- BreakPoss lastPos = null;
-
- while ((curLM = (Row)getChildLM()) != null) {
- // Make break positions
- // Set up a LayoutContext
- int ipd = context.getRefIPD();
- BreakPoss bp;
-
- LayoutContext childLC = new LayoutContext(0);
- childLC.setStackLimit(
- MinOptMax.subtract(context.getStackLimit(),
- stackSize));
- childLC.setRefIPD(ipd);
-
- curLM.setColumns(columns);
-
- boolean over = false;
-
- while (!curLM.isFinished()) {
- if ((bp = curLM.getNextBreakPoss(childLC)) != null) {
- if (stackSize.opt + bp.getStackingSize().opt > context.getStackLimit().max) {
- // reset to last break
- if (lastPos != null) {
- LayoutManager lm = lastPos.getLayoutManager();
- lm.resetPosition(lastPos.getPosition());
- if (lm != curLM) {
- curLM.resetPosition(null);
- }
- } else {
- curLM.resetPosition(null);
- }
- over = true;
- break;
- }
- stackSize.add(bp.getStackingSize());
- lastPos = bp;
- childBreaks.add(bp);
-
- if (bp.nextBreakOverflows()) {
- over = true;
- break;
- }
-
- childLC.setStackLimit(MinOptMax.subtract(
- context.getStackLimit(), stackSize));
- }
- }
- BreakPoss breakPoss = new BreakPoss(
- new LeafPosition(this, childBreaks.size() - 1));
- if (over) {
- breakPoss.setFlag(BreakPoss.NEXT_OVERFLOWS, true);
- }
- breakPoss.setStackingSize(stackSize);
- return breakPoss;
- }
-
- setFinished(true);
- return null;
- }
-
- /**
- * Set the x offset of this body within the table.
- * This is used to set the row offsets.
- * @param off the x offset
- */
- public void setXOffset(int off) {
- xoffset = off;
- }
-
- /**
- * Set the y offset of this body within the table.
- * This is used to set the row offsets.
- *
- * @param off the y offset position
- */
- public void setYOffset(int off) {
- yoffset = off;
- }
-
- /**
- * Add the areas for the break points.
- * This sets the offset of each row as it is added.
- *
- * @param parentIter the position iterator
- * @param layoutContext the layout context for adding areas
- */
- public void addAreas(PositionIterator parentIter,
- LayoutContext layoutContext) {
- getParentArea(null);
-
- Row childLM;
- int iStartPos = 0;
- LayoutContext lc = new LayoutContext(0);
- int rowoffset = 0;
- while (parentIter.hasNext()) {
- LeafPosition lfp = (LeafPosition) parentIter.next();
- // Add the block areas to Area
- PositionIterator breakPosIter
- = new BreakPossPosIter(childBreaks, iStartPos,
- lfp.getLeafPos() + 1);
- iStartPos = lfp.getLeafPos() + 1;
- int lastheight = 0;
- while ((childLM = (Row)breakPosIter.getNextChildLM()) != null) {
- childLM.setXOffset(xoffset);
- childLM.setYOffset(yoffset + rowoffset);
- childLM.addAreas(breakPosIter, lc);
- lastheight = childLM.getRowHeight();
- }
- rowoffset += lastheight;
- }
- bodyHeight = rowoffset;
-
- flush();
-
- childBreaks.clear();
- //curBlockArea = null;
- }
-
- /**
- * Get the body height of the body after adjusting.
- * Should only be called after adding the body areas.
- *
- * @return the body height of this body
- */
- public int getBodyHeight() {
- return bodyHeight;
- }
-
- /**
- * Return an Area which can contain the passed childArea. The childArea
- * may not yet have any content, but it has essential traits set.
- * In general, if the LayoutManager already has an Area it simply returns
- * it. Otherwise, it makes a new Area of the appropriate class.
- * It gets a parent area for its area by calling its parent LM.
- * Finally, based on the dimensions of the parent area, it initializes
- * its own area. This includes setting the content IPD and the maximum
- * BPD.
- *
- * @param childArea the child area
- * @return the parent are of the child
- */
- public Area getParentArea(Area childArea) {
- return parentLM.getParentArea(childArea);
- }
-
- /**
- * Add the child area.
- * The table-header, table-footer and table-body areas return
- * the areas return by the children.
- *
- * @param childArea the child area to add
- */
- public void addChildArea(Area childArea) {
- parentLM.addChildArea(childArea);
- }
-
- /**
- * Reset the position of the layout manager.
- *
- * @param resetPos the position to reset to
- */
- public void resetPosition(Position resetPos) {
- if (resetPos == null) {
- reset(null);
- }
- }
-
- /**
- * Create a body area.
- * This area has the background and width set.
- *
- * @return the new body area
- */
- public Area createColumnArea() {
- Area curBlockArea = new Block();
-
- TraitSetter.addBackground(curBlockArea, fobj.getCommonBorderPaddingBackground());
- return curBlockArea;
- }
-
-}
-
public void addAreas(PositionIterator parentIter,
LayoutContext layoutContext) {
getParentArea(null);
- addID(fobj.getId());
+ getPSLM().addIDToPage(fobj.getId());
LayoutManager childLM;
int iStartPos = 0;
import org.apache.fop.fo.flow.TableCell;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.LengthRangeProperty;
+import org.apache.fop.layoutmgr.AreaAdditionUtil;
+import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.KnuthGlue;
+import org.apache.fop.layoutmgr.KnuthPenalty;
import org.apache.fop.layoutmgr.LayoutManager;
import org.apache.fop.layoutmgr.LeafPosition;
import org.apache.fop.layoutmgr.BreakPoss;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.MinOptMaxUtil;
+import org.apache.fop.layoutmgr.NonLeafPosition;
import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.BreakPossPosIter;
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.layoutmgr.TraitSetter;
import org.apache.fop.area.Area;
import org.apache.fop.area.Block;
import org.apache.fop.area.Trait;
import org.apache.fop.traits.MinOptMax;
+import org.apache.tools.ant.taskdefs.condition.IsSet;
import java.util.ArrayList;
+import java.util.LinkedList;
import java.util.List;
/**
* LayoutManager for a table-cell FO.
* A cell contains blocks. These blocks fill the cell.
*/
-public class Cell extends BlockStackingLayoutManager {
+public class Cell extends BlockStackingLayoutManager implements BlockLevelLayoutManager {
+
private TableCell fobj;
+ private PrimaryGridUnit gridUnit;
private Block curBlockArea;
private int borderAndPaddingBPD;
private boolean emptyCell = true;
- /** List of Lists containing GridUnit instances, one List per row. */
+ /** List of Lists containing OldGridUnit instances, one List per row. */
private List rows = new java.util.ArrayList();
/**
* Create a new Cell layout manager.
* @node table-cell FO for which to create the LM
*/
- public Cell(TableCell node) {
+ public Cell(TableCell node, PrimaryGridUnit pgu) {
super(node);
fobj = node;
+ this.gridUnit = pgu;
}
/** @return the table-cell FO */
return this.fobj;
}
+ private boolean isSeparateBorderModel() {
+ return fobj.isSeparateBorderModel();
+ }
+
/**
* @see org.apache.fop.layoutmgr.AbstractLayoutManager#initProperties()
*/
borderAndPaddingBPD = 0;
borderAndPaddingBPD += fobj.getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
borderAndPaddingBPD += fobj.getCommonBorderPaddingBackground().getBorderAfterWidth(false);
- if (!fobj.isSeparateBorderModel()) {
+ if (!isSeparateBorderModel()) {
borderAndPaddingBPD /= 2;
}
borderAndPaddingBPD += fobj.getCommonBorderPaddingBackground().getPaddingBefore(false);
private int getIPIndents() {
int iIndents = 0;
- startBorderWidth = 0;
- endBorderWidth = 0;
- for (int i = 0; i < rows.size(); i++) {
- List gridUnits = (List)rows.get(i);
- startBorderWidth = Math.max(startBorderWidth,
- ((GridUnit)gridUnits.get(0)).
- effBorders.getBorderStartWidth(false));
- endBorderWidth = Math.max(endBorderWidth,
- ((GridUnit)gridUnits.get(gridUnits.size() - 1)).
- effBorders.getBorderEndWidth(false));
- }
- //iIndents += fobj.getCommonBorderPaddingBackground().getBorderStartWidth(false);
+ int[] startEndBorderWidths = gridUnit.getStartEndBorderWidths();
+ startBorderWidth += startEndBorderWidths[0];
+ endBorderWidth += startEndBorderWidths[1];
iIndents += startBorderWidth;
- //iIndents += fobj.getCommonBorderPaddingBackground().getBorderEndWidth(false);
iIndents += endBorderWidth;
- if (!fobj.isSeparateBorderModel()) {
+ if (!isSeparateBorderModel()) {
iIndents /= 2;
}
iIndents += fobj.getCommonBorderPaddingBackground().getPaddingStart(false);
return iIndents;
}
+ /**
+ * @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(org.apache.fop.layoutmgr.LayoutContext, int)
+ */
+ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+ MinOptMax stackSize = new MinOptMax();
+ MinOptMax stackLimit = new MinOptMax(context.getStackLimit());
+
+ BreakPoss lastPos = null;
+
+ referenceIPD = context.getRefIPD();
+ cellIPD = referenceIPD;
+ cellIPD -= getIPIndents();
+ if (isSeparateBorderModel()) {
+ int borderSep = fobj.getBorderSeparation().getLengthPair()
+ .getIPD().getLength().getValue();
+ cellIPD -= borderSep;
+ }
+
+ LinkedList returnedList = null;
+ LinkedList contentList = new LinkedList();
+ LinkedList returnList = new LinkedList();
+ Position returnPosition = new NonLeafPosition(this, null);
+
+ BlockLevelLayoutManager curLM; // currently active LM
+ BlockLevelLayoutManager prevLM = null; // previously active LM
+ while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
+ LayoutContext childLC = new LayoutContext(0);
+ // curLM is a ?
+ childLC.setStackLimit(MinOptMax.subtract(context
+ .getStackLimit(), stackLimit));
+ childLC.setRefIPD(cellIPD);
+
+ // get elements from curLM
+ returnedList = curLM.getNextKnuthElements(childLC, alignment);
+ if (returnedList.size() == 1
+ && ((KnuthElement) returnedList.getFirst()).isPenalty()
+ && ((KnuthPenalty) returnedList.getFirst()).getP() == -KnuthElement.INFINITE) {
+ // a descendant of this block has break-before
+ if (returnList.size() == 0) {
+ // the first child (or its first child ...) has
+ // break-before;
+ // all this block, including space before, will be put in
+ // the
+ // following page
+ }
+ contentList.addAll(returnedList);
+
+ // "wrap" the Position inside each element
+ // moving the elements from contentList to returnList
+ returnedList = new LinkedList();
+ wrapPositionElements(contentList, returnList);
+
+ return returnList;
+ } else {
+ if (prevLM != null) {
+ // there is a block handled by prevLM
+ // before the one handled by curLM
+ if (mustKeepTogether()
+ || prevLM.mustKeepWithNext()
+ || curLM.mustKeepWithPrevious()) {
+ // add an infinite penalty to forbid a break between
+ // blocks
+ contentList.add(new KnuthPenalty(0,
+ KnuthElement.INFINITE, false,
+ new Position(this), false));
+ } else if (!((KnuthElement) contentList.getLast()).isGlue()) {
+ // add a null penalty to allow a break between blocks
+ contentList.add(new KnuthPenalty(0, 0, false,
+ new Position(this), false));
+ } else {
+ // the last element in contentList is a glue;
+ // it is a feasible breakpoint, there is no need to add
+ // a penalty
+ }
+ }
+ contentList.addAll(returnedList);
+ if (returnedList.size() == 0) {
+ //Avoid NoSuchElementException below (happens with empty blocks)
+ continue;
+ }
+ if (((KnuthElement) returnedList.getLast()).isPenalty()
+ && ((KnuthPenalty) returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+ // a descendant of this block has break-after
+ if (curLM.isFinished()) {
+ // there is no other content in this block;
+ // it's useless to add space after before a page break
+ setFinished(true);
+ }
+
+ returnedList = new LinkedList();
+ wrapPositionElements(contentList, returnList);
+
+ return returnList;
+ }
+ }
+ prevLM = curLM;
+ }
+
+ returnedList = new LinkedList();
+ wrapPositionElements(contentList, returnList);
+
+ setFinished(true);
+ return returnList;
+ }
+
/**
* Get the next break possibility for this cell.
* A cell contains blocks so there are breaks around the blocks
* @param context the layout context
* @return the next break possibility
*/
- public BreakPoss getNextBreakPoss(LayoutContext context) {
+ public BreakPoss getNextBreakPossOLDOLDOLD(LayoutContext context) {
LayoutManager curLM; // currently active LM
MinOptMax stackSize = new MinOptMax();
this.inRowIPDOffset = off;
}
+ /**
+ * Set the content height for this cell. This method is used during
+ * addAreas() stage.
+ *
+ * @param h the height of the contents of this cell
+ */
+ public void setContentHeight(int h) {
+ usedBPD = h;
+ }
+
/**
* Set the row height that contains this cell. This method is used during
* addAreas() stage.
rowHeight = h;
}
+ private int getContentHeight(int rowHeight, GridUnit gu) {
+ int bpd = rowHeight;
+ if (isSeparateBorderModel()) {
+ bpd -= gu.getPrimary().getBorders().getBorderBeforeWidth(false);
+ bpd -= gu.getPrimary().getBorders().getBorderAfterWidth(false);
+ } else {
+ bpd -= gu.getPrimary().getHalfMaxBorderWidth();
+ }
+ CommonBorderPaddingBackground cbpb
+ = gu.getCell().getCommonBorderPaddingBackground();
+ bpd -= cbpb.getPaddingBefore(false);
+ bpd -= cbpb.getPaddingAfter(false);
+ return bpd;
+ }
+
/**
* Add the areas for the break points.
* The cell contains block stacking layout managers
public void addAreas(PositionIterator parentIter,
LayoutContext layoutContext) {
getParentArea(null);
- BreakPoss bp1 = (BreakPoss)parentIter.peekNext();
- bBogus = !bp1.generatesAreas();
+ //BreakPoss bp1 = (BreakPoss)parentIter.peekNext();
+ bBogus = false;//!bp1.generatesAreas();
if (!isBogus()) {
- addID(fobj.getId());
+ getPSLM().addIDToPage(fobj.getId());
}
- if (fobj.isSeparateBorderModel()) {
+ if (isSeparateBorderModel()) {
if (!emptyCell || fobj.showEmptyCells()) {
TraitSetter.addBorders(curBlockArea, fobj.getCommonBorderPaddingBackground());
TraitSetter.addBackground(curBlockArea, fobj.getCommonBorderPaddingBackground());
}
} else {
TraitSetter.addBackground(curBlockArea, fobj.getCommonBorderPaddingBackground());
- //TODO Set these booleans right
- boolean[] outer = new boolean[] {false, false, false, false};
- if (rows.size() == 1 && ((List)rows.get(0)).size() == 1) {
+ boolean[] outer = new boolean[] {
+ gridUnit.getFlag(GridUnit.FIRST_IN_TABLE),
+ gridUnit.getFlag(GridUnit.LAST_IN_TABLE),
+ gridUnit.getFlag(GridUnit.IN_FIRST_COLUMN),
+ gridUnit.getFlag(GridUnit.IN_LAST_COLUMN)};
+ if (!gridUnit.hasSpanning()) {
//Can set the borders directly if there's no span
- CommonBorderPaddingBackground effBorders =
- ((GridUnit)((List)rows.get(0)).get(0)).effBorders;
- //TODO Next line is a temporary hack!
- TraitSetter.addCollapsingBorders(curBlockArea,
- fobj.getCommonBorderPaddingBackground(), outer);
TraitSetter.addCollapsingBorders(curBlockArea,
- effBorders, outer);
+ gridUnit.getBorders(), outer);
} else {
int dy = yoffset;
- for (int y = 0; y < rows.size(); y++) {
- List gridUnits = (List)rows.get(y);
+ for (int y = 0; y < gridUnit.getRows().size(); y++) {
+ GridUnit[] gridUnits = (GridUnit[])gridUnit.getRows().get(y);
int dx = xoffset;
int lastRowHeight = 0;
- for (int x = 0; x < gridUnits.size(); x++) {
- GridUnit gu = (GridUnit)gridUnits.get(x);
- if (!gu.effBorders.hasBorder()) {
+ for (int x = 0; x < gridUnits.length; x++) {
+ GridUnit gu = gridUnits[x];
+ if (!gu.hasBorders()) {
continue;
}
Block block = new Block();
block.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
block.setPositioning(Block.ABSOLUTE);
- block.setBPD(gu.row.getRowHeight());
- lastRowHeight = gu.row.getRowHeight();
- int ipd = gu.column.getWidth().getValue();
- int borderStartWidth = gu.effBorders.getBorderStartWidth(false) / 2;
+
+ int bpd = getContentHeight(rowHeight, gu);
+ if (isSeparateBorderModel()) {
+ bpd += (gu.getBorders().getBorderBeforeWidth(false));
+ bpd += (gu.getBorders().getBorderAfterWidth(false));
+ } else {
+ bpd += gridUnit.getHalfMaxBeforeBorderWidth()
+ - (gu.getBorders().getBorderBeforeWidth(false) / 2);
+ bpd += gridUnit.getHalfMaxAfterBorderWidth()
+ - (gu.getBorders().getBorderAfterWidth(false) / 2);
+ }
+ block.setBPD(bpd);
+ //TODO This needs to be fixed for row spanning
+ lastRowHeight = rowHeight;
+ int ipd = gu.getColumn().getColumnWidth().getValue();
+ int borderStartWidth = gu.getBorders().getBorderStartWidth(false) / 2;
ipd -= borderStartWidth;
- ipd -= gu.effBorders.getBorderEndWidth(false) / 2;
+ ipd -= gu.getBorders().getBorderEndWidth(false) / 2;
block.setIPD(ipd);
block.setXOffset(dx + borderStartWidth);
- block.setYOffset(dy);
- TraitSetter.addCollapsingBorders(block, gu.effBorders, outer);
+ int halfCollapsingBorderHeight = 0;
+ if (!isSeparateBorderModel()) {
+ halfCollapsingBorderHeight +=
+ gu.getBorders().getBorderBeforeWidth(false) / 2;
+ }
+ block.setYOffset(dy - halfCollapsingBorderHeight);
+ TraitSetter.addCollapsingBorders(block, gu.getBorders(), outer);
parentLM.addChildArea(block);
- dx += gu.column.getWidth().getValue();
+ dx += gu.getColumn().getColumnWidth().getValue();
}
dy += lastRowHeight;
}
}
}
- LayoutManager childLM;
- int iStartPos = 0;
- LayoutContext lc = new LayoutContext(0);
- while (parentIter.hasNext()) {
- LeafPosition lfp = (LeafPosition) parentIter.next();
- // Add the block areas to Area
- PositionIterator breakPosIter =
- new BreakPossPosIter(childBreaks, iStartPos,
- lfp.getLeafPos() + 1);
- iStartPos = lfp.getLeafPos() + 1;
- while ((childLM = breakPosIter.getNextChildLM()) != null) {
- childLM.addAreas(breakPosIter, lc);
- }
- }
-
+ AreaAdditionUtil.addAreas(parentIter, layoutContext);
- int contentBPD = rowHeight;
- contentBPD -= borderAndPaddingBPD;
+ int contentBPD = getContentHeight(rowHeight, gridUnit);
curBlockArea.setBPD(contentBPD);
flush();
curBlockArea.setPositioning(Block.ABSOLUTE);
int indent = 0;
indent += startBorderWidth;
- if (!fobj.isSeparateBorderModel()) {
+ if (!isSeparateBorderModel()) {
indent /= 2;
}
indent += fobj.getCommonBorderPaddingBackground().getPaddingStart(false);
// set position
int halfBorderSep = 0;
- if (fobj.isSeparateBorderModel()) {
+ if (isSeparateBorderModel()) {
halfBorderSep = fobj.getBorderSeparation().getLengthPair()
.getIPD().getLength().getValue() / 2;
}
- int halfCollapsingBorderHeight = 0;
- if (!fobj.isSeparateBorderModel()) {
- halfCollapsingBorderHeight +=
- fobj.getCommonBorderPaddingBackground().getBorderBeforeWidth(false) / 2;
+ int borderAdjust = 0;
+ if (!isSeparateBorderModel()) {
+ if (gridUnit.hasSpanning()) {
+ borderAdjust -= gridUnit.getHalfMaxBeforeBorderWidth();
+ } else {
+ borderAdjust += gridUnit.getHalfMaxBeforeBorderWidth();
+ }
+ } else {
+ //borderAdjust += gridUnit.getBorders().getBorderBeforeWidth(false);
}
curBlockArea.setXOffset(xoffset + inRowIPDOffset + halfBorderSep + indent);
- curBlockArea.setYOffset(yoffset - halfCollapsingBorderHeight);
+ curBlockArea.setYOffset(yoffset - borderAdjust);
curBlockArea.setIPD(cellIPD);
//curBlockArea.setHeight();
}
}
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#negotiateBPDAdjustment(int, org.apache.fop.layoutmgr.KnuthElement)
+ */
+ public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#discardSpace(org.apache.fop.layoutmgr.KnuthGlue)
+ */
+ public void discardSpace(KnuthGlue spaceGlue) {
+ // TODO Auto-generated method stub
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
+ */
+ public boolean mustKeepTogether() {
+ //TODO Keeps will have to be more sophisticated sooner or later
+ return ((BlockLevelLayoutManager)getParent()).mustKeepTogether()/*
+ || !fobj.getKeepTogether().getWithinPage().isAuto()
+ || !fobj.getKeepTogether().getWithinColumn().isAuto()*/;
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
+ */
+ public boolean mustKeepWithPrevious() {
+ return false; //TODO FIX ME
+ /*
+ return !fobj.getKeepWithPrevious().getWithinPage().isAuto()
+ || !fobj.getKeepWithPrevious().getWithinColumn().isAuto();
+ */
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
+ */
+ public boolean mustKeepWithNext() {
+ return false; //TODO FIX ME
+ /*
+ return !fobj.getKeepWithNext().getWithinPage().isAuto()
+ || !fobj.getKeepWithNext().getWithinColumn().isAuto();
+ */
+ }
+
}
*/
public abstract class CollapsingBorderModel {
+ /** before side */
+ protected static final int BEFORE = CommonBorderPaddingBackground.BEFORE;
+ /** after side */
+ protected static final int AFTER = CommonBorderPaddingBackground.AFTER;
+ /** start side */
+ protected static final int START = CommonBorderPaddingBackground.START;
+ /** end side */
+ protected static final int END = CommonBorderPaddingBackground.END;
+
+ /** Flag: current grid unit is either start or end of the table. */
+ public static final int VERTICAL_START_END_OF_TABLE = 1;
+
/** Indicates that the cell is/starts in the first row being painted on a particular page */
- public static final int FIRST_ROW_IN_TABLE_PART = 1;
+ //public static final int FIRST_ROW_IN_TABLE_PART = 1;
/** Indicates that the cell is/ends in the last row being painted on a particular page */
- public static final int LAST_ROW_IN_TABLE_PART = 2;
+ //public static final int LAST_ROW_IN_TABLE_PART = 2;
/** Indicates that the cell is/starts in the first row of a body/table-header/table-footer */
- public static final int FIRST_ROW_IN_GROUP = 4;
+ //public static final int FIRST_ROW_IN_GROUP = 4;
/** Indicates that the cell is/end in the last row of a body/table-header/table-footer */
- public static final int LAST_ROW_IN_GROUP = 8;
+ //public static final int LAST_ROW_IN_GROUP = 8;
private static CollapsingBorderModel collapse = null;
private static CollapsingBorderModel collapseWithPrecedence = null;
/**
* Determines the winning BorderInfo.
- * @param current cell info of the current element
- * @param neighbour cell info of the neighbouring element
+ * @param current grid unit of the current element
+ * @param neighbour grid unit of the neighbouring element
* @return the winning BorderInfo
*/
public abstract BorderInfo determineWinner(
package org.apache.fop.layoutmgr.table;
import org.apache.fop.fo.Constants;
-import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
+import org.apache.fop.fo.flow.Table;
+import org.apache.fop.fo.flow.TableBody;
+import org.apache.fop.fo.flow.TableCell;
+import org.apache.fop.fo.flow.TableColumn;
+import org.apache.fop.fo.flow.TableRow;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo;
/**
*/
public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel {
- private static final int BEFORE = CommonBorderPaddingBackground.BEFORE;
- private static final int AFTER = CommonBorderPaddingBackground.AFTER;
- private static final int START = CommonBorderPaddingBackground.START;
- private static final int END = CommonBorderPaddingBackground.END;
-
public BorderInfo determineWinner(GridUnit currentGridUnit,
GridUnit otherGridUnit, int side, int flags) {
final boolean vertical = isVerticalRelation(side);
final int otherSide = getOtherSide(side);
//Get cells
- Cell currentCell = currentGridUnit.layoutManager;
- Cell otherCell = null;
+ TableCell currentCell = currentGridUnit.getCell();
+ TableCell otherCell = null;
if (otherGridUnit != null) {
- otherCell = otherGridUnit.layoutManager;
+ otherCell = otherGridUnit.getCell();
}
//Get rows
- Row currentRow = currentGridUnit.row;
- Row otherRow = null;
+ TableRow currentRow = currentGridUnit.getRow();
+ TableRow otherRow = null;
if (vertical && otherCell != null) {
- otherRow = otherGridUnit.row;
+ otherRow = otherGridUnit.getRow();
}
//get bodies
- Body currentBody = (Body)currentRow.getParent();
- Body otherBody = null;
+ TableBody currentBody = currentGridUnit.getBody();
+ TableBody otherBody = null;
if (otherRow != null) {
- otherBody = (Body)otherRow.getParent();
+ otherBody = otherGridUnit.getBody();
}
//get columns
- Column currentColumn = (Column)currentGridUnit.column;
- Column otherColumn = null;
+ TableColumn currentColumn = currentGridUnit.getColumn();
+ TableColumn otherColumn = null;
if (otherGridUnit != null) {
- otherColumn = (Column)otherGridUnit.column;
+ otherColumn = otherGridUnit.getColumn();
}
//TODO get column groups
//Get table
- TableLayoutManager table = (TableLayoutManager)currentBody.getParent();
+ Table table = currentGridUnit.getTable();
//----------------------------------------------------------------------
//We're creating two arrays containing the applicable BorderInfos for
if (otherGridUnit != null) {
other[0] = otherGridUnit.getOriginalBorderInfoForCell(otherSide);
}
- if (side == BEFORE
- || side == AFTER
- || (currentColumn.isFirst() && side == START)
- || (currentColumn.isLast() && side == END)) {
+ if ((currentRow != null)
+ && (side == BEFORE
+ || side == AFTER
+ || (currentGridUnit.getFlag(GridUnit.IN_FIRST_COLUMN) && side == START)
+ || (currentGridUnit.getFlag(GridUnit.IN_LAST_COLUMN) && side == END))) {
//row
- current[1] = currentRow.getFObj().getCommonBorderPaddingBackground().getBorderInfo(side);
+ current[1] = currentRow.getCommonBorderPaddingBackground().getBorderInfo(side);
}
if (otherRow != null) {
//row
- other[1] = otherRow.getFObj().getCommonBorderPaddingBackground().getBorderInfo(otherSide);
+ other[1] = otherRow.getCommonBorderPaddingBackground().getBorderInfo(otherSide);
}
- if ((side == BEFORE && currentRow.isFirstInBody())
- || (side == AFTER && currentRow.isLastInBody())
- || (currentColumn.isFirst() && side == START)
- || (currentColumn.isLast() && side == END)) {
+ if ((side == BEFORE && currentGridUnit.getFlag(GridUnit.FIRST_IN_BODY))
+ || (side == AFTER && currentGridUnit.getFlag(GridUnit.LAST_IN_BODY))
+ || (currentGridUnit.getFlag(GridUnit.IN_FIRST_COLUMN) && side == START)
+ || (currentGridUnit.getFlag(GridUnit.IN_LAST_COLUMN) && side == END)) {
//row group (=body, table-header or table-footer)
- current[2] = currentBody.getFObj().getCommonBorderPaddingBackground().getBorderInfo(side);
+ current[2] = currentBody.getCommonBorderPaddingBackground().getBorderInfo(side);
}
- if ((otherSide == BEFORE && otherRow.isFirstInBody())
- || (otherSide == AFTER && otherRow.isLastInBody())) {
+ if (otherGridUnit != null
+ && otherBody != null
+ && ((otherSide == BEFORE && otherGridUnit.getFlag(GridUnit.FIRST_IN_BODY))
+ || (otherSide == AFTER && otherGridUnit.getFlag(GridUnit.LAST_IN_BODY)))) {
//row group (=body, table-header or table-footer)
- other[2] = otherBody.getFObj().getCommonBorderPaddingBackground().getBorderInfo(otherSide);
+ other[2] = otherBody.getCommonBorderPaddingBackground().getBorderInfo(otherSide);
}
if ((side == BEFORE && otherGridUnit == null)
|| (side == AFTER && otherGridUnit == null)
|| (side == START)
|| (side == END)) {
//column
- current[3] = currentColumn.getFObj().getCommonBorderPaddingBackground().getBorderInfo(side);
+ current[3] = currentColumn.getCommonBorderPaddingBackground().getBorderInfo(side);
}
if (otherColumn != null) {
//column
- other[3] = otherColumn.getFObj().getCommonBorderPaddingBackground().getBorderInfo(otherSide);
+ other[3] = otherColumn.getCommonBorderPaddingBackground().getBorderInfo(otherSide);
}
//TODO current[4] and other[4] for column groups
- if (otherGridUnit == null) {
+ if (otherGridUnit == null
+ && ((side == BEFORE && (flags & VERTICAL_START_END_OF_TABLE) > 0)
+ || (side == AFTER && (flags & VERTICAL_START_END_OF_TABLE) > 0)
+ || (side == START)
+ || (side == END))) {
//table
- current[5] = table.getTable().getCommonBorderPaddingBackground().getBorderInfo(side);
+ current[5] = table.getCommonBorderPaddingBackground().getBorderInfo(side);
}
//other[6] is always null, since it's always the same table
// *** Rule 2 ***
if (!doRule2(current, other)) {
- return null; //paint no border
}
// *** Rule 3 ***
}
private BorderInfo doRule1(BorderInfo[] current, BorderInfo[] other) {
- for (int i = 0; i < current.length - 1; i++) {
+ for (int i = 0; i < current.length; i++) {
if ((current[i] != null) && (current[i].getStyle() == Constants.EN_HIDDEN)) {
return current[i];
}
private boolean doRule2(BorderInfo[] current, BorderInfo[] other) {
boolean found = false;
- for (int i = 0; i < current.length - 1; i++) {
+ for (int i = 0; i < current.length; i++) {
if ((current[i] != null) && (current[i].getStyle() != Constants.EN_NONE)) {
found = true;
break;
private BorderInfo doRule3(BorderInfo[] current, BorderInfo[] other) {
int width = 0;
//Find max border width
- for (int i = 0; i < current.length - 1; i++) {
+ for (int i = 0; i < current.length; i++) {
if ((current[i] != null) && (current[i].getRetainedWidth() > width)) {
width = current[i].getRetainedWidth();
}
BorderInfo widest = null;
int count = 0;
//See if there's only one with the widest border
- for (int i = 0; i < current.length - 1; i++) {
+ for (int i = 0; i < current.length; i++) {
if ((current[i] != null) && (current[i].getRetainedWidth() == width)) {
count++;
if (widest == null) {
widest = current[i];
}
- break;
} else {
current[i] = null; //Discard the narrower ones
}
if (widest == null) {
widest = other[i];
}
- break;
} else {
other[i] = null; //Discard the narrower ones
}
private BorderInfo doRule4(BorderInfo[] current, BorderInfo[] other) {
int pref = getPreferenceValue(Constants.EN_INSET); //Lowest preference
//Find highest preference value
- for (int i = 0; i < current.length - 1; i++) {
+ for (int i = 0; i < current.length; i++) {
if (current[i] != null) {
int currPref = getPreferenceValue(current[i].getStyle());
if (currPref > pref) {
pref = currPref;
- break;
}
}
if (other[i] != null) {
int currPref = getPreferenceValue(other[i].getStyle());
if (currPref > pref) {
pref = currPref;
- break;
}
}
}
BorderInfo preferred = null;
int count = 0;
//See if there's only one with the preferred border style
- for (int i = 0; i < current.length - 1; i++) {
+ for (int i = 0; i < current.length; i++) {
if (current[i] != null) {
int currPref = getPreferenceValue(current[i].getStyle());
if (currPref == pref) {
}
private BorderInfo doRule5(BorderInfo[] current, BorderInfo[] other) {
- for (int i = 0; i < current.length - 1; i++) {
+ for (int i = 0; i < current.length; i++) {
if (current[i] != null) {
return current[i];
}
+++ /dev/null
-/*
- * Copyright 1999-2005 The Apache Software Foundation.
- *
- * Licensed 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.table;
-
-import org.apache.fop.datatypes.Length;
-import org.apache.fop.layoutmgr.AbstractLayoutManager;
-import org.apache.fop.layoutmgr.BreakPoss;
-import org.apache.fop.layoutmgr.LayoutContext;
-import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.TraitSetter;
-import org.apache.fop.fo.flow.TableCell;
-import org.apache.fop.fo.flow.TableColumn;
-import org.apache.fop.area.Area;
-import org.apache.fop.area.Block;
-
-/**
- * LayoutManager for a table-column FO.
- * The table creates an area for the table-column background, this class
- * is used to do the area creation. This is used during the layout to handle
- * column properties.
- */
-public class Column extends AbstractLayoutManager {
-
- private TableColumn fobj;
-
- private Length columnWidth;
-
- /**
- * Create a new column layout manager.
- * @param node the table-column FO
- */
- public Column(TableColumn node) {
- super(node);
- fobj = node;
- columnWidth = fobj.getColumnWidth();
- }
-
- /** @return the table-column FO */
- public TableColumn getFObj() {
- return this.fobj;
- }
-
- /** @return true if the column is the first column */
- public boolean isFirst() {
- return ((TableLayoutManager)getParent()).isFirst(this);
- }
-
- /** @return true if the column is the last column */
- public boolean isLast() {
- return ((TableLayoutManager)getParent()).isLast(this);
- }
-
- /**
- * Get the next break possibility.
- * Columns do not create or return any areas.
- *
- * @param context the layout context
- * @return the break possibility, always null
- */
- public BreakPoss getNextBreakPoss(LayoutContext context) {
- return null;
- }
-
- /**
- * Add the areas.
- * Although this adds no areas it is used to add the id
- * reference of the table-column.
- *
- * @param parentIter the position iterator
- * @param layoutContext the context
- */
- public void addAreas(PositionIterator parentIter,
- LayoutContext layoutContext) {
- }
-
- /**
- * Get the parent area.
- * This does nothing.
- *
- * @param childArea the child area
- * @return always null
- */
- public Area getParentArea(Area childArea) {
- return null;
- }
-
- /**
- * Overrides the default column-with coming from the FO.
- * @param width the new width to use
- */
- public void setWidth(Length width) {
- this.columnWidth = width;
- }
-
- /**
- * Get the width of this column.
- *
- * @return the width of the column
- */
- public Length getWidth() {
- return columnWidth;
- }
-
- /**
- * Create a column area.
- * This area has the background and width set.
- * The Body manager will then set the offset of the column.
- *
- * @return the new column area
- */
- public Area createColumnArea() {
- Area curBlockArea = new Block();
-
- TraitSetter.addBackground(curBlockArea, fobj.getCommonBorderPaddingBackground());
- return curBlockArea;
- }
-}
-
--- /dev/null
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.table;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.fo.flow.Table;
+import org.apache.fop.fo.flow.TableColumn;
+
+/**
+ * Class holding a number of columns making up the column setup of a row.
+ */
+public class ColumnSetup {
+
+ /** Logger **/
+ private static Log log = LogFactory.getLog(ColumnSetup.class);
+
+ private Table table;
+ private List columns = new java.util.ArrayList();
+ private int maxColIndexReferenced = 0;
+
+ public ColumnSetup(Table table) {
+ this.table = table;
+ prepareExplicitColumns();
+ if (getColumnCount() == 0) {
+ createColumnsFromFirstRow();
+ }
+ }
+
+ private void prepareExplicitColumns() {
+ List rawCols = table.getColumns();
+ if (rawCols != null) {
+ int colnum = 1;
+ ListIterator iter = rawCols.listIterator();
+ while (iter.hasNext()) {
+ TableColumn col = (TableColumn)iter.next();
+ if (col.hasColumnNumber()) {
+ colnum = col.getColumnNumber();
+ }
+ for (int i = 0; i < col.getNumberColumnsRepeated(); i++) {
+ while (colnum > columns.size()) {
+ columns.add(null);
+ }
+ columns.set(colnum - 1, col);
+ colnum++;
+ }
+ }
+ //Post-processing the list (looking for gaps)
+ int pos = 1;
+ ListIterator ppIter = columns.listIterator();
+ while (ppIter.hasNext()) {
+ TableColumn col = (TableColumn)ppIter.next();
+ if (col == null) {
+ log.error("Found a gap in the table-columns at position " + pos);
+ }
+ pos++;
+ }
+ }
+ }
+
+ /**
+ * Returns a column. If the index of the column is bigger than the number of explicitly
+ * defined columns the last column is returned.
+ * @param index index of the column (1 is the first column)
+ * @return the requested column
+ */
+ public TableColumn getColumn(int index) {
+ int size = columns.size();
+ if (index > size) {
+ maxColIndexReferenced = Math.max(maxColIndexReferenced, index);
+ return (TableColumn)columns.get(size - 1);
+ } else {
+ return (TableColumn)columns.get(index - 1);
+ }
+ }
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ return columns.toString();
+ }
+
+ /** @return the number of columns in the setup. */
+ public int getColumnCount() {
+ if (maxColIndexReferenced > columns.size()) {
+ return maxColIndexReferenced;
+ } else {
+ return columns.size();
+ }
+ }
+
+ /** @return an Iterator over all columns */
+ public Iterator iterator() {
+ return this.columns.iterator();
+ }
+
+ private void createColumnsFromFirstRow() {
+ //TODO Create oldColumns from first row here
+ //--> rule 2 in "fixed table layout", see CSS2, 17.5.2
+ //Alternative: extend oldColumns on-the-fly, but in this case we need the
+ //new property evaluation context so proportional-column-width() works
+ //correctly.
+ if (columns.size() == 0) {
+ this.columns.add(table.getDefaultColumn());
+ }
+ }
+
+ /**
+ * @param col column index (1 is first column)
+ * @return the X offset of the requested column
+ */
+ public int getXOffset(int col) {
+ int xoffset = 0;
+ for (int i = 1; i < col; i++) {
+ xoffset += getColumn(i).getColumnWidth().getValue();
+ }
+ return xoffset;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.table;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * This class represents an effective row in a table and holds a list of grid units occupying
+ * the row as well as some additional values.
+ */
+public class EffRow {
+
+ private List gridUnits = new java.util.ArrayList();
+ private int index;
+ private int bodyType;
+ private MinOptMax height;
+ private MinOptMax explicitHeight;
+
+ public EffRow(int index, int bodyType) {
+ this.index = index;
+ this.bodyType = bodyType;
+ this.height = height;
+ }
+
+ public int getIndex() {
+ return this.index;
+ }
+
+ public int getBodyType() {
+ return this.bodyType;
+ }
+
+ public MinOptMax getHeight() {
+ return this.height;
+ }
+
+ public void setHeight(MinOptMax mom) {
+ this.height = mom;
+ }
+
+ public MinOptMax getExplicitHeight() {
+ return this.explicitHeight;
+ }
+
+ public void setExplicitHeight(MinOptMax mom) {
+ this.explicitHeight = mom;
+ }
+
+ public List getGridUnits() {
+ return gridUnits;
+ }
+
+ /**
+ * Returns the grid unit at a given position.
+ * @param column index of the grid unit in the row (zero based)
+ * @return the requested grid unit.
+ */
+ public GridUnit getGridUnit(int column) {
+ return (GridUnit)gridUnits.get(column);
+ }
+
+ /**
+ * Returns the grid unit at a given position. In contrast to getGridUnit() this
+ * method returns null if there's no grid unit at the given position. The number of
+ * grid units for row x can be smaller than the number of grid units for row x-1.
+ * @param column index of the grid unit in the row (zero based)
+ * @return the requested grid unit or null if there's no grid unit at this position.
+ */
+ public GridUnit safelyGetGridUnit(int column) {
+ if (column < gridUnits.size()) {
+ return (GridUnit)gridUnits.get(column);
+ } else {
+ return null;
+ }
+ }
+
+ public void setFlagForAllGridUnits(int flag, boolean value) {
+ Iterator iter = gridUnits.iterator();
+ while (iter.hasNext()) {
+ GridUnit gu = (GridUnit)iter.next();
+ gu.setFlag(flag, value);
+ }
+ }
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ StringBuffer sb = new StringBuffer("EffRow {");
+ sb.append(index);
+ if (getBodyType() == TableRowIterator.BODY) {
+ sb.append(" in body");
+ } else if (getBodyType() == TableRowIterator.HEADER) {
+ sb.append(" in header");
+ } else {
+ sb.append(" in footer");
+ }
+ sb.append(", ").append(height);
+ sb.append(", ").append(explicitHeight);
+ sb.append(", ").append(gridUnits.size()).append(" gu");
+ sb.append("}");
+ return sb.toString();
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.table;
+
+import org.apache.fop.fo.flow.TableBody;
+import org.apache.fop.fo.flow.TableColumn;
+import org.apache.fop.fo.flow.TableRow;
+
+/**
+ * GridUnit subclass for empty grid units.
+ */
+public class EmptyGridUnit extends GridUnit {
+
+ private TableRow row;
+ private TableBody body;
+
+ /**
+ * @param row Optional table-row instance
+ * @param column table-column instance
+ * @param body table-body the grid unit belongs to
+ * @param startCol column index
+ */
+ public EmptyGridUnit(TableRow row, TableColumn column, TableBody body,
+ int startCol) {
+ super(null, null, column, startCol, 0);
+ this.row = row;
+ this.body = body;
+ }
+
+ /** @see org.apache.fop.layoutmgr.table.GridUnit#isPrimary() */
+ public boolean isPrimary() {
+ return true;
+ }
+
+ /** @see org.apache.fop.layoutmgr.table.GridUnit#getBody() */
+ public TableBody getBody() {
+ return this.body;
+ }
+
+ /** @see org.apache.fop.layoutmgr.table.GridUnit#getRow() */
+ public TableRow getRow() {
+ return this.row;
+ }
+}
package org.apache.fop.layoutmgr.table;
+import org.apache.fop.fo.FONode;
import org.apache.fop.fo.flow.Table;
+import org.apache.fop.fo.flow.TableBody;
+import org.apache.fop.fo.flow.TableCell;
+import org.apache.fop.fo.flow.TableColumn;
+import org.apache.fop.fo.flow.TableRow;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo;
-
+/**
+ * This class represents one grid unit inside a table.
+ */
public class GridUnit {
+
+ /** Indicates that the grid unit is in the first column. */
+ public static final int IN_FIRST_COLUMN = 0;
+ /** Indicates that the grid unit is in the last column. */
+ public static final int IN_LAST_COLUMN = 1;
+ /** Indicates that the grid unit is in the first row (context: table). */
+ public static final int FIRST_IN_TABLE = 2;
+ /** Indicates that the grid unit is in the first row (context: body). */
+ public static final int FIRST_IN_BODY = 3;
+ /** Indicates that the grid unit is in the last row (context: body). */
+ public static final int LAST_IN_BODY = 4;
+ /** Indicates that the grid unit is in the last row (context: table). */
+ public static final int LAST_IN_TABLE = 5;
- /** layout manager for the cell occupying this grid unit, may be null */
- public Cell layoutManager;
- /** layout manager for the column that this grid unit belongs to */
- public Column column;
- /** layout manager for the row that this grid unit belongs to */
- public Row row;
+ /** Primary grid unit */
+ private PrimaryGridUnit primary;
+ /** Table cell which occupies this grid unit */
+ private TableCell cell;
+ /** Table row which occupied this grid unit (may be null) */
+ private TableRow row;
+ /** Table column that this grid unit belongs to */
+ private TableColumn column;
+
+ /** start index of grid unit within row in column direction */
+ private int startCol;
/** index of grid unit within cell in column direction */
- public int colSpanIndex;
+ private int colSpanIndex;
/** index of grid unit within cell in row direction */
- public int rowSpanIndex;
- /** effective borders for a cell slot (used for collapsing border model) */
- public CommonBorderPaddingBackground effBorders;
+ private int rowSpanIndex;
+ /** effective borders for a cell slot */
+ private CommonBorderPaddingBackground effBorders;
+ /** flags for the grid unit */
+ private byte flags = 0;
+
+
+ public GridUnit(TableCell cell, TableColumn column, int startCol, int colSpanIndex) {
+ this(null, cell, column, startCol, colSpanIndex);
+ }
+
+ public GridUnit(PrimaryGridUnit primary, TableColumn column, int startCol, int colSpanIndex) {
+ this(primary, primary.getCell(), column, startCol, colSpanIndex);
+ }
- public GridUnit(Cell layoutManager, int colSpanIndex) {
- this.layoutManager = layoutManager;
+ protected GridUnit(PrimaryGridUnit primary, TableCell cell, TableColumn column, int startCol, int colSpanIndex) {
+ this.primary = primary;
+ this.cell = cell;
+ this.column = column;
+ this.startCol = startCol;
this.colSpanIndex = colSpanIndex;
- this.rowSpanIndex = 0;
}
- public GridUnit(Cell layoutManager) {
- this(layoutManager, 0);
+ public TableCell getCell() {
+ return this.cell;
+ }
+
+ public TableColumn getColumn() {
+ return this.column;
+ }
+
+ public TableRow getRow() {
+ if (this.row != null) {
+ return this.row;
+ } else if (getCell().getParent() instanceof TableRow) {
+ return (TableRow)getCell().getParent();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Sets the table-row FO, if applicable.
+ * @param rowFO the table-row FO
+ */
+ public void setRow(TableRow rowFO) {
+ this.row = rowFO;
+ }
+
+ public TableBody getBody() {
+ FONode node = getCell();
+ while (node != null && !(node instanceof TableBody)) {
+ node = node.getParent();
+ }
+ return (TableBody)node;
+ }
+
+ public Table getTable() {
+ FONode node = getBody();
+ while (node != null && !(node instanceof Table)) {
+ node = node.getParent();
+ }
+ return (Table)node;
+ }
+
+ /**
+ * @return the primary grid unit if this is a spanned grid unit
+ */
+ public PrimaryGridUnit getPrimary() {
+ return (isPrimary() ? (PrimaryGridUnit)this : this.primary);
}
- /** @return true if the grid unit is the primary of a cell */
- public boolean isPrimaryGridUnit() {
- return (colSpanIndex == 0) && (rowSpanIndex == 0);
+ public boolean isPrimary() {
+ return false;
+ }
+
+ public boolean isEmpty() {
+ return this.cell == null;
+ }
+
+ public int getStartCol() {
+ return this.startCol;
}
/** @return true if the grid unit is the last in column spanning direction */
public boolean isLastGridUnitColSpan() {
- if (layoutManager != null) {
- return (colSpanIndex == layoutManager.getFObj().getNumberColumnsSpanned() - 1);
+ if (cell != null) {
+ return (colSpanIndex == cell.getNumberColumnsSpanned() - 1);
} else {
return true;
}
/** @return true if the grid unit is the last in column spanning direction */
public boolean isLastGridUnitRowSpan() {
- if (layoutManager != null) {
- return (rowSpanIndex == layoutManager.getFObj().getNumberRowsSpanned() - 1);
+ if (cell != null) {
+ return (rowSpanIndex == cell.getNumberRowsSpanned() - 1);
} else {
return true;
}
}
- /** @return true if the cell is part of a span in column direction */
- public boolean isColSpan() {
- return (colSpanIndex > 0);
+ /**
+ * @return the index of the grid unit inside a cell in row direction
+ */
+ public int getRowSpanIndex() {
+ return this.rowSpanIndex;
+ }
+
+ /**
+ * @return the index of the grid unit inside a cell in column direction
+ */
+ public int getColSpanIndex() {
+ return this.colSpanIndex;
}
+ /**
+ * Returns a BorderInfo instance for a side of the currently applicable cell before border
+ * resolution (i.e. the value from the FO). A return value of null indicates an empty cell.
+ * See CollapsingBorderModel(EyeCatching) where this method is used.
+ * @param side for which side to return the BorderInfo
+ * @return the requested BorderInfo instance or null if the grid unit is an empty cell
+ */
public BorderInfo getOriginalBorderInfoForCell(int side) {
- if (layoutManager != null) {
- return layoutManager.getFObj().getCommonBorderPaddingBackground().getBorderInfo(side);
+ if (cell != null) {
+ return cell.getCommonBorderPaddingBackground().getBorderInfo(side);
} else {
return null;
}
}
/**
- * Assign the borders from the given cell to this cell info. Used in
+ * @return the resolved normal borders for this grid unit
+ */
+ public CommonBorderPaddingBackground getBorders() {
+ return this.effBorders;
+ }
+
+ /**
+ * @return true if the grid unit has any borders.
+ */
+ public boolean hasBorders() {
+ return (getBorders() != null) && getBorders().hasBorder();
+ }
+
+ /**
+ * Assigns the borders from the given cell to this cell info. Used in
* case of separate border model.
- * @param current cell to take the borders from
*/
- public void assignBorder(Cell current) {
- if (current != null) {
- this.effBorders = current.getFObj().getCommonBorderPaddingBackground();
+ public void assignBorderForSeparateBorderModel() {
+ if (cell != null) {
+ this.effBorders = cell.getCommonBorderPaddingBackground();
}
}
/**
- * Assign the borders directly.
- * @param borders the borders to use
+ * Resolve collapsing borders for the given cell. Used in case of the collapsing border model.
+ * @param other neighbouring grid unit if any
+ * @param side the side to resolve (one of CommonBorderPaddingBackground.BEFORE|AFTER|START|END)
*/
- public void assignBorder(CommonBorderPaddingBackground borders) {
- if (borders != null) {
- this.effBorders = borders;
- }
+ public void resolveBorder(GridUnit other, int side) {
+ resolveBorder(other, side, 0);
}
/**
- * Resolve collapsing borders for the given cell and store the resulting
- * borders in this cell info. Use in case of the collapsing border model.
- * @param current cell to resolve borders for
- * @param before cell before the current cell, if any
- * @param after cell after the current cell, if any
- * @param start cell preceeding the current cell, if any
- * @param end cell succeeding of the current cell, if any
+ * Resolve collapsing borders for the given cell. Used in case of the collapsing border model.
+ * @param other neighbouring grid unit if any
+ * @param side the side to resolve (one of CommonBorderPaddingBackground.BEFORE|AFTER|START|END)
+ * @param resFlags flags for the border resolution
*/
- public static void resolveBorder(Table table,
- CommonBorderPaddingBackground target,
- GridUnit current, GridUnit other, int side) {
- if (current == null) {
- return;
- }
-
+ public void resolveBorder(GridUnit other, int side, int resFlags) {
CollapsingBorderModel borderModel = CollapsingBorderModel.getBorderModelFor(
- table.getBorderCollapse());
- target.setBorderInfo(
- borderModel.determineWinner(current, other,
- side, 0), side);
+ getTable().getBorderCollapse());
+ if (this.effBorders == null) {
+ this.effBorders = new CommonBorderPaddingBackground();
+ }
+ this.effBorders.setBorderInfo(
+ borderModel.determineWinner(this, other,
+ side, resFlags), side);
}
-}
\ No newline at end of file
+ public boolean getFlag(int which) {
+ return (flags & (1 << which)) != 0;
+ }
+
+ public void setFlag(int which, boolean value) {
+ if (value) {
+ flags |= (1 << which); //set flag
+ } else {
+ flags &= ~(1 << which); //clear flag
+ }
+ }
+
+ /**
+ * @return the grid unit just below this grid unit if the cell is spanning.
+ */
+ public GridUnit createNextRowSpanningGridUnit() {
+ if (isLastGridUnitRowSpan()) {
+ return null;
+ } else {
+ //cloning the current GridUnit with adjustments
+ GridUnit gu = new GridUnit(getPrimary(), getColumn(), startCol, colSpanIndex);
+ gu.rowSpanIndex = rowSpanIndex + 1;
+ return gu;
+ }
+ }
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ if (isEmpty()) {
+ sb.append("EMPTY");
+ } else if (isPrimary()) {
+ sb.append("Primary");
+ }
+ sb.append("GridUnit:");
+ if (colSpanIndex > 0) {
+ sb.append(" colSpan=").append(colSpanIndex);
+ }
+ if (rowSpanIndex > 0) {
+ sb.append(" rowSpan=").append(rowSpanIndex);
+ }
+ sb.append(" startCol=").append(startCol);
+ sb.append(" flags=").append(Integer.toBinaryString(flags));
+ return sb.toString();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.table;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.fop.fo.flow.TableCell;
+import org.apache.fop.fo.flow.TableColumn;
+import org.apache.fop.fo.properties.LengthRangeProperty;
+
+/**
+ * This class represents a primary grid unit of a spanned cell.
+ */
+public class PrimaryGridUnit extends GridUnit {
+
+ /** Cell layout manager. */
+ private Cell cellLM;
+ /** List of Knuth elements representing the contents of the cell. */
+ private LinkedList elements;
+ /** Index of row where this cell starts */
+ private int startRow;
+ /** Links to the spanned grid units. (List of GridUnit arrays, one array represents a row) */
+ private List rows;
+ /** The calculated size of the cell's content. (cached value) */
+ private int contentLength = -1;
+
+ public PrimaryGridUnit(TableCell cell, TableColumn column, int startCol, int startRow) {
+ super(cell, column, startCol, 0);
+ this.startRow = startRow;
+ if (cell != null) {
+ cellLM = new Cell(cell, this);
+ }
+ }
+
+ public Cell getCellLM() {
+ return cellLM;
+ }
+
+ public boolean isPrimary() {
+ return true;
+ }
+
+ public void setElements(LinkedList elements) {
+ this.elements = elements;
+ }
+
+ public LinkedList getElements() {
+ return this.elements;
+ }
+
+ /**
+ * @return Returns the half the maximum before border width of this cell.
+ */
+ public int getHalfMaxBeforeBorderWidth() {
+ int value = 0;
+ if (getRows() != null) {
+ int before = 0;
+ //first row for before borders
+ GridUnit[] row = (GridUnit[])getRows().get(0);
+ for (int i = 0; i < row.length; i++) {
+ if (row[i].hasBorders()) {
+ before = Math.max(before,
+ row[i].getBorders().getBorderBeforeWidth(false));
+ }
+ }
+ value += before / 2;
+ } else {
+ if (hasBorders()) {
+ value += getBorders().getBorderBeforeWidth(false) / 2;
+ }
+ }
+ return value;
+ }
+
+ /**
+ * @return Returns the half the maximum after border width of this cell.
+ */
+ public int getHalfMaxAfterBorderWidth() {
+ int value = 0;
+ if (getRows() != null) {
+ //Last row for after borders
+ int after = 0;
+ GridUnit[] row = (GridUnit[])getRows().get(getRows().size() - 1);
+ for (int i = 0; i < row.length; i++) {
+ if (row[i].hasBorders()) {
+ after = Math.max(after, row[i].getBorders().getBorderAfterWidth(false));
+ }
+ }
+ value += after / 2;
+ } else {
+ if (hasBorders()) {
+ value += getBorders().getBorderAfterWidth(false) / 2;
+ }
+ }
+ return value;
+ }
+
+ /**
+ * @return Returns the sum of half the maximum before and after border
+ * widths of this cell.
+ */
+ public int getHalfMaxBorderWidth() {
+ return getHalfMaxBeforeBorderWidth() + getHalfMaxAfterBorderWidth();
+ }
+
+ /** @param value The length of the cell content to remember. */
+ public void setContentLength(int value) {
+ this.contentLength = value;
+ }
+
+ /** @return Returns the length of the cell content. */
+ public int getContentLength() {
+ return contentLength;
+ }
+
+ /**
+ * @return Returns the length of the cell content after the bpd/height attributes on cell
+ * and row have been taken into account.
+ */
+ public int getEffectiveContentLength() {
+ int value = getContentLength();
+ if (!getCell().getBlockProgressionDimension().getMinimum().isAuto()) {
+ value = Math.max(value,
+ getCell().getBlockProgressionDimension().getMinimum().getLength().getValue());
+ }
+ if (getRow() != null
+ && !getRow().getBlockProgressionDimension().getMinimum().isAuto()) {
+ value = Math.max(value,
+ getRow().getBlockProgressionDimension().getMinimum().getLength().getValue());
+ }
+ return value;
+ }
+
+ /** @return true if cell/row has an explicit BPD/height */
+ public boolean hasBPD() {
+ if (!getCell().getBlockProgressionDimension().getOptimum().isAuto()) {
+ return true;
+ }
+ if (getRow() != null
+ && !getRow().getBlockProgressionDimension().getOptimum().isAuto()) {
+ return true;
+ }
+ return false;
+ }
+
+ public List getRows() {
+ return this.rows;
+ }
+
+ public void addRow(GridUnit[] row) {
+ if (rows == null) {
+ rows = new java.util.ArrayList();
+ }
+ rows.add(row);
+ }
+
+ public int getStartRow() {
+ return this.startRow;
+ }
+
+ public int[] getStartEndBorderWidths() {
+ int[] widths = new int[2];
+ if (rows == null) {
+ widths[0] = getBorders().getBorderStartWidth(false);
+ widths[1] = getBorders().getBorderEndWidth(false);
+ } else {
+ for (int i = 0; i < rows.size(); i++) {
+ GridUnit[] gridUnits = (GridUnit[])rows.get(i);
+ widths[0] = Math.max(widths[0],
+ (gridUnits[0]).
+ getBorders().getBorderStartWidth(false));
+ widths[1] = Math.max(widths[1],
+ (gridUnits[gridUnits.length - 1]).
+ getBorders().getBorderEndWidth(false));
+ }
+ }
+ return widths;
+ }
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ StringBuffer sb = new StringBuffer(super.toString());
+ sb.append(" startRow=").append(startRow);
+ return sb.toString();
+ }
+
+ /** @return true if this cell spans over more than one grid unit. */
+ public boolean hasSpanning() {
+ return (getCell().getNumberColumnsSpanned() > 1)
+ || (getCell().getNumberRowsSpanned() > 1);
+ }
+
+}
+++ /dev/null
-/*
- * Copyright 1999-2005 The Apache Software Foundation.
- *
- * Licensed 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.table;
-
-import org.apache.fop.fo.FONode;
-import org.apache.fop.fo.flow.Table;
-import org.apache.fop.fo.flow.TableBody;
-import org.apache.fop.fo.flow.TableCell;
-import org.apache.fop.fo.flow.TableRow;
-import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
-import org.apache.fop.fo.properties.LengthRangeProperty;
-import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
-import org.apache.fop.layoutmgr.LayoutManager;
-import org.apache.fop.layoutmgr.LeafPosition;
-import org.apache.fop.layoutmgr.BreakPoss;
-import org.apache.fop.layoutmgr.LayoutContext;
-import org.apache.fop.layoutmgr.MinOptMaxUtil;
-import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.BreakPossPosIter;
-import org.apache.fop.layoutmgr.Position;
-import org.apache.fop.layoutmgr.TraitSetter;
-import org.apache.fop.area.Area;
-import org.apache.fop.area.Block;
-import org.apache.fop.area.Trait;
-import org.apache.fop.traits.MinOptMax;
-
-import java.util.Iterator;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.ListIterator;
-
-/**
- * LayoutManager for a table-row FO.
- * The row contains cells that are organised according to the columns.
- * A break in a table row will contain breaks for each table cell.
- * If there are row spanning cells then these cells belong to this row
- * but effect the occupied columns of future rows.
- */
-public class Row extends BlockStackingLayoutManager {
-
- private TableRow fobj;
-
- private List gridUnits = null;
- private List columns = null;
- private int referenceIPD;
- private int rowHeight;
- private int xoffset;
- private int yoffset;
-
- private class RowPosition extends LeafPosition {
- protected List cellBreaks;
- protected RowPosition(LayoutManager lm, int pos, List l) {
- super(lm, pos);
- cellBreaks = l;
- }
- }
-
- /**
- * Create a new row layout manager.
- *
- */
- public Row(TableRow node) {
- super(node);
- fobj = node;
- }
-
- /** @return the table-row FO */
- public TableRow getFObj() {
- return this.fobj;
- }
-
- /**
- * @return the table owning this row
- */
- public Table getTable() {
- FONode node = fobj.getParent();
- while (!(node instanceof Table)) {
- node = node.getParent();
- }
- return (Table)node;
- }
-
- /**
- * Set the columns from the table.
- *
- * @param cols the list of columns for this table
- */
- public void setColumns(List cols) {
- columns = cols;
- }
-
- /** @return true if this is the layout manager for the first row in a body. */
- public boolean isFirstInBody() {
- return ((TableBody)getFObj().getParent()).isFirst(getFObj());
- }
-
- /** @return true if this is the layout manager for the last row in a body. */
- public boolean isLastInBody() {
- return ((TableBody)getFObj().getParent()).isLast(getFObj());
- }
-
- /**
- * Gets the Column at a given index.
- * @param index index of the column (index must be >= 1)
- * @return the requested Column
- */
- private Column getColumn(int index) {
- int size = columns.size();
- if (index > size - 1) {
- return (Column)columns.get(size - 1);
- } else {
- return (Column)columns.get(index - 1);
- }
- }
-
- private void prepareGridUnits() {
- gridUnits = new java.util.ArrayList();
- List availableCells = new java.util.ArrayList();
- // add cells to list
- while (childLMiter.hasNext()) {
- curChildLM = (LayoutManager) childLMiter.next();
- curChildLM.setParent(this);
- curChildLM.initialize();
- availableCells.add(curChildLM);
- }
-
- //Transfer available cells to their slots
- int colnum = 1;
- ListIterator iter = availableCells.listIterator();
- while (iter.hasNext()) {
- Cell cellLM = (Cell)iter.next();
- TableCell cell = cellLM.getFObj();
- if (cell.hasColumnNumber()) {
- colnum = cell.getColumnNumber();
- }
- while (colnum > gridUnits.size()) {
- gridUnits.add(null);
- }
- if (gridUnits.get(colnum - 1) != null) {
- log.error("Overlapping cell at position " + colnum);
- }
- //Add cell info for primary slot
- GridUnit info = new GridUnit(cellLM);
- info.row = this;
- gridUnits.set(colnum - 1, info);
- info.column = getColumn(colnum);
-
- //Add cell infos on spanned slots if any
- for (int j = 1; j < cell.getNumberColumnsSpanned(); j++) {
- colnum++;
- GridUnit infoSpan = new GridUnit(cellLM, j);
- infoSpan.row = this;
- infoSpan.column = getColumn(colnum);
- if (colnum > gridUnits.size()) {
- gridUnits.add(infoSpan);
- } else {
- if (gridUnits.get(colnum - 1) != null) {
- log.error("Overlapping cell at position " + colnum);
- //TODO throw layout exception
- }
- gridUnits.set(colnum - 1, infoSpan);
- }
- }
- colnum++;
- }
-
- //Post-processing the list (looking for gaps and resolve start and end borders)
- postProcessGridUnits();
- }
-
- private void postProcessGridUnits() {
- for (int pos = 1; pos <= gridUnits.size(); pos++) {
- GridUnit gu = (GridUnit)gridUnits.get(pos - 1);
-
- //Empty grid units
- if (gu == null) {
- //Add grid unit
- gu = new GridUnit(null);
- gu.row = this;
- gu.column = getColumn(pos);
- gridUnits.set(pos - 1, gu);
- }
- }
-
- //Border resolution now that the empty grid units are filled
- for (int pos = 1; pos <= gridUnits.size(); pos++) {
- GridUnit starting = (GridUnit)gridUnits.get(pos - 1);
-
- //Border resolution
- if (getTable().isSeparateBorderModel()) {
- starting.assignBorder(starting.layoutManager);
- } else {
- //Neighbouring grid unit at start edge
- GridUnit start = null;
- int find = pos - 1;
- while (find >= 1) {
- GridUnit candidate = (GridUnit)gridUnits.get(find - 1);
- if (candidate.isLastGridUnitColSpan()) {
- start = candidate;
- break;
- }
- find--;
- }
-
- //Ending grid unit for current cell
- GridUnit ending = null;
- if (starting.layoutManager != null) {
- pos += starting.layoutManager.getFObj().getNumberColumnsSpanned() - 1;
- }
- ending = (GridUnit)gridUnits.get(pos - 1);
-
- //Neighbouring grid unit at end edge
- GridUnit end = null;
- find = pos + 1;
- while (find <= gridUnits.size()) {
- GridUnit candidate = (GridUnit)gridUnits.get(find - 1);
- if (candidate.isPrimaryGridUnit()) {
- end = candidate;
- break;
- }
- find++;
- }
- CommonBorderPaddingBackground borders = new CommonBorderPaddingBackground();
- GridUnit.resolveBorder(getTable(), borders, starting,
- (start != null ? start : null),
- CommonBorderPaddingBackground.START);
- starting.effBorders = borders;
- if (starting != ending) {
- borders = new CommonBorderPaddingBackground();
- }
- GridUnit.resolveBorder(getTable(), borders, ending,
- (end != null ? end : null),
- CommonBorderPaddingBackground.END);
- ending.effBorders = borders;
- //Only start and end borders here, before and after during layout
- //TODO resolve before and after borders during layout
- }
- }
- }
-
- /**
- * Get the cell info for a cell.
- *
- * @param pos the position of the cell (must be >= 1)
- * @return the cell info object
- */
- protected GridUnit getCellInfo(int pos) {
- if (gridUnits == null) {
- prepareGridUnits();
- }
- if (pos <= gridUnits.size()) {
- return (GridUnit)gridUnits.get(pos - 1);
- } else {
- return null;
- }
- }
-
- /**
- * Get the next break possibility.
- * A row needs to get the possible breaks for each cell
- * in the row and find a suitable break across all cells.
- *
- * @param context the layout context for getting breaks
- * @return the next break possibility
- */
- public BreakPoss getNextBreakPoss(LayoutContext context) {
- //LayoutManager curLM; // currently active LM
- GridUnit curGridUnit; //currently active grid unit
-
- BreakPoss lastPos = null;
- List breakList = new java.util.ArrayList();
-
- int min = 0;
- int opt = 0;
- int max = 0;
-
- // This is used for the displacement of the individual cells
- int ipdOffset = 0;
-
- int startColumn = 1;
- boolean over = false;
-
- while ((curGridUnit = getCellInfo(startColumn)) != null) {
- Cell cellLM = curGridUnit.layoutManager;
- if (curGridUnit.isColSpan()) {
- //skip spanned slots
- startColumn++;
- continue;
- }
-
- List childBreaks = new ArrayList();
- MinOptMax stackSize = new MinOptMax();
-
- // Set up a LayoutContext
- // the ipd is from the current column
- referenceIPD = context.getRefIPD();
- BreakPoss bp;
-
- LayoutContext childLC = new LayoutContext(0);
- childLC.setStackLimit(
- MinOptMax.subtract(context.getStackLimit(),
- stackSize));
-
- //Determine which columns this cell will occupy
- List spannedGridUnits = new java.util.ArrayList();
- getGridUnitsForCell(cellLM, startColumn, spannedGridUnits);
- int childRefIPD = 0;
- for (int i = 0; i < spannedGridUnits.size(); i++) {
- Column col = ((GridUnit)spannedGridUnits.get(i)).column;
- childRefIPD += col.getWidth().getValue();
- }
- childLC.setRefIPD(childRefIPD);
-
- if (cellLM != null) {
- cellLM.addGridUnitsFromRow(spannedGridUnits);
- cellLM.setInRowIPDOffset(ipdOffset);
- while (!cellLM.isFinished()) {
- if ((bp = cellLM.getNextBreakPoss(childLC)) != null) {
- if (stackSize.opt + bp.getStackingSize().opt > context.getStackLimit().max) {
- // reset to last break
- if (lastPos != null) {
- LayoutManager lm = lastPos.getLayoutManager();
- lm.resetPosition(lastPos.getPosition());
- if (lm != cellLM) {
- cellLM.resetPosition(null);
- }
- } else {
- cellLM.resetPosition(null);
- }
- over = true;
- break;
- }
- stackSize.add(bp.getStackingSize());
- lastPos = bp;
- childBreaks.add(bp);
-
- if (bp.nextBreakOverflows()) {
- over = true;
- break;
- }
-
- childLC.setStackLimit(MinOptMax.subtract(
- context.getStackLimit(), stackSize));
- }
- }
- startColumn += cellLM.getFObj().getNumberColumnsSpanned();
- } else {
- //Skipping empty cells
- //log.debug("empty cell at pos " + startColumn);
- startColumn++;
- }
-
- //Adjust in-row x offset for individual cells
- ipdOffset += childRefIPD;
-
-
- // the min is the maximum min of all cells
- if (stackSize.min > min) {
- min = stackSize.min;
- }
- // the optimum is the maximum of all optimums
- if (stackSize.opt > opt) {
- opt = stackSize.opt;
- }
- // the maximum is the largest maximum
- if (stackSize.max > max) {
- max = stackSize.max;
- }
-
- if (childBreaks.size() > 0) {
- breakList.add(childBreaks);
- }
- }
- MinOptMax rowSize = new MinOptMax(min, opt, max);
- LengthRangeProperty specifiedBPD = fobj.getBlockProgressionDimension();
- if (specifiedBPD.getEnum() != EN_AUTO) {
- if ((specifiedBPD.getMaximum().getEnum() != EN_AUTO)
- && (specifiedBPD.getMaximum().getLength().getValue() < rowSize.min)) {
- log.warn("maximum height of row is smaller than the minimum "
- + "height of its contents");
- }
- MinOptMaxUtil.restrict(rowSize, specifiedBPD);
- }
- rowHeight = rowSize.opt;
-
- boolean fin = true;
- startColumn = 1;
- //Check if any of the cell LMs haven't finished, yet
- while ((curGridUnit = getCellInfo(startColumn)) != null) {
- Cell cellLM = curGridUnit.layoutManager;
- if (cellLM == null) {
- //skip empty cell
- startColumn++;
- continue;
- }
- if (!cellLM.isFinished()) {
- fin = false;
- break;
- }
- startColumn += cellLM.getFObj().getNumberColumnsSpanned();
- }
-
- setFinished(fin);
- RowPosition rp = new RowPosition(this, breakList.size() - 1, breakList);
- BreakPoss breakPoss = new BreakPoss(rp);
- if (over) {
- breakPoss.setFlag(BreakPoss.NEXT_OVERFLOWS, true);
- }
- breakPoss.setStackingSize(rowSize);
- return breakPoss;
- }
-
- /**
- * Determines the grid units that are spanned by the given cell.
- * @param cellLM table-cell LM
- * @param startCell starting cell index (must be >= 1)
- * @param spannedGridUnits List to receive the applicable grid units
- */
- private void getGridUnitsForCell(Cell cellLM, int startCell, List spannedGridUnits) {
- int count;
- if (cellLM != null) {
- count = cellLM.getFObj().getNumberColumnsSpanned();
- } else {
- count = 1;
- }
- spannedGridUnits.clear();
- for (int i = 0; i < count; i++) {
- spannedGridUnits.add(this.gridUnits.get(startCell + i - 1));
- }
- }
-
- /**
- * Reset the layoutmanager "iterator" so that it will start
- * with the passed Position's generating LM
- * on the next call to getChildLM.
- * @param pos a Position returned by a child layout manager
- * representing a potential break decision.
- * If pos is null, then back up to the first child LM.
- */
- protected void reset(Position pos) {
- //LayoutManager curLM; // currently active LM
- GridUnit curGridUnit;
- int cellIndex = 1;
-
- if (pos == null) {
- while ((curGridUnit = getCellInfo(cellIndex)) != null) {
- if (curGridUnit.layoutManager != null) {
- curGridUnit.layoutManager.resetPosition(null);
- }
- cellIndex++;
- }
- } else {
- RowPosition rpos = (RowPosition)pos;
- List breaks = rpos.cellBreaks;
-
- while ((curGridUnit = getCellInfo(cellIndex)) != null) {
- if (curGridUnit.layoutManager != null) {
- List childbreaks = (List)breaks.get(cellIndex);
- curGridUnit.layoutManager.resetPosition(
- (Position)childbreaks.get(childbreaks.size() - 1));
- }
- cellIndex++;
- }
- }
-
- setFinished(false);
- }
-
- /**
- * Set the x position offset of this row.
- * This is used to set the position of the areas returned by this row.
- *
- * @param off the x offset
- */
- public void setXOffset(int off) {
- xoffset = off;
- }
-
- /**
- * Set the y position offset of this row.
- * This is used to set the position of the areas returned by this row.
- *
- * @param off the y offset
- */
- public void setYOffset(int off) {
- yoffset = off;
- }
-
- /**
- * Add the areas for the break points.
- * This sets the offset of each cell as it is added.
- *
- * @param parentIter the position iterator
- * @param layoutContext the layout context for adding areas
- */
- public void addAreas(PositionIterator parentIter,
- LayoutContext layoutContext) {
- getParentArea(null);
- BreakPoss bp1 = (BreakPoss)parentIter.peekNext();
- bBogus = !bp1.generatesAreas();
- if (!isBogus()) {
- addID(fobj.getId());
- }
-
- Cell childLM;
- int iStartPos = 0;
- LayoutContext lc = new LayoutContext(0);
- while (parentIter.hasNext()) {
- RowPosition lfp = (RowPosition) parentIter.next();
-
- //area exclusively for painting the row background
- Block rowArea = getRowArea();
- if (rowArea != null) {
- rowArea.setBPD(rowHeight);
- rowArea.setIPD(referenceIPD);
- rowArea.setXOffset(xoffset);
- rowArea.setYOffset(yoffset);
- parentLM.addChildArea(rowArea);
- }
-
- for (Iterator iter = lfp.cellBreaks.iterator(); iter.hasNext();) {
- List cellsbr = (List)iter.next();
- BreakPossPosIter breakPosIter;
- breakPosIter = new BreakPossPosIter(cellsbr, 0, cellsbr.size());
- iStartPos = lfp.getLeafPos() + 1;
-
- while ((childLM = (Cell)breakPosIter.getNextChildLM()) != null) {
- childLM.setXOffset(xoffset);
- childLM.setYOffset(yoffset);
- childLM.setRowHeight(rowHeight);
- childLM.addAreas(breakPosIter, lc);
- }
- }
- }
-
- flush();
- }
-
- /**
- * Get the row height of the row after adjusting.
- * Should only be called after adding the row areas.
- *
- * @return the row height of this row after adjustment
- */
- public int getRowHeight() {
- return rowHeight;
- }
-
- /**
- * Return an Area which can contain the passed childArea. The childArea
- * may not yet have any content, but it has essential traits set.
- * In general, if the LayoutManager already has an Area it simply returns
- * it. Otherwise, it makes a new Area of the appropriate class.
- * It gets a parent area for its area by calling its parent LM.
- * Finally, based on the dimensions of the parent area, it initializes
- * its own area. This includes setting the content IPD and the maximum
- * BPD.
- *
- * @param childArea the child area
- * @return the parent are for the child
- */
- public Area getParentArea(Area childArea) {
- return parentLM.getParentArea(childArea);
- }
-
- /**
- * Add the child.
- * Rows return the areas returned by the child elements.
- * This simply adds the area to the parent layout manager.
- *
- * @param childArea the child area
- */
- public void addChildArea(Area childArea) {
- parentLM.addChildArea(childArea);
- }
-
- /**
- * Reset the position of this layout manager.
- *
- * @param resetPos the position to reset to
- */
- public void resetPosition(Position resetPos) {
- if (resetPos == null) {
- reset(null);
- }
- }
-
-
- /**
- * Get the area for this row for background.
- *
- * @return the row area
- */
- public Block getRowArea() {
- if (fobj.getCommonBorderPaddingBackground().hasBackground()) {
- Block block = new Block();
- block.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
- block.setPositioning(Block.ABSOLUTE);
- TraitSetter.addBackground(block, fobj.getCommonBorderPaddingBackground());
- return block;
- } else {
- return null;
- }
- }
-
-}
-
public void addAreas(PositionIterator parentIter,
LayoutContext layoutContext) {
getParentArea(null);
- addID(fobj.getId());
+ getPSLM().addIDToPage(fobj.getId());
LayoutManager childLM;
int iStartPos = 0;
--- /dev/null
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.table;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.area.Block;
+import org.apache.fop.area.Trait;
+import org.apache.fop.fo.flow.Table;
+import org.apache.fop.fo.flow.TableRow;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
+import org.apache.fop.fo.properties.LengthRangeProperty;
+import org.apache.fop.layoutmgr.ElementListUtils;
+import org.apache.fop.layoutmgr.KnuthBox;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.KnuthPenalty;
+import org.apache.fop.layoutmgr.KnuthPossPosIter;
+import org.apache.fop.layoutmgr.LayoutContext;
+import org.apache.fop.layoutmgr.LayoutManager;
+import org.apache.fop.layoutmgr.MinOptMaxUtil;
+import org.apache.fop.layoutmgr.Position;
+import org.apache.fop.layoutmgr.PositionIterator;
+import org.apache.fop.layoutmgr.TraitSetter;
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * Layout manager for table contents, particularly managing the creation of combined element lists.
+ */
+public class TableContentLayoutManager {
+
+ /** Logger **/
+ private static Log log = LogFactory.getLog(TableContentLayoutManager.class);
+
+ private TableLayoutManager tableLM;
+ private TableRowIterator trIter;
+ private TableRowIterator headerIter;
+ private TableRowIterator footerIter;
+ private LinkedList headerList;
+ private LinkedList footerList;
+ private int headerNetHeight = 0;
+ private int footerNetHeight = 0;
+
+ private int startXOffset;
+ private int usedBPD;
+
+ /**
+ * Main constructor
+ * @param parent Parent layout manager
+ */
+ public TableContentLayoutManager(TableLayoutManager parent) {
+ this.tableLM = parent;
+ Table table = getTableLM().getTable();
+ this.trIter = new TableRowIterator(table, getTableLM().getColumns(), TableRowIterator.BODY);
+ if (table.getTableHeader() != null) {
+ headerIter = new TableRowIterator(table,
+ getTableLM().getColumns(), TableRowIterator.HEADER);
+ }
+ if (table.getTableFooter() != null) {
+ footerIter = new TableRowIterator(table,
+ getTableLM().getColumns(), TableRowIterator.FOOTER);
+ }
+ }
+
+ /**
+ * @return the table layout manager
+ */
+ public TableLayoutManager getTableLM() {
+ return this.tableLM;
+ }
+
+ /** @return true if the table uses the separate border model. */
+ private boolean isSeparateBorderModel() {
+ return getTableLM().getTable().isSeparateBorderModel();
+ }
+
+ /**
+ * @return the column setup of this table
+ */
+ public ColumnSetup getColumns() {
+ return getTableLM().getColumns();
+ }
+
+ /** @return the net header height */
+ protected int getHeaderNetHeight() {
+ return this.headerNetHeight;
+ }
+
+ /** @return the net footer height */
+ protected int getFooterNetHeight() {
+ return this.headerNetHeight;
+ }
+
+ /** @return the header element list */
+ protected LinkedList getHeaderElements() {
+ return this.headerList;
+ }
+
+ /** @return the footer element list */
+ protected LinkedList getFooterElements() {
+ return this.footerList;
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(org.apache.fop.layoutmgr.LayoutContext, int)
+ */
+ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+ log.debug("==> Columns: " + getTableLM().getColumns());
+ KnuthBox headerAsFirst = null;
+ KnuthBox headerAsSecondToLast = null;
+ KnuthBox footerAsLast = null;
+ if (headerIter != null) {
+ this.headerList = getKnuthElementsForRowIterator(
+ headerIter, context, alignment, TableRowIterator.HEADER);
+ ElementListUtils.removeLegalBreaks(this.headerList);
+ this.headerNetHeight = ElementListUtils.calcContentLength(this.headerList);
+ if (log.isDebugEnabled()) {
+ log.debug("==> Header: " + headerNetHeight + " - " + this.headerList);
+ }
+ TableHeaderFooterPosition pos = new TableHeaderFooterPosition(
+ getTableLM(), true, this.headerList);
+ KnuthBox box = new KnuthBox(headerNetHeight, pos, false);
+ if (getTableLM().getTable().omitHeaderAtBreak()) {
+ //We can simply add the table header at the beginning of the whole list
+ headerAsFirst = box;
+ //returnList.add(0, box);
+ } else {
+ headerAsSecondToLast = box;
+ //returnList.add(box);
+ }
+ }
+ if (footerIter != null) {
+ this.footerList = getKnuthElementsForRowIterator(
+ footerIter, context, alignment, TableRowIterator.FOOTER);
+ ElementListUtils.removeLegalBreaks(this.footerList);
+ this.footerNetHeight = ElementListUtils.calcContentLength(this.footerList);
+ if (log.isDebugEnabled()) {
+ log.debug("==> Footer: " + footerNetHeight + " - " + this.footerList);
+ }
+ if (true /*getTableLM().getTable().omitFooterAtBreak()*/) {
+ //We can simply add the table header at the end of the whole list
+ TableHeaderFooterPosition pos = new TableHeaderFooterPosition(
+ getTableLM(), false, this.footerList);
+ KnuthBox box = new KnuthBox(footerNetHeight, pos, false);
+ footerAsLast = box;
+ //returnList.add(box);
+ }
+ }
+ LinkedList returnList = getKnuthElementsForRowIterator(
+ trIter, context, alignment, TableRowIterator.BODY);
+ if (headerAsFirst != null) {
+ returnList.add(0, headerAsFirst);
+ } else if (headerAsSecondToLast != null) {
+ returnList.add(headerAsSecondToLast);
+ }
+ if (footerAsLast != null) {
+ returnList.add(footerAsLast);
+ }
+ return returnList;
+ }
+
+ /**
+ * Creates Knuth elements by iterating over a TableRowIterator.
+ * @param iter TableRowIterator instance to fetch rows from
+ * @param context Active LayoutContext
+ * @param alignment alignment indicator
+ * @param bodyType Indicates what kind of body is being processed (BODY, HEADER or FOOTER)
+ * @return An element list
+ */
+ private LinkedList getKnuthElementsForRowIterator(TableRowIterator iter,
+ LayoutContext context, int alignment, int bodyType) {
+ LinkedList returnList = new LinkedList();
+ EffRow[] rowGroup = null;
+ while ((rowGroup = iter.getNextRowGroup()) != null) {
+ if (!isSeparateBorderModel()) {
+ resolveNormalBeforeAfterBordersForRowGroup(rowGroup, iter);
+ }
+ createElementsForRowGroup(context, alignment, bodyType,
+ returnList, rowGroup);
+ }
+
+ //Remove last penalty
+ KnuthElement last = (KnuthElement)returnList.getLast();
+ if (last.isPenalty() && last.getW() == 0 && last.getP() == 0) {
+ returnList.removeLast();
+ }
+ return returnList;
+ }
+
+ /**
+ * Resolves normal borders for a row group.
+ * @param iter Table row iterator to operate on
+ */
+ private void resolveNormalBeforeAfterBordersForRowGroup(EffRow[] rowGroup,
+ TableRowIterator iter) {
+ for (int rgi = 0; rgi < rowGroup.length; rgi++) {
+ EffRow row = rowGroup[rgi];
+ EffRow prev = iter.getCachedRow(row.getIndex() - 1);
+ EffRow next = iter.getCachedRow(row.getIndex() + 1);
+ if (next == null) {
+ //It wasn't read, yet, or we are at the last row
+ next = iter.getNextRow();
+ iter.backToPreviewRow();
+ }
+ if ((prev == null) && (iter == this.trIter) && (this.headerIter != null)) {
+ prev = this.headerIter.getLastRow();
+ }
+ if ((next == null) && (iter == this.headerIter)) {
+ next = this.trIter.getFirstRow();
+ }
+ if ((next == null) && (iter == this.trIter) && (this.footerIter != null)) {
+ next = this.footerIter.getFirstRow();
+ }
+ if ((prev == null) && (iter == this.footerIter)) {
+ //TODO This could be bad for memory consumption because it already causes the
+ //whole body iterator to be prefetched!
+ prev = this.trIter.getLastRow();
+ }
+ log.debug(prev + " - " + row + " - " + next);
+
+ //Determine the grid units necessary for getting all the borders right
+ int guCount = row.getGridUnits().size();
+ if (prev != null) {
+ guCount = Math.max(guCount, prev.getGridUnits().size());
+ }
+ if (next != null) {
+ guCount = Math.max(guCount, next.getGridUnits().size());
+ }
+ GridUnit gu = row.getGridUnit(0);
+ //Create empty grid units to hold resolved borders of neighbouring cells
+ //TODO maybe this needs to be done differently (and sooner)
+ for (int i = 0; i < guCount - row.getGridUnits().size(); i++) {
+ //TODO This block in untested!
+ int pos = row.getGridUnits().size() + i;
+ row.getGridUnits().add(new EmptyGridUnit(gu.getRow(),
+ this.tableLM.getColumns().getColumn(pos + 1), gu.getBody(),
+ pos));
+ }
+
+ //Now resolve normal borders
+ if (getTableLM().getTable().isSeparateBorderModel()) {
+ //nop, borders are already assigned at this point
+ } else {
+ for (int i = 0; i < row.getGridUnits().size(); i++) {
+ gu = row.getGridUnit(i);
+ GridUnit other;
+ int flags = 0;
+ if (prev != null && i < prev.getGridUnits().size()) {
+ other = prev.getGridUnit(i);
+ } else {
+ other = null;
+ }
+ if (other == null
+ || other.isEmpty()
+ || gu.isEmpty()
+ || gu.getPrimary() != other.getPrimary()) {
+ if ((iter == this.trIter)
+ && gu.getFlag(GridUnit.FIRST_IN_TABLE)
+ && (this.headerIter == null)) {
+ flags |= CollapsingBorderModel.VERTICAL_START_END_OF_TABLE;
+ }
+ if ((iter == this.headerIter)
+ && gu.getFlag(GridUnit.FIRST_IN_TABLE)) {
+ flags |= CollapsingBorderModel.VERTICAL_START_END_OF_TABLE;
+ }
+ gu.resolveBorder(other,
+ CommonBorderPaddingBackground.BEFORE, flags);
+ }
+
+ flags = 0;
+ if (next != null && i < next.getGridUnits().size()) {
+ other = next.getGridUnit(i);
+ } else {
+ other = null;
+ }
+ if (other == null
+ || other.isEmpty()
+ || gu.isEmpty()
+ || gu.getPrimary() != other.getPrimary()) {
+ if ((iter == this.trIter)
+ && gu.getFlag(GridUnit.LAST_IN_TABLE)
+ && (this.footerIter == null)) {
+ flags |= CollapsingBorderModel.VERTICAL_START_END_OF_TABLE;
+ }
+ if ((iter == this.footerIter)
+ && gu.getFlag(GridUnit.LAST_IN_TABLE)) {
+ flags |= CollapsingBorderModel.VERTICAL_START_END_OF_TABLE;
+ }
+ gu.resolveBorder(other,
+ CommonBorderPaddingBackground.AFTER, flags);
+ }
+ }
+
+ }
+ }
+ }
+
+ /**
+ * Creates Knuth elements for a row group (see TableRowIterator.getNextRowGroup()).
+ * @param context Active LayoutContext
+ * @param alignment alignment indicator
+ * @param bodyType Indicates what kind of body is being processed (BODY, HEADER or FOOTER)
+ * @param returnList List to received the generated elements
+ * @param rowGroup row group to process
+ */
+ private void createElementsForRowGroup(LayoutContext context, int alignment,
+ int bodyType, LinkedList returnList,
+ EffRow[] rowGroup) {
+ log.debug("Handling row group with " + rowGroup.length + " rows...");
+ MinOptMax[] rowHeights = new MinOptMax[rowGroup.length];
+ MinOptMax[] explicitRowHeights = new MinOptMax[rowGroup.length];
+ EffRow row;
+ int maxColumnCount = 0;
+ List pgus = new java.util.ArrayList(); //holds a list of a row's primary grid units
+ for (int rgi = 0; rgi < rowGroup.length; rgi++) {
+ row = rowGroup[rgi];
+ rowHeights[rgi] = new MinOptMax(0, 0, Integer.MAX_VALUE);
+ explicitRowHeights[rgi] = new MinOptMax(0, 0, Integer.MAX_VALUE);
+
+ pgus.clear();
+ TableRow tableRow = null;
+ int minContentHeight = 0;
+ int maxCellHeight = 0;
+ for (int j = 0; j < row.getGridUnits().size(); j++) {
+ maxColumnCount = Math.max(maxColumnCount, row.getGridUnits().size());
+ GridUnit gu = row.getGridUnit(j);
+ if ((gu.isPrimary() || (gu.getColSpanIndex() == 0 && gu.isLastGridUnitRowSpan()))
+ && !gu.isEmpty()) {
+ PrimaryGridUnit primary = gu.getPrimary();
+
+ if (gu.isPrimary()) {
+ primary.getCellLM().setParent(tableLM);
+
+ //Determine the table-row if any
+ if (tableRow == null && primary.getRow() != null) {
+ tableRow = primary.getRow();
+
+ //Check for bpd on row, see CSS21, 17.5.3 Table height algorithms
+ LengthRangeProperty bpd = tableRow.getBlockProgressionDimension();
+ if (!bpd.getMinimum().isAuto()) {
+ minContentHeight = Math.max(minContentHeight,
+ bpd.getMinimum().getLength().getValue());
+ }
+ MinOptMaxUtil.restrict(explicitRowHeights[rgi], bpd);
+
+ }
+
+ //Calculate width of cell
+ int spanWidth = 0;
+ for (int i = primary.getStartCol();
+ i < primary.getStartCol() + primary.getCell().getNumberColumnsSpanned();
+ i++) {
+ spanWidth += getTableLM().getColumns().getColumn(i + 1)
+ .getColumnWidth().getValue();
+ }
+ LayoutContext childLC = new LayoutContext(0);
+ childLC.setStackLimit(context.getStackLimit()); //necessary?
+ childLC.setRefIPD(spanWidth);
+
+ //Get the element list for the cell contents
+ LinkedList elems = primary.getCellLM().getNextKnuthElements(childLC, alignment);
+ primary.setElements(elems);
+ log.debug("Elements: " + elems);
+ }
+
+
+ //Calculate height of cell contents
+ primary.setContentLength(ElementListUtils.calcContentLength(
+ primary.getElements()));
+ maxCellHeight = Math.max(maxCellHeight, primary.getContentLength());
+
+ //Calculate height of row, see CSS21, 17.5.3 Table height algorithms
+ if (gu.isLastGridUnitRowSpan()) {
+ int effCellContentHeight = minContentHeight;
+ LengthRangeProperty bpd = primary.getCell().getBlockProgressionDimension();
+ if (!bpd.getMinimum().isAuto()) {
+ effCellContentHeight = Math.max(effCellContentHeight,
+ bpd.getMinimum().getLength().getValue());
+ }
+ if (gu.getRowSpanIndex() == 0) {
+ //TODO ATM only non-row-spanned cells are taken for this
+ MinOptMaxUtil.restrict(explicitRowHeights[rgi], bpd);
+ }
+ effCellContentHeight = Math.max(effCellContentHeight,
+ primary.getContentLength());
+ int borderWidths;
+ if (isSeparateBorderModel()) {
+ borderWidths = primary.getBorders().getBorderBeforeWidth(false)
+ + primary.getBorders().getBorderAfterWidth(false);
+ } else {
+ borderWidths = primary.getHalfMaxBorderWidth();
+ }
+ int padding = 0;
+ CommonBorderPaddingBackground cbpb
+ = primary.getCell().getCommonBorderPaddingBackground();
+ padding += cbpb.getPaddingBefore(false);
+ padding += cbpb.getPaddingAfter(false);
+ int effRowHeight = effCellContentHeight + padding + borderWidths;
+ for (int previous = 0; previous < gu.getRowSpanIndex(); previous++) {
+ effRowHeight -= rowHeights[rgi - previous - 1].opt;
+ }
+ if (effRowHeight > rowHeights[rgi].min) {
+ //This is the new height of the (grid) row
+ MinOptMaxUtil.extendMinimum(rowHeights[rgi], effRowHeight, false);
+ row.setHeight(rowHeights[rgi]);
+ }
+ }
+
+ if (gu.isPrimary()) {
+ pgus.add(primary);
+ }
+ }
+ }
+
+ row.setExplicitHeight(explicitRowHeights[rgi]);
+ if (row.getHeight().opt > row.getExplicitHeight().max) {
+ log.warn("Contents of row " + row.getIndex() + " violate a maximum constraint "
+ + "in block-progression-dimension. Due to its contents the row grows "
+ + "to " + row.getHeight().opt + " millipoints. The row constraint resolve "
+ + "to " + row.getExplicitHeight());
+ }
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("rowGroup:");
+ for (int i = 0; i < rowHeights.length; i++) {
+ log.debug(" height=" + rowHeights[i] + " explicit=" + explicitRowHeights[i]);
+ }
+ }
+ TableStepper stepper = new TableStepper(this);
+ LinkedList returnedList = stepper.getCombinedKnuthElementsForRowGroup(
+ rowGroup, maxColumnCount, bodyType);
+ if (returnedList != null) {
+ returnList.addAll(returnedList);
+ }
+
+ }
+
+ protected int getXOffsetOfGridUnit(GridUnit gu) {
+ int col = gu.getStartCol();
+ return startXOffset + getTableLM().getColumns().getXOffset(col + 1);
+ }
+
+ public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
+ this.usedBPD = 0;
+ RowPainter painter = new RowPainter(layoutContext);
+
+ List positions = new java.util.ArrayList();
+ List footerElements = null;
+ Position lastPos = null;
+ while (parentIter.hasNext()) {
+ Position pos = (Position)parentIter.next();
+ lastPos = pos;
+ if (pos instanceof TableHeaderFooterPosition) {
+ TableHeaderFooterPosition thfpos = (TableHeaderFooterPosition)pos;
+ //these positions need to be unpacked
+ if (thfpos.header) {
+ //header positions for the last part are the second-to-last element and need to
+ //be handled first before all other TableContentPositions
+ PositionIterator nestedIter = new KnuthPossPosIter(thfpos.nestedElements);
+ while (nestedIter.hasNext()) {
+ Position containedPos = (Position)nestedIter.next();
+ if (containedPos instanceof TableContentPosition) {
+ TableContentPosition tcpos = (TableContentPosition)containedPos;
+ painter.handleTableContentPosition(tcpos);
+ } else {
+ log.debug("Ignoring position: " + containedPos);
+ }
+ }
+ painter.addAreasAndFlushRow(true);
+ } else {
+ //Positions for footers are simply added at the end
+ footerElements = thfpos.nestedElements;
+ }
+ } else if (pos instanceof TableHFPenaltyPosition) {
+ //ignore for now, see special handling below if break is at a penalty
+ //Only if the last position in this part/page us such a position it will be used
+ } else {
+ //leave order as is for the rest
+ positions.add(pos);
+ }
+ }
+ if (lastPos instanceof TableHFPenaltyPosition) {
+ TableHFPenaltyPosition penaltyPos = (TableHFPenaltyPosition)lastPos;
+ log.debug("Break at penalty!");
+ if (penaltyPos.headerElements != null) {
+ //Header positions for the penalty position are in the last element and need to
+ //be handled first before all other TableContentPositions
+ PositionIterator nestedIter = new KnuthPossPosIter(penaltyPos.headerElements);
+ while (nestedIter.hasNext()) {
+ Position containedPos = (Position)nestedIter.next();
+ if (containedPos instanceof TableContentPosition) {
+ TableContentPosition tcpos = (TableContentPosition)containedPos;
+ painter.handleTableContentPosition(tcpos);
+ } else {
+ log.debug("Ignoring position: " + containedPos);
+ }
+ }
+ painter.addAreasAndFlushRow(true);
+ }
+ if (penaltyPos.footerElements != null) {
+ footerElements = penaltyPos.footerElements;
+ }
+ }
+
+
+ Iterator posIter = positions.iterator();
+ //Iterate over all steps
+ while (posIter.hasNext()) {
+ Position pos = (Position)posIter.next();
+ if (pos instanceof TableContentPosition) {
+ TableContentPosition tcpos = (TableContentPosition)pos;
+ painter.handleTableContentPosition(tcpos);
+ } else {
+ log.debug("Ignoring position: " + pos);
+ }
+ }
+ painter.addAreasAndFlushRow(true);
+
+ if (footerElements != null) {
+ //Positions for footers are simply added at the end
+ PositionIterator iter = new KnuthPossPosIter(footerElements);
+ while (iter.hasNext()) {
+ Position pos = (Position)iter.next();
+ if (pos instanceof TableContentPosition) {
+ TableContentPosition tcpos = (TableContentPosition)pos;
+ painter.handleTableContentPosition(tcpos);
+ } else {
+ log.debug("Ignoring position: " + pos);
+ }
+ }
+ painter.addAreasAndFlushRow(true);
+ }
+
+ painter.notifyEndOfSequence();
+ this.usedBPD += painter.getAccumulatedBPD();
+ }
+
+ private class RowPainter {
+
+ private TableRow rowFO = null;
+ private int colCount = getColumns().getColumnCount();
+ private int yoffset = 0;
+ private int accumulatedBPD = 0;
+ private EffRow lastRow = null;
+ private LayoutContext layoutContext;
+ private int lastRowHeight = 0;
+ private int[] firstRow = new int[3];
+ private Map[] rowOffsets = new Map[] {new java.util.HashMap(),
+ new java.util.HashMap(), new java.util.HashMap()};
+
+ //These three variables are our buffer to recombine the individual steps into cells
+ private PrimaryGridUnit[] gridUnits = new PrimaryGridUnit[colCount];
+ private int[] start = new int[colCount];
+ private int[] end = new int[colCount];
+ private int[] partLength = new int[colCount];
+
+ public RowPainter(LayoutContext layoutContext) {
+ this.layoutContext = layoutContext;
+ Arrays.fill(firstRow, -1);
+ }
+
+ public int getAccumulatedBPD() {
+ return this.accumulatedBPD;
+ }
+
+ public void notifyEndOfSequence() {
+ this.accumulatedBPD += lastRowHeight; //for last row
+ }
+
+ public void handleTableContentPosition(TableContentPosition tcpos) {
+ log.debug("===handleTableContentPosition(" + tcpos);
+ rowFO = null;
+ if (lastRow != tcpos.row && lastRow != null) {
+ addAreasAndFlushRow(false);
+ yoffset += lastRowHeight;
+ this.accumulatedBPD += lastRowHeight;
+ }
+ lastRow = tcpos.row;
+ Iterator partIter = tcpos.gridUnitParts.iterator();
+ //Iterate over all grid units in the current step
+ while (partIter.hasNext()) {
+ GridUnitPart gup = (GridUnitPart)partIter.next();
+ log.debug(">" + gup);
+ int colIndex = gup.pgu.getStartCol();
+ if (gridUnits[colIndex] != gup.pgu) {
+ gridUnits[colIndex] = gup.pgu;
+ start[colIndex] = gup.start;
+ end[colIndex] = gup.end;
+ } else {
+ if (gup.end < end[colIndex]) {
+ throw new IllegalStateException("Internal Error: stepper problem");
+ }
+ end[colIndex] = gup.end;
+ }
+ if (rowFO == null) {
+ //Find the row if any
+ rowFO = gridUnits[colIndex].getRow();
+ }
+ }
+ }
+
+ public int addAreasAndFlushRow(boolean forcedFlush) {
+ int actualRowHeight = 0;
+ int readyCount = 0;
+
+ int bt = lastRow.getBodyType();
+ rowOffsets[bt].put(new Integer(lastRow.getIndex()), new Integer(yoffset));
+
+ for (int i = 0; i < gridUnits.length; i++) {
+ if ((gridUnits[i] != null)
+ && (forcedFlush || (end[i] == gridUnits[i].getElements().size() - 1))) {
+ log.debug("getting len for " + i + " "
+ + start[i] + "-" + end[i]);
+ readyCount++;
+ int len = ElementListUtils.calcContentLength(
+ gridUnits[i].getElements(), start[i], end[i]);
+ partLength[i] = len;
+ log.debug("len of part: " + len);
+ if (start[i] == 0 && lastRow.getExplicitHeight().min > 0) {
+ len = Math.max(len, lastRow.getExplicitHeight().opt);
+ }
+
+ //Now add the borders to the contentLength
+ if (isSeparateBorderModel()) {
+ len += gridUnits[i].getBorders().getBorderBeforeWidth(false);
+ len += gridUnits[i].getBorders().getBorderAfterWidth(false);
+ }
+ int startRow = Math.max(gridUnits[i].getStartRow(), firstRow[bt]);
+ Integer storedOffset = (Integer)rowOffsets[bt].get(new Integer(startRow));
+ int effYOffset;
+ if (storedOffset != null) {
+ effYOffset = storedOffset.intValue();
+ } else {
+ effYOffset = yoffset;
+ }
+ len -= yoffset - effYOffset;
+ actualRowHeight = Math.max(actualRowHeight, len);
+ }
+ }
+ if (readyCount == 0) {
+ return 0;
+ }
+ lastRowHeight = actualRowHeight;
+
+ //Add areas for row
+ addRowBackgroundArea(rowFO, actualRowHeight, layoutContext.getRefIPD(), yoffset);
+ for (int i = 0; i < gridUnits.length; i++) {
+ GridUnit currentGU = lastRow.safelyGetGridUnit(i);
+ if ((gridUnits[i] != null)
+ && (forcedFlush || (end[i] == gridUnits[i].getElements().size() - 1))
+ && (currentGU == null || currentGU.isLastGridUnitRowSpan())) {
+ //the last line in the "if" above is to avoid a premature end of an
+ //row-spanned cell because no GridUnitParts are generated after a cell is
+ //finished with its content. currentGU can be null if there's no grid unit
+ //at this place in the current row (empty cell and no borders to process)
+ if (log.isDebugEnabled()) {
+ log.debug((forcedFlush ? "FORCED " : "") + "flushing..." + i + " "
+ + start[i] + "-" + end[i]);
+ }
+ addAreasForCell(gridUnits[i], start[i], end[i],
+ layoutContext, lastRow, yoffset,
+ partLength[i], actualRowHeight);
+ gridUnits[i] = null;
+ start[i] = 0;
+ end[i] = 0;
+ partLength[i] = 0;
+ }
+ }
+ return actualRowHeight;
+ }
+
+ private void addAreasForCell(PrimaryGridUnit pgu, int start, int end,
+ LayoutContext layoutContext, EffRow row,
+ int yoffset, int contentHeight, int rowHeight) {
+ int bt = row.getBodyType();
+ if (firstRow[bt] < 0) {
+ firstRow[bt] = row.getIndex();
+ }
+ //Determine the first row in this sequence
+ //TODO Maybe optimize since addAreasAndFlushRow uses almost the same code
+ int startRow = Math.max(pgu.getStartRow(), firstRow[bt]);
+ int effYOffset = ((Integer)rowOffsets[bt].get(new Integer(startRow))).intValue();
+ int effCellHeight = rowHeight;
+ effCellHeight += yoffset - effYOffset;
+ log.debug("current row: " + row.getIndex());
+ log.debug("start row: " + pgu.getStartRow() + " " + yoffset + " " + effYOffset);
+ log.debug("contentHeight: " + contentHeight + " rowHeight=" + rowHeight
+ + " effCellHeight=" + effCellHeight);
+ Cell cellLM = pgu.getCellLM();
+ cellLM.setXOffset(getXOffsetOfGridUnit(pgu));
+ cellLM.setYOffset(effYOffset);
+ cellLM.setContentHeight(contentHeight);
+ cellLM.setRowHeight(effCellHeight);
+ //cellLM.setRowHeight(row.getHeight().opt);
+ cellLM.addAreas(new KnuthPossPosIter(pgu.getElements(),
+ start, end + 1), layoutContext);
+ }
+
+ }
+
+ /**
+ * Get the area for a row for background.
+ * @param row the table-row object or null
+ * @return the row area or null if there's no background to paint
+ */
+ public Block getRowArea(TableRow row) {
+ if (row == null || !row.getCommonBorderPaddingBackground().hasBackground()) {
+ return null;
+ } else {
+ Block block = new Block();
+ block.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
+ block.setPositioning(Block.ABSOLUTE);
+ TraitSetter.addBackground(block, row.getCommonBorderPaddingBackground());
+ return block;
+ }
+ }
+
+ public void addRowBackgroundArea(TableRow row, int bpd, int ipd, int yoffset) {
+ //Add row background if any
+ Block rowBackground = getRowArea(row);
+ if (rowBackground != null) {
+ rowBackground.setBPD(bpd);
+ rowBackground.setIPD(ipd);
+ rowBackground.setXOffset(this.startXOffset);
+ rowBackground.setYOffset(yoffset);
+ getTableLM().addChildArea(rowBackground);
+ }
+ }
+
+
+ /**
+ * Sets the overall starting x-offset. Used for proper placement of cells.
+ * @param startXOffset starting x-offset (table's start-indent)
+ */
+ public void setStartXOffset(int startXOffset) {
+ this.startXOffset = startXOffset;
+ }
+
+ public int getUsedBPD() {
+ return this.usedBPD;
+ }
+
+ protected static class GridUnitPart {
+
+ protected PrimaryGridUnit pgu;
+ protected int start;
+ protected int end;
+
+ protected GridUnitPart(PrimaryGridUnit pgu, int start, int end) {
+ this.pgu = pgu;
+ this.start = start;
+ this.end = end;
+ }
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ StringBuffer sb = new StringBuffer("Part: ");
+ sb.append(start).append("-").append(end);
+ sb.append(" ").append(pgu);
+ return sb.toString();
+ }
+
+ }
+
+ public static class TableContentPosition extends Position {
+
+ protected List gridUnitParts;
+ protected EffRow row;
+
+ protected TableContentPosition(LayoutManager lm, List gridUnitParts,
+ EffRow row) {
+ super(lm);
+ this.gridUnitParts = gridUnitParts;
+ this.row = row;
+ }
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ StringBuffer sb = new StringBuffer("TableContentPosition {");
+ sb.append(gridUnitParts);
+ sb.append("}");
+ return sb.toString();
+ }
+ }
+
+ public static class TableHeaderFooterPosition extends Position {
+
+ protected boolean header;
+ protected List nestedElements;
+
+ protected TableHeaderFooterPosition(LayoutManager lm,
+ boolean header, List nestedElements) {
+ super(lm);
+ this.header = header;
+ this.nestedElements = nestedElements;
+ }
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ StringBuffer sb = new StringBuffer("Table");
+ sb.append(header ? "Header" : "Footer");
+ sb.append("Position {");
+ sb.append(nestedElements);
+ sb.append("}");
+ return sb.toString();
+ }
+ }
+
+ public static class TableHFPenaltyPosition extends Position {
+
+ protected List headerElements;
+ protected List footerElements;
+
+ protected TableHFPenaltyPosition(LayoutManager lm) {
+ super(lm);
+ }
+
+ /** @see java.lang.Object#toString() */
+ public String toString() {
+ StringBuffer sb = new StringBuffer("TableHFPenaltyPosition");
+ sb.append(" {");
+ sb.append("header:");
+ sb.append(headerElements);
+ sb.append(", footer:");
+ sb.append(footerElements);
+ sb.append("}");
+ return sb.toString();
+ }
+ }
+
+ private class KnuthBoxCellWithBPD extends KnuthBox {
+
+ private PrimaryGridUnit pgu;
+
+ public KnuthBoxCellWithBPD(int w, PrimaryGridUnit pgu) {
+ super(w, null, true);
+ this.pgu = pgu;
+ }
+ }
+
+}
import org.apache.fop.datatypes.Length;
import org.apache.fop.datatypes.PercentBase;
import org.apache.fop.fo.flow.Table;
+import org.apache.fop.fo.flow.TableColumn;
import org.apache.fop.fo.properties.TableColLength;
+import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.KnuthGlue;
+import org.apache.fop.layoutmgr.KnuthPenalty;
import org.apache.fop.layoutmgr.LayoutManager;
import org.apache.fop.layoutmgr.LeafPosition;
-import org.apache.fop.layoutmgr.BreakPoss;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.BreakPossPosIter;
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.layoutmgr.TraitSetter;
import org.apache.fop.area.Area;
import org.apache.fop.traits.SpaceVal;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
/**
* LayoutManager for a table FO.
- * A table consists of columns, table header, table footer and multiple
+ * A table consists of oldColumns, table header, table footer and multiple
* table bodies.
* The header, footer and body add the areas created from the table cells.
- * The table then creates areas for the columns, bodies and rows
+ * The table then creates areas for the oldColumns, bodies and rows
* the render background.
*/
-public class TableLayoutManager extends BlockStackingLayoutManager {
+public class TableLayoutManager extends BlockStackingLayoutManager
+ implements BlockLevelLayoutManager {
private Table fobj;
- private List columns = null;
+ private TableContentLayoutManager contentLM;
+ private ColumnSetup columns = null;
private Block curBlockArea;
- private List bodyBreaks = new java.util.ArrayList();
- private BreakPoss headerBreak;
- private BreakPoss footerBreak;
- private boolean firstRowHandled = false;
-
private int referenceIPD;
private boolean autoLayout = true;
public TableLayoutManager(Table node) {
super(node);
fobj = node;
+ this.columns = new ColumnSetup(node);
}
/** @return the table FO */
}
/**
- * Set the columns for this table.
- *
- * @param cols the list of column layout managers
+ * @return the column setup for this table.
*/
- public void setColumns(List cols) {
- columns = cols;
+ public ColumnSetup getColumns() {
+ return this.columns;
}
-
+
/** @see org.apache.fop.layoutmgr.AbstractLayoutManager#initProperties() */
protected void initProperties() {
super.initProperties();
}
/**
- * Get the next break possibility.
- * The break possibility depends on the height of the header and footer
- * and possible breaks inside the table body.
- *
- * @param context the layout context for finding breaks
- * @return the next break possibility
+ * @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(org.apache.fop.layoutmgr.LayoutContext, int)
*/
- public BreakPoss getNextBreakPoss(LayoutContext context) {
- Body curLM; // currently active LM
+ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+
+ //Body curLM; // currently active LM
referenceIPD = context.getRefIPD();
if (fobj.getInlineProgressionDimension().getOptimum().getEnum() != EN_AUTO) {
stackSize.add(spaceBefore);
}
- BreakPoss lastPos = null;
-
fobj.setLayoutDimension(PercentBase.BLOCK_IPD, referenceIPD);
fobj.setLayoutDimension(PercentBase.BLOCK_BPD, context.getStackLimit().opt);
fobj.setLayoutDimension(PercentBase.REFERENCE_AREA_IPD, referenceIPD);
fobj.setLayoutDimension(PercentBase.REFERENCE_AREA_BPD, context.getStackLimit().opt);
- if (columns == null) {
- createColumnsFromFirstRow();
- }
-
// either works out table of column widths or if proportional-column-width function
// is used works out total factor, so that value of single unit can be computed.
int sumCols = 0;
float factors = 0;
- if (columns != null) {
- for (Iterator i = columns.iterator(); i.hasNext(); ) {
- Column column = (Column) i.next();
- Length width = column.getWidth();
- sumCols += width.getValue();
- if (width instanceof TableColLength) {
- factors += ((TableColLength) width).getTableUnits();
- }
+ for (Iterator i = columns.iterator(); i.hasNext(); ) {
+ TableColumn column = (TableColumn) i.next();
+ Length width = column.getColumnWidth();
+ sumCols += width.getValue();
+ if (width instanceof TableColLength) {
+ factors += ((TableColLength) width).getTableUnits();
}
}
- // sets TABLE_UNITS in case where one or more columns is defined using proportional-column-width
+ // sets TABLE_UNITS in case where one or more oldColumns is defined using
+ // proportional-column-width
if (sumCols < contentIPD) {
if (fobj.getLayoutDimension(PercentBase.TABLE_UNITS).floatValue() == 0.0) {
fobj.setLayoutDimension(PercentBase.TABLE_UNITS,
(contentIPD - sumCols) / factors);
}
}
-
- boolean headerFooterBuilt = false;
-
- while ((curLM = (Body)getChildLM()) != null) {
- if (!headerFooterBuilt) {
- //Calculate the headers and footers only when needed
- MinOptMax headerSize = null;
- if (getTable().getTableHeader() != null) {
- if (!getTable().omitHeaderAtBreak() || !firstRowHandled) {
- Body tableHeader = new Body(getTable().getTableHeader());
- tableHeader.setParent(this);
- headerBreak = getHeight(tableHeader, context);
- headerSize = headerBreak.getStackingSize();
- stackSize.add(headerSize);
- }
- }
- //TODO Implement table-omit-footer-at-break once the page breaking
- //is improved, so we don't have to do this twice
- MinOptMax footerSize = null;
- if (getTable().getTableFooter() != null) {
- Body tableFooter = new Body(getTable().getTableFooter());
- tableFooter.setParent(this);
- footerBreak = getHeight(tableFooter, context);
- footerSize = footerBreak.getStackingSize();
- stackSize.add(footerSize);
- }
+ LinkedList returnedList = null;
+ LinkedList contentList = new LinkedList();
+ LinkedList returnList = new LinkedList();
+ //Position returnPosition = new NonLeafPosition(this, null);
+ //Body prevLM = null;
- if (stackSize.opt > context.getStackLimit().max) {
- BreakPoss breakPoss = new BreakPoss(
- new LeafPosition(this, 0));
- breakPoss.setFlag(BreakPoss.NEXT_OVERFLOWS, true);
- breakPoss.setStackingSize(stackSize);
- return breakPoss;
+ LayoutContext childLC = new LayoutContext(0);
+ childLC.setStackLimit(
+ MinOptMax.subtract(context.getStackLimit(),
+ stackSize));
+ childLC.setRefIPD(context.getRefIPD());
+
+ contentLM = new TableContentLayoutManager(this);
+ returnedList = contentLM.getNextKnuthElements(childLC, alignment);
+ log.debug(returnedList);
+
+ if (returnedList.size() == 1
+ && ((KnuthElement) returnedList.getFirst()).isPenalty()
+ && ((KnuthPenalty) returnedList.getFirst()).getP() == -KnuthElement.INFINITE) {
+ // a descendant of this block has break-before
+ if (returnList.size() == 0) {
+ // the first child (or its first child ...) has
+ // break-before;
+ // all this block, including space before, will be put in
+ // the
+ // following page
+ //FIX ME
+ //bSpaceBeforeServed = false;
}
- headerFooterBuilt = true;
- }
-
- // Make break positions
- // Set up a LayoutContext
- int ipd = context.getRefIPD();
- BreakPoss bp;
-
- LayoutContext childLC = new LayoutContext(0);
- childLC.setStackLimit(
- MinOptMax.subtract(context.getStackLimit(),
- stackSize));
- childLC.setRefIPD(ipd);
-
- curLM.setColumns(columns);
-
- boolean over = false;
- while (!curLM.isFinished()) {
- if ((bp = curLM.getNextBreakPoss(childLC)) != null) {
- if (stackSize.opt + bp.getStackingSize().opt > context.getStackLimit().max) {
- // reset to last break
- if (lastPos != null) {
- LayoutManager lm = lastPos.getLayoutManager();
- lm.resetPosition(lastPos.getPosition());
- if (lm != curLM) {
- curLM.resetPosition(null);
- }
- } else {
- curLM.resetPosition(null);
- }
- over = true;
- break;
+ contentList.addAll(returnedList);
+
+ // "wrap" the Position inside each element
+ // moving the elements from contentList to returnList
+ returnedList = new LinkedList();
+ wrapPositionElements(contentList, returnList);
+
+ return returnList;
+ } else {
+ /*
+ if (prevLM != null) {
+ // there is a block handled by prevLM
+ // before the one handled by curLM
+ if (mustKeepTogether()
+ || prevLM.mustKeepWithNext()
+ || curLM.mustKeepWithPrevious()) {
+ // add an infinite penalty to forbid a break between
+ // blocks
+ contentList.add(new KnuthPenalty(0,
+ KnuthElement.INFINITE, false,
+ new Position(this), false));
+ } else if (!((KnuthElement) contentList.getLast()).isGlue()) {
+ // add a null penalty to allow a break between blocks
+ contentList.add(new KnuthPenalty(0, 0, false,
+ new Position(this), false));
+ } else {
+ // the last element in contentList is a glue;
+ // it is a feasible breakpoint, there is no need to add
+ // a penalty
}
- stackSize.add(bp.getStackingSize());
- lastPos = bp;
- bodyBreaks.add(bp);
- firstRowHandled = true;
-
- if (bp.nextBreakOverflows()) {
- over = true;
- break;
+ }*/
+ contentList.addAll(returnedList);
+ /*
+ if (returnedList.size() == 0) {
+ //Avoid NoSuchElementException below (happens with empty blocks)
+ continue;
+ }*/
+ if (((KnuthElement) returnedList.getLast()).isPenalty()
+ && ((KnuthPenalty) returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+ // a descendant of this block has break-after
+ if (false /*curLM.isFinished()*/) {
+ // there is no other content in this block;
+ // it's useless to add space after before a page break
+ setFinished(true);
}
- childLC.setStackLimit(MinOptMax.subtract(
- context.getStackLimit(), stackSize));
+ returnedList = new LinkedList();
+ wrapPositionElements(contentList, returnList);
+
+ return returnList;
}
- }
- BreakPoss breakPoss = new BreakPoss(
- new LeafPosition(this, bodyBreaks.size() - 1));
- if (over) {
- breakPoss.setFlag(BreakPoss.NEXT_OVERFLOWS, true);
- }
- breakPoss.setStackingSize(stackSize);
- return breakPoss;
+
}
+ wrapPositionElements(contentList, returnList);
setFinished(true);
- return null;
- }
-
- private void createColumnsFromFirstRow() {
- this.columns = new java.util.ArrayList();
- //TODO Create columns from first row here
- //--> rule 2 in "fixed table layout", see CSS2, 17.5.2
- //Alternative: extend columns on-the-fly, but in this case we need the
- //new property evaluation context so proportional-column-width() works
- //correctly.
- if (columns.size() == 0) {
- Column col = new Column(getTable().getDefaultColumn());
- col.setParent(this);
- this.columns.add(col);
- }
- }
-
- /**
- * @param column the column to check
- * @return true if the column is the first column
- */
- public boolean isFirst(Column column) {
- return (this.columns.size() == 0 || this.columns.get(0) == column);
- }
-
- /**
- * @param column the column to check
- * @return true if the column is the last column
- */
- public boolean isLast(Column column) {
- return (this.columns.size() == 0 || this.columns.get(columns.size() - 1) == column);
+ return returnList;
}
- /**
- * Get the break possibility and height of the table header or footer.
- *
- * @param lm the header or footer layout manager
- * @param context the parent layout context
- * @return the break possibility containing the stacking size
- */
- protected BreakPoss getHeight(Body lm, LayoutContext context) {
- int referenceIPD = context.getRefIPD();
- int contentIPD = referenceIPD - getIPIndents();
- BreakPoss bp;
-
- MinOptMax stackSize = new MinOptMax();
-
- LayoutContext childLC = new LayoutContext(0);
- childLC.setStackLimit(context.getStackLimit());
- childLC.setRefIPD(contentIPD);
-
- lm.setColumns(columns);
-
- List breaks = new java.util.ArrayList();
- while (!lm.isFinished()) {
- if ((bp = lm.getNextBreakPoss(childLC)) != null) {
- stackSize.add(bp.getStackingSize());
- breaks.add(bp);
- childLC.setStackLimit(MinOptMax.subtract(
- context.getStackLimit(), stackSize));
- }
- }
- BreakPoss breakPoss = new BreakPoss(
- new SectionPosition(this, breaks.size() - 1, breaks));
- breakPoss.setStackingSize(stackSize);
- return breakPoss;
- }
-
/**
* The table area is a reference area that contains areas for
- * columns, bodies, rows and the contents are in cells.
+ * oldColumns, bodies, rows and the contents are in cells.
*
* @param parentIter the position iterator
* @param layoutContext the layout context for adding areas
public void addAreas(PositionIterator parentIter,
LayoutContext layoutContext) {
getParentArea(null);
- addID(fobj.getId());
+ getPSLM().addIDToPage(fobj.getId());
// if adjusted space before
double adjust = layoutContext.getSpaceAdjust();
// add column, body then row areas
int tableHeight = 0;
- Body childLM;
+ //Body childLM;
LayoutContext lc = new LayoutContext(0);
- // add table header areas
- if (headerBreak != null) {
- SectionPosition pos = (SectionPosition)headerBreak.getPosition();
- List list = pos.list;
- PositionIterator breakPosIter = new BreakPossPosIter(list, 0, list.size() + 1);
- while ((childLM = (Body)breakPosIter.getNextChildLM()) != null) {
- childLM.setXOffset(startXOffset);
- childLM.addAreas(breakPosIter, lc);
- tableHeight += childLM.getBodyHeight();
- }
- }
-
- int iStartPos = 0;
- while (parentIter.hasNext()) {
- LeafPosition lfp = (LeafPosition) parentIter.next();
- // Add the block areas to Area
- PositionIterator breakPosIter =
- new BreakPossPosIter(bodyBreaks, iStartPos,
- lfp.getLeafPos() + 1);
- iStartPos = lfp.getLeafPos() + 1;
- while ((childLM = (Body)breakPosIter.getNextChildLM()) != null) {
- childLM.setXOffset(startXOffset);
- childLM.setYOffset(tableHeight);
- childLM.addAreas(breakPosIter, lc);
- tableHeight += childLM.getBodyHeight();
- }
- }
- // add footer areas
- if (footerBreak != null) {
- SectionPosition pos = (SectionPosition)footerBreak.getPosition();
- List list = pos.list;
- PositionIterator breakPosIter = new BreakPossPosIter(list, 0, list.size() + 1);
- while ((childLM = (Body)breakPosIter.getNextChildLM()) != null) {
- childLM.setXOffset(startXOffset);
- childLM.setYOffset(tableHeight);
- childLM.addAreas(breakPosIter, lc);
- tableHeight += childLM.getBodyHeight();
- }
- }
+ lc.setRefIPD(referenceIPD - getIPIndents());
+ contentLM.setStartXOffset(startXOffset);
+ contentLM.addAreas(parentIter, lc);
+ tableHeight += contentLM.getUsedBPD();
curBlockArea.setBPD(tableHeight);
// if adjusted space after
addBlockSpacing(adjust, spaceAfter);
- bodyBreaks.clear();
+ //bodyBreaks.clear();
curBlockArea = null;
}
}
}
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#negotiateBPDAdjustment(int, org.apache.fop.layoutmgr.KnuthElement)
+ */
+ public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#discardSpace(org.apache.fop.layoutmgr.KnuthGlue)
+ */
+ public void discardSpace(KnuthGlue spaceGlue) {
+ // TODO Auto-generated method stub
+
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
+ */
+ public boolean mustKeepTogether() {
+ //TODO Keeps will have to be more sophisticated sooner or later
+ return ((BlockLevelLayoutManager)getParent()).mustKeepTogether()
+ || !fobj.getKeepTogether().getWithinPage().isAuto()
+ || !fobj.getKeepTogether().getWithinColumn().isAuto();
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
+ */
+ public boolean mustKeepWithPrevious() {
+ return !fobj.getKeepWithPrevious().getWithinPage().isAuto()
+ || !fobj.getKeepWithPrevious().getWithinColumn().isAuto();
+ }
+
+ /**
+ * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
+ */
+ public boolean mustKeepWithNext() {
+ return !fobj.getKeepWithNext().getWithinPage().isAuto()
+ || !fobj.getKeepWithNext().getWithinColumn().isAuto();
+ }
+
}
--- /dev/null
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.table;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.fo.flow.Table;
+import org.apache.fop.fo.flow.TableBody;
+import org.apache.fop.fo.flow.TableCell;
+import org.apache.fop.fo.flow.TableColumn;
+import org.apache.fop.fo.flow.TableRow;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
+
+
+/**
+ * <p>Iterator that lets the table layout manager step over all rows of a table.
+ * </p>
+ * <p>Note: This class is not thread-safe.
+ * </p>
+ */
+public class TableRowIterator {
+
+ /** Selects the list of table-body elements for iteration. */
+ public static final int BODY = 0;
+ /** Selects the table-header element for iteration. */
+ public static final int HEADER = 1;
+ /** Selects the table-footer element for iteration. */
+ public static final int FOOTER = 2;
+
+ /** Logger **/
+ private static Log log = LogFactory.getLog(TableRowIterator.class);
+
+ /** The table on with this instance operates. */
+ protected Table table;
+ private ColumnSetup columns;
+ private int type;
+
+ /** Holds the current row (TableCell instances) */
+ private List currentRow = new java.util.ArrayList();
+ /** Holds the grid units of cell from the last row while will span over the current row
+ * (GridUnit instance) */
+ private List lastRowsSpanningCells = new java.util.ArrayList();
+ private int currentRowIndex = -1;
+ //TODO rows should later be a Jakarta Commons LinkedList so concurrent modifications while
+ //using a ListIterator are possible
+ /** List of cache rows. */
+ private List rows = new java.util.ArrayList();
+ //private int indexOfFirstRowInList;
+ private int currentIndex = -1;
+
+ //prefetch state
+ private ListIterator bodyIterator = null;
+ private ListIterator childInBodyIterator = null;
+
+ public TableRowIterator(Table table, ColumnSetup columns, int what) {
+ this.table = table;
+ this.columns = columns;
+ this.type = what;
+ switch(what) {
+ case HEADER: {
+ List bodyList = new java.util.ArrayList();
+ bodyList.add(table.getTableHeader());
+ this.bodyIterator = bodyList.listIterator();
+ break;
+ }
+ case FOOTER: {
+ List bodyList = new java.util.ArrayList();
+ bodyList.add(table.getTableFooter());
+ this.bodyIterator = bodyList.listIterator();
+ break;
+ }
+ default: {
+ this.bodyIterator = table.getChildNodes();
+ }
+ }
+ }
+
+ public void prefetchAll() {
+ while (prefetchNext()) {
+ log.trace("found row...");
+ }
+ }
+
+ /**
+ * Returns the next row group if any. A row group in this context is the minimum number of
+ * consecutive rows which contains all spanned grid units of its cells.
+ * @return the next row group, or null
+ */
+ public EffRow[] getNextRowGroup() {
+ EffRow firstRowInGroup = getNextRow();
+ if (firstRowInGroup == null) {
+ return null;
+ }
+ EffRow lastRowInGroup = firstRowInGroup;
+ int lastIndex = lastRowInGroup.getIndex();
+ boolean allFinished = true;
+ do {
+ Iterator iter = lastRowInGroup.getGridUnits().iterator();
+ while (iter.hasNext()) {
+ GridUnit gu = (GridUnit)iter.next();
+ if (!gu.isLastGridUnitRowSpan()) {
+ allFinished = false;
+ break;
+ }
+ }
+ if (!allFinished) {
+ lastIndex = lastRowInGroup.getIndex();
+ lastRowInGroup = getNextRow();
+ if (lastRowInGroup == null) {
+ allFinished = true;
+ }
+ }
+ } while (!allFinished);
+ int rowCount = lastIndex - firstRowInGroup.getIndex() + 1;
+ EffRow[] rowGroup = new EffRow[rowCount];
+ for (int i = 0; i < rowCount; i++) {
+ rowGroup[i] = getCachedRow(i + firstRowInGroup.getIndex());
+ }
+ return rowGroup;
+ }
+
+ public EffRow getNextRow() {
+ currentIndex++;
+ boolean moreRows = true;
+ while (moreRows && rows.size() < currentIndex + 1) {
+ moreRows = prefetchNext();
+ }
+ if (currentIndex < rows.size()) {
+ return getCachedRow(currentIndex);
+ } else {
+ return null;
+ }
+ }
+
+ public void backToPreviewRow() {
+ currentIndex--;
+ }
+
+ public EffRow getFirstRow() {
+ if (rows.size() == 0) {
+ prefetchNext();
+ }
+ return getCachedRow(0);
+ }
+
+ public EffRow getLastRow() {
+ while (prefetchNext()) {
+ //nop
+ }
+ return getCachedRow(rows.size() - 1);
+ }
+
+ public EffRow getCachedRow(int index) {
+ if (index < 0 || index >= rows.size()) {
+ return null;
+ } else {
+ return (EffRow)rows.get(index);
+ }
+ }
+
+ private boolean prefetchNext() {
+ boolean firstInTable = false;
+ boolean firstInBody = false;
+ if (childInBodyIterator != null) {
+ if (!childInBodyIterator.hasNext()) {
+ //force skip on to next body
+ childInBodyIterator = null;
+ }
+ }
+ if (childInBodyIterator == null) {
+ if (bodyIterator.hasNext()) {
+ childInBodyIterator = ((TableBody)bodyIterator.next()).getChildNodes();
+ if (rows.size() == 0) {
+ firstInTable = true;
+ }
+ firstInBody = true;
+ } else {
+ //no more rows
+ if (rows.size() > 0) {
+ getCachedRow(rows.size() - 1).setFlagForAllGridUnits(
+ GridUnit.LAST_IN_BODY, true);
+ if ((type == FOOTER || table.getTableFooter() == null)
+ && type != HEADER) {
+ getCachedRow(rows.size() - 1).setFlagForAllGridUnits(
+ GridUnit.LAST_IN_TABLE, true);
+ }
+ }
+ return false;
+ }
+ }
+ Object node = childInBodyIterator.next();
+ this.currentRow.clear();
+ this.currentRowIndex++;
+ TableRow rowFO = null;
+ if (node instanceof TableRow) {
+ rowFO = (TableRow)node;
+ ListIterator cellIterator = rowFO.getChildNodes();
+ while (cellIterator.hasNext()) {
+ this.currentRow.add(cellIterator.next());
+ }
+ } else if (node instanceof TableCell) {
+ this.currentRow.add(node);
+ if (!((TableCell)node).endsRow()) {
+ while (childInBodyIterator.hasNext()) {
+ TableCell cell = (TableCell)childInBodyIterator.next();
+ if (cell.startsRow()) {
+ //next row already starts here, one step back
+ childInBodyIterator.previous();
+ break;
+ }
+ this.currentRow.add(cell);
+ if (cell.endsRow()) {
+ break;
+ }
+ }
+ }
+ } else {
+ throw new IllegalStateException("Illegal class found: " + node.getClass().getName());
+ }
+ EffRow gridUnits = buildGridRow(this.currentRow, rowFO);
+ if (firstInBody) {
+ gridUnits.setFlagForAllGridUnits(GridUnit.FIRST_IN_BODY, true);
+ }
+ if (firstInTable && (type == HEADER || table.getTableHeader() == null)
+ && type != FOOTER) {
+ gridUnits.setFlagForAllGridUnits(GridUnit.FIRST_IN_TABLE, true);
+ }
+ log.debug(gridUnits);
+ rows.add(gridUnits);
+ return true;
+ }
+
+ private void safelySetListItem(List list, int position, Object obj) {
+ while (position >= list.size()) {
+ list.add(null);
+ }
+ list.set(position, obj);
+ }
+
+ private Object safelyGetListItem(List list, int position) {
+ if (position >= list.size()) {
+ return null;
+ } else {
+ return list.get(position);
+ }
+ }
+
+ private EffRow buildGridRow(List cells, TableRow rowFO) {
+ EffRow row = new EffRow(this.currentRowIndex, type);
+ List gridUnits = row.getGridUnits();
+
+ TableBody bodyFO = null;
+
+ //Create all row-spanned grid units based on information from the last row
+ int colnum = 1;
+ ListIterator spanIter = lastRowsSpanningCells.listIterator();
+ GridUnit[] horzSpan = null;
+ while (spanIter.hasNext()) {
+ GridUnit gu = (GridUnit)spanIter.next();
+ if (gu != null) {
+ if (gu.getColSpanIndex() == 0) {
+ horzSpan = new GridUnit[gu.getCell().getNumberColumnsSpanned()];
+ }
+ GridUnit newGU = gu.createNextRowSpanningGridUnit();
+ newGU.setRow(rowFO);
+ safelySetListItem(gridUnits, colnum - 1, newGU);
+ horzSpan[newGU.getColSpanIndex()] = newGU;
+ if (newGU.isLastGridUnitColSpan()) {
+ //Add the array of row-spanned grid units to the primary grid unit
+ newGU.getPrimary().addRow(horzSpan);
+ horzSpan = null;
+ }
+ if (newGU.isLastGridUnitRowSpan()) {
+ spanIter.set(null);
+ } else {
+ spanIter.set(newGU);
+ }
+ }
+ colnum++;
+ }
+
+ //Transfer available cells to their slots
+ colnum = 1;
+ ListIterator iter = cells.listIterator();
+ while (iter.hasNext()) {
+ TableCell cell = (TableCell)iter.next();
+
+ if (cell.hasColumnNumber()) {
+ colnum = cell.getColumnNumber();
+ } else {
+ //Skip columns with spanning grid units
+ while (safelyGetListItem(gridUnits, colnum - 1) != null) {
+ colnum++;
+ }
+ }
+
+ if (safelyGetListItem(gridUnits, colnum - 1) != null) {
+ log.error("Overlapping cell at position " + colnum);
+ //TODO throw layout exception
+ }
+ TableColumn col = columns.getColumn(colnum);
+
+ //Add grid unit for primary grid unit
+ PrimaryGridUnit gu = new PrimaryGridUnit(cell, col, colnum - 1, this.currentRowIndex);
+ safelySetListItem(gridUnits, colnum - 1, gu);
+ boolean hasRowSpanningLeft = !gu.isLastGridUnitRowSpan();
+ if (hasRowSpanningLeft) {
+ safelySetListItem(lastRowsSpanningCells, colnum - 1, gu);
+ }
+
+ if (gu.hasSpanning()) {
+ //Add grid units on spanned slots if any
+ horzSpan = new GridUnit[cell.getNumberColumnsSpanned()];
+ horzSpan[0] = gu;
+ for (int j = 1; j < cell.getNumberColumnsSpanned(); j++) {
+ colnum++;
+ GridUnit guSpan = new GridUnit(gu, columns.getColumn(colnum), colnum - 1, j);
+ if (safelyGetListItem(gridUnits, colnum - 1) != null) {
+ log.error("Overlapping cell at position " + colnum);
+ //TODO throw layout exception
+ }
+ safelySetListItem(gridUnits, colnum - 1, guSpan);
+ if (hasRowSpanningLeft) {
+ safelySetListItem(lastRowsSpanningCells, colnum - 1, gu);
+ }
+ horzSpan[j] = guSpan;
+ }
+ gu.addRow(horzSpan);
+ }
+
+ //Gather info for empty grid units (used later)
+ if (bodyFO == null) {
+ bodyFO = gu.getBody();
+ }
+
+ colnum++;
+ }
+
+ //Post-processing the list (looking for gaps and resolve start and end borders)
+ fillEmptyGridUnits(gridUnits, rowFO, bodyFO);
+ resolveStartEndBorders(gridUnits);
+
+ return row;
+ }
+
+ private void fillEmptyGridUnits(List gridUnits, TableRow row, TableBody body) {
+ for (int pos = 1; pos <= gridUnits.size(); pos++) {
+ GridUnit gu = (GridUnit)gridUnits.get(pos - 1);
+
+ //Empty grid units
+ if (gu == null) {
+ //Add grid unit
+ gu = new EmptyGridUnit(row, columns.getColumn(pos), body,
+ pos - 1);
+ gridUnits.set(pos - 1, gu);
+ }
+
+ //Set flags
+ gu.setFlag(GridUnit.IN_FIRST_COLUMN, (pos == 1));
+ gu.setFlag(GridUnit.IN_LAST_COLUMN, (pos == gridUnits.size()));
+ }
+ }
+
+ private void resolveStartEndBorders(List gridUnits) {
+ for (int pos = 1; pos <= gridUnits.size(); pos++) {
+ GridUnit starting = (GridUnit)gridUnits.get(pos - 1);
+
+ //Border resolution
+ if (table.isSeparateBorderModel()) {
+ starting.assignBorderForSeparateBorderModel();
+ } else {
+ //Neighbouring grid unit at start edge
+ GridUnit start = null;
+ int find = pos - 1;
+ while (find >= 1) {
+ GridUnit candidate = (GridUnit)gridUnits.get(find - 1);
+ if (candidate.isLastGridUnitColSpan()) {
+ start = candidate;
+ break;
+ }
+ find--;
+ }
+
+ //Ending grid unit for current cell
+ GridUnit ending = null;
+ if (starting.getCell() != null) {
+ pos += starting.getCell().getNumberColumnsSpanned() - 1;
+ }
+ ending = (GridUnit)gridUnits.get(pos - 1);
+
+ //Neighbouring grid unit at end edge
+ GridUnit end = null;
+ find = pos + 1;
+ while (find <= gridUnits.size()) {
+ GridUnit candidate = (GridUnit)gridUnits.get(find - 1);
+ if (candidate.isPrimary()) {
+ end = candidate;
+ break;
+ }
+ find++;
+ }
+ //CommonBorderPaddingBackground borders = new CommonBorderPaddingBackground();
+ starting.resolveBorder(start,
+ CommonBorderPaddingBackground.START);
+ //starting.setBorders(borders);
+ /*
+ if (starting != ending) {
+ borders = new CommonBorderPaddingBackground();
+ }*/
+ ending.resolveBorder(end,
+ CommonBorderPaddingBackground.END);
+ //ending.setBorders(borders);
+ //Only start and end borders here, before and after during layout
+ //TODO resolve before and after borders during layout
+ }
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed 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.table;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
+import org.apache.fop.layoutmgr.ElementListUtils;
+import org.apache.fop.layoutmgr.KnuthBox;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.KnuthPenalty;
+import org.apache.fop.layoutmgr.table.TableContentLayoutManager.GridUnitPart;
+import org.apache.fop.layoutmgr.table.TableContentLayoutManager.TableContentPosition;
+import org.apache.fop.layoutmgr.table.TableContentLayoutManager.TableHFPenaltyPosition;
+
+/**
+ * This class processes row groups to create combined element lists for tables.
+ */
+public class TableStepper {
+
+ /** Logger **/
+ private static Log log = LogFactory.getLog(TableStepper.class);
+
+ private TableContentLayoutManager tclm;
+
+ private EffRow[] rowGroup;
+ private int totalHeight;
+ private int activeRow;
+ private List[] elementLists;
+ private int[] startRow;
+ private int[] start;
+ private int[] end;
+ private int[] widths;
+ private int[] baseWidth;
+ private int[] borderBefore;
+ private int[] borderAfter;
+ private boolean rowBacktrackForLastStep;
+
+ /**
+ * Main constructor
+ * @param tclm The parent TableContentLayoutManager
+ */
+ public TableStepper(TableContentLayoutManager tclm) {
+ this.tclm = tclm;
+ this.activeRow = 0;
+ }
+
+ private void setup(int columnCount) {
+ elementLists = new List[columnCount];
+ startRow = new int[columnCount];
+ start = new int[columnCount];
+ end = new int[columnCount];
+ widths = new int[columnCount];
+ baseWidth = new int[columnCount];
+ borderBefore = new int[columnCount];
+ borderAfter = new int[columnCount];
+ Arrays.fill(end, -1);
+ }
+
+ private EffRow getActiveRow() {
+ return rowGroup[activeRow];
+ }
+
+ private GridUnit getActiveGridUnit(int column) {
+ return getActiveRow().getGridUnit(column);
+ }
+
+ private PrimaryGridUnit getActivePrimaryGridUnit(int column) {
+ return getActiveGridUnit(column).getPrimary();
+ }
+
+ private void calcTotalHeight() {
+ totalHeight = 0;
+ for (int i = 0; i < rowGroup.length; i++) {
+ totalHeight += rowGroup[i].getHeight().opt;
+ }
+ log.debug("totalHeight=" + totalHeight);
+ }
+
+ private int getMaxRemainingHeight() {
+ int maxW = 0;
+ if (!rowBacktrackForLastStep) {
+ for (int i = 0; i < widths.length; i++) {
+ if (elementLists[i] == null) {
+ continue;
+ }
+ if (getActivePrimaryGridUnit(i).getCell().getNumberRowsSpanned() > 1) {
+ continue;
+ }
+ int len = widths[i];
+ if (len > 0) {
+ len += borderBefore[i] + borderAfter[i];
+ }
+ if (len == rowGroup[activeRow].getHeight().opt) {
+ //row is filled
+ maxW = 0;
+ break;
+ }
+ maxW = Math.max(maxW, rowGroup[activeRow].getHeight().opt - len);
+ }
+ }
+ for (int i = activeRow + 1; i < rowGroup.length; i++) {
+ maxW += rowGroup[i].getHeight().opt;
+ }
+ //log.debug("maxRemainingHeight=" + maxW);
+ return maxW;
+ }
+
+ private void setupElementList(int column) {
+ GridUnit gu = getActiveGridUnit(column);
+ EffRow row = getActiveRow();
+ if (gu.isPrimary() && !gu.isEmpty()) {
+ PrimaryGridUnit pgu = (PrimaryGridUnit)gu;
+ if (row.getExplicitHeight().min > 0) {
+ boolean contentsSmaller = ElementListUtils.removeLegalBreaks(
+ pgu.getElements(), row.getExplicitHeight());
+ if (contentsSmaller) {
+ List list = new java.util.ArrayList(1);
+ list.add(new KnuthBoxCellWithBPD(
+ row.getExplicitHeight().opt, pgu));
+ elementLists[column] = list;
+ } else {
+ //Copy elements (LinkedList) to array lists to improve
+ //element access performance
+ elementLists[column] = new java.util.ArrayList(pgu.getElements());
+ }
+ } else {
+ //Copy elements (LinkedList) to array lists to improve
+ //element access performance
+ elementLists[column] = new java.util.ArrayList(pgu.getElements());
+ }
+ if (isSeparateBorderModel()) {
+ borderBefore[column] = pgu.getBorders().getBorderBeforeWidth(false);
+ } else {
+ borderBefore[column] = pgu.getBorders().getBorderBeforeWidth(false) / 2;
+ }
+ start[column] = 0;
+ end[column] = -1;
+ widths[column] = 0;
+ startRow[column] = activeRow;
+ }
+ }
+
+ private void initializeElementLists() {
+ for (int i = 0; i < start.length; i++) {
+ setupElementList(i);
+ }
+ }
+
+ /**
+ * Creates the combined element list for a row group.
+ * @param rowGroup the row group
+ * @param maxColumnCount the maximum number of columns to expect
+ * @param bodyType Indicates what type of body is processed (boder, header or footer)
+ * @return the combined element list
+ */
+ public LinkedList getCombinedKnuthElementsForRowGroup(
+ EffRow[] rowGroup, int maxColumnCount, int bodyType) {
+ this.rowGroup = rowGroup;
+ setup(maxColumnCount);
+ initializeElementLists();
+ calcTotalHeight();
+
+ int laststep = 0;
+ int step;
+ int addedBoxLen = 0;
+ LinkedList returnList = new LinkedList();
+ while ((step = getNextStep(laststep)) > 0) {
+ if (rowBacktrackForLastStep) {
+ //Even though we've already switched to the next row, we have to
+ //calculate as if we were still on the previous row
+ activeRow--;
+ }
+ int increase = step - laststep;
+ int penaltyLen = step + getMaxRemainingHeight() - totalHeight;
+ int boxLen = step - addedBoxLen - penaltyLen;
+ addedBoxLen += boxLen;
+
+ //Put all involved grid units into a list
+ List gridUnitParts = new java.util.ArrayList(maxColumnCount);
+ for (int i = 0; i < start.length; i++) {
+ if (end[i] >= start[i]) {
+ PrimaryGridUnit pgu = getActivePrimaryGridUnit(i);
+ if (start[i] == 0 && end[i] == 0
+ && elementLists[i].size() == 1
+ && elementLists[i].get(0) instanceof KnuthBoxCellWithBPD) {
+ //Special case: Cell with fixed BPD
+ gridUnitParts.add(new GridUnitPart(pgu,
+ 0, pgu.getElements().size() - 1));
+ } else {
+ gridUnitParts.add(new GridUnitPart(pgu, start[i], end[i]));
+ }
+ }
+ }
+ //log.debug(">>> guPARTS: " + gridUnitParts);
+
+ //Create elements for step
+ int effPenaltyLen = penaltyLen;
+ if (isSeparateBorderModel()) {
+ CommonBorderPaddingBackground borders
+ = getTableLM().getTable().getCommonBorderPaddingBackground();
+ effPenaltyLen += borders.getBorderBeforeWidth(false);
+ effPenaltyLen += borders.getBorderAfterWidth(false);
+ }
+ TableContentPosition tcpos = new TableContentPosition(getTableLM(),
+ gridUnitParts, getActiveRow());
+ log.debug(" - " + rowBacktrackForLastStep + " - " + activeRow + " - " + tcpos);
+ returnList.add(new KnuthBox(boxLen, tcpos, false));
+ TableHFPenaltyPosition penaltyPos = new TableHFPenaltyPosition(getTableLM());
+ if (bodyType == TableRowIterator.BODY) {
+ if (!getTableLM().getTable().omitHeaderAtBreak()) {
+ effPenaltyLen += tclm.getHeaderNetHeight();
+ penaltyPos.headerElements = tclm.getHeaderElements();
+ }
+ if (!getTableLM().getTable().omitFooterAtBreak()) {
+ effPenaltyLen += tclm.getFooterNetHeight();
+ penaltyPos.footerElements = tclm.getFooterElements();
+ }
+ }
+ returnList.add(new KnuthPenalty(effPenaltyLen, 0, false, penaltyPos, false));
+
+ log.debug("step=" + step + " (+" + increase + ")"
+ + " box=" + boxLen
+ + " penalty=" + penaltyLen
+ + " effPenalty=" + effPenaltyLen);
+
+ laststep = step;
+ if (rowBacktrackForLastStep) {
+ //If row was set to previous, restore now
+ activeRow++;
+ }
+ }
+ return returnList;
+ }
+
+ private int getNextStep(int lastStep) {
+ int[] backupWidths = new int[start.length];
+ System.arraycopy(widths, 0, backupWidths, 0, backupWidths.length);
+
+ //set starting points
+ int rowPendingIndicator = 0;
+ for (int i = 0; i < start.length; i++) {
+ if (elementLists[i] == null) {
+ continue;
+ }
+ if (end[i] < elementLists[i].size()) {
+ start[i] = end[i] + 1;
+ if (end[i] + 1 < elementLists[i].size()
+ && getActivePrimaryGridUnit(i).isLastGridUnitRowSpan()) {
+ rowPendingIndicator++;
+ }
+ } else {
+ start[i] = -1; //end of list reached
+ end[i] = -1;
+ }
+ }
+
+ if (rowPendingIndicator == 0) {
+ if (activeRow < rowGroup.length - 1) {
+ activeRow++;
+ log.debug("===> new row: " + activeRow);
+ initializeElementLists();
+ for (int i = 0; i < backupWidths.length; i++) {
+ if (end[i] < 0) {
+ backupWidths[i] = 0;
+ }
+ }
+ }
+ }
+
+ //Get next possible sequence for each cell
+ int seqCount = 0;
+ for (int i = 0; i < start.length; i++) {
+ if (elementLists[i] == null) {
+ continue;
+ }
+ while (end[i] + 1 < elementLists[i].size()) {
+ end[i]++;
+ KnuthElement el = (KnuthElement)elementLists[i].get(end[i]);
+ if (el.isPenalty()) {
+ if (el.getP() < KnuthElement.INFINITE) {
+ //First legal break point
+ break;
+ }
+ } else if (el.isGlue()) {
+ KnuthElement prev = (KnuthElement)elementLists[i].get(end[i] - 1);
+ if (prev.isBox()) {
+ //Second legal break point
+ break;
+ }
+ widths[i] += el.getW();
+ } else {
+ widths[i] += el.getW();
+ }
+ }
+ if (end[i] < start[i]) {
+ widths[i] = backupWidths[i];
+ } else {
+ seqCount++;
+ }
+ //log.debug("part " + start[i] + "-" + end[i] + " " + widths[i]);
+ if (end[i] + 1 >= elementLists[i].size()) {
+ //element list for this cell is finished
+ if (isSeparateBorderModel()) {
+ borderAfter[i] = getActivePrimaryGridUnit(i)
+ .getBorders().getBorderAfterWidth(false);
+ } else {
+ borderAfter[i] = getActivePrimaryGridUnit(i).getHalfMaxAfterBorderWidth();
+ }
+ } else {
+ //element list for this cell is not finished
+ if (isSeparateBorderModel()) {
+ borderAfter[i] = getActivePrimaryGridUnit(i)
+ .getBorders().getBorderAfterWidth(false);
+ } else {
+ //TODO fix me!
+ borderAfter[i] = getActivePrimaryGridUnit(i).getHalfMaxAfterBorderWidth();
+ }
+ }
+ log.debug("borders before=" + borderBefore[i] + " after=" + borderAfter[i]);
+ }
+ if (seqCount == 0) {
+ return 0;
+ }
+
+ //Determine smallest possible step
+ int minStep = Integer.MAX_VALUE;
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < widths.length; i++) {
+ baseWidth[i] = 0;
+ for (int prevRow = 0; prevRow < startRow[i]; prevRow++) {
+ baseWidth[i] += rowGroup[prevRow].getHeight().opt;
+ }
+ baseWidth[i] += borderBefore[i] + borderAfter[i];
+ if (end[i] >= start[i]) {
+ int len = baseWidth[i] + widths[i];
+ sb.append(len + " ");
+ minStep = Math.min(len, minStep);
+ }
+ }
+ log.debug("candidate steps: " + sb);
+
+ //Check for constellations that would result in overlapping borders
+ /*
+ for (int i = 0; i < widths.length; i++) {
+
+ }*/
+
+ //Reset bigger-than-minimum sequences
+ rowBacktrackForLastStep = false;
+ for (int i = 0; i < widths.length; i++) {
+ int len = baseWidth[i] + widths[i];
+ if (len > minStep) {
+ widths[i] = backupWidths[i];
+ end[i] = start[i] - 1;
+ if (baseWidth[i] + widths[i] > minStep) {
+ log.debug("Meeeeep!");
+ rowBacktrackForLastStep = true;
+ }
+ }
+ }
+ if (log.isDebugEnabled()) {
+ /*StringBuffer*/ sb = new StringBuffer();
+ for (int i = 0; i < widths.length; i++) {
+ if (end[i] >= start[i]) {
+ sb.append(i + ": " + start[i] + "-" + end[i] + "(" + widths[i] + "), ");
+ } else {
+ sb.append(i + ": skip, ");
+ }
+ }
+ log.debug(sb.toString());
+ }
+
+ return minStep;
+ }
+
+
+ /** @return true if the table uses the separate border model. */
+ private boolean isSeparateBorderModel() {
+ return getTableLM().getTable().isSeparateBorderModel();
+ }
+
+ /** @return the table layout manager */
+ private TableLayoutManager getTableLM() {
+ return this.tclm.getTableLM();
+ }
+
+ private class KnuthBoxCellWithBPD extends KnuthBox {
+
+ private PrimaryGridUnit pgu;
+
+ public KnuthBoxCellWithBPD(int w, PrimaryGridUnit pgu) {
+ super(w, null, true);
+ this.pgu = pgu;
+ }
+ }
+
+}
currentBPPosition = 0;
currentIPPosition = 0;
- RegionReference region = port.getRegion();
+ RegionReference regionReference = port.getRegionReference();
handleRegionTraits(port);
// shouldn't the viewport have the CTM
- startVParea(region.getCTM());
+ startVParea(regionReference.getCTM());
// do after starting viewport area
- if (region.getRegionClass() == FO_REGION_BODY) {
- renderBodyRegion((BodyRegion) region);
+ if (regionReference.getRegionClass() == FO_REGION_BODY) {
+ renderBodyRegion((BodyRegion) regionReference);
} else {
- renderRegion(region);
+ renderRegion(regionReference);
}
endVParea();
}
Span span = null;
List spans = mr.getSpans();
+ int saveBPPos = currentBPPosition;
for (int count = 0; count < spans.size(); count++) {
span = (Span) spans.get(count);
int offset = (mr.getWidth()
- - (span.getColumnCount() - 1) * mr.getColumnGap())
- / span.getColumnCount() + mr.getColumnGap();
+ - (mr.getColumnCount() - 1) * mr.getColumnGap())
+ / mr.getColumnCount() + mr.getColumnGap();
for (int c = 0; c < span.getColumnCount(); c++) {
NormalFlow flow = (NormalFlow) span.getNormalFlow(c);
- renderFlow(flow);
- currentIPPosition += offset;
+ if (flow != null) {
+ currentBPPosition = saveBPPos;
+ renderFlow(flow);
+ currentIPPosition += flow.getIPD();
+ currentIPPosition += offset;
+ }
}
currentIPPosition = saveIPPos;
- currentBPPosition += span.getHeight();
+ currentBPPosition = saveBPPos + span.getHeight();
}
}
float width = (float)(viewArea.getWidth() / 1000f);
float height = (float)(viewArea.getHeight() / 1000f);
- if (region.getRegion().getRegionClass() == FO_REGION_BODY) {
+ if (region.getRegionReference().getRegionClass() == FO_REGION_BODY) {
currentBPPosition = region.getBorderAndPaddingWidthBefore();
currentIPPosition = region.getBorderAndPaddingWidthStart();
}
}
}
- boolean b[] = new boolean[] {
+ boolean[] b = new boolean[] {
(bpsBefore != null), (bpsEnd != null),
(bpsAfter != null), (bpsStart != null)};
if (!b[0] && !b[1] && !b[2] && !b[3]) {
return;
}
- float bw[] = new float[] {
+ float[] bw = new float[] {
(b[0] ? bpsBefore.width / 1000f : 0.0f),
(b[1] ? bpsEnd.width / 1000f : 0.0f),
(b[2] ? bpsAfter.width / 1000f : 0.0f),
(b[3] ? bpsStart.width / 1000f : 0.0f)};
- float clipw[] = new float[] {
+ float[] clipw = new float[] {
BorderProps.getClippedWidth(bpsBefore) / 1000f,
BorderProps.getClippedWidth(bpsEnd) / 1000f,
BorderProps.getClippedWidth(bpsAfter) / 1000f,
width -= clipw[3];
width -= clipw[1];
- boolean slant[] = new boolean[] {
+ boolean[] slant = new boolean[] {
(b[3] && b[0]), (b[0] && b[1]), (b[1] && b[2]), (b[2] && b[3])};
if (bpsBefore != null) {
endTextObject();
lineTo(sx2, innery);
closePath();
clip();
- drawBorderLine(sx1a, outery, ex1a, innery, true, true, bpsBefore.style, bpsBefore.color);
+ drawBorderLine(sx1a, outery, ex1a, innery, true, true,
+ bpsBefore.style, bpsBefore.color);
restoreGraphicsState();
}
if (bpsEnd != null) {
if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
sx1a -= clipw[3];
}
- if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
+ if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) {
ex1a += clipw[1];
}
lineTo(ex1a, outery);
float sy1 = starty;
float sy2 = (slant[0] ? sy1 + bw[0] - clipw[0] : sy1);
float ey1 = sy1 + height;
- float ey2 = (slant[3] ? ey1 - bw[2] + clipw[2]: ey1);
+ float ey2 = (slant[3] ? ey1 - bw[2] + clipw[2] : ey1);
float outerx = startx - clipw[3];
float clipx = outerx + clipw[3];
float innerx = outerx + bw[3];
if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) {
ey1a += clipw[2];
}
- lineTo(outerx, sy1a);
lineTo(outerx, ey1a);
+ lineTo(outerx, sy1a);
}
lineTo(clipx, sy1);
lineTo(innerx, sy2);
addTraitAttributes(port);
addAttribute("rect", port.getViewArea());
startElement("regionViewport", atts);
- RegionReference region = port.getRegion();
+ RegionReference region = port.getRegionReference();
atts.clear();
addAreaAttributes(region);
addTraitAttributes(region);
/*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
package org.apache.fop.traits;
+import org.apache.fop.datatypes.KeepValue;
import org.apache.fop.fo.Constants;
/**
public int breakBefore; // enum constant BreakBefore.xxx
public int breakAfter; // enum constant BreakAfter.xxx
+ public KeepValue keepWithPrevious; /*LF*/
+ public KeepValue keepWithNext; /*LF*/
+ public KeepValue keepTogether; /*LF*/
+ public int orphans; /*LF*/
+ public int widows; /*LF*/
+ public int blockProgressionUnit; /*LF*/
+ public int lineStackingStrategy; /*LF*/
public boolean bIsSpan;
public SpaceVal spaceBefore;
public SpaceVal spaceAfter;
<fo:page-sequence master-reference="normal" white-space-collapse="true">
<fo:flow flow-name="xsl-region-body">
<fo:block>line1</fo:block>
- <fo:block break-before="column">line2</fo:block>
+ <fo:block break-before="column">line2 break-before="column"</fo:block>
<fo:block>line3</fo:block>
- <fo:block break-before="page">line4</fo:block>
+ <fo:block break-before="page">line4 break-before="page"</fo:block>
<fo:block>line5</fo:block>
- <fo:block break-before="even-page">line6</fo:block>
+ <fo:block break-before="even-page">line6 break-before="even-page"</fo:block>
<fo:block>line7</fo:block>
- <fo:block break-before="even-page">line8</fo:block>
+ <fo:block break-before="even-page">line8 break-before="even-page"</fo:block>
<fo:block>line9</fo:block>
- <fo:block break-before="odd-page">line10</fo:block>
+ <fo:block break-before="odd-page">line10 break-before="odd-page"</fo:block>
<fo:block>line11</fo:block>
- <fo:block break-before="odd-page">line12</fo:block>
+ <fo:block break-before="odd-page">line12 break-before="odd-page"</fo:block>
<fo:block>line13</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
</fo>
<checks>
- <eval expected="1" xpath="//lineArea[. = 'line1']/ancestor::pageViewport/@nr"/>
- <eval expected="2" xpath="//lineArea[. = 'line2']/ancestor::pageViewport/@nr"/>
- <eval expected="2" xpath="//lineArea[. = 'line3']/ancestor::pageViewport/@nr"/>
- <eval expected="3" xpath="//lineArea[. = 'line4']/ancestor::pageViewport/@nr"/>
- <eval expected="3" xpath="//lineArea[. = 'line5']/ancestor::pageViewport/@nr"/>
- <eval expected="4" xpath="//lineArea[. = 'line6']/ancestor::pageViewport/@nr"/>
- <eval expected="4" xpath="//lineArea[. = 'line7']/ancestor::pageViewport/@nr"/>
- <eval expected="6" xpath="//lineArea[. = 'line8']/ancestor::pageViewport/@nr"/>
- <eval expected="6" xpath="//lineArea[. = 'line9']/ancestor::pageViewport/@nr"/>
- <eval expected="7" xpath="//lineArea[. = 'line10']/ancestor::pageViewport/@nr"/>
- <eval expected="7" xpath="//lineArea[. = 'line11']/ancestor::pageViewport/@nr"/>
- <eval expected="9" xpath="//lineArea[. = 'line12']/ancestor::pageViewport/@nr"/>
- <eval expected="9" xpath="//lineArea[. = 'line13']/ancestor::pageViewport/@nr"/>
+ <eval expected="1" xpath="//lineArea[starts-with(., 'line1')]/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//lineArea[starts-with(., 'line2')]/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//lineArea[starts-with(., 'line3')]/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//lineArea[starts-with(., 'line4')]/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//lineArea[starts-with(., 'line5')]/ancestor::pageViewport/@nr"/>
+ <eval expected="4" xpath="//lineArea[starts-with(., 'line6')]/ancestor::pageViewport/@nr"/>
+ <eval expected="4" xpath="//lineArea[starts-with(., 'line7')]/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//lineArea[starts-with(., 'line8')]/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//lineArea[starts-with(., 'line9')]/ancestor::pageViewport/@nr"/>
+ <eval expected="7" xpath="//lineArea[starts-with(., 'line10')]/ancestor::pageViewport/@nr"/>
+ <eval expected="7" xpath="//lineArea[starts-with(., 'line11')]/ancestor::pageViewport/@nr"/>
+ <eval expected="9" xpath="//lineArea[starts-with(., 'line12')]/ancestor::pageViewport/@nr"/>
+ <eval expected="9" xpath="//lineArea[starts-with(., 'line13')]/ancestor::pageViewport/@nr"/>
</checks>
</testcase>
<fo:page-sequence master-reference="normal" white-space-collapse="true">
<fo:flow flow-name="xsl-region-body">
<fo:block break-after="column">line1</fo:block>
- <fo:block>line2</fo:block>
+ <fo:block>line2, last block had break-after="column"</fo:block>
<fo:block break-after="page">line3</fo:block>
- <fo:block>line4</fo:block>
+ <fo:block>line4, last block had break-after="page"</fo:block>
<fo:block break-after="even-page">line5</fo:block>
- <fo:block>line6</fo:block>
+ <fo:block>line6, last block had break-after="even-page"</fo:block>
<fo:block break-after="even-page">line7</fo:block>
- <fo:block>line8</fo:block>
+ <fo:block>line8, last block had break-after="even-page"</fo:block>
<fo:block break-after="odd-page">line9</fo:block>
- <fo:block>line10</fo:block>
+ <fo:block>line10, last block had break-after="odd-page"</fo:block>
<fo:block break-after="odd-page">line11</fo:block>
- <fo:block>line12</fo:block>
+ <fo:block>line12, last block had break-after="odd-page"</fo:block>
<fo:block>line13</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
</fo>
<checks>
- <eval expected="1" xpath="//lineArea[. = 'line1']/ancestor::pageViewport/@nr"/>
- <eval expected="2" xpath="//lineArea[. = 'line2']/ancestor::pageViewport/@nr"/>
- <eval expected="2" xpath="//lineArea[. = 'line3']/ancestor::pageViewport/@nr"/>
- <eval expected="3" xpath="//lineArea[. = 'line4']/ancestor::pageViewport/@nr"/>
- <eval expected="3" xpath="//lineArea[. = 'line5']/ancestor::pageViewport/@nr"/>
- <eval expected="4" xpath="//lineArea[. = 'line6']/ancestor::pageViewport/@nr"/>
- <eval expected="4" xpath="//lineArea[. = 'line7']/ancestor::pageViewport/@nr"/>
- <eval expected="6" xpath="//lineArea[. = 'line8']/ancestor::pageViewport/@nr"/>
- <eval expected="6" xpath="//lineArea[. = 'line9']/ancestor::pageViewport/@nr"/>
- <eval expected="7" xpath="//lineArea[. = 'line10']/ancestor::pageViewport/@nr"/>
- <eval expected="7" xpath="//lineArea[. = 'line11']/ancestor::pageViewport/@nr"/>
- <eval expected="9" xpath="//lineArea[. = 'line12']/ancestor::pageViewport/@nr"/>
- <eval expected="9" xpath="//lineArea[. = 'line13']/ancestor::pageViewport/@nr"/>
+ <eval expected="1" xpath="//lineArea[starts-with(., 'line1')]/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//lineArea[starts-with(., 'line2')]/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//lineArea[starts-with(., 'line3')]/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//lineArea[starts-with(., 'line4')]/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//lineArea[starts-with(., 'line5')]/ancestor::pageViewport/@nr"/>
+ <eval expected="4" xpath="//lineArea[starts-with(., 'line6')]/ancestor::pageViewport/@nr"/>
+ <eval expected="4" xpath="//lineArea[starts-with(., 'line7')]/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//lineArea[starts-with(., 'line8')]/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//lineArea[starts-with(., 'line9')]/ancestor::pageViewport/@nr"/>
+ <eval expected="7" xpath="//lineArea[starts-with(., 'line10')]/ancestor::pageViewport/@nr"/>
+ <eval expected="7" xpath="//lineArea[starts-with(., 'line11')]/ancestor::pageViewport/@nr"/>
+ <eval expected="9" xpath="//lineArea[starts-with(., 'line12')]/ancestor::pageViewport/@nr"/>
+ <eval expected="9" xpath="//lineArea[starts-with(., 'line13')]/ancestor::pageViewport/@nr"/>
</checks>
</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2005 The Apache Software Foundation
+
+ Licensed 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 keep-together.
+ </p>
+ <p>
+ Widows and Orphans are disabled in this test to avoid side-effects.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg" white-space-collapse="true" widows="0" orphans="0">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="4.5 * 14.4pt">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>block1</fo:block>
+ <fo:block>block2</fo:block>
+ <fo:block keep-together.within-page="always">block3
+ <fo:inline font-style="italic">
+ The quick brown fox jumps over the lazy dog.
+ The quick brown fox jumps over the lazy dog.
+ The quick brown fox jumps over the lazy dog.
+ </fo:inline>
+ </fo:block>
+ <fo:block>block4</fo:block>
+ <fo:block>block5</fo:block>
+ <fo:block keep-together.within-page="always">
+ <fo:block>block6</fo:block>
+ <fo:block>block7</fo:block>
+ <fo:block>block8</fo:block>
+ <fo:block>block9</fo:block>
+ </fo:block>
+ <fo:block>block10</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ <fo:page-sequence master-reference="normal">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>block11</fo:block>
+ <fo:block>block12</fo:block>
+ <fo:block keep-together.within-column="always">block13
+ <fo:inline font-style="italic">
+ The quick brown fox jumps over the lazy dog.
+ The quick brown fox jumps over the lazy dog.
+ The quick brown fox jumps over the lazy dog.
+ </fo:inline>
+ </fo:block>
+ <fo:block>block14</fo:block>
+ <fo:block>block15</fo:block>
+ <fo:block keep-together.within-column="always">
+ <fo:block>block16</fo:block>
+ <fo:block>block17</fo:block>
+ <fo:block>block18</fo:block>
+ <fo:block>block19</fo:block>
+ </fo:block>
+ <fo:block>block20</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ <fo:page-sequence master-reference="normal">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>block21</fo:block>
+ <fo:block>block22</fo:block>
+ <fo:block-container keep-together.within-page="always">
+ <fo:block>block23</fo:block>
+ <fo:block>block24</fo:block>
+ <fo:block>block25</fo:block>
+ </fo:block-container>
+ <fo:block>block26</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ <fo:page-sequence master-reference="normal">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>block31</fo:block>
+ <fo:block>block32</fo:block>
+ <fo:block keep-together.within-page="always">
+ <fo:block-container>
+ <fo:block>block33</fo:block>
+ </fo:block-container>
+ <fo:block>block34</fo:block>
+ <fo:block>block35</fo:block>
+ </fo:block>
+ <fo:block>block36</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="1" xpath="//lineArea[starts-with(., 'block1')]/ancestor::pageViewport/@nr"/>
+ <eval expected="1" xpath="//lineArea[starts-with(., 'block2')]/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//lineArea[starts-with(., 'block3')]/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//lineArea[starts-with(., 'block4')]/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//lineArea[starts-with(., 'block5')]/ancestor::pageViewport/@nr"/>
+ <eval expected="4" xpath="//lineArea[starts-with(., 'block6')]/ancestor::pageViewport/@nr"/>
+ <eval expected="4" xpath="//lineArea[starts-with(., 'block7')]/ancestor::pageViewport/@nr"/>
+ <eval expected="4" xpath="//lineArea[starts-with(., 'block8')]/ancestor::pageViewport/@nr"/>
+ <eval expected="4" xpath="//lineArea[starts-with(., 'block9')]/ancestor::pageViewport/@nr"/>
+ <eval expected="5" xpath="//lineArea[starts-with(., 'block10')]/ancestor::pageViewport/@nr"/>
+
+ <eval expected="6" xpath="//lineArea[starts-with(., 'block11')]/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//lineArea[starts-with(., 'block12')]/ancestor::pageViewport/@nr"/>
+ <eval expected="7" xpath="//lineArea[starts-with(., 'block13')]/ancestor::pageViewport/@nr"/>
+ <eval expected="7" xpath="//lineArea[starts-with(., 'block14')]/ancestor::pageViewport/@nr"/>
+ <eval expected="8" xpath="//lineArea[starts-with(., 'block15')]/ancestor::pageViewport/@nr"/>
+ <eval expected="9" xpath="//lineArea[starts-with(., 'block16')]/ancestor::pageViewport/@nr"/>
+ <eval expected="9" xpath="//lineArea[starts-with(., 'block17')]/ancestor::pageViewport/@nr"/>
+ <eval expected="9" xpath="//lineArea[starts-with(., 'block18')]/ancestor::pageViewport/@nr"/>
+ <eval expected="9" xpath="//lineArea[starts-with(., 'block19')]/ancestor::pageViewport/@nr"/>
+ <eval expected="10" xpath="//lineArea[starts-with(., 'block20')]/ancestor::pageViewport/@nr"/>
+
+ <eval expected="11" xpath="//lineArea[starts-with(., 'block21')]/ancestor::pageViewport/@nr"/>
+ <eval expected="11" xpath="//lineArea[starts-with(., 'block22')]/ancestor::pageViewport/@nr"/>
+ <eval expected="12" xpath="//lineArea[starts-with(., 'block23')]/ancestor::pageViewport/@nr"/>
+ <eval expected="12" xpath="//lineArea[starts-with(., 'block24')]/ancestor::pageViewport/@nr"/>
+ <eval expected="12" xpath="//lineArea[starts-with(., 'block25')]/ancestor::pageViewport/@nr"/>
+ <eval expected="12" xpath="//lineArea[starts-with(., 'block26')]/ancestor::pageViewport/@nr"/>
+
+ <eval expected="13" xpath="//lineArea[starts-with(., 'block31')]/ancestor::pageViewport/@nr"/>
+ <eval expected="13" xpath="//lineArea[starts-with(., 'block32')]/ancestor::pageViewport/@nr"/>
+ <eval expected="14" xpath="//lineArea[starts-with(., 'block33')]/ancestor::pageViewport/@nr"/>
+ <eval expected="14" xpath="//lineArea[starts-with(., 'block34')]/ancestor::pageViewport/@nr"/>
+ <eval expected="14" xpath="//lineArea[starts-with(., 'block35')]/ancestor::pageViewport/@nr"/>
+ <eval expected="14" xpath="//lineArea[starts-with(., 'block36')]/ancestor::pageViewport/@nr"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2005 The Apache Software Foundation
+
+ Licensed 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 keep-with-next.
+ </p>
+ <p>
+ Widows and Orphans are disabled in this test to avoid side-effects.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg" white-space-collapse="true" widows="0" orphans="0">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="4.5 * 14.4pt">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>block1</fo:block>
+ <fo:block>block2</fo:block>
+ <fo:block keep-with-next.within-page="always">block3</fo:block>
+ <fo:block keep-with-next.within-page="always">block4</fo:block>
+ <fo:block>block5</fo:block>
+ <fo:block keep-with-next.within-page="always">
+ <fo:block>block6</fo:block>
+ <fo:block>block7</fo:block>
+ <fo:block>block8</fo:block>
+ <fo:block>block9</fo:block>
+ <fo:block>block10</fo:block>
+ </fo:block>
+ <fo:block>block11</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ <fo:page-sequence master-reference="normal">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>block21</fo:block>
+ <fo:block>block22</fo:block>
+ <fo:block keep-with-next.within-column="always">block23</fo:block>
+ <fo:block keep-with-next.within-column="always">block24</fo:block>
+ <fo:block>block25</fo:block>
+ <fo:block keep-with-next.within-column="always">
+ <fo:block>block26</fo:block>
+ <fo:block>block27</fo:block>
+ <fo:block>block28</fo:block>
+ <fo:block>block29</fo:block>
+ <fo:block>block30</fo:block>
+ </fo:block>
+ <fo:block>block31</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="1" xpath="//lineArea[starts-with(., 'block1')]/ancestor::pageViewport/@nr"/>
+ <eval expected="1" xpath="//lineArea[starts-with(., 'block2')]/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//lineArea[starts-with(., 'block3')]/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//lineArea[starts-with(., 'block4')]/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//lineArea[starts-with(., 'block5')]/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//lineArea[starts-with(., 'block6')]/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//lineArea[starts-with(., 'block7')]/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//lineArea[starts-with(., 'block8')]/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//lineArea[starts-with(., 'block9')]/ancestor::pageViewport/@nr"/>
+ <eval expected="4" xpath="//lineArea[starts-with(., 'block10')]/ancestor::pageViewport/@nr"/>
+ <eval expected="4" xpath="//lineArea[starts-with(., 'block11')]/ancestor::pageViewport/@nr"/>
+
+ <eval expected="5" xpath="//lineArea[starts-with(., 'block21')]/ancestor::pageViewport/@nr"/>
+ <eval expected="5" xpath="//lineArea[starts-with(., 'block22')]/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//lineArea[starts-with(., 'block23')]/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//lineArea[starts-with(., 'block24')]/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//lineArea[starts-with(., 'block25')]/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//lineArea[starts-with(., 'block26')]/ancestor::pageViewport/@nr"/>
+ <eval expected="7" xpath="//lineArea[starts-with(., 'block27')]/ancestor::pageViewport/@nr"/>
+ <eval expected="7" xpath="//lineArea[starts-with(., 'block28')]/ancestor::pageViewport/@nr"/>
+ <eval expected="7" xpath="//lineArea[starts-with(., 'block29')]/ancestor::pageViewport/@nr"/>
+ <eval expected="8" xpath="//lineArea[starts-with(., 'block30')]/ancestor::pageViewport/@nr"/>
+ <eval expected="8" xpath="//lineArea[starts-with(., 'block31')]/ancestor::pageViewport/@nr"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2005 The Apache Software Foundation
+
+ Licensed 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 keep-with-previous.
+ </p>
+ <p>
+ Widows and Orphans are disabled in this test to avoid side-effects.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg" white-space-collapse="true" widows="0" orphans="0">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="4.5 * 14.4pt">
+ <fo:region-body/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>block1</fo:block>
+ <fo:block>block2</fo:block>
+ <fo:block>block3</fo:block>
+ <fo:block keep-with-previous.within-page="always">block4</fo:block>
+ <fo:block keep-with-previous.within-page="always">block5</fo:block>
+ <fo:block>
+ <fo:block>block6</fo:block>
+ <fo:block>block7</fo:block>
+ <fo:block>block8</fo:block>
+ <fo:block>block9</fo:block>
+ <fo:block>block10</fo:block>
+ </fo:block>
+ <fo:block keep-with-previous.within-page="always">block11</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ <fo:page-sequence master-reference="normal">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>block21</fo:block>
+ <fo:block>block22</fo:block>
+ <fo:block>block23</fo:block>
+ <fo:block keep-with-previous.within-column="always">block24</fo:block>
+ <fo:block keep-with-previous.within-column="always">block25</fo:block>
+ <fo:block>
+ <fo:block>block26</fo:block>
+ <fo:block>block27</fo:block>
+ <fo:block>block28</fo:block>
+ <fo:block>block29</fo:block>
+ <fo:block>block30</fo:block>
+ </fo:block>
+ <fo:block keep-with-previous.within-column="always">block31</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="1" xpath="//lineArea[starts-with(., 'block1')]/ancestor::pageViewport/@nr"/>
+ <eval expected="1" xpath="//lineArea[starts-with(., 'block2')]/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//lineArea[starts-with(., 'block3')]/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//lineArea[starts-with(., 'block4')]/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//lineArea[starts-with(., 'block5')]/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//lineArea[starts-with(., 'block6')]/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//lineArea[starts-with(., 'block7')]/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//lineArea[starts-with(., 'block8')]/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//lineArea[starts-with(., 'block9')]/ancestor::pageViewport/@nr"/>
+ <eval expected="4" xpath="//lineArea[starts-with(., 'block10')]/ancestor::pageViewport/@nr"/>
+ <eval expected="4" xpath="//lineArea[starts-with(., 'block11')]/ancestor::pageViewport/@nr"/>
+
+ <eval expected="5" xpath="//lineArea[starts-with(., 'block21')]/ancestor::pageViewport/@nr"/>
+ <eval expected="5" xpath="//lineArea[starts-with(., 'block22')]/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//lineArea[starts-with(., 'block23')]/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//lineArea[starts-with(., 'block24')]/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//lineArea[starts-with(., 'block25')]/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//lineArea[starts-with(., 'block26')]/ancestor::pageViewport/@nr"/>
+ <eval expected="7" xpath="//lineArea[starts-with(., 'block27')]/ancestor::pageViewport/@nr"/>
+ <eval expected="7" xpath="//lineArea[starts-with(., 'block28')]/ancestor::pageViewport/@nr"/>
+ <eval expected="7" xpath="//lineArea[starts-with(., 'block29')]/ancestor::pageViewport/@nr"/>
+ <eval expected="8" xpath="//lineArea[starts-with(., 'block30')]/ancestor::pageViewport/@nr"/>
+ <eval expected="8" xpath="//lineArea[starts-with(., 'block31')]/ancestor::pageViewport/@nr"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2005 The Apache Software Foundation
+
+ Licensed 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 multi-column documents.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="360pt" page-height="4in">
+ <fo:region-body column-count="2" column-gap="20pt"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal" white-space-collapse="true">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>
+ <fo:block>line1</fo:block>
+ <fo:block>line2</fo:block>
+ <fo:block background-color="orange">line3</fo:block>
+ <fo:block>line4</fo:block>
+ <fo:block>line5</fo:block>
+ <fo:block>line6</fo:block>
+ <fo:block>line7</fo:block>
+ <fo:block>line8</fo:block>
+ <fo:block>line9</fo:block>
+ <fo:block>line10</fo:block>
+ <fo:block>line11</fo:block>
+ <fo:block>line12</fo:block>
+ <fo:block>line13</fo:block>
+ <fo:block>line14</fo:block>
+ <fo:block>line15</fo:block>
+ <fo:block>line16</fo:block>
+ <fo:block>line17</fo:block>
+ <fo:block>line18</fo:block>
+ <fo:block>line19</fo:block>
+ <fo:block>line20</fo:block>
+ <fo:block>line21</fo:block>
+ <fo:block>line22</fo:block>
+ <fo:block>line23</fo:block>
+ <fo:block>line24</fo:block>
+ <fo:block>line25</fo:block>
+ <fo:block>line26</fo:block>
+ <fo:block>line27</fo:block>
+ <fo:block>line28</fo:block>
+ <fo:block>line29</fo:block>
+ <fo:block>line30</fo:block>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="1" xpath="count(//pageViewport)"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2005 The Apache Software Foundation
+
+ Licensed 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 multi-column documents.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="320pt" page-height="4in">
+ <fo:region-body column-count="3" column-gap="10pt"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="normal" white-space-collapse="true">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>
+ <fo:block>line1</fo:block>
+ <fo:block>line2</fo:block>
+ <fo:block>line3</fo:block>
+ <fo:block>line4</fo:block>
+ <fo:block>line5</fo:block>
+ <fo:block>line6</fo:block>
+ <fo:block>line7</fo:block>
+ <fo:block>line8</fo:block>
+ <fo:block>line9</fo:block>
+ <fo:block>line10</fo:block>
+ <fo:block>line11</fo:block>
+ <fo:block>line12</fo:block>
+ <fo:block>line13</fo:block>
+ </fo:block>
+ <fo:block span="all">
+ <fo:block>This line is spanned over all columns.</fo:block>
+ <fo:block>This line is spanned over all columns.</fo:block>
+ <fo:block>This line is spanned over all columns.</fo:block>
+ <fo:block>This line is spanned over all columns.</fo:block>
+ </fo:block>
+ <fo:block>
+ <fo:block>line1</fo:block>
+ <fo:block>line2</fo:block>
+ <fo:block>line3</fo:block>
+ <fo:block>line4</fo:block>
+ <fo:block>line5</fo:block>
+ <fo:block>line6</fo:block>
+ <fo:block>line7</fo:block>
+ <fo:block>line8</fo:block>
+ <fo:block>line9</fo:block>
+ <fo:block>line10</fo:block>
+ <fo:block>line11</fo:block>
+ <fo:block>line12</fo:block>
+ <fo:block>line13</fo:block>
+ </fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="1" xpath="count(//pageViewport)"/>
+ </checks>
+</testcase>
<fo:region-before background-color="lightgray" extent="20pt"/>
</fo:simple-page-master>
<fo:simple-page-master master-name="master2" page-width="5in" page-height="5in" margin-left="10pt" margin-top="20pt" margin-right="30pt" margin-bottom="40pt" background-color="rgb(100%, 90%, 90%)" reference-orientation="90">
- <fo:region-body background-color="rgb(100%, 80%, 80%)" margin-top="20pt"/>
- <fo:region-before background-color="lightgray" extent="20pt" margin-left="50pt"/>
+ <fo:region-body background-color="rgb(100%, 80%, 80%)" margin-top="35pt"/>
+ <fo:region-before background-color="lightgray" extent="20pt" margin-left="50pt" overflow="hidden"/>
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="master1" white-space-collapse="true">
</fo:flow>
</fo:page-sequence>
<fo:page-sequence master-reference="master2" white-space-collapse="true">
- <fo:static-content flow-name="xsl-region-before">
- <fo:block>region-before</fo:block>
+ <fo:static-content flow-name="xsl-region-before" widows="0" orphans="0">
+ <fo:block>region-before1</fo:block>
+ <fo:block>region-before2</fo:block>
+ <fo:block>region-before3</fo:block>
</fo:static-content>
<fo:flow flow-name="xsl-region-body">
<fo:block>testing margin with reference-orientation="90"</fo:block>
<eval expected="20000" xpath="//pageViewport[2]/page/regionViewport[1]/regionBefore/@bpda"/>
<eval expected="300000" xpath="//pageViewport[2]/page/regionViewport[1]/regionBefore/block[1]/@ipda"/>
<eval expected="14400" xpath="//pageViewport[2]/page/regionViewport[1]/regionBefore/block[1]/@bpda"/>
- <eval expected="30000 20000 300000 300000" xpath="//pageViewport[2]/page/regionViewport[2]/@rect"/>
+ <eval expected="45000 20000 285000 300000" xpath="//pageViewport[2]/page/regionViewport[2]/@rect"/>
<eval expected="300000" xpath="//pageViewport[2]/page/regionViewport[2]/regionBody/@ipda"/>
- <eval expected="300000" xpath="//pageViewport[2]/page/regionViewport[2]/regionBody/@bpda"/>
+ <eval expected="285000" xpath="//pageViewport[2]/page/regionViewport[2]/regionBody/@bpda"/>
</checks>
</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2005 The Apache Software Foundation
+
+ Licensed 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 page-masters (all regions).
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="master1" page-width="5in" page-height="5in" background-color="rgb(100%, 90%, 90%)">
+ <fo:region-body background-color="rgb(100%, 100%, 80%)" margin="30pt" display-align="fill"/>
+ <fo:region-before background-color="rgb(100%, 80%, 80%)" extent="20pt"/>
+ <fo:region-after background-color="rgb(80%, 80%, 80%)" extent="20pt" precedence="true" display-align="after"/>
+ <fo:region-start background-color="rgb(80%, 100%, 80%)" extent="20pt"/>
+ <fo:region-end background-color="rgb(80%, 80%, 100%)" extent="20pt" reference-orientation="270"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="master1" white-space-collapse="true" orphans="0" widows="0">
+ <fo:static-content flow-name="xsl-region-before">
+ <fo:block>region-before1</fo:block>
+ <fo:block>region-before2</fo:block>
+ <fo:block>region-before3</fo:block>
+ </fo:static-content>
+ <fo:static-content flow-name="xsl-region-after">
+ <fo:block font-size="50%">region-after1 with precedence and display-align="after"</fo:block>
+ </fo:static-content>
+ <fo:static-content flow-name="xsl-region-start">
+ <fo:block>region-start1</fo:block>
+ <fo:block>region-start2</fo:block>
+ <fo:block>region-start3</fo:block>
+ </fo:static-content>
+ <fo:static-content flow-name="xsl-region-end">
+ <fo:block>region-end1</fo:block>
+ <fo:block>region-end2</fo:block>
+ <fo:block>region-end3</fo:block>
+ </fo:static-content>
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block>testing all-regions1</fo:block>
+ <fo:block>testing all-regions2</fo:block>
+ <fo:block>testing all-regions3</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ </checks>
+</testcase>