浏览代码

Merge of branch Temp_KnuthStylePageBreaking back into HEAD.

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-ffa450edef68
tags/fop-0_90-alpha1
Jeremias Maerki 19 年前
父节点
当前提交
e835307c93
共有 100 个文件被更改,包括 10472 次插入3085 次删除
  1. 3
    0
      src/java/org/apache/fop/apps/CommandLineOptions.java
  2. 1
    1
      src/java/org/apache/fop/apps/FOUserAgent.java
  3. 3
    10
      src/java/org/apache/fop/area/AreaTreeHandler.java
  4. 26
    61
      src/java/org/apache/fop/area/BodyRegion.java
  5. 29
    10
      src/java/org/apache/fop/area/MainReference.java
  6. 3
    1
      src/java/org/apache/fop/area/NormalFlow.java
  7. 108
    2
      src/java/org/apache/fop/area/Page.java
  8. 57
    8
      src/java/org/apache/fop/area/PageViewport.java
  9. 16
    24
      src/java/org/apache/fop/area/RegionReference.java
  10. 22
    15
      src/java/org/apache/fop/area/RegionViewport.java
  11. 34
    27
      src/java/org/apache/fop/area/Span.java
  12. 5
    2
      src/java/org/apache/fop/fo/Constants.java
  13. 8
    0
      src/java/org/apache/fop/fo/FOPropertyMapping.java
  14. 6
    7
      src/java/org/apache/fop/fo/FObj.java
  15. 1
    0
      src/java/org/apache/fop/fo/PropertySets.java
  16. 0
    1
      src/java/org/apache/fop/fo/flow/Inline.java
  17. 10
    3
      src/java/org/apache/fop/fo/flow/Marker.java
  18. 10
    1
      src/java/org/apache/fop/fo/flow/RetrieveMarker.java
  19. 26
    7
      src/java/org/apache/fop/fo/flow/Table.java
  20. 3
    2
      src/java/org/apache/fop/fo/flow/TableBody.java
  21. 1
    0
      src/java/org/apache/fop/fo/flow/TableCell.java
  22. 22
    3
      src/java/org/apache/fop/fo/flow/TableColumn.java
  23. 27
    8
      src/java/org/apache/fop/fo/flow/Wrapper.java
  24. 3
    13
      src/java/org/apache/fop/fo/pagination/RegionBA.java
  25. 11
    0
      src/java/org/apache/fop/fo/pagination/RegionBody.java
  26. 2
    12
      src/java/org/apache/fop/fo/pagination/RegionSE.java
  27. 49
    0
      src/java/org/apache/fop/fo/pagination/SideRegion.java
  28. 661
    0
      src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
  29. 55
    149
      src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
  30. 80
    0
      src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java
  31. 4
    4
      src/java/org/apache/fop/layoutmgr/BasicLinkLayoutManager.java
  32. 0
    1
      src/java/org/apache/fop/layoutmgr/BidiLayoutManager.java
  33. 688
    65
      src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
  34. 243
    26
      src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
  35. 51
    0
      src/java/org/apache/fop/layoutmgr/BlockLevelLayoutManager.java
  36. 1076
    1
      src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java
  37. 783
    0
      src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
  38. 8
    8
      src/java/org/apache/fop/layoutmgr/CharacterLayoutManager.java
  39. 8
    54
      src/java/org/apache/fop/layoutmgr/ContentLayoutManager.java
  40. 136
    0
      src/java/org/apache/fop/layoutmgr/ElementListUtils.java
  41. 1
    1
      src/java/org/apache/fop/layoutmgr/ExternalGraphicLayoutManager.java
  42. 271
    28
      src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java
  43. 1
    1
      src/java/org/apache/fop/layoutmgr/ICLayoutManager.java
  44. 2
    1
      src/java/org/apache/fop/layoutmgr/InlineLayoutManager.java
  45. 6
    28
      src/java/org/apache/fop/layoutmgr/InlineLevelLayoutManager.java
  46. 216
    3
      src/java/org/apache/fop/layoutmgr/InlineStackingLayoutManager.java
  47. 1
    2
      src/java/org/apache/fop/layoutmgr/InstreamForeignObjectLM.java
  48. 41
    0
      src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java
  49. 13
    29
      src/java/org/apache/fop/layoutmgr/KnuthBox.java
  50. 35
    6
      src/java/org/apache/fop/layoutmgr/KnuthGlue.java
  51. 64
    0
      src/java/org/apache/fop/layoutmgr/KnuthInlineBox.java
  52. 4
    3
      src/java/org/apache/fop/layoutmgr/KnuthParagraph.java
  53. 44
    0
      src/java/org/apache/fop/layoutmgr/KnuthPenalty.java
  54. 12
    4
      src/java/org/apache/fop/layoutmgr/KnuthPossPosIter.java
  55. 83
    0
      src/java/org/apache/fop/layoutmgr/KnuthSequence.java
  56. 40
    0
      src/java/org/apache/fop/layoutmgr/LayoutContext.java
  57. 30
    78
      src/java/org/apache/fop/layoutmgr/LayoutManager.java
  58. 19
    7
      src/java/org/apache/fop/layoutmgr/LayoutManagerMaker.java
  59. 27
    29
      src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java
  60. 5
    5
      src/java/org/apache/fop/layoutmgr/LeaderLayoutManager.java
  61. 17
    13
      src/java/org/apache/fop/layoutmgr/LeafNodeLayoutManager.java
  62. 882
    268
      src/java/org/apache/fop/layoutmgr/LineLayoutManager.java
  63. 236
    0
      src/java/org/apache/fop/layoutmgr/LineLayoutPossibilities.java
  64. 28
    0
      src/java/org/apache/fop/layoutmgr/MinOptMaxUtil.java
  65. 14
    1
      src/java/org/apache/fop/layoutmgr/NonLeafPosition.java
  66. 81
    0
      src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
  67. 3
    3
      src/java/org/apache/fop/layoutmgr/PageNumberCitationLayoutManager.java
  68. 2
    2
      src/java/org/apache/fop/layoutmgr/PageNumberLayoutManager.java
  69. 277
    446
      src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java
  70. 14
    1
      src/java/org/apache/fop/layoutmgr/Position.java
  71. 1
    1
      src/java/org/apache/fop/layoutmgr/PositionIterator.java
  72. 201
    17
      src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java
  73. 102
    33
      src/java/org/apache/fop/layoutmgr/TextLayoutManager.java
  74. 51
    13
      src/java/org/apache/fop/layoutmgr/list/Item.java
  75. 49
    16
      src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java
  76. 298
    18
      src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java
  77. 0
    270
      src/java/org/apache/fop/layoutmgr/table/Body.java
  78. 1
    1
      src/java/org/apache/fop/layoutmgr/table/Caption.java
  79. 256
    70
      src/java/org/apache/fop/layoutmgr/table/Cell.java
  80. 18
    6
      src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModel.java
  81. 50
    49
      src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModelEyeCatching.java
  82. 0
    134
      src/java/org/apache/fop/layoutmgr/table/Column.java
  83. 140
    0
      src/java/org/apache/fop/layoutmgr/table/ColumnSetup.java
  84. 121
    0
      src/java/org/apache/fop/layoutmgr/table/EffRow.java
  85. 60
    0
      src/java/org/apache/fop/layoutmgr/table/EmptyGridUnit.java
  86. 216
    58
      src/java/org/apache/fop/layoutmgr/table/GridUnit.java
  87. 210
    0
      src/java/org/apache/fop/layoutmgr/table/PrimaryGridUnit.java
  88. 0
    623
      src/java/org/apache/fop/layoutmgr/table/Row.java
  89. 1
    1
      src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java
  90. 863
    0
      src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java
  91. 156
    231
      src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java
  92. 439
    0
      src/java/org/apache/fop/layoutmgr/table/TableRowIterator.java
  93. 418
    0
      src/java/org/apache/fop/layoutmgr/table/TableStepper.java
  94. 15
    10
      src/java/org/apache/fop/render/AbstractRenderer.java
  95. 10
    9
      src/java/org/apache/fop/render/pdf/PDFRenderer.java
  96. 1
    1
      src/java/org/apache/fop/render/xml/XMLRenderer.java
  97. 9
    1
      src/java/org/apache/fop/traits/LayoutProps.java
  98. 19
    19
      test/layoutengine/testcases/breaks1.xml
  99. 19
    19
      test/layoutengine/testcases/breaks2.xml
  100. 0
    0
      test/layoutengine/testcases/keep-together1.xml

+ 3
- 0
src/java/org/apache/fop/apps/CommandLineOptions.java 查看文件

@@ -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"

+ 1
- 1
src/java/org/apache/fop/apps/FOUserAgent.java 查看文件

@@ -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;

+ 3
- 10
src/java/org/apache/fop/area/AreaTreeHandler.java 查看文件

@@ -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();
}
}

+ 26
- 61
src/java/org/apache/fop/area/BodyRegion.java 查看文件

@@ -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;

+ 29
- 10
src/java/org/apache/fop/area/MainReference.java 查看文件

@@ -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();
}

/**

+ 3
- 1
src/java/org/apache/fop/area/NormalFlow.java 查看文件

@@ -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);
}
}


+ 108
- 2
src/java/org/apache/fop/area/Page.java 查看文件

@@ -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();
}
}

+ 57
- 8
src/java/org/apache/fop/area/PageViewport.java 查看文件

@@ -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;
}
}

+ 16
- 24
src/java/org/apache/fop/area/RegionReference.java 查看文件

@@ -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;

+ 22
- 15
src/java/org/apache/fop/area/RegionViewport.java 查看文件

@@ -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();
}

+ 34
- 27
src/java/org/apache/fop/area/Span.java 查看文件

@@ -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.");
}
}

}


+ 5
- 2
src/java/org/apache/fop/fo/Constants.java 查看文件

@@ -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;
}

+ 8
- 0
src/java/org/apache/fop/fo/FOPropertyMapping.java 查看文件

@@ -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() {

+ 6
- 7
src/java/org/apache/fop/fo/FObj.java 查看文件

@@ -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);

+ 1
- 0
src/java/org/apache/fop/fo/PropertySets.java 查看文件

@@ -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);

+ 0
- 1
src/java/org/apache/fop/fo/flow/Inline.java 查看文件

@@ -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;


+ 10
- 3
src/java/org/apache/fop/fo/flow/Marker.java 查看文件

@@ -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");
}
}
/**

+ 10
- 1
src/java/org/apache/fop/fo/flow/RetrieveMarker.java 查看文件

@@ -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");
}
}
/**

+ 26
- 7
src/java/org/apache/fop/fo/flow/Table.java 查看文件

@@ -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;

+ 3
- 2
src/java/org/apache/fop/fo/flow/TableBody.java 查看文件

@@ -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;

+ 1
- 0
src/java/org/apache/fop/fo/flow/TableCell.java 查看文件

@@ -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);
}


+ 22
- 3
src/java/org/apache/fop/fo/flow/TableColumn.java 查看文件

@@ -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();
}
}


+ 27
- 8
src/java/org/apache/fop/fo/flow/Wrapper.java 查看文件

@@ -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.
*/

+ 3
- 13
src/java/org/apache/fop/fo/pagination/RegionBA.java 查看文件

@@ -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;

+ 11
- 0
src/java/org/apache/fop/fo/pagination/RegionBody.java 查看文件

@@ -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.");
}
}

/**

+ 2
- 12
src/java/org/apache/fop/fo/pagination/RegionSE.java 查看文件

@@ -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.

+ 49
- 0
src/java/org/apache/fop/fo/pagination/SideRegion.java 查看文件

@@ -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;
}
}

+ 661
- 0
src/java/org/apache/fop/layoutmgr/AbstractBreaker.java 查看文件

@@ -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;
}

}

+ 55
- 149
src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java 查看文件

@@ -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);
}
}

}


+ 80
- 0
src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java 查看文件

@@ -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);
}
}
}

+ 4
- 4
src/java/org/apache/fop/layoutmgr/BasicLinkLayoutManager.java 查看文件

@@ -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);
}
}
}

+ 0
- 1
src/java/org/apache/fop/layoutmgr/BidiLayoutManager.java 查看文件

@@ -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);

+ 688
- 65
src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java 查看文件

@@ -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;
}
}


+ 243
- 26
src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java 查看文件

@@ -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;
}
}


+ 51
- 0
src/java/org/apache/fop/layoutmgr/BlockLevelLayoutManager.java 查看文件

@@ -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();

}

+ 1076
- 1
src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java
文件差异内容过多而无法显示
查看文件


+ 783
- 0
src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java 查看文件

@@ -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>&gt;= -1 && &lt; 0 means that the break is to wider than the line,
* but within the minimim values of the glues.</li>
* <li>&gt;0 && &lt 1 means that the break is smaller than the line width,
* but within the maximum values of the glues.</li>
* <li>&gt 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;
}
}
}

+ 8
- 8
src/java/org/apache/fop/layoutmgr/CharacterLayoutManager.java 查看文件

@@ -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());
}
}


+ 8
- 54
src/java/org/apache/fop/layoutmgr/ContentLayoutManager.java 查看文件

@@ -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();
}
}


+ 136
- 0
src/java/org/apache/fop/layoutmgr/ElementListUtils.java 查看文件

@@ -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);
}
}

+ 1
- 1
src/java/org/apache/fop/layoutmgr/ExternalGraphicLayoutManager.java 查看文件

@@ -208,7 +208,7 @@ public class ExternalGraphicLayoutManager extends LeafNodeLayoutManager {
}
protected void addId() {
addID(fobj.getId());
getPSLM().addIDToPage(fobj.getId());
}
}


+ 271
- 28
src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java 查看文件

@@ -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;
}
}


+ 1
- 1
src/java/org/apache/fop/layoutmgr/ICLayoutManager.java 查看文件

@@ -44,6 +44,6 @@ public class ICLayoutManager extends LeafNodeLayoutManager {
}

protected void addId() {
addID(fobj.getId());
getPSLM().addIDToPage(fobj.getId());
}
}

+ 2
- 1
src/java/org/apache/fop/layoutmgr/InlineLayoutManager.java 查看文件

@@ -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;
}
}*/
}


+ 6
- 28
src/java/org/apache/fop/layoutmgr/InlineLevelLayoutManager.java 查看文件

@@ -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);
}

+ 216
- 3
src/java/org/apache/fop/layoutmgr/InlineStackingLayoutManager.java 查看文件

@@ -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;
}
}


+ 1
- 2
src/java/org/apache/fop/layoutmgr/InstreamForeignObjectLM.java 查看文件

@@ -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());
}
}


+ 41
- 0
src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java 查看文件

@@ -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;
}
}

+ 13
- 29
src/java/org/apache/fop/layoutmgr/KnuthBox.java 查看文件

@@ -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();
}
}

+ 35
- 6
src/java/org/apache/fop/layoutmgr/KnuthGlue.java 查看文件

@@ -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();
}
}

+ 64
- 0
src/java/org/apache/fop/layoutmgr/KnuthInlineBox.java 查看文件

@@ -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;
}
}

+ 4
- 3
src/java/org/apache/fop/layoutmgr/KnuthParagraph.java 查看文件

@@ -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;
}

+ 44
- 0
src/java/org/apache/fop/layoutmgr/KnuthPenalty.java 查看文件

@@ -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();
}
}

+ 12
- 4
src/java/org/apache/fop/layoutmgr/KnuthPossPosIter.java 查看文件

@@ -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

+ 83
- 0
src/java/org/apache/fop/layoutmgr/KnuthSequence.java 查看文件

@@ -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);
}
}

+ 40
- 0
src/java/org/apache/fop/layoutmgr/LayoutContext.java 查看文件

@@ -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()) +

+ 30
- 78
src/java/org/apache/fop/layoutmgr/LayoutManager.java 查看文件

@@ -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);
}

+ 19
- 7
src/java/org/apache/fop/layoutmgr/LayoutManagerMaker.java 查看文件

@@ -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);

}


+ 27
- 29
src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java 查看文件

@@ -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) {

+ 5
- 5
src/java/org/apache/fop/layoutmgr/LeaderLayoutManager.java 查看文件

@@ -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());
}
}

+ 17
- 13
src/java/org/apache/fop/layoutmgr/LeafNodeLayoutManager.java 查看文件

@@ -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;

+ 882
- 268
src/java/org/apache/fop/layoutmgr/LineLayoutManager.java
文件差异内容过多而无法显示
查看文件


+ 236
- 0
src/java/org/apache/fop/layoutmgr/LineLayoutPossibilities.java 查看文件

@@ -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("++++++++++");
}
}

+ 28
- 0
src/java/org/apache/fop/layoutmgr/MinOptMaxUtil.java 查看文件

@@ -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;
}
}

+ 14
- 1
src/java/org/apache/fop/layoutmgr/NonLeafPosition.java 查看文件

@@ -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();
}
}


+ 81
- 0
src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java 查看文件

@@ -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;
}

}

+ 3
- 3
src/java/org/apache/fop/layoutmgr/PageNumberCitationLayoutManager.java 查看文件

@@ -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());
}
}


+ 2
- 2
src/java/org/apache/fop/layoutmgr/PageNumberLayoutManager.java 查看文件

@@ -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());
}
}


+ 277
- 446
src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java 查看文件

@@ -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);
}
}

+ 14
- 1
src/java/org/apache/fop/layoutmgr/Position.java 查看文件

@@ -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();
}
}


+ 1
- 1
src/java/org/apache/fop/layoutmgr/PositionIterator.java 查看文件

@@ -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();

+ 201
- 17
src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java 查看文件

@@ -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
}
}
}


+ 102
- 33
src/java/org/apache/fop/layoutmgr/TextLayoutManager.java 查看文件

@@ -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,

+ 51
- 13
src/java/org/apache/fop/layoutmgr/list/Item.java 查看文件

@@ -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);

+ 49
- 16
src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java 查看文件

@@ -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();


+ 298
- 18
src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java 查看文件

@@ -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();


+ 0
- 270
src/java/org/apache/fop/layoutmgr/table/Body.java 查看文件

@@ -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;
}

}


+ 1
- 1
src/java/org/apache/fop/layoutmgr/table/Caption.java 查看文件

@@ -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;

+ 256
- 70
src/java/org/apache/fop/layoutmgr/table/Cell.java 查看文件

@@ -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();
*/
}

}


+ 18
- 6
src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModel.java 查看文件

@@ -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(

+ 50
- 49
src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModelEyeCatching.java 查看文件

@@ -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];
}

+ 0
- 134
src/java/org/apache/fop/layoutmgr/table/Column.java 查看文件

@@ -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;
}
}


+ 140
- 0
src/java/org/apache/fop/layoutmgr/table/ColumnSetup.java 查看文件

@@ -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;
}

}

+ 121
- 0
src/java/org/apache/fop/layoutmgr/table/EffRow.java 查看文件

@@ -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();
}
}

+ 60
- 0
src/java/org/apache/fop/layoutmgr/table/EmptyGridUnit.java 查看文件

@@ -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;
}
}

+ 216
- 58
src/java/org/apache/fop/layoutmgr/table/GridUnit.java 查看文件

@@ -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();
}

}

+ 210
- 0
src/java/org/apache/fop/layoutmgr/table/PrimaryGridUnit.java 查看文件

@@ -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);
}
}

+ 0
- 623
src/java/org/apache/fop/layoutmgr/table/Row.java 查看文件

@@ -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;
}
}

}


+ 1
- 1
src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java 查看文件

@@ -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;

+ 863
- 0
src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java 查看文件

@@ -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;
}
}

}

+ 156
- 231
src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java 查看文件

@@ -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();
}

}


+ 439
- 0
src/java/org/apache/fop/layoutmgr/table/TableRowIterator.java 查看文件

@@ -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
}
}
}

}

+ 418
- 0
src/java/org/apache/fop/layoutmgr/table/TableStepper.java 查看文件

@@ -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;
}
}
}

+ 15
- 10
src/java/org/apache/fop/render/AbstractRenderer.java 查看文件

@@ -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();
}
}


+ 10
- 9
src/java/org/apache/fop/render/pdf/PDFRenderer.java 查看文件

@@ -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);

+ 1
- 1
src/java/org/apache/fop/render/xml/XMLRenderer.java 查看文件

@@ -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);

+ 9
- 1
src/java/org/apache/fop/traits/LayoutProps.java 查看文件

@@ -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;

+ 19
- 19
test/layoutengine/testcases/breaks1.xml 查看文件

@@ -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>

+ 19
- 19
test/layoutengine/testcases/breaks2.xml 查看文件

@@ -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>

+ 0
- 0
test/layoutengine/testcases/keep-together1.xml 查看文件


部分文件因为文件数量过多而无法显示

正在加载...
取消
保存