Temp_KnuthStylePageBreaking branch and HEAD have been tagged prior to the merge, so merging uncommitted work from the branch should be easier. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@198627 13f79535-47bb-0310-9956-ffa450edef68tags/fop-0_90-alpha1
@@ -149,6 +149,8 @@ public class CommandLineOptions implements Constants { | |||
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")) { | |||
@@ -612,6 +614,7 @@ public class CommandLineOptions implements Constants { | |||
+ " -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" |
@@ -85,7 +85,7 @@ public class FOUserAgent { | |||
* 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; |
@@ -230,16 +230,9 @@ public class AreaTreeHandler extends FOEventHandler { | |||
// 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(); | |||
} | |||
} |
@@ -19,10 +19,9 @@ | |||
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. | |||
@@ -34,31 +33,15 @@ public class BodyRegion extends RegionReference { | |||
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); | |||
} | |||
/** | |||
@@ -89,42 +72,11 @@ public class BodyRegion extends RegionReference { | |||
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. | |||
* | |||
@@ -144,12 +96,27 @@ public class BodyRegion extends RegionReference { | |||
} | |||
/** | |||
* 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; | |||
} | |||
@@ -159,11 +126,9 @@ public class BodyRegion extends RegionReference { | |||
* @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; |
@@ -28,15 +28,17 @@ import java.util.Iterator; | |||
* 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); | |||
} | |||
@@ -45,8 +47,15 @@ public class MainReference extends Area { | |||
* | |||
* @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(); | |||
} | |||
/** | |||
@@ -58,6 +67,15 @@ public class MainReference extends Area { | |||
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 | |||
@@ -86,13 +104,14 @@ public class MainReference extends Area { | |||
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(); | |||
} | |||
/** |
@@ -26,9 +26,11 @@ package org.apache.fop.area; | |||
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); | |||
} | |||
} | |||
@@ -18,10 +18,20 @@ | |||
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. | |||
@@ -45,13 +55,109 @@ public class Page implements Serializable, Cloneable { | |||
// 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) { | |||
@@ -97,7 +203,7 @@ public class Page implements Serializable, Cloneable { | |||
return true; | |||
} | |||
else { | |||
BodyRegion body = (BodyRegion)regionBody.getRegion(); | |||
BodyRegion body = (BodyRegion)regionBody.getRegionReference(); | |||
return body.isEmpty(); | |||
} | |||
} |
@@ -18,6 +18,7 @@ | |||
package org.apache.fop.area; | |||
import java.awt.Rectangle; | |||
import java.awt.geom.Rectangle2D; | |||
import java.io.ObjectOutputStream; | |||
import java.io.ObjectInputStream; | |||
@@ -31,6 +32,7 @@ import org.apache.commons.logging.Log; | |||
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. | |||
@@ -46,6 +48,7 @@ public class PageViewport implements Resolvable, Cloneable { | |||
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; | |||
@@ -72,12 +75,26 @@ public class PageViewport implements Resolvable, Cloneable { | |||
/** | |||
* 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; | |||
} | |||
/** | |||
@@ -85,8 +102,29 @@ public class PageViewport implements Resolvable, Cloneable { | |||
* @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(); | |||
} | |||
/** | |||
@@ -229,6 +267,11 @@ public class PageViewport implements Resolvable, Cloneable { | |||
*/ | |||
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) { | |||
@@ -385,7 +428,7 @@ public class PageViewport implements Resolvable, Cloneable { | |||
*/ | |||
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; | |||
} | |||
@@ -407,4 +450,10 @@ public class PageViewport implements Resolvable, Cloneable { | |||
sb.append(getPageNumberString()); | |||
return sb.toString(); | |||
} | |||
/** | |||
* @return Returns the spm. | |||
*/ | |||
public SimplePageMaster getSPM() { | |||
return spm; | |||
} | |||
} |
@@ -24,26 +24,29 @@ import java.util.List; | |||
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; | |||
} | |||
/** | |||
@@ -58,6 +61,13 @@ public class RegionReference extends Area implements Cloneable { | |||
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. | |||
@@ -95,24 +105,6 @@ public class RegionReference extends Area implements Cloneable { | |||
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. | |||
@@ -121,7 +113,7 @@ public class RegionReference extends Area implements Cloneable { | |||
* @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; |
@@ -23,17 +23,19 @@ import java.io.IOException; | |||
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 | |||
*/ | |||
@@ -43,21 +45,21 @@ public class RegionViewport extends Area implements Cloneable { | |||
} | |||
/** | |||
* 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; | |||
} | |||
/** | |||
@@ -69,6 +71,11 @@ public class RegionViewport extends Area implements Cloneable { | |||
clip = c; | |||
} | |||
/** @return true if the viewport should be clipped. */ | |||
public boolean isClip() { | |||
return this.clip; | |||
} | |||
/** | |||
* Get the view area of this viewport. | |||
* | |||
@@ -86,7 +93,7 @@ public class RegionViewport extends Area implements Cloneable { | |||
out.writeFloat((float) viewArea.getHeight()); | |||
out.writeBoolean(clip); | |||
out.writeObject(props); | |||
out.writeObject(region); | |||
out.writeObject(regionReference); | |||
} | |||
private void readObject(java.io.ObjectInputStream in) | |||
@@ -95,7 +102,7 @@ public class RegionViewport extends Area implements Cloneable { | |||
in.readFloat(), in.readFloat()); | |||
clip = in.readBoolean(); | |||
props = (HashMap)in.readObject(); | |||
setRegion((RegionReference) in.readObject()); | |||
setRegionReference((RegionReference) in.readObject()); | |||
} | |||
/** | |||
@@ -106,7 +113,7 @@ public class RegionViewport extends Area implements Cloneable { | |||
*/ | |||
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(); | |||
} |
@@ -32,36 +32,37 @@ public class Span extends Area { | |||
// 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; | |||
} | |||
/** | |||
@@ -70,16 +71,16 @@ public class Span extends Area { | |||
* @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; | |||
} | |||
/** | |||
@@ -91,15 +92,21 @@ public class Span extends Area { | |||
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."); | |||
} | |||
} | |||
} | |||
@@ -375,7 +375,8 @@ public interface Constants { | |||
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 | |||
@@ -553,5 +554,7 @@ public interface 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; | |||
} |
@@ -1354,6 +1354,8 @@ public class FOPropertyMapping implements Constants { | |||
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); | |||
@@ -1536,6 +1538,12 @@ public class FOPropertyMapping implements Constants { | |||
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() { |
@@ -18,9 +18,8 @@ | |||
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; | |||
@@ -39,7 +38,7 @@ public abstract class FObj extends FONode implements Constants { | |||
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 | |||
@@ -164,7 +163,7 @@ public abstract class FObj extends FONode implements Constants { | |||
addMarker((Marker) child); | |||
} else { | |||
if (childNodes == null) { | |||
childNodes = new ArrayList(); | |||
childNodes = new java.util.ArrayList(); | |||
} | |||
childNodes.add(child); | |||
} | |||
@@ -198,7 +197,7 @@ public abstract class FObj extends FONode implements Constants { | |||
*/ | |||
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)); | |||
} | |||
@@ -210,7 +209,7 @@ public abstract class FObj extends FONode implements Constants { | |||
*/ | |||
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)); | |||
} | |||
@@ -307,7 +306,7 @@ public abstract class FObj extends FONode implements Constants { | |||
} | |||
} | |||
if (markers == null) { | |||
markers = new HashMap(); | |||
markers = new java.util.HashMap(); | |||
} | |||
if (!markers.containsKey(mcname)) { | |||
markers.put(mcname, marker); |
@@ -395,6 +395,7 @@ public class PropertySets { | |||
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); |
@@ -28,7 +28,6 @@ import org.apache.fop.fo.InlineCharIterator; | |||
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; | |||
@@ -20,8 +20,6 @@ package org.apache.fop.fo.flow; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.ListIterator; | |||
import org.xml.sax.Locator; | |||
@@ -29,7 +27,6 @@ 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; | |||
@@ -60,7 +57,17 @@ public class Marker extends FObjMixed { | |||
* @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"); | |||
} | |||
} | |||
/** |
@@ -22,7 +22,6 @@ import java.util.Map; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.xml.sax.Locator; | |||
@@ -66,9 +65,19 @@ public class RetrieveMarker extends FObjMixed { | |||
* @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"); | |||
} | |||
} | |||
/** |
@@ -238,6 +238,14 @@ public class Table extends FObj { | |||
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; | |||
@@ -285,20 +293,31 @@ public class Table extends FObj { | |||
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; |
@@ -102,9 +102,10 @@ public class TableBody extends FObj { | |||
getParent().removeChild(this); | |||
} | |||
} | |||
/* | |||
if (tableCellsFound) { | |||
convertCellsToRows(); | |||
} | |||
}*/ | |||
savedPropertyList = null; //Release reference | |||
} | |||
@@ -148,7 +149,7 @@ public class TableBody extends FObj { | |||
*/ | |||
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; |
@@ -156,6 +156,7 @@ public class TableCell extends FObj { | |||
if (!blockItemFound) { | |||
missingChildElementError("marker* (%block;)+"); | |||
} | |||
//TODO Complain about startsRow|endsRow=true if parent is a table-row | |||
getFOEventHandler().endCell(this); | |||
} | |||
@@ -137,13 +137,16 @@ public class TableColumn extends FObj { | |||
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"; | |||
@@ -153,5 +156,21 @@ public class TableColumn extends FObj { | |||
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(); | |||
} | |||
} | |||
@@ -1,5 +1,5 @@ | |||
/* | |||
* 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. | |||
@@ -19,28 +19,26 @@ | |||
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 | |||
*/ | |||
@@ -62,6 +60,27 @@ public class Wrapper extends FObjMixed { | |||
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. | |||
*/ |
@@ -1,5 +1,5 @@ | |||
/* | |||
* 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. | |||
@@ -22,16 +22,14 @@ package org.apache.fop.fo.pagination; | |||
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 | |||
@@ -47,19 +45,11 @@ public abstract class RegionBA extends Region { | |||
*/ | |||
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; |
@@ -25,6 +25,7 @@ import org.apache.fop.apps.FOPException; | |||
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; | |||
@@ -54,6 +55,16 @@ public class RegionBody extends Region { | |||
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."); | |||
} | |||
} | |||
/** |
@@ -1,5 +1,5 @@ | |||
/* | |||
* 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. | |||
@@ -22,16 +22,14 @@ package org.apache.fop.fo.pagination; | |||
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 | |||
/** | |||
@@ -46,16 +44,8 @@ public abstract class RegionSE extends Region { | |||
*/ | |||
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. |
@@ -0,0 +1,49 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -0,0 +1,661 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -21,23 +21,21 @@ package org.apache.fop.layoutmgr; | |||
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; | |||
@@ -51,7 +49,7 @@ public abstract class AbstractLayoutManager implements LayoutManager, Constants | |||
/** | |||
* 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; | |||
@@ -80,15 +78,6 @@ public abstract class AbstractLayoutManager implements LayoutManager, Constants | |||
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); | |||
@@ -120,38 +109,6 @@ public abstract class AbstractLayoutManager implements LayoutManager, Constants | |||
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; | |||
@@ -296,126 +253,60 @@ public abstract class AbstractLayoutManager implements LayoutManager, Constants | |||
* 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 | |||
@@ -430,9 +321,12 @@ public abstract class AbstractLayoutManager implements LayoutManager, Constants | |||
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); | |||
} | |||
} | |||
@@ -440,6 +334,20 @@ public abstract class AbstractLayoutManager implements LayoutManager, Constants | |||
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 | |||
*/ | |||
@@ -489,6 +397,4 @@ public abstract class AbstractLayoutManager implements LayoutManager, Constants | |||
addChildLM(lm); | |||
} | |||
} | |||
} | |||
@@ -0,0 +1,80 @@ | |||
/* | |||
* 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); | |||
} | |||
} | |||
} |
@@ -51,13 +51,13 @@ public class BasicLinkLayoutManager extends InlineLayoutManager { | |||
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); | |||
} | |||
} | |||
} |
@@ -37,7 +37,6 @@ public class BidiLayoutManager extends LeafNodeLayoutManager { | |||
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); |
@@ -18,14 +18,15 @@ | |||
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; | |||
@@ -40,7 +41,6 @@ import org.apache.fop.traits.SpaceVal; | |||
* LayoutManager for a block-container FO. | |||
*/ | |||
public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
private BlockContainer fobj; | |||
private BlockViewport viewportBlockArea; | |||
private Block referenceArea; | |||
@@ -56,7 +56,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
private int vpContentIPD; | |||
private int vpContentBPD; | |||
private int usedBPD; | |||
// When viewport should grow with the content. | |||
private boolean autoHeight = true; | |||
@@ -73,6 +73,15 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
//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. | |||
@@ -80,45 +89,40 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
*/ | |||
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() { | |||
@@ -127,16 +131,16 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
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; | |||
} | |||
@@ -149,6 +153,396 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
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) | |||
*/ | |||
@@ -159,7 +553,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
} | |||
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; | |||
@@ -181,19 +575,19 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
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; | |||
@@ -228,10 +622,10 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
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 | |||
@@ -348,7 +742,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
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; | |||
} | |||
@@ -365,7 +759,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
allocIPD += getIPIndents(); | |||
} else { | |||
if (isFixed()) { | |||
allocIPD = (int)getPageViewport().getViewArea().getWidth(); | |||
allocIPD = (int)getCurrentPV().getViewArea().getWidth(); | |||
} else { | |||
allocIPD = context.getRefIPD(); | |||
} | |||
@@ -381,19 +775,19 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
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) { | |||
@@ -423,9 +817,9 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
//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; | |||
} | |||
@@ -438,6 +832,186 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
* @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); | |||
@@ -450,8 +1024,8 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
}*/ | |||
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; | |||
@@ -469,12 +1043,12 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
} | |||
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); | |||
}*/ | |||
@@ -501,11 +1075,11 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
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); | |||
@@ -574,15 +1148,16 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
//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(); | |||
@@ -595,6 +1170,54 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { | |||
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; | |||
} | |||
} | |||
@@ -18,6 +18,7 @@ | |||
package org.apache.fop.layoutmgr; | |||
import java.util.LinkedList; | |||
import java.util.ListIterator; | |||
import java.util.List; | |||
@@ -35,9 +36,7 @@ import org.apache.fop.traits.MinOptMax; | |||
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. */ | |||
@@ -64,13 +63,14 @@ public class BlockLayoutManager extends BlockStackingLayoutManager { | |||
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. | |||
@@ -78,15 +78,15 @@ public class BlockLayoutManager extends BlockStackingLayoutManager { | |||
*/ | |||
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(); | |||
} | |||
/** | |||
@@ -95,8 +95,18 @@ public class BlockLayoutManager extends BlockStackingLayoutManager { | |||
* 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*/ } | |||
} | |||
/** | |||
@@ -155,7 +165,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager { | |||
*/ | |||
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()) { | |||
@@ -173,15 +183,15 @@ public class BlockLayoutManager extends BlockStackingLayoutManager { | |||
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(); | |||
@@ -204,8 +214,8 @@ public class BlockLayoutManager extends BlockStackingLayoutManager { | |||
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! | |||
@@ -263,7 +273,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager { | |||
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)); | |||
@@ -289,10 +299,42 @@ public class BlockLayoutManager extends BlockStackingLayoutManager { | |||
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); | |||
@@ -305,8 +347,9 @@ public class BlockLayoutManager extends BlockStackingLayoutManager { | |||
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 { | |||
@@ -328,17 +371,183 @@ public class BlockLayoutManager extends BlockStackingLayoutManager { | |||
} | |||
} 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. | |||
@@ -354,20 +563,21 @@ public class BlockLayoutManager extends BlockStackingLayoutManager { | |||
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 | |||
@@ -421,5 +631,12 @@ public class BlockLayoutManager extends BlockStackingLayoutManager { | |||
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; | |||
} | |||
} | |||
@@ -0,0 +1,51 @@ | |||
/* | |||
* 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(); | |||
} |
@@ -0,0 +1,783 @@ | |||
/* | |||
* 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; | |||
} | |||
} | |||
} |
@@ -137,7 +137,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager { | |||
// 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 { | |||
@@ -145,14 +145,14 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager { | |||
// 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)); | |||
} | |||
@@ -171,7 +171,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager { | |||
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 { | |||
@@ -220,7 +220,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager { | |||
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) { | |||
@@ -231,7 +231,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager { | |||
} 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)); | |||
@@ -242,7 +242,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager { | |||
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 | |||
@@ -256,7 +256,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager { | |||
} | |||
protected void addId() { | |||
addID(fobj.getId()); | |||
getPSLM().addIDToPage(fobj.getId()); | |||
} | |||
} | |||
@@ -19,21 +19,15 @@ | |||
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; | |||
@@ -47,7 +41,6 @@ import org.apache.commons.logging.LogFactory; | |||
*/ | |||
public class ContentLayoutManager implements InlineLevelLayoutManager { | |||
private FOUserAgent userAgent; | |||
private AreaTreeHandler areaTreeHandler; | |||
private Area holder; | |||
private int stackSize; | |||
private LayoutManager parentLM; | |||
@@ -117,7 +110,7 @@ public class ContentLayoutManager implements InlineLevelLayoutManager { | |||
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(); | |||
} | |||
@@ -205,10 +198,6 @@ public class ContentLayoutManager implements InlineLevelLayoutManager { | |||
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; | |||
@@ -252,44 +241,6 @@ public class ContentLayoutManager implements InlineLevelLayoutManager { | |||
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 | |||
*/ | |||
@@ -358,8 +309,8 @@ public class ContentLayoutManager implements InlineLevelLayoutManager { | |||
return contentList; | |||
} | |||
public KnuthElement addALetterSpaceTo(KnuthElement element) { | |||
return element; | |||
public List addALetterSpaceTo(List oldList) { | |||
return oldList; | |||
} | |||
public void getWordChars(StringBuffer sbChars, Position pos) { | |||
@@ -373,10 +324,13 @@ public class ContentLayoutManager implements InlineLevelLayoutManager { | |||
} | |||
public LinkedList getChangedKnuthElements(List oldList, | |||
int flaggedPenalty, | |||
/*int flaggedPenalty,*/ | |||
int alignment) { | |||
return null; | |||
} | |||
public PageSequenceLayoutManager getPSLM() { | |||
return parentLM.getPSLM(); | |||
} | |||
} | |||
@@ -0,0 +1,136 @@ | |||
/* | |||
* 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); | |||
} | |||
} |
@@ -208,7 +208,7 @@ public class ExternalGraphicLayoutManager extends LeafNodeLayoutManager { | |||
} | |||
protected void addId() { | |||
addID(fobj.getId()); | |||
getPSLM().addIDToPage(fobj.getId()); | |||
} | |||
} | |||
@@ -19,13 +19,14 @@ | |||
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. | |||
@@ -33,8 +34,8 @@ import org.apache.fop.traits.MinOptMax; | |||
* 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 */ | |||
@@ -51,6 +52,22 @@ public class FlowLayoutManager extends BlockStackingLayoutManager { | |||
*/ | |||
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. | |||
@@ -61,9 +78,7 @@ public class FlowLayoutManager extends BlockStackingLayoutManager { | |||
fobj = node; | |||
} | |||
/** | |||
* @see org.apache.fop.layoutmgr.LayoutManager#getNextBreakPoss(LayoutContext) | |||
*/ | |||
/* | |||
public BreakPoss getNextBreakPoss(LayoutContext context) { | |||
// currently active LM | |||
@@ -131,26 +146,266 @@ public class FlowLayoutManager extends BlockStackingLayoutManager { | |||
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(); | |||
} | |||
@@ -184,17 +439,5 @@ public class FlowLayoutManager extends BlockStackingLayoutManager { | |||
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; | |||
} | |||
} | |||
@@ -44,6 +44,6 @@ public class ICLayoutManager extends LeafNodeLayoutManager { | |||
} | |||
protected void addId() { | |||
addID(fobj.getId()); | |||
getPSLM().addIDToPage(fobj.getId()); | |||
} | |||
} |
@@ -179,6 +179,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager | |||
return null; | |||
} | |||
/* | |||
public KnuthElement addALetterSpaceTo(KnuthElement element) { | |||
NonLeafPosition savedPos = (NonLeafPosition) element.getPosition(); | |||
element.setPosition(savedPos.getPosition()); | |||
@@ -325,6 +326,6 @@ public class InlineLayoutManager extends InlineStackingLayoutManager | |||
returnList.add(returnedElement); | |||
} | |||
return returnList; | |||
} | |||
}*/ | |||
} | |||
@@ -1,5 +1,5 @@ | |||
/* | |||
* 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. | |||
@@ -18,7 +18,6 @@ | |||
package org.apache.fop.layoutmgr; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
/** | |||
@@ -26,25 +25,15 @@ 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 | |||
@@ -70,15 +59,4 @@ public interface InlineLevelLayoutManager extends LayoutManager { | |||
*/ | |||
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); | |||
} |
@@ -20,11 +20,14 @@ package org.apache.fop.layoutmgr; | |||
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; | |||
@@ -37,7 +40,8 @@ import org.apache.fop.traits.MinOptMax; | |||
* 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 { | |||
@@ -69,7 +73,7 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager { | |||
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; | |||
@@ -534,7 +538,7 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager { | |||
// Current child layout context | |||
protected LayoutContext getContext() { | |||
return childLC ; | |||
return childLC; | |||
} | |||
protected void addSpace(Area parentArea, MinOptMax spaceRange, | |||
@@ -558,5 +562,214 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager { | |||
} | |||
} | |||
} | |||
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; | |||
} | |||
} | |||
@@ -19,7 +19,6 @@ | |||
package org.apache.fop.layoutmgr; | |||
// Java | |||
import java.awt.geom.Point2D; | |||
import java.awt.geom.Rectangle2D; | |||
// FOP | |||
@@ -194,7 +193,7 @@ public class InstreamForeignObjectLM extends LeafNodeLayoutManager { | |||
* @see org.apache.fop.layoutmgr.LeafNodeLayoutManager#addId() | |||
*/ | |||
protected void addId() { | |||
addID(fobj.getId()); | |||
getPSLM().addIDToPage(fobj.getId()); | |||
} | |||
} | |||
@@ -0,0 +1,41 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -32,49 +32,33 @@ package org.apache.fop.layoutmgr; | |||
* 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(); | |||
} | |||
} |
@@ -45,8 +45,10 @@ package org.apache.fop.layoutmgr; | |||
* to get these values. | |||
*/ | |||
public class KnuthGlue extends KnuthElement { | |||
private int stretchability; | |||
private int shrinkability; | |||
private int adjustmentClass = -1; | |||
/** | |||
* Create a new KnuthGlue. | |||
@@ -63,21 +65,48 @@ public class KnuthGlue extends KnuthElement { | |||
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(); | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -15,9 +15,10 @@ | |||
*/ | |||
/* $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; | |||
@@ -60,7 +61,7 @@ public class KnuthParagraph { | |||
/** | |||
* The paragraph of KnuthElements. | |||
*/ | |||
private ArrayList par; | |||
private List par; | |||
/** | |||
* The width of a line. | |||
@@ -114,7 +115,7 @@ public class KnuthParagraph { | |||
protected static Log log = LogFactory.getLog(KnuthParagraph.class); | |||
public KnuthParagraph(ArrayList par) { | |||
public KnuthParagraph(List par) { | |||
this.best = new BestRecords(); | |||
this.par = par; | |||
} |
@@ -37,8 +37,12 @@ package org.apache.fop.layoutmgr; | |||
* 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. | |||
@@ -55,6 +59,14 @@ public class KnuthPenalty extends KnuthElement { | |||
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; | |||
} | |||
@@ -76,4 +88,36 @@ public class KnuthPenalty extends KnuthElement { | |||
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(); | |||
} | |||
} |
@@ -1,5 +1,5 @@ | |||
/* | |||
* 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. | |||
@@ -26,14 +26,22 @@ public class KnuthPossPosIter extends PositionIterator { | |||
/** | |||
* 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 | |||
@@ -0,0 +1,83 @@ | |||
/* | |||
* 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); | |||
} | |||
} |
@@ -87,6 +87,10 @@ public class LayoutContext { | |||
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; | |||
@@ -100,6 +104,10 @@ public class LayoutContext { | |||
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??? | |||
} | |||
@@ -235,6 +243,38 @@ public class LayoutContext { | |||
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()) + |
@@ -18,31 +18,16 @@ | |||
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. | |||
@@ -62,6 +47,12 @@ public interface LayoutManager { | |||
*/ | |||
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 | |||
@@ -167,68 +158,6 @@ public interface LayoutManager { | |||
*/ | |||
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 | |||
@@ -254,4 +183,27 @@ public interface LayoutManager { | |||
* @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); | |||
} |
@@ -1,5 +1,5 @@ | |||
/* | |||
* 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. | |||
@@ -19,7 +19,9 @@ package org.apache.fop.layoutmgr; | |||
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 | |||
@@ -34,14 +36,24 @@ public interface LayoutManagerMaker { | |||
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); | |||
} | |||
@@ -21,14 +21,11 @@ import java.util.ArrayList; | |||
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; | |||
@@ -61,13 +58,14 @@ import org.apache.fop.fo.pagination.Flow; | |||
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; | |||
/** | |||
@@ -88,7 +86,7 @@ public class LayoutManagerMapping implements LayoutManagerMaker { | |||
/** | |||
* 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()); | |||
@@ -112,14 +110,13 @@ public class LayoutManagerMapping implements LayoutManagerMaker { | |||
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()); | |||
@@ -141,22 +138,26 @@ public class LayoutManagerMapping implements LayoutManagerMaker { | |||
/** | |||
* @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 | |||
@@ -305,14 +306,9 @@ public class LayoutManagerMapping implements LayoutManagerMaker { | |||
} | |||
} | |||
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(); | |||
@@ -347,19 +343,21 @@ public class LayoutManagerMapping implements LayoutManagerMaker { | |||
} | |||
} | |||
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)); | |||
@@ -383,7 +381,7 @@ public class LayoutManagerMapping implements LayoutManagerMaker { | |||
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) { |
@@ -263,7 +263,7 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager { | |||
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, | |||
@@ -273,7 +273,7 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager { | |||
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)); | |||
@@ -308,7 +308,7 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager { | |||
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, | |||
@@ -318,7 +318,7 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager { | |||
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)); | |||
@@ -327,6 +327,6 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager { | |||
} | |||
protected void addId() { | |||
addID(fobj.getId()); | |||
getPSLM().addIDToPage(fobj.getId()); | |||
} | |||
} |
@@ -77,6 +77,12 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager | |||
super(node); | |||
} | |||
/** | |||
* Create a Leaf node layout mananger. | |||
*/ | |||
public LeafNodeLayoutManager() { | |||
} | |||
/** | |||
* get the inline area. | |||
* @param context the context used to create the area | |||
@@ -194,10 +200,10 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager | |||
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: | |||
@@ -269,21 +275,19 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager | |||
// 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) { | |||
@@ -295,7 +299,7 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager | |||
} | |||
public LinkedList getChangedKnuthElements(List oldList, | |||
int flaggedPenalty, | |||
/*int flaggedPenalty,*/ | |||
int alignment) { | |||
if (isFinished()) { | |||
return null; | |||
@@ -305,9 +309,9 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager | |||
// 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; |
@@ -0,0 +1,236 @@ | |||
/* | |||
* 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("++++++++++"); | |||
} | |||
} |
@@ -63,6 +63,17 @@ public class MinOptMaxUtil { | |||
} | |||
} | |||
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. | |||
@@ -77,4 +88,21 @@ public class MinOptMaxUtil { | |||
} | |||
} | |||
/** | |||
* 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; | |||
} | |||
} |
@@ -1,5 +1,5 @@ | |||
/* | |||
* 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. | |||
@@ -30,5 +30,18 @@ public class NonLeafPosition extends Position { | |||
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(); | |||
} | |||
} | |||
@@ -0,0 +1,81 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -58,7 +58,7 @@ public class PageNumberCitationLayoutManager extends LeafNodeLayoutManager { | |||
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); | |||
} | |||
} | |||
@@ -71,7 +71,7 @@ public class PageNumberCitationLayoutManager extends LeafNodeLayoutManager { | |||
* 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(); | |||
@@ -118,7 +118,7 @@ public class PageNumberCitationLayoutManager extends LeafNodeLayoutManager { | |||
} | |||
protected void addId() { | |||
addID(fobj.getId()); | |||
getPSLM().addIDToPage(fobj.getId()); | |||
} | |||
} | |||
@@ -46,7 +46,7 @@ public class PageNumberLayoutManager extends LeafNodeLayoutManager { | |||
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)); | |||
@@ -69,7 +69,7 @@ public class PageNumberLayoutManager extends LeafNodeLayoutManager { | |||
} | |||
protected void addId() { | |||
addID(fobj.getId()); | |||
getPSLM().addIDToPage(fobj.getId()); | |||
} | |||
} | |||
@@ -20,119 +20,93 @@ package org.apache.fop.layoutmgr; | |||
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(); | |||
} | |||
/** | |||
@@ -142,42 +116,106 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { | |||
*/ | |||
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; | |||
@@ -191,79 +229,70 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { | |||
* @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. | |||
@@ -273,40 +302,34 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { | |||
* @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 | |||
@@ -317,14 +340,19 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { | |||
* 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 | |||
@@ -339,7 +367,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { | |||
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) { | |||
@@ -351,71 +379,55 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { | |||
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; | |||
} | |||
@@ -423,59 +435,31 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { | |||
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). | |||
@@ -487,103 +471,41 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { | |||
*/ | |||
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); | |||
} | |||
} | |||
@@ -594,130 +516,39 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { | |||
* 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); | |||
} | |||
} |
@@ -1,5 +1,5 @@ | |||
/* | |||
* 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. | |||
@@ -37,5 +37,18 @@ public class Position { | |||
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(); | |||
} | |||
} | |||
@@ -28,7 +28,7 @@ public abstract class PositionIterator implements Iterator { | |||
private LayoutManager childLM; | |||
private boolean bHasNext; | |||
PositionIterator(Iterator pIter) { | |||
protected PositionIterator(Iterator pIter) { | |||
parentIter = pIter; | |||
lookAhead(); | |||
//checkNext(); |
@@ -21,11 +21,16 @@ package org.apache.fop.layoutmgr; | |||
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. | |||
@@ -34,8 +39,7 @@ import java.util.Map; | |||
* 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) { | |||
@@ -46,10 +50,106 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager { | |||
* 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) | |||
*/ | |||
@@ -86,7 +186,9 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager { | |||
* @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); | |||
@@ -103,8 +205,9 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager { | |||
} | |||
blockBreaks.clear(); | |||
*/ | |||
flush(); | |||
region = null; | |||
targetRegion = null; | |||
} | |||
@@ -115,25 +218,106 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager { | |||
* @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 | |||
} | |||
} | |||
} | |||
@@ -37,8 +37,7 @@ import org.apache.fop.traits.MinOptMax; | |||
* 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. | |||
@@ -131,6 +130,7 @@ public class TextLayoutManager extends AbstractLayoutManager | |||
* @param node The FOText object to be rendered | |||
*/ | |||
public TextLayoutManager(FOText node) { | |||
super(); | |||
foText = node; | |||
textArray = new char[node.endIndex - node.startIndex]; | |||
@@ -645,10 +645,10 @@ public class TextLayoutManager extends AbstractLayoutManager | |||
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: | |||
@@ -706,7 +706,7 @@ public class TextLayoutManager extends AbstractLayoutManager | |||
- 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, | |||
@@ -724,14 +724,14 @@ public class TextLayoutManager extends AbstractLayoutManager | |||
(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; | |||
@@ -791,42 +791,64 @@ public class TextLayoutManager extends AbstractLayoutManager | |||
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); | |||
@@ -837,19 +859,38 @@ public class TextLayoutManager extends AbstractLayoutManager | |||
} | |||
} | |||
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) { | |||
@@ -948,7 +989,7 @@ public class TextLayoutManager extends AbstractLayoutManager | |||
} | |||
public LinkedList getChangedKnuthElements(List oldList, | |||
int flaggedPenalty, | |||
/*int flaggedPenalty,*/ | |||
int alignment) { | |||
if (isFinished()) { | |||
return null; | |||
@@ -960,13 +1001,21 @@ public class TextLayoutManager extends AbstractLayoutManager | |||
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)); | |||
@@ -979,17 +1028,37 @@ public class TextLayoutManager extends AbstractLayoutManager | |||
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 | |||
@@ -1003,7 +1072,7 @@ public class TextLayoutManager extends AbstractLayoutManager | |||
- 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, |
@@ -27,14 +27,16 @@ 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.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. | |||
@@ -50,6 +52,20 @@ public class Item extends BlockStackingLayoutManager { | |||
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. | |||
*/ | |||
@@ -152,6 +168,11 @@ public class Item extends BlockStackingLayoutManager { | |||
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 | |||
@@ -166,26 +187,43 @@ public class Item extends BlockStackingLayoutManager { | |||
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); |
@@ -25,8 +25,8 @@ 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.NonLeafPosition; | |||
import org.apache.fop.layoutmgr.TraitSetter; | |||
import org.apache.fop.area.Area; | |||
import org.apache.fop.area.Block; | |||
@@ -34,6 +34,8 @@ import org.apache.fop.traits.MinOptMax; | |||
import org.apache.fop.traits.SpaceVal; | |||
import java.util.ArrayList; | |||
import java.util.Iterator; | |||
import java.util.LinkedList; | |||
import java.util.List; | |||
/** | |||
@@ -46,14 +48,26 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager { | |||
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; | |||
@@ -64,7 +78,7 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager { | |||
}*/ | |||
/** | |||
* 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) { | |||
@@ -166,6 +180,11 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager { | |||
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. | |||
@@ -182,26 +201,40 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager { | |||
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(); | |||
@@ -27,17 +27,22 @@ 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.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. | |||
@@ -49,11 +54,11 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager { | |||
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 | |||
@@ -68,6 +73,38 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager { | |||
} | |||
} | |||
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 | |||
@@ -226,6 +263,213 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager { | |||
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. | |||
@@ -242,26 +486,62 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager { | |||
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(); | |||
@@ -1,270 +0,0 @@ | |||
/* | |||
* 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; | |||
} | |||
} | |||
@@ -138,7 +138,7 @@ public class Caption extends BlockStackingLayoutManager { | |||
public void addAreas(PositionIterator parentIter, | |||
LayoutContext layoutContext) { | |||
getParentArea(null); | |||
addID(fobj.getId()); | |||
getPSLM().addIDToPage(fobj.getId()); | |||
LayoutManager childLM; | |||
int iStartPos = 0; |
@@ -23,30 +23,39 @@ import org.apache.fop.fo.flow.Table; | |||
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; | |||
@@ -65,16 +74,17 @@ public class Cell extends BlockStackingLayoutManager { | |||
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 */ | |||
@@ -82,6 +92,10 @@ public class Cell extends BlockStackingLayoutManager { | |||
return this.fobj; | |||
} | |||
private boolean isSeparateBorderModel() { | |||
return fobj.isSeparateBorderModel(); | |||
} | |||
/** | |||
* @see org.apache.fop.layoutmgr.AbstractLayoutManager#initProperties() | |||
*/ | |||
@@ -90,7 +104,7 @@ public class Cell extends BlockStackingLayoutManager { | |||
borderAndPaddingBPD = 0; | |||
borderAndPaddingBPD += fobj.getCommonBorderPaddingBackground().getBorderBeforeWidth(false); | |||
borderAndPaddingBPD += fobj.getCommonBorderPaddingBackground().getBorderAfterWidth(false); | |||
if (!fobj.isSeparateBorderModel()) { | |||
if (!isSeparateBorderModel()) { | |||
borderAndPaddingBPD /= 2; | |||
} | |||
borderAndPaddingBPD += fobj.getCommonBorderPaddingBackground().getPaddingBefore(false); | |||
@@ -121,22 +135,12 @@ public class Cell extends BlockStackingLayoutManager { | |||
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); | |||
@@ -144,6 +148,111 @@ public class Cell extends BlockStackingLayoutManager { | |||
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 | |||
@@ -152,7 +261,7 @@ public class Cell extends BlockStackingLayoutManager { | |||
* @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(); | |||
@@ -270,6 +379,16 @@ public class Cell extends BlockStackingLayoutManager { | |||
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. | |||
@@ -280,6 +399,21 @@ public class Cell extends BlockStackingLayoutManager { | |||
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 | |||
@@ -291,40 +425,38 @@ public class Cell extends BlockStackingLayoutManager { | |||
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; | |||
} | |||
@@ -332,18 +464,35 @@ public class Cell extends BlockStackingLayoutManager { | |||
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; | |||
} | |||
@@ -364,24 +513,9 @@ public class Cell extends BlockStackingLayoutManager { | |||
} | |||
} | |||
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(); | |||
@@ -410,23 +544,28 @@ public class Cell extends BlockStackingLayoutManager { | |||
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(); | |||
@@ -461,5 +600,52 @@ public class Cell extends BlockStackingLayoutManager { | |||
} | |||
} | |||
/** | |||
* @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(); | |||
*/ | |||
} | |||
} | |||
@@ -28,14 +28,26 @@ import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; | |||
*/ | |||
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; | |||
@@ -111,8 +123,8 @@ public abstract class CollapsingBorderModel { | |||
/** | |||
* 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( |
@@ -19,7 +19,11 @@ | |||
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; | |||
/** | |||
@@ -29,48 +33,43 @@ 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 | |||
@@ -85,44 +84,51 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel { | |||
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 | |||
@@ -136,7 +142,6 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel { | |||
// *** Rule 2 *** | |||
if (!doRule2(current, other)) { | |||
return null; //paint no border | |||
} | |||
// *** Rule 3 *** | |||
@@ -161,7 +166,7 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel { | |||
} | |||
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]; | |||
} | |||
@@ -174,7 +179,7 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel { | |||
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; | |||
@@ -190,7 +195,7 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel { | |||
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(); | |||
} | |||
@@ -201,13 +206,12 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel { | |||
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 | |||
} | |||
@@ -216,7 +220,6 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel { | |||
if (widest == null) { | |||
widest = other[i]; | |||
} | |||
break; | |||
} else { | |||
other[i] = null; //Discard the narrower ones | |||
} | |||
@@ -231,26 +234,24 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel { | |||
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) { | |||
@@ -284,7 +285,7 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel { | |||
} | |||
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]; | |||
} |
@@ -1,134 +0,0 @@ | |||
/* | |||
* 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; | |||
} | |||
} | |||
@@ -0,0 +1,140 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -0,0 +1,121 @@ | |||
/* | |||
* 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(); | |||
} | |||
} |
@@ -0,0 +1,60 @@ | |||
/* | |||
* 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; | |||
} | |||
} |
@@ -18,45 +18,135 @@ | |||
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; | |||
} | |||
@@ -64,68 +154,136 @@ public class GridUnit { | |||
/** @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); | |||
} | |||
} | |||
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(); | |||
} | |||
} |
@@ -0,0 +1,210 @@ | |||
/* | |||
* 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); | |||
} | |||
} |
@@ -1,623 +0,0 @@ | |||
/* | |||
* 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; | |||
} | |||
} | |||
} | |||
@@ -139,7 +139,7 @@ public class TableAndCaptionLayoutManager extends BlockStackingLayoutManager { | |||
public void addAreas(PositionIterator parentIter, | |||
LayoutContext layoutContext) { | |||
getParentArea(null); | |||
addID(fobj.getId()); | |||
getPSLM().addIDToPage(fobj.getId()); | |||
LayoutManager childLM; | |||
int iStartPos = 0; |
@@ -0,0 +1,863 @@ | |||
/* | |||
* 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; | |||
} | |||
} | |||
} |
@@ -21,14 +21,17 @@ package org.apache.fop.layoutmgr.table; | |||
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; | |||
@@ -37,28 +40,26 @@ import org.apache.fop.traits.MinOptMax; | |||
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; | |||
@@ -82,6 +83,7 @@ public class TableLayoutManager extends BlockStackingLayoutManager { | |||
public TableLayoutManager(Table node) { | |||
super(node); | |||
fobj = node; | |||
this.columns = new ColumnSetup(node); | |||
} | |||
/** @return the table FO */ | |||
@@ -90,14 +92,12 @@ public class TableLayoutManager extends BlockStackingLayoutManager { | |||
} | |||
/** | |||
* 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(); | |||
@@ -118,15 +118,11 @@ public class TableLayoutManager extends BlockStackingLayoutManager { | |||
} | |||
/** | |||
* 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) { | |||
@@ -146,200 +142,122 @@ public class TableLayoutManager extends BlockStackingLayoutManager { | |||
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 | |||
@@ -347,7 +265,7 @@ public class TableLayoutManager extends BlockStackingLayoutManager { | |||
public void addAreas(PositionIterator parentIter, | |||
LayoutContext layoutContext) { | |||
getParentArea(null); | |||
addID(fobj.getId()); | |||
getPSLM().addIDToPage(fobj.getId()); | |||
// if adjusted space before | |||
double adjust = layoutContext.getSpaceAdjust(); | |||
@@ -359,49 +277,14 @@ public class TableLayoutManager extends BlockStackingLayoutManager { | |||
// 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); | |||
@@ -420,7 +303,7 @@ public class TableLayoutManager extends BlockStackingLayoutManager { | |||
// if adjusted space after | |||
addBlockSpacing(adjust, spaceAfter); | |||
bodyBreaks.clear(); | |||
//bodyBreaks.clear(); | |||
curBlockArea = null; | |||
} | |||
@@ -474,5 +357,47 @@ public class TableLayoutManager extends BlockStackingLayoutManager { | |||
} | |||
} | |||
/** | |||
* @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(); | |||
} | |||
} | |||
@@ -0,0 +1,439 @@ | |||
/* | |||
* 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 | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,418 @@ | |||
/* | |||
* 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; | |||
} | |||
} | |||
} |
@@ -259,16 +259,16 @@ public abstract class AbstractRenderer | |||
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(); | |||
} | |||
@@ -374,19 +374,24 @@ public abstract class AbstractRenderer | |||
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(); | |||
} | |||
} | |||
@@ -517,7 +517,7 @@ public class PDFRenderer extends PrintRenderer { | |||
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(); | |||
} | |||
@@ -655,18 +655,18 @@ public class PDFRenderer extends PrintRenderer { | |||
} | |||
} | |||
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, | |||
@@ -678,7 +678,7 @@ public class PDFRenderer extends PrintRenderer { | |||
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(); | |||
@@ -710,7 +710,8 @@ public class PDFRenderer extends PrintRenderer { | |||
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) { | |||
@@ -765,7 +766,7 @@ public class PDFRenderer extends PrintRenderer { | |||
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); | |||
@@ -785,7 +786,7 @@ public class PDFRenderer extends PrintRenderer { | |||
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]; | |||
@@ -801,8 +802,8 @@ public class PDFRenderer extends PrintRenderer { | |||
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); |
@@ -378,7 +378,7 @@ public class XMLRenderer extends AbstractRenderer { | |||
addTraitAttributes(port); | |||
addAttribute("rect", port.getViewArea()); | |||
startElement("regionViewport", atts); | |||
RegionReference region = port.getRegion(); | |||
RegionReference region = port.getRegionReference(); | |||
atts.clear(); | |||
addAreaAttributes(region); | |||
addTraitAttributes(region); |
@@ -1,5 +1,5 @@ | |||
/* | |||
* 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. | |||
@@ -18,6 +18,7 @@ | |||
package org.apache.fop.traits; | |||
import org.apache.fop.datatypes.KeepValue; | |||
import org.apache.fop.fo.Constants; | |||
/** | |||
@@ -29,6 +30,13 @@ public class LayoutProps { | |||
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; |
@@ -31,35 +31,35 @@ | |||
<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> |
@@ -31,35 +31,35 @@ | |||
<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> |