/* * 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.Iterator; 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.flow.table.EffRow; import org.apache.fop.fo.flow.table.GridUnit; import org.apache.fop.fo.flow.table.PrimaryGridUnit; import org.apache.fop.fo.flow.table.TableColumn; import org.apache.fop.fo.flow.table.TableRow; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.LengthRangeProperty; import org.apache.fop.layoutmgr.ElementListObserver; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.traits.MinOptMax; import org.apache.fop.util.BreakUtil; class RowGroupLayoutManager { private static Log log = LogFactory.getLog(RowGroupLayoutManager.class); private static final MinOptMax MAX_STRETCH = MinOptMax.getInstance(0, 0, Integer.MAX_VALUE); private EffRow[] rowGroup; private TableLayoutManager tableLM; private TableStepper tableStepper; RowGroupLayoutManager(TableLayoutManager tableLM, EffRow[] rowGroup, TableStepper tableStepper) { this.tableLM = tableLM; this.rowGroup = rowGroup; this.tableStepper = tableStepper; } public LinkedList getNextKnuthElements(LayoutContext context, int alignment, int bodyType) { LinkedList returnList = new LinkedList(); createElementsForRowGroup(context, alignment, bodyType, returnList); context.updateKeepWithPreviousPending(rowGroup[0].getKeepWithPrevious()); context.updateKeepWithNextPending(rowGroup[rowGroup.length - 1].getKeepWithNext()); int breakBefore = Constants.EN_AUTO; TableRow firstRow = rowGroup[0].getTableRow(); if (firstRow != null) { breakBefore = firstRow.getBreakBefore(); } context.setBreakBefore(BreakUtil.compareBreakClasses(breakBefore, rowGroup[0].getBreakBefore())); int breakAfter = Constants.EN_AUTO; TableRow lastRow = rowGroup[rowGroup.length - 1].getTableRow(); if (lastRow != null) { breakAfter = lastRow.getBreakAfter(); } context.setBreakAfter(BreakUtil.compareBreakClasses(breakAfter, rowGroup[rowGroup.length - 1].getBreakAfter())); return returnList; } /** * 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 */ private void createElementsForRowGroup(LayoutContext context, int alignment, int bodyType, LinkedList returnList) { log.debug("Handling row group with " + rowGroup.length + " rows..."); EffRow row; for (EffRow aRowGroup : rowGroup) { row = aRowGroup; for (Object o : row.getGridUnits()) { GridUnit gu = (GridUnit) o; if (gu.isPrimary()) { PrimaryGridUnit primary = gu.getPrimary(); // TODO a new LM must be created for every new static-content primary.createCellLM(); primary.getCellLM().setParent(tableLM); //Calculate width of cell int spanWidth = 0; Iterator colIter = tableLM.getTable().getColumns().listIterator( primary.getColIndex()); for (int i = 0, c = primary.getCell().getNumberColumnsSpanned(); i < c; i++) { spanWidth += ((TableColumn) colIter.next()).getColumnWidth().getValue( tableLM); } LayoutContext childLC = LayoutContext.newInstance(); childLC.setStackLimitBP(context.getStackLimitBP()); //necessary? childLC.setRefIPD(spanWidth); //Get the element list for the cell contents List elems = primary.getCellLM().getNextKnuthElements( childLC, alignment); ElementListObserver.observe(elems, "table-cell", primary.getCell().getId()); primary.setElements(elems); } } } computeRowHeights(); List elements = tableStepper.getCombinedKnuthElementsForRowGroup(context, rowGroup, bodyType); returnList.addAll(elements); } /** * Calculate the heights of the rows in the row group, see CSS21, 17.5.3 Table height * algorithms. * * TODO this method will need to be adapted once clarification has been made by the * W3C regarding whether borders or border-separation must be included or not */ private void computeRowHeights() { log.debug("rowGroup:"); MinOptMax[] rowHeights = new MinOptMax[rowGroup.length]; EffRow row; for (int rgi = 0; rgi < rowGroup.length; rgi++) { row = rowGroup[rgi]; // The BPD of the biggest cell in the row // int maxCellBPD = 0; MinOptMax explicitRowHeight; TableRow tableRowFO = rowGroup[rgi].getTableRow(); if (tableRowFO == null) { rowHeights[rgi] = MAX_STRETCH; explicitRowHeight = MAX_STRETCH; } else { LengthRangeProperty rowBPD = tableRowFO.getBlockProgressionDimension(); rowHeights[rgi] = rowBPD.toMinOptMax(tableLM); explicitRowHeight = rowBPD.toMinOptMax(tableLM); } for (Object o : row.getGridUnits()) { GridUnit gu = (GridUnit) o; if (!gu.isEmpty() && gu.getColSpanIndex() == 0 && gu.isLastGridUnitRowSpan()) { PrimaryGridUnit primary = gu.getPrimary(); int effectiveCellBPD = 0; LengthRangeProperty cellBPD = primary.getCell().getBlockProgressionDimension(); if (!cellBPD.getMinimum(tableLM).isAuto()) { effectiveCellBPD = cellBPD.getMinimum(tableLM).getLength() .getValue(tableLM); } if (!cellBPD.getOptimum(tableLM).isAuto()) { effectiveCellBPD = cellBPD.getOptimum(tableLM).getLength() .getValue(tableLM); } if (gu.getRowSpanIndex() == 0) { effectiveCellBPD = Math.max(effectiveCellBPD, explicitRowHeight.getOpt()); } effectiveCellBPD = Math.max(effectiveCellBPD, primary.getContentLength()); int borderWidths = primary.getBeforeAfterBorderWidth(); int padding = 0; CommonBorderPaddingBackground cbpb = primary.getCell() .getCommonBorderPaddingBackground(); padding += cbpb.getPaddingBefore(false, primary.getCellLM()); padding += cbpb.getPaddingAfter(false, primary.getCellLM()); int effRowHeight = effectiveCellBPD + padding + borderWidths; for (int prev = rgi - 1; prev >= rgi - gu.getRowSpanIndex(); prev--) { effRowHeight -= rowHeights[prev].getOpt(); } if (effRowHeight > rowHeights[rgi].getMin()) { // This is the new height of the (grid) row rowHeights[rgi] = rowHeights[rgi].extendMinimum(effRowHeight); } } } row.setHeight(rowHeights[rgi]); row.setExplicitHeight(explicitRowHeight); // TODO re-enable and improve after clarification //See http://markmail.org/message/h25ycwwu7qglr4k4 // if (maxCellBPD > row.getExplicitHeight().max) { //old: // log.warn(FONode.decorateWithContextInfo( // "The contents of row " + (row.getIndex() + 1) // + " are taller than they should be (there is a" // + " block-progression-dimension or height constraint // + " on the indicated row)." // + " Due to its contents the row grows" // + " to " + maxCellBPD + " millipoints, but the row shouldn't get" // + " any taller than " + row.getExplicitHeight() + " millipoints.", // row.getTableRow())); //new (with events): // BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Factory.create( // tableRow.getUserAgent().getEventBroadcaster()); // eventProducer.rowTooTall(this, row.getIndex() + 1, // maxCellBPD, row.getExplicitHeight().max, tableRow.getLocator()); // } } } }