You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RowGroupLayoutManager.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.layoutmgr.table;
  19. import java.util.LinkedList;
  20. import java.util.List;
  21. import org.apache.commons.logging.Log;
  22. import org.apache.commons.logging.LogFactory;
  23. import org.apache.fop.fo.Constants;
  24. import org.apache.fop.fo.FONode;
  25. import org.apache.fop.fo.flow.table.EffRow;
  26. import org.apache.fop.fo.flow.table.GridUnit;
  27. import org.apache.fop.fo.flow.table.PrimaryGridUnit;
  28. import org.apache.fop.fo.flow.table.TableRow;
  29. import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
  30. import org.apache.fop.fo.properties.LengthRangeProperty;
  31. import org.apache.fop.layoutmgr.BreakElement;
  32. import org.apache.fop.layoutmgr.ElementListObserver;
  33. import org.apache.fop.layoutmgr.ElementListUtils;
  34. import org.apache.fop.layoutmgr.KnuthElement;
  35. import org.apache.fop.layoutmgr.KnuthPenalty;
  36. import org.apache.fop.layoutmgr.LayoutContext;
  37. import org.apache.fop.layoutmgr.ListElement;
  38. import org.apache.fop.layoutmgr.MinOptMaxUtil;
  39. import org.apache.fop.traits.MinOptMax;
  40. class RowGroupLayoutManager {
  41. private static Log log = LogFactory.getLog(TableContentLayoutManager.class);
  42. private EffRow[] rowGroup;
  43. private TableLayoutManager tableLM;
  44. private TableStepper tableStepper;
  45. RowGroupLayoutManager(TableLayoutManager tableLM, EffRow[] rowGroup,
  46. TableStepper tableStepper) {
  47. this.tableLM = tableLM;
  48. this.rowGroup = rowGroup;
  49. this.tableStepper = tableStepper;
  50. }
  51. /**
  52. *
  53. * @return one of {@link Constants#EN_AUTO}, {@link Constants#EN_COLUMN},
  54. * {@link Constants#EN_PAGE}, {@link Constants#EN_EVEN_PAGE}, or
  55. * {@link Constants#EN_ODD_PAGE}
  56. */
  57. int getBreakBefore() {
  58. TableRow rowFO = rowGroup[0].getTableRow();
  59. if (rowFO == null) {
  60. return Constants.EN_AUTO;
  61. } else {
  62. return rowFO.getBreakBefore();
  63. }
  64. }
  65. /**
  66. *
  67. * @return one of {@link Constants#EN_AUTO}, {@link Constants#EN_COLUMN},
  68. * {@link Constants#EN_PAGE}, {@link Constants#EN_EVEN_PAGE}, or
  69. * {@link Constants#EN_ODD_PAGE}
  70. */
  71. int getBreakAfter() {
  72. TableRow rowFO = rowGroup[rowGroup.length - 1].getTableRow();
  73. if (rowFO == null) {
  74. return Constants.EN_AUTO;
  75. } else {
  76. return rowFO.getBreakAfter();
  77. }
  78. }
  79. public LinkedList getNextKnuthElements(LayoutContext context, int alignment, int bodyType) {
  80. LinkedList returnList = new LinkedList();
  81. //Reset keep-with-next when remaining inside the table.
  82. //The context flag is only used to propagate keep-with-next to the outside.
  83. //The clearing is ok here because createElementsForRowGroup already handles
  84. //the keep when inside a table.
  85. context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING, false);
  86. //Element list creation
  87. createElementsForRowGroup(context, alignment, bodyType, returnList);
  88. //Handle keeps
  89. if (context.isKeepWithNextPending()) {
  90. log.debug("child LM (row group) signals pending keep-with-next");
  91. }
  92. if (context.isKeepWithPreviousPending()) {
  93. log.debug("child LM (row group) signals pending keep-with-previous");
  94. if (returnList.size() > 0) {
  95. //Modify last penalty
  96. ListElement last = (ListElement)returnList.getLast();
  97. if (last.isPenalty()) {
  98. BreakElement breakPoss = (BreakElement)last;
  99. //Only honor keep if there's no forced break
  100. if (!breakPoss.isForcedBreak()) {
  101. breakPoss.setPenaltyValue(KnuthPenalty.INFINITE);
  102. }
  103. }
  104. }
  105. }
  106. return returnList;
  107. }
  108. /**
  109. * Creates Knuth elements for a row group (see TableRowIterator.getNextRowGroup()).
  110. * @param context Active LayoutContext
  111. * @param alignment alignment indicator
  112. * @param bodyType Indicates what kind of body is being processed (BODY, HEADER or FOOTER)
  113. * @param returnList List to received the generated elements
  114. * @param rowGroup row group to process
  115. */
  116. private void createElementsForRowGroup(LayoutContext context, int alignment,
  117. int bodyType, LinkedList returnList) {
  118. log.debug("Handling row group with " + rowGroup.length + " rows...");
  119. MinOptMax[] rowHeights = new MinOptMax[rowGroup.length];
  120. MinOptMax[] explicitRowHeights = new MinOptMax[rowGroup.length];
  121. EffRow row;
  122. int maxColumnCount = 0;
  123. List pgus = new java.util.ArrayList(); //holds a list of a row's primary grid units
  124. for (int rgi = 0; rgi < rowGroup.length; rgi++) {
  125. row = rowGroup[rgi];
  126. rowHeights[rgi] = new MinOptMax(0, 0, Integer.MAX_VALUE);
  127. explicitRowHeights[rgi] = new MinOptMax(0, 0, Integer.MAX_VALUE);
  128. pgus.clear();
  129. TableRow tableRow = null;
  130. // The row's minimum content height; 0 if the row's height is auto, otherwise
  131. // the .minimum component of the explicitly specified value
  132. int minRowBPD = 0;
  133. // The BPD of the biggest cell in the row
  134. int maxCellBPD = 0;
  135. for (int j = 0; j < row.getGridUnits().size(); j++) {
  136. assert maxColumnCount == 0 || maxColumnCount == row.getGridUnits().size();
  137. maxColumnCount = Math.max(maxColumnCount, row.getGridUnits().size());
  138. GridUnit gu = row.getGridUnit(j);
  139. if ((gu.isPrimary() || (gu.getColSpanIndex() == 0 && gu.isLastGridUnitRowSpan()))
  140. && !gu.isEmpty()) {
  141. PrimaryGridUnit primary = gu.getPrimary();
  142. if (gu.isPrimary()) {
  143. primary.createCellLM(); // TODO a new LM must be created for every new static-content
  144. primary.getCellLM().setParent(tableLM);
  145. //Determine the table-row if any
  146. if (tableRow == null && primary.getRow() != null) {
  147. tableRow = primary.getRow();
  148. //Check for bpd on row, see CSS21, 17.5.3 Table height algorithms
  149. LengthRangeProperty rowBPD = tableRow.getBlockProgressionDimension();
  150. if (!rowBPD.getMinimum(tableLM).isAuto()) {
  151. minRowBPD = Math.max(minRowBPD,
  152. rowBPD.getMinimum(tableLM).getLength().getValue(tableLM));
  153. }
  154. MinOptMaxUtil.restrict(explicitRowHeights[rgi], rowBPD, tableLM);
  155. }
  156. //Calculate width of cell
  157. int spanWidth = 0;
  158. for (int i = primary.getStartCol();
  159. i < primary.getStartCol()
  160. + primary.getCell().getNumberColumnsSpanned();
  161. i++) {
  162. if (tableLM.getColumns().getColumn(i + 1) != null) {
  163. spanWidth += tableLM.getColumns().getColumn(i + 1)
  164. .getColumnWidth().getValue(tableLM);
  165. }
  166. }
  167. LayoutContext childLC = new LayoutContext(0);
  168. childLC.setStackLimit(context.getStackLimit()); //necessary?
  169. childLC.setRefIPD(spanWidth);
  170. //Get the element list for the cell contents
  171. LinkedList elems = primary.getCellLM().getNextKnuthElements(
  172. childLC, alignment);
  173. ElementListObserver.observe(elems, "table-cell", primary.getCell().getId());
  174. if ((elems.size() > 0)
  175. && ((KnuthElement)elems.getLast()).isForcedBreak()) {
  176. // a descendant of this block has break-after
  177. log.debug("Descendant of table-cell signals break: "
  178. + primary.getCellLM().isFinished());
  179. }
  180. primary.setElements(elems);
  181. if (childLC.isKeepWithNextPending()) {
  182. log.debug("child LM signals pending keep-with-next");
  183. primary.setFlag(GridUnit.KEEP_WITH_NEXT_PENDING, true);
  184. }
  185. if (childLC.isKeepWithPreviousPending()) {
  186. log.debug("child LM signals pending keep-with-previous");
  187. primary.setFlag(GridUnit.KEEP_WITH_PREVIOUS_PENDING, true);
  188. }
  189. }
  190. //Calculate height of row, see CSS21, 17.5.3 Table height algorithms
  191. if (gu.isLastGridUnitRowSpan()) {
  192. // The effective cell's bpd, after taking into account bpd
  193. // (possibly explicitly) set on the row or on the cell, and the
  194. // cell's content length
  195. int effectiveCellBPD = minRowBPD;
  196. LengthRangeProperty cellBPD = primary.getCell()
  197. .getBlockProgressionDimension();
  198. if (!cellBPD.getMinimum(tableLM).isAuto()) {
  199. effectiveCellBPD = Math.max(effectiveCellBPD,
  200. cellBPD.getMinimum(tableLM).getLength().getValue(tableLM));
  201. }
  202. if (!cellBPD.getOptimum(tableLM).isAuto()) {
  203. effectiveCellBPD = Math.max(effectiveCellBPD,
  204. cellBPD.getOptimum(tableLM).getLength().getValue(tableLM));
  205. }
  206. if (gu.getRowSpanIndex() == 0) {
  207. //TODO ATM only non-row-spanned cells are taken for this
  208. MinOptMaxUtil.restrict(explicitRowHeights[rgi], cellBPD, tableLM);
  209. }
  210. effectiveCellBPD = Math.max(effectiveCellBPD,
  211. primary.getContentLength());
  212. int borderWidths;
  213. if (tableLM.getTable().isSeparateBorderModel()) {
  214. borderWidths = primary.getBorders().getBorderBeforeWidth(false)
  215. + primary.getBorders().getBorderAfterWidth(false);
  216. } else {
  217. borderWidths = primary.getHalfMaxBorderWidth();
  218. }
  219. int padding = 0;
  220. maxCellBPD = Math.max(maxCellBPD, effectiveCellBPD);
  221. CommonBorderPaddingBackground cbpb
  222. = primary.getCell().getCommonBorderPaddingBackground();
  223. padding += cbpb.getPaddingBefore(false, primary.getCellLM());
  224. padding += cbpb.getPaddingAfter(false, primary.getCellLM());
  225. int effRowHeight = effectiveCellBPD
  226. + padding + borderWidths
  227. + 2 * tableLM.getHalfBorderSeparationBPD();
  228. for (int previous = 0; previous < gu.getRowSpanIndex(); previous++) {
  229. effRowHeight -= rowHeights[rgi - previous - 1].opt;
  230. }
  231. if (effRowHeight > rowHeights[rgi].min) {
  232. //This is the new height of the (grid) row
  233. MinOptMaxUtil.extendMinimum(rowHeights[rgi], effRowHeight, false);
  234. }
  235. }
  236. if (gu.isPrimary()) {
  237. pgus.add(primary);
  238. }
  239. }
  240. }
  241. row.setHeight(rowHeights[rgi]);
  242. row.setExplicitHeight(explicitRowHeights[rgi]);
  243. if (maxCellBPD > row.getExplicitHeight().max) {
  244. log.warn(FONode.decorateWithContextInfo(
  245. "The contents of row " + (row.getIndex() + 1)
  246. + " are taller than they should be (there is a"
  247. + " block-progression-dimension or height constraint on the indicated row)."
  248. + " Due to its contents the row grows"
  249. + " to " + maxCellBPD + " millipoints, but the row shouldn't get"
  250. + " any taller than " + row.getExplicitHeight() + " millipoints.",
  251. row.getTableRow()));
  252. }
  253. }
  254. if (log.isDebugEnabled()) {
  255. log.debug("rowGroup:");
  256. for (int i = 0; i < rowHeights.length; i++) {
  257. log.debug(" height=" + rowHeights[i] + " explicit=" + explicitRowHeights[i]);
  258. }
  259. }
  260. LinkedList returnedList = tableStepper.getCombinedKnuthElementsForRowGroup(
  261. context, rowGroup, maxColumnCount, bodyType);
  262. if (returnedList != null) {
  263. returnList.addAll(returnedList);
  264. }
  265. }
  266. }