Package org.apache.fop.layoutmgr.table

Source Code of org.apache.fop.layoutmgr.table.TableCellLayoutManager

/*
* 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: TableCellLayoutManager.java 331912 2005-11-08 22:34:30Z adelmelle $ */
package org.apache.fop.layoutmgr.table;

import java.util.LinkedList;

import org.apache.fop.fo.FONode;
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.layoutmgr.AreaAdditionUtil;
import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
import org.apache.fop.layoutmgr.BreakElement;
import org.apache.fop.layoutmgr.KnuthElement;
import org.apache.fop.layoutmgr.KnuthGlue;
import org.apache.fop.layoutmgr.LayoutContext;
import org.apache.fop.layoutmgr.ListElement;
import org.apache.fop.layoutmgr.PositionIterator;
import org.apache.fop.layoutmgr.Position;
import org.apache.fop.layoutmgr.SpaceResolver;
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;

/**
* LayoutManager for a table-cell FO.
* A cell contains blocks. These blocks fill the cell.
*/
public class TableCellLayoutManager extends BlockStackingLayoutManager
            implements BlockLevelLayoutManager {
   
    private PrimaryGridUnit gridUnit;
   
    private Block curBlockArea;

    private int inRowIPDOffset;
   
    private int xoffset;
    private int yoffset;
    private int cellIPD;
    private int rowHeight;
    private int usedBPD;
    private int startBorderWidth;
    private int endBorderWidth;
    private int borderAndPaddingBPD;
    private boolean emptyCell = true;

    /**
     * Create a new Cell layout manager.
     * @param node table-cell FO for which to create the LM
     * @param pgu primary grid unit for the cell
     */
    public TableCellLayoutManager(TableCell node, PrimaryGridUnit pgu) {
        super(node);
        fobj = node;
        this.gridUnit = pgu;
    }

    /** @return the table-cell FO */
    public TableCell getTableCell() {
        return (TableCell)this.fobj;
    }
   
    private boolean isSeparateBorderModel() {
        return getTableCell().isSeparateBorderModel();
    }
   
    /** @see org.apache.fop.layoutmgr.LayoutManager#initialize() */
    public void initialize() {
        borderAndPaddingBPD = 0;
        borderAndPaddingBPD += getTableCell()
            .getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
        borderAndPaddingBPD += getTableCell()
            .getCommonBorderPaddingBackground().getBorderAfterWidth(false);
        if (!isSeparateBorderModel()) {
            borderAndPaddingBPD /= 2;
        }
        borderAndPaddingBPD += getTableCell().getCommonBorderPaddingBackground()
                .getPaddingBefore(false, this);
        borderAndPaddingBPD += getTableCell().getCommonBorderPaddingBackground()
                .getPaddingAfter(false, this);
    }
   
    /**
     * @return the table owning this cell
     */
    public Table getTable() {
        FONode node = fobj.getParent();
        while (!(node instanceof Table)) {
            node = node.getParent();
        }
        return (Table)node;
    }
   

    private int getIPIndents() {
        int iIndents = 0;
        int[] startEndBorderWidths = gridUnit.getStartEndBorderWidths();
        startBorderWidth += startEndBorderWidths[0];
        endBorderWidth += startEndBorderWidths[1];
        iIndents += startBorderWidth;
        iIndents += endBorderWidth;
        if (!isSeparateBorderModel()) {
            iIndents /= 2;
        }
        iIndents += getTableCell().getCommonBorderPaddingBackground().getPaddingStart(false, this);
        iIndents += getTableCell().getCommonBorderPaddingBackground().getPaddingEnd(false, this);
        return iIndents;
    }
   
    /**
     * @see org.apache.fop.layoutmgr.LayoutManager
     */
    public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
        MinOptMax stackLimit = new MinOptMax(context.getStackLimit());

        referenceIPD = context.getRefIPD();
        cellIPD = referenceIPD;
        cellIPD -= getIPIndents();
        if (isSeparateBorderModel()) {
            int borderSep = getTableCell().getBorderSeparation().getLengthPair()
                    .getIPD().getLength().getValue(this);
            cellIPD -= borderSep;
        }

        LinkedList returnedList = null;
        LinkedList contentList = new LinkedList();
        LinkedList returnList = new LinkedList();

        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 (childLC.isKeepWithNextPending()) {
                log.debug("child LM signals pending keep with next");
            }
            if (contentList.size() == 0 && childLC.isKeepWithPreviousPending()) {
                context.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING);
                childLC.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING, false);
            }
           
            if (returnedList.size() == 1
                    && ((ListElement)returnedList.getFirst()).isForcedBreak()) {
                // 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);

                //Space resolution
                SpaceResolver.resolveElementList(returnList);
               
                return returnList;
            } else {
                if (prevLM != null) {
                    // there is a block handled by prevLM
                    // before the one handled by curLM
                    if (mustKeepTogether()
                            || context.isKeepWithNextPending()
                            || childLC.isKeepWithPreviousPending()) {
                        //Clear keep pending flag
                        context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING, false);
                        childLC.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING, false);
                        // add an infinite penalty to forbid a break between
                        // blocks
                        contentList.add(new BreakElement(
                                new Position(this), KnuthElement.INFINITE, context));
                        //contentList.add(new KnuthPenalty(0,
                        //        KnuthElement.INFINITE, false,
                        //        new Position(this), false));
                    } else if (!((ListElement) contentList.getLast()).isGlue()) {
                        // add a null penalty to allow a break between blocks
                        contentList.add(new BreakElement(
                                new Position(this), 0, context));
                        //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 (((ListElement)returnedList.getLast()).isForcedBreak()) {
                    // 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);

                    //Space resolution
                    SpaceResolver.resolveElementList(returnList);
                   
                    return returnList;
                }
            }
            if (childLC.isKeepWithNextPending()) {
                //Clear and propagate
                childLC.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING, false);
                context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING);
            }
            prevLM = curLM;
        }

        returnedList = new LinkedList();
        wrapPositionElements(contentList, returnList);
       
        //Space resolution
        SpaceResolver.resolveElementList(returnList);
       
        setFinished(true);
        return returnList;
    }
   
    /**
     * Set the y offset of this cell.
     * This offset is used to set the absolute position of the cell.
     *
     * @param off the y direction offset
     */
    public void setYOffset(int off) {
        yoffset = off;
    }

    /**
     * Set the x offset of this cell (usually the same as its parent row).
     * This offset is used to determine the absolute position of the cell.
     *
     * @param off the x offset
     */
    public void setXOffset(int off) {
        xoffset = off;
    }

    /**
     * Set the IPD offset of this cell inside the table-row.
     * This offset is used to determine the absolute position of the cell.
     * @param off the IPD offset
     */
    public void setInRowIPDOffset(int off) {
        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.
     *
     * @param h the height of the row
     */
    public void setRowHeight(int h) {
        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, this);
        bpd -= cbpb.getPaddingAfter(false, this);
        return bpd;
    }
   
    /**
     * Add the areas for the break points.
     * The cell contains block stacking layout managers
     * that add block areas.
     *
     * @param parentIter the iterator of the break positions
     * @param layoutContext the layout context for adding the areas
     */
    public void addAreas(PositionIterator parentIter,
                         LayoutContext layoutContext) {
        getParentArea(null);

        getPSLM().addIDToPage(getTableCell().getId());

        if (isSeparateBorderModel()) {
            if (!emptyCell || getTableCell().showEmptyCells()) {
                TraitSetter.addBorders(curBlockArea,
                        getTableCell().getCommonBorderPaddingBackground(), this);
            }
        } else {
            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
                TraitSetter.addCollapsingBorders(curBlockArea,
                        gridUnit.getBorders(), outer, this);
            } else {
                int dy = yoffset;
                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.length; x++) {
                        GridUnit gu = gridUnits[x];
                        if (!gu.hasBorders()) {
                            continue;
                        }
                       
                        //Blocks for painting grid unit borders
                        Block block = new Block();
                        block.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
                        block.setPositioning(Block.ABSOLUTE);

                        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);
                        lastRowHeight = rowHeight;
                        int ipd = gu.getColumn().getColumnWidth().getValue(this);
                        int borderStartWidth = gu.getBorders().getBorderStartWidth(false) / 2;
                        ipd -= borderStartWidth;
                        ipd -= gu.getBorders().getBorderEndWidth(false) / 2;
                        block.setIPD(ipd);
                        block.setXOffset(dx + borderStartWidth);
                        int halfCollapsingBorderHeight = 0;
                        if (!isSeparateBorderModel()) {
                            halfCollapsingBorderHeight
                                += gu.getBorders().getBorderBeforeWidth(false) / 2;
                        }
                        block.setYOffset(dy - halfCollapsingBorderHeight);
                        TraitSetter.addCollapsingBorders(block, gu.getBorders(), outer, this);
                        parentLM.addChildArea(block);
                        dx += gu.getColumn().getColumnWidth().getValue(this);
                    }
                    dy += lastRowHeight;
                }
                log.warn("TODO Add collapsed border painting for spanned cells");
            }
        }

        //Handle display-align
        int contentBPD = getContentHeight(rowHeight, gridUnit);
        if (usedBPD < contentBPD) {
            if (getTableCell().getDisplayAlign() == EN_CENTER) {
                Block space = new Block();
                space.setBPD((contentBPD - usedBPD) / 2);
                curBlockArea.addBlock(space);
            } else if (getTableCell().getDisplayAlign() == EN_AFTER) {
                Block space = new Block();
                space.setBPD((contentBPD - usedBPD));
                curBlockArea.addBlock(space);
            }
        }

        AreaAdditionUtil.addAreas(this, parentIter, layoutContext);
       
        curBlockArea.setBPD(contentBPD);

        // Add background after we know the BPD
        if (isSeparateBorderModel()) {
            if (!emptyCell || getTableCell().showEmptyCells()) {
                TraitSetter.addBackground(curBlockArea,
                        getTableCell().getCommonBorderPaddingBackground(),
                        this);
            }
        } else {
            TraitSetter.addBackground(curBlockArea,
                    getTableCell().getCommonBorderPaddingBackground(),
                    this);
        }
       
        flush();

        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.
     * 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 to get the parent for
     * @return the parent area
     */
    public Area getParentArea(Area childArea) {
        if (curBlockArea == null) {
            curBlockArea = new Block();
            curBlockArea.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
            TraitSetter.setProducerID(curBlockArea, getTableCell().getId());
            curBlockArea.setPositioning(Block.ABSOLUTE);
            int indent = 0;
            indent += startBorderWidth;
            if (!isSeparateBorderModel()) {
                indent /= 2;
            }
            indent += getTableCell()
                    .getCommonBorderPaddingBackground().getPaddingStart(false, this);
            // set position
            int halfBorderSep = 0;
            if (isSeparateBorderModel()) {
                halfBorderSep = getTableCell().getBorderSeparation().getLengthPair()
                        .getIPD().getLength().getValue(this) / 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 - borderAdjust);
            curBlockArea.setIPD(cellIPD);
            //curBlockArea.setHeight();

            // Set up dimensions
            /*Area parentArea =*/ parentLM.getParentArea(curBlockArea);
            // Get reference IPD from parentArea
            setCurrentArea(curBlockArea); // ??? for generic operations
        }
        return curBlockArea;
    }

    /**
     * Add the child to the cell block area.
     *
     * @param childArea the child to add to the cell
     */
    public void addChildArea(Area childArea) {
        if (curBlockArea != null) {
            curBlockArea.addBlock((Block) childArea);
        }
    }

    /**
     * Reset the position of the layout.
     *
     * @param resetPos the position to reset to
     */
    public void resetPosition(Position resetPos) {
        if (resetPos == null) {
            reset(null);
        }
    }

    /**
     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager
     */
    public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
        // TODO Auto-generated method stub
        return 0;
    }

    /**
     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager
     */
    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
        boolean keep = ((BlockLevelLayoutManager)getParent()).mustKeepTogether();
        if (gridUnit.getRow() != null) {
            keep |= gridUnit.getRow().mustKeepTogether();
        }
        return keep;
    }

    /**
     * @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();
            */
    }
   
    // --------- Property Resolution related functions --------- //
   
    /**
     * Returns the IPD of the content area
     * @return the IPD of the content area
     */
    public int getContentAreaIPD() {
        return cellIPD;
    }
  
    /**
     * Returns the BPD of the content area
     * @return the BPD of the content area
     */
    public int getContentAreaBPD() {
        if (curBlockArea != null) {
            return curBlockArea.getBPD();
        } else {
            log.error("getContentAreaBPD called on unknown BPD");
            return -1;
        }
    }
  
    /**
     * @see org.apache.fop.layoutmgr.LayoutManager#getGeneratesReferenceArea
     */
    public boolean getGeneratesReferenceArea() {
        return true;
    }
  
    /**
     * @see org.apache.fop.layoutmgr.LayoutManager#getGeneratesBlockArea
     */
    public boolean getGeneratesBlockArea() {
        return true;
    }

}
TOP

Related Classes of org.apache.fop.layoutmgr.table.TableCellLayoutManager

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.