Package org.apache.fop.layoutmgr.table

Source Code of org.apache.fop.layoutmgr.table.TableStepper$KnuthBoxCellWithBPD

/*
* 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: TableStepper.java 356592 2005-12-13 19:48:04Z jeremias $ */

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.Constants;
import org.apache.fop.fo.FONode;
import org.apache.fop.fo.flow.TableRow;
import org.apache.fop.layoutmgr.BreakElement;
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.LayoutContext;
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[] paddingBefore;
    private int[] borderAfter;
    private int[] paddingAfter;
    private boolean rowBacktrackForLastStep;
    private boolean skippedStep;
    private boolean[] keepWithNextSignals;
    private boolean[] forcedBreaks;
   
    /**
     * 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];
        paddingBefore = new int[columnCount];
        borderAfter = new int[columnCount];
        paddingAfter = new int[columnCount];
        keepWithNextSignals = new boolean[columnCount];
        forcedBreaks = new boolean[columnCount];
        Arrays.fill(end, -1);
    }
   
    private void clearBreakCondition() {
        Arrays.fill(forcedBreaks, false);
    }
   
    private boolean isBreakCondition() {
        for (int i = 0; i < forcedBreaks.length; i++) {
            if (forcedBreaks[i]) {
                return true;
            }
        }
        return false;
    }
   
    private EffRow getActiveRow() {
        return rowGroup[activeRow];
    }
   
    private GridUnit getActiveGridUnit(int column) {
        return getActiveRow().safelyGetGridUnit(column);
    }
   
    private PrimaryGridUnit getActivePrimaryGridUnit(int column) {
        GridUnit gu = getActiveGridUnit(column);
        if (gu == null) {
            return null;
        } else {
            return gu.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 (end[i] == elementLists[i].size() - 1) {
                    continue;
                }
                GridUnit gu = getActiveGridUnit(i);
                if (!gu.isLastGridUnitRowSpan()) {
                    continue;
                }
                int len = widths[i];
                if (len > 0) {
                    len += 2 * getTableLM().getHalfBorderSeparationBPD();
                    len += borderBefore[i] + borderAfter[i];
                    len += paddingBefore[i] + paddingAfter[i];
                }
                int nominalHeight = rowGroup[activeRow].getHeight().opt;
                for (int r = 0; r < gu.getRowSpanIndex(); r++) {
                    nominalHeight += rowGroup[activeRow - r - 1].getHeight().opt;
                }
                if (len == nominalHeight) {
                    //row is filled
                    maxW = 0;
                    break;
                }
                maxW = Math.max(maxW, nominalHeight - 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 == null || gu.isEmpty()) {
            elementLists[column] = null;
            start[column] = 0;
            end[column] = -1;
            widths[column] = 0;
            startRow[column] = activeRow;
            keepWithNextSignals[column] = false;
            forcedBreaks[column] = false;
        } else if (gu.isPrimary()) {
            PrimaryGridUnit pgu = (PrimaryGridUnit)gu;
            boolean makeBoxForWholeRow = false;
            if (row.getExplicitHeight().min > 0) {
                boolean contentsSmaller = ElementListUtils.removeLegalBreaks(
                        pgu.getElements(), row.getExplicitHeight());
                if (contentsSmaller) {
                    makeBoxForWholeRow = true;
                }
            }
            if (pgu.isLastGridUnitRowSpan() && pgu.getRow() != null) {
                makeBoxForWholeRow |= pgu.getRow().mustKeepTogether();
                makeBoxForWholeRow |= pgu.getTable().mustKeepTogether();
            }
            if (makeBoxForWholeRow) {
                List list = new java.util.ArrayList(1);
                int height = row.getExplicitHeight().opt;
                if (height == 0) {
                    height = row.getHeight().opt;
                }
                list.add(new KnuthBoxCellWithBPD(height, pgu));
                elementLists[column] = list;
            } 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;
            }
            paddingBefore[column] = pgu.getBorders().getPaddingBefore(false, pgu.getCellLM());
            paddingAfter[column] = pgu.getBorders().getPaddingAfter(false, pgu.getCellLM());
            start[column] = 0;
            end[column] = -1;
            widths[column] = 0;
            startRow[column] = activeRow;
            keepWithNextSignals[column] = false;
            forcedBreaks[column] = false;
        }
    }
   
    private void initializeElementLists() {
        for (int i = 0; i < start.length; i++) {
            setupElementList(i);
        }
    }

    /**
     * Creates the combined element list for a row group.
     * @param context Active LayoutContext
     * @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(
            LayoutContext context,
            EffRow[] rowGroup, int maxColumnCount, int bodyType) {
        this.rowGroup = rowGroup;
        setup(maxColumnCount);
        initializeElementLists();
        calcTotalHeight();
       
        boolean signalKeepWithNext = false;
        int laststep = 0;
        int step;
        int addedBoxLen = 0;
        TableContentPosition lastTCPos = null;
        LinkedList returnList = new LinkedList();
        while ((step = getNextStep(laststep)) >= 0) {
            int normalRow = activeRow;
            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 = rowGroup[startRow[i]].getGridUnit(i).getPrimary();
                    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]));
                    }
                    if (end[i] + 1 == elementLists[i].size()) {
                        if (pgu.getFlag(GridUnit.KEEP_WITH_NEXT_PENDING)) {
                            log.debug("PGU has pending keep-with-next");
                            keepWithNextSignals[i] = true;
                        }
                        if (pgu.getRow() != null && pgu.getRow().mustKeepWithNext()) {
                            log.debug("table-row causes keep-with-next");
                            keepWithNextSignals[i] = true;
                        }
                    }
                    if (start[i] == 0 && end[i] >= 0) {
                        if (pgu.getFlag(GridUnit.KEEP_WITH_PREVIOUS_PENDING)) {
                            log.debug("PGU has pending keep-with-previous");
                            if (returnList.size() == 0) {
                                context.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING);
                            }
                        }
                        if (pgu.getRow() != null && pgu.getRow().mustKeepWithPrevious()) {
                            log.debug("table-row causes keep-with-previous");
                            if (returnList.size() == 0) {
                                context.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING);
                            }
                        }
                    }
                }
            }
            //log.debug(">>> guPARTS: " + gridUnitParts);
           
            //Create elements for step
            int effPenaltyLen = penaltyLen;
            TableContentPosition tcpos = new TableContentPosition(getTableLM(),
                    gridUnitParts, rowGroup[normalRow]);
            if (returnList.size() == 0) {
                tcpos.setFlag(TableContentPosition.FIRST_IN_ROWGROUP, true);
            }
            lastTCPos = tcpos;
            if (log.isDebugEnabled()) {
                log.debug(" - backtrack=" + rowBacktrackForLastStep
                        + " - row=" + 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();
                }
            }
            int p = 0;
            boolean allCellsHaveContributed = true;
            signalKeepWithNext = false;
            for (int i = 0; i < start.length; i++) {
                if (start[i] == 0 && end[i] < 0 && elementLists[i] != null) {
                    allCellsHaveContributed = false;
                }
                signalKeepWithNext |= keepWithNextSignals[i];
            }
            if (!allCellsHaveContributed) {
                //Not all cells have contributed to a newly started row. The penalty here is
                //used to avoid breaks resulting in badly broken tables.
                //See also: http://marc.theaimsgroup.com/?t=112248999600005&r=1&w=2
                p = 900; //KnuthPenalty.INFINITE; //TODO Arbitrary value. Please refine.
            }
            if (signalKeepWithNext || getTableLM().mustKeepTogether()) {
                p = KnuthPenalty.INFINITE;
            }
            if (skippedStep) {
                p = KnuthPenalty.INFINITE;
                //Need to avoid breaking because borders and/or paddding from other columns would
                //not fit in the available space (see getNextStep())
            }
            if (isBreakCondition()) {
                if (skippedStep) {
                    log.error("This is a conflict situation. The output may be wrong."
                            + " Please send your FO file to fop-dev@xmlgraphics.apache.org!");
                }
                p = -KnuthPenalty.INFINITE; //Overrides any keeps (see 4.8 in XSL 1.0)
                clearBreakCondition();
            }
            //returnList.add(new KnuthPenalty(effPenaltyLen, p, false, penaltyPos, false));
            returnList.add(new BreakElement(penaltyPos, effPenaltyLen, p, 0, context));

            log.debug("step=" + step + " (+" + increase + ")"
                    + " box=" + boxLen
                    + " penalty=" + penaltyLen
                    + " effPenalty=" + effPenaltyLen);
           
            laststep = step;
            if (rowBacktrackForLastStep) {
                //If row was set to previous, restore now
                activeRow++;
            }
        }
        if (signalKeepWithNext) {
            //Last step signalled a keep-with-next. Since the last penalty will be removed,
            //we have to signal the still pending last keep-with-next using the LayoutContext.
            context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING);
        }
        if (isBreakCondition()) {
            ((BreakElement)returnList.getLast()).setPenaltyValue(-KnuthPenalty.INFINITE);
        }
        if (lastTCPos != null) {
            lastTCPos.setFlag(TableContentPosition.LAST_IN_ROWGROUP, true);
        }
        return returnList;
    }
   
    private int getNextStep(int lastStep) {
        //Check for forced break conditions
        /*
        if (isBreakCondition()) {
            return -1;
        }*/
       
        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()
                        && getActiveGridUnit(i).isLastGridUnitRowSpan()) {
                    rowPendingIndicator++;
                }
            } else {
                start[i] = -1; //end of list reached
                end[i] = -1;
            }
        }

        if (rowPendingIndicator == 0) {
            if (activeRow < rowGroup.length - 1) {
                TableRow rowFO = getActiveRow().getTableRow();
                if (rowFO != null && rowFO.getBreakAfter() != Constants.EN_AUTO) {
                    log.warn(FONode.decorateWithContextInfo(
                            "break-after ignored on table-row because of row spanning "
                            + "in progress (See XSL 1.0, 7.19.1)", rowFO));
                }
                activeRow++;
                if (log.isDebugEnabled()) {
                    log.debug("===> new row: " + activeRow);
                }
                initializeElementLists();
                for (int i = 0; i < backupWidths.length; i++) {
                    if (end[i] < 0) {
                        backupWidths[i] = 0;
                    }
                }
                rowFO = getActiveRow().getTableRow();
                if (rowFO != null && rowFO.getBreakBefore() != Constants.EN_AUTO) {
                    log.warn(FONode.decorateWithContextInfo(
                            "break-before ignored on table-row because of row spanning "
                            + "in progress (See XSL 1.0, 7.19.2)", rowFO));
                }
            }
        }

        //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) {
                        log.debug("FORCED break encountered!");
                        forcedBreaks[i] = true;
                        break;
                    } else if (el.getP() < KnuthElement.INFINITE) {
                        //First legal break point
                        break;
                    }
                } else if (el.isGlue()) {
                    if (end[i] > 0) {
                        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();
                }
            }
            if (log.isTraceEnabled()) {
                log.trace("borders before=" + borderBefore[i] + " after=" + borderAfter[i]);
                log.trace("padding before=" + paddingBefore[i] + " after=" + paddingAfter[i]);
            }
        }
        if (seqCount == 0) {
            return -1;
        }

        //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] += 2 * getTableLM().getHalfBorderSeparationBPD();
            baseWidth[i] += borderBefore[i] + borderAfter[i];
            baseWidth[i] += paddingBefore[i] + paddingAfter[i];
            if (end[i] >= start[i]) {
                int len = baseWidth[i] + widths[i];
                sb.append(len + " ");
                minStep = Math.min(len, minStep);
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("candidate steps: " + sb + " lastStep=" + lastStep);
        }

        //Check for constellations that would result in overlapping borders
        /*
        for (int i = 0; i < widths.length; i++) {
           
        }*/
       
        //Reset bigger-than-minimum sequences
        //See http://people.apache.org/~jeremias/fop/NextStepAlgoNotes.pdf
        rowBacktrackForLastStep = false;
        skippedStep = 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("minStep vs. border/padding increase conflict:");
                    if (activeRow == 0) {
                        log.debug("  First row. Skip this step.");
                        skippedStep = true;
                    } else {
                        log.debug("  row-span situation: backtracking to last row");
                        //Stay on the previous row for another step because borders and padding on
                        //columns may make their contribution to the step bigger than the addition
                        //of the next element for this step would make the step to grow.
                        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;
        }
    }
   
}
TOP

Related Classes of org.apache.fop.layoutmgr.table.TableStepper$KnuthBoxCellWithBPD

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.