123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482 |
- /*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
- /* $Id$ */
-
- package org.apache.fop.layoutmgr.table;
-
- 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.fo.flow.table.ConditionalBorder;
- import org.apache.fop.fo.flow.table.EffRow;
- import org.apache.fop.fo.flow.table.PrimaryGridUnit;
- 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;
-
- /**
- * A cell playing in the construction of steps for a row-group.
- */
- class ActiveCell {
-
- private static Log log = LogFactory.getLog(ActiveCell.class);
-
- private PrimaryGridUnit pgu;
- /** Knuth elements for this active cell. */
- private List elementList;
- /** Iterator over the Knuth element list. */
- private ListIterator knuthIter;
- /** Number of the row where the row-span ends, zero-based. */
- private int endRowIndex;
- /** Length of the Knuth elements not yet included in the steps. */
- private int remainingLength;
- /** Total length of this cell's content plus the lengths of the previous rows. */
- private int totalLength;
- /** Length of the Knuth elements already included in the steps. */
- private int includedLength;
-
- private int paddingBeforeNormal;
- private int paddingBeforeLeading;
- private int paddingAfterNormal;
- private int paddingAfterTrailing;
-
- private int bpBeforeNormal;
- private int bpBeforeLeading;
- private int bpAfterNormal;
- private int bpAfterTrailing;
-
- /** True if the next CellPart that will be created will be the last one for this cell. */
- private boolean lastCellPart;
-
- private boolean keepWithNextSignal;
-
- private int spanIndex = 0;
-
- private Step previousStep;
- private Step nextStep;
- /**
- * The step following nextStep. Computing it early allows to calculate
- * {@link Step#condBeforeContentLength}, thus to easily determine the remaining
- * length. That also helps for {@link #increaseCurrentStep(int)}.
- */
- private Step afterNextStep;
-
- /**
- * Auxiliary class to store all the informations related to a breaking step.
- */
- private static class Step {
- /** Index, in the list of Knuth elements, of the element starting this step. */
- private int start;
- /** Index, in the list of Knuth elements, of the element ending this step. */
- private int end;
- /** Length of the Knuth elements up to this step. */
- private int contentLength;
- /** Total length up to this step, including paddings and borders. */
- private int totalLength;
- /** Length of the penalty ending this step, if any. */
- private int penaltyLength;
- /**
- * One of {@link Constants#EN_AUTO}, {@link Constants#EN_COLUMN},
- * {@link Constants#EN_PAGE}, {@link Constants#EN_EVEN_PAGE},
- * {@link Constants#EN_ODD_PAGE}. Set to auto if the break isn't at a penalty
- * element.
- */
- private int breakClass;
- /**
- * Length of the optional content at the beginning of the step. That is, content
- * that will not appear if this step starts a new page.
- */
- private int condBeforeContentLength;
-
- Step(int contentLength) {
- this.contentLength = contentLength;
- this.end = -1;
- }
-
- Step(Step other) {
- set(other);
- }
-
- void set(Step other) {
- this.start = other.start;
- this.end = other.end;
- this.contentLength = other.contentLength;
- this.totalLength = other.totalLength;
- this.penaltyLength = other.penaltyLength;
- this.condBeforeContentLength = other.condBeforeContentLength;
- this.breakClass = other.breakClass;
- }
-
- /** {@inheritDoc} */
- public String toString() {
- return "Step: start=" + start + " end=" + end + " length=" + totalLength;
- }
- }
-
- ActiveCell(PrimaryGridUnit pgu, EffRow row, int rowIndex, int previousRowsLength,
- TableLayoutManager tableLM) {
- this.pgu = pgu;
- CommonBorderPaddingBackground bordersPaddings = pgu.getCell()
- .getCommonBorderPaddingBackground();
- TableCellLayoutManager cellLM = pgu.getCellLM();
- paddingBeforeNormal = bordersPaddings.getPaddingBefore(false, cellLM);
- paddingBeforeLeading = bordersPaddings.getPaddingBefore(true, cellLM);
- paddingAfterNormal = bordersPaddings.getPaddingAfter(false, cellLM);
- paddingAfterTrailing = bordersPaddings.getPaddingAfter(true, cellLM);
- bpBeforeNormal = paddingBeforeNormal
- + pgu.getBeforeBorderWidth(0, ConditionalBorder.NORMAL);
- bpBeforeLeading = paddingBeforeLeading
- + pgu.getBeforeBorderWidth(0, ConditionalBorder.REST);
- bpAfterNormal = paddingAfterNormal + pgu.getAfterBorderWidth(ConditionalBorder.NORMAL);
- bpAfterTrailing = paddingAfterTrailing + pgu.getAfterBorderWidth(0, ConditionalBorder.REST);
- boolean makeBoxForWholeRow = false;
- if (row.getExplicitHeight().min > 0) {
- boolean contentsSmaller = ElementListUtils.removeLegalBreaks(
- pgu.getElements(), row.getExplicitHeight());
- if (contentsSmaller) {
- makeBoxForWholeRow = true;
- }
- }
- if (makeBoxForWholeRow) {
- elementList = new java.util.ArrayList(1);
- int height = row.getHeight().opt;
- height -= 2 * tableLM.getHalfBorderSeparationBPD();
- height -= bpBeforeNormal + bpAfterNormal;
- elementList.add(new KnuthBoxCellWithBPD(height));
- } else {
- elementList = pgu.getElements();
- }
- knuthIter = elementList.listIterator();
- includedLength = -1; // Avoid troubles with cells having content of zero length
- totalLength = previousRowsLength + ElementListUtils.calcContentLength(elementList);
- endRowIndex = rowIndex + pgu.getCell().getNumberRowsSpanned() - 1;
- keepWithNextSignal = false;
- remainingLength = totalLength - previousRowsLength;
-
- afterNextStep = new Step(previousRowsLength);
- previousStep = new Step(afterNextStep);
- gotoNextLegalBreak();
- nextStep = new Step(afterNextStep);
- if (afterNextStep.end < elementList.size() - 1) {
- gotoNextLegalBreak();
- }
- }
-
- PrimaryGridUnit getPrimaryGridUnit() {
- return pgu;
- }
-
- /**
- * Returns true if this cell ends on the given row.
- *
- * @param rowIndex index of a row in the row-group, zero-based
- * @return true if this cell ends on the given row
- */
- boolean endsOnRow(int rowIndex) {
- return rowIndex == endRowIndex;
- }
-
- /**
- * Returns the length of this cell's content not yet included in the steps, plus the
- * cell's borders and paddings if applicable.
- *
- * @return the remaining length, zero if the cell is finished
- */
- int getRemainingLength() {
- if (includedInLastStep() && (nextStep.end == elementList.size() - 1)) {
- // The cell is finished
- return 0;
- } else {
- return bpBeforeLeading + remainingLength + bpAfterNormal;
- }
- }
-
- private void gotoNextLegalBreak() {
- afterNextStep.penaltyLength = 0;
- afterNextStep.condBeforeContentLength = 0;
- afterNextStep.breakClass = Constants.EN_AUTO;
- boolean breakFound = false;
- boolean prevIsBox = false;
- boolean boxFound = false;
- while (!breakFound && knuthIter.hasNext()) {
- KnuthElement el = (KnuthElement) knuthIter.next();
- if (el.isPenalty()) {
- prevIsBox = false;
- if (el.getP() < KnuthElement.INFINITE) {
- // First legal break point
- breakFound = true;
- afterNextStep.penaltyLength = el.getW();
- KnuthPenalty p = (KnuthPenalty) el;
- if (p.isForcedBreak()) {
- afterNextStep.breakClass = p.getBreakClass();
- }
- }
- } else if (el.isGlue()) {
- if (prevIsBox) {
- // Second legal break point
- breakFound = true;
- } else {
- afterNextStep.contentLength += el.getW();
- if (!boxFound) {
- afterNextStep.condBeforeContentLength += el.getW();
- }
- }
- prevIsBox = false;
- } else {
- prevIsBox = true;
- boxFound = true;
- afterNextStep.contentLength += el.getW();
- }
- }
- afterNextStep.end = knuthIter.nextIndex() - 1;
- afterNextStep.totalLength = bpBeforeNormal
- + afterNextStep.contentLength + afterNextStep.penaltyLength
- + bpAfterTrailing;
- }
-
- /**
- * Returns the minimal step that is needed for this cell to contribute some content.
- *
- * @return the step for this cell's first legal break
- */
- int getFirstStep() {
- log.debug(this + ": min first step = " + nextStep.totalLength);
- return nextStep.totalLength;
- }
-
- /**
- * Returns the last step for this cell.
- *
- * @return the step including all of the cell's content plus the normal borders and paddings
- */
- int getLastStep() {
- assert nextStep.end == elementList.size() - 1;
- assert nextStep.contentLength == totalLength && nextStep.penaltyLength == 0;
- int lastStep = bpBeforeNormal + totalLength + bpAfterNormal;
- log.debug(this + ": last step = " + lastStep);
- return lastStep;
- }
-
- /**
- * Increases the next step up to the given limit.
- *
- * @param limit the length up to which the next step is allowed to increase
- * @see #signalRowFirstStep(int)
- * @see #signalRowLastStep(int)
- */
- private void increaseCurrentStep(int limit) {
- if (nextStep.end < elementList.size() - 1) {
- while (afterNextStep.totalLength <= limit && nextStep.breakClass == Constants.EN_AUTO) {
- nextStep.set(afterNextStep);
- if (afterNextStep.end >= elementList.size() - 1) {
- break;
- }
- gotoNextLegalBreak();
- }
- }
- }
-
- /**
- * Gets the selected first step for the current row. If this cell's first step is
- * smaller, then it may be able to add some more of its content, since there will be
- * no break before the given step anyway.
- *
- * @param firstStep the current row's first step
- */
- void signalRowFirstStep(int firstStep) {
- increaseCurrentStep(firstStep);
- if (log.isTraceEnabled()) {
- log.trace(this + ": first step increased to " + nextStep.totalLength);
- }
- }
-
- /** See {@link #signalRowFirstStep(int)}. */
- void signalRowLastStep(int lastStep) {
- increaseCurrentStep(lastStep);
- if (log.isTraceEnabled()) {
- log.trace(this + ": next step increased to " + nextStep.totalLength);
- }
- }
-
- /**
- * Returns the total length up to the next legal break, not yet included in the steps.
- *
- * @return the total length up to the next legal break (-1 signals no further step)
- */
- int getNextStep() {
- if (includedInLastStep()) {
- previousStep.set(nextStep);
- if (nextStep.end >= elementList.size() - 1) {
- nextStep.start = elementList.size();
- return -1;
- } else {
- nextStep.set(afterNextStep);
- nextStep.start = previousStep.end + 1;
- afterNextStep.start = nextStep.start;
- if (afterNextStep.end < elementList.size() - 1) {
- gotoNextLegalBreak();
- }
- }
- }
- return nextStep.totalLength;
- }
-
- private boolean includedInLastStep() {
- return includedLength == nextStep.contentLength;
- }
-
- /**
- * Signals the length of the chosen next step, so that this cell determines whether
- * its own step may be included or not.
- *
- * @param minStep length of the chosen next step
- * @return the break class of the step, if any. One of {@link Constants#EN_AUTO},
- * {@link Constants#EN_COLUMN}, {@link Constants#EN_PAGE},
- * {@link Constants#EN_EVEN_PAGE}, {@link Constants#EN_ODD_PAGE}. EN_AUTO if this
- * cell's step is not included in the next step.
- */
- int signalNextStep(int minStep) {
- if (nextStep.totalLength <= minStep) {
- includedLength = nextStep.contentLength;
- remainingLength = totalLength - includedLength - afterNextStep.condBeforeContentLength;
- return nextStep.breakClass;
- } else {
- return Constants.EN_AUTO;
- }
- }
-
- /**
- * Receives indication that the next row is about to start, and that (collapse)
- * borders must be updated accordingly.
- */
- void nextRowStarts() {
- spanIndex++;
- // Subtract the old value of bpAfterTrailing...
- nextStep.totalLength -= bpAfterTrailing;
- afterNextStep.totalLength -= bpAfterTrailing;
-
- bpAfterTrailing = paddingAfterTrailing
- + pgu.getAfterBorderWidth(spanIndex, ConditionalBorder.REST);
-
- // ... and add the new one
- nextStep.totalLength += bpAfterTrailing;
- afterNextStep.totalLength += bpAfterTrailing;
- // TODO if the new after border is greater than the previous one the next step may
- // increase further than the row's first step, which can lead to wrong output in
- // some cases
- }
-
- /**
- * Receives indication that the current row is ending, and that (collapse) borders
- * must be updated accordingly.
- *
- * @param rowIndex the index of the ending row
- */
- void endRow(int rowIndex) {
- if (endsOnRow(rowIndex)) {
- bpAfterTrailing = paddingAfterNormal
- + pgu.getAfterBorderWidth(ConditionalBorder.LEADING_TRAILING);
- lastCellPart = true;
- } else {
- bpBeforeLeading = paddingBeforeLeading
- + pgu.getBeforeBorderWidth(spanIndex + 1, ConditionalBorder.REST);
- }
- }
-
- /**
- * Returns true if this cell would be finished after the given step. That is, it would
- * be included in the step and the end of its content would be reached.
- *
- * @param step the next step
- * @return true if this cell finishes at the given step
- */
- boolean finishes(int step) {
- return nextStep.totalLength <= step && (nextStep.end == elementList.size() - 1);
- }
-
- /**
- * Creates and returns a CellPart instance for the content of this cell which
- * is included in the next step.
- *
- * @return a CellPart instance
- */
- CellPart createCellPart() {
- if (nextStep.end + 1 == elementList.size()) {
- keepWithNextSignal = pgu.mustKeepWithNext();
- // TODO if keep-with-next is set on the row, must every cell of the row
- // contribute some content from children blocks?
- // see http://mail-archives.apache.org/mod_mbox/xmlgraphics-fop-dev/200802.mbox/
- // %3c47BDA379.4050606@anyware-tech.com%3e
- // Assuming no, but if yes the following code should enable this behaviour
- // if (pgu.getRow() != null && pgu.getRow().mustKeepWithNext()) {
- // keepWithNextSignal = true;
- // }
- }
- int bpBeforeFirst;
- if (nextStep.start == 0) {
- bpBeforeFirst = pgu.getBeforeBorderWidth(0, ConditionalBorder.LEADING_TRAILING)
- + paddingBeforeNormal;
- } else {
- bpBeforeFirst = bpBeforeLeading;
- }
- int length = nextStep.contentLength - nextStep.condBeforeContentLength
- - previousStep.contentLength;
- if (!includedInLastStep() || nextStep.start == elementList.size()) {
- return new CellPart(pgu, nextStep.start, previousStep.end, lastCellPart,
- 0, 0, previousStep.penaltyLength,
- bpBeforeNormal, bpBeforeFirst, bpAfterNormal, bpAfterTrailing);
- } else if (nextStep.start == 0 && nextStep.end == 0
- && elementList.size() == 1
- && elementList.get(0) instanceof KnuthBoxCellWithBPD) {
- //Special case: Cell with fixed BPD
- return new CellPart(pgu, 0, pgu.getElements().size() - 1, lastCellPart,
- nextStep.condBeforeContentLength, length, nextStep.penaltyLength,
- bpBeforeNormal, bpBeforeFirst, bpAfterNormal, bpAfterTrailing);
- } else {
- return new CellPart(pgu, nextStep.start, nextStep.end, lastCellPart,
- nextStep.condBeforeContentLength, length, nextStep.penaltyLength,
- bpBeforeNormal, bpBeforeFirst, bpAfterNormal, bpAfterTrailing);
- }
- }
-
- boolean keepWithNextSignal() {
- return keepWithNextSignal;
- }
-
-
- /** {@inheritDoc} */
- public String toString() {
- return "Cell " + (pgu.getRowIndex() + 1) + "." + (pgu.getColIndex() + 1);
- }
-
-
- /**
- * Marker class denoting table cells fitting in just one box (no legal break inside).
- */
- private static class KnuthBoxCellWithBPD extends KnuthBox {
-
- public KnuthBoxCellWithBPD(int w) {
- super(w, null, true);
- }
- }
- }
|