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.

TableCellLayoutManager.java 32KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  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 java.util.Map;
  22. import org.apache.commons.logging.Log;
  23. import org.apache.commons.logging.LogFactory;
  24. import org.apache.fop.area.Area;
  25. import org.apache.fop.area.Block;
  26. import org.apache.fop.area.Trait;
  27. import org.apache.fop.fo.flow.Marker;
  28. import org.apache.fop.fo.flow.table.ConditionalBorder;
  29. import org.apache.fop.fo.flow.table.GridUnit;
  30. import org.apache.fop.fo.flow.table.PrimaryGridUnit;
  31. import org.apache.fop.fo.flow.table.Table;
  32. import org.apache.fop.fo.flow.table.TableCell;
  33. import org.apache.fop.fo.flow.table.TableColumn;
  34. import org.apache.fop.fo.flow.table.TableFooter;
  35. import org.apache.fop.fo.flow.table.TableHeader;
  36. import org.apache.fop.fo.flow.table.TablePart;
  37. import org.apache.fop.fo.flow.table.TableRow;
  38. import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
  39. import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo;
  40. import org.apache.fop.layoutmgr.AbstractLayoutManager;
  41. import org.apache.fop.layoutmgr.AreaAdditionUtil;
  42. import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
  43. import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
  44. import org.apache.fop.layoutmgr.ElementListObserver;
  45. import org.apache.fop.layoutmgr.ElementListUtils;
  46. import org.apache.fop.layoutmgr.Keep;
  47. import org.apache.fop.layoutmgr.KnuthBox;
  48. import org.apache.fop.layoutmgr.KnuthElement;
  49. import org.apache.fop.layoutmgr.KnuthGlue;
  50. import org.apache.fop.layoutmgr.KnuthPenalty;
  51. import org.apache.fop.layoutmgr.LayoutContext;
  52. import org.apache.fop.layoutmgr.LayoutManager;
  53. import org.apache.fop.layoutmgr.LocalBreaker;
  54. import org.apache.fop.layoutmgr.Position;
  55. import org.apache.fop.layoutmgr.PositionIterator;
  56. import org.apache.fop.layoutmgr.RetrieveTableMarkerLayoutManager;
  57. import org.apache.fop.layoutmgr.SpaceResolver;
  58. import org.apache.fop.layoutmgr.TraitSetter;
  59. import org.apache.fop.traits.BorderProps;
  60. import org.apache.fop.traits.MinOptMax;
  61. import org.apache.fop.util.ListUtil;
  62. /**
  63. * LayoutManager for a table-cell FO.
  64. * A cell contains blocks. These blocks fill the cell.
  65. */
  66. public class TableCellLayoutManager extends BlockStackingLayoutManager
  67. implements BlockLevelLayoutManager {
  68. /**
  69. * logging instance
  70. */
  71. private static Log log = LogFactory.getLog(TableCellLayoutManager.class);
  72. private PrimaryGridUnit primaryGridUnit;
  73. private Block curBlockArea;
  74. private int xoffset;
  75. private int yoffset;
  76. private int cellIPD;
  77. private int totalHeight;
  78. private int usedBPD;
  79. private boolean emptyCell = true;
  80. private boolean isDescendantOfTableFooter;
  81. private boolean isDescendantOfTableHeader;
  82. private boolean hasRetrieveTableMarker;
  83. // place holder for the addAreas arguments
  84. private boolean savedAddAreasArguments;
  85. private PositionIterator savedParentIter;
  86. private LayoutContext savedLayoutContext;
  87. private int[] savedSpannedGridRowHeights;
  88. private int savedStartRow;
  89. private int savedEndRow;
  90. private int savedBorderBeforeWhich;
  91. private int savedBorderAfterWhich;
  92. private boolean savedFirstOnPage;
  93. private boolean savedLastOnPage;
  94. private RowPainter savedPainter;
  95. private int savedFirstRowHeight;
  96. // this is set to false when the table-cell has a retrieve-table-marker and is in the table-header
  97. private boolean flushArea = true;
  98. // this information is set by the RowPainter
  99. private boolean isLastTrait;
  100. /**
  101. * Create a new Cell layout manager.
  102. * @param node table-cell FO for which to create the LM
  103. * @param pgu primary grid unit for the cell
  104. */
  105. public TableCellLayoutManager(TableCell node, PrimaryGridUnit pgu) {
  106. super(node);
  107. this.primaryGridUnit = pgu;
  108. this.isDescendantOfTableHeader = node.getParent().getParent() instanceof TableHeader
  109. || node.getParent() instanceof TableHeader;
  110. this.isDescendantOfTableFooter = node.getParent().getParent() instanceof TableFooter
  111. || node.getParent() instanceof TableFooter;
  112. this.hasRetrieveTableMarker = node.hasRetrieveTableMarker();
  113. }
  114. /** @return the table-cell FO */
  115. public TableCell getTableCell() {
  116. return (TableCell)this.fobj;
  117. }
  118. private boolean isSeparateBorderModel() {
  119. return getTable().isSeparateBorderModel();
  120. }
  121. /**
  122. * @return the table owning this cell
  123. */
  124. public Table getTable() {
  125. return getTableCell().getTable();
  126. }
  127. /** {@inheritDoc} */
  128. protected int getIPIndents() {
  129. int[] startEndBorderWidths = primaryGridUnit.getStartEndBorderWidths();
  130. startIndent = startEndBorderWidths[0];
  131. endIndent = startEndBorderWidths[1];
  132. if (isSeparateBorderModel()) {
  133. int borderSep = getTable().getBorderSeparation().getLengthPair().getIPD().getLength()
  134. .getValue(this);
  135. startIndent += borderSep / 2;
  136. endIndent += borderSep / 2;
  137. } else {
  138. startIndent /= 2;
  139. endIndent /= 2;
  140. }
  141. startIndent += getTableCell().getCommonBorderPaddingBackground().getPaddingStart(false,
  142. this);
  143. endIndent += getTableCell().getCommonBorderPaddingBackground().getPaddingEnd(false, this);
  144. return startIndent + endIndent;
  145. }
  146. /**
  147. * {@inheritDoc}
  148. */
  149. public List getNextKnuthElements(LayoutContext context, int alignment) {
  150. MinOptMax stackLimit = context.getStackLimitBP();
  151. referenceIPD = context.getRefIPD();
  152. cellIPD = referenceIPD;
  153. cellIPD -= getIPIndents();
  154. List returnedList;
  155. List contentList = new LinkedList();
  156. List returnList = new LinkedList();
  157. LayoutManager curLM; // currently active LM
  158. LayoutManager prevLM = null; // previously active LM
  159. while ((curLM = getChildLM()) != null) {
  160. LayoutContext childLC = LayoutContext.newInstance();
  161. // curLM is a ?
  162. childLC.setStackLimitBP(context.getStackLimitBP().minus(stackLimit));
  163. childLC.setRefIPD(cellIPD);
  164. // get elements from curLM
  165. returnedList = curLM.getNextKnuthElements(childLC, alignment);
  166. if (childLC.isKeepWithNextPending()) {
  167. log.debug("child LM signals pending keep with next");
  168. }
  169. if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) {
  170. primaryGridUnit.setKeepWithPrevious(childLC.getKeepWithPreviousPending());
  171. childLC.clearKeepWithPreviousPending();
  172. }
  173. if (prevLM != null
  174. && !ElementListUtils.endsWithForcedBreak(contentList)) {
  175. // there is a block handled by prevLM
  176. // before the one handled by curLM
  177. addInBetweenBreak(contentList, context, childLC);
  178. }
  179. contentList.addAll(returnedList);
  180. if (returnedList.isEmpty()) {
  181. //Avoid NoSuchElementException below (happens with empty blocks)
  182. continue;
  183. }
  184. if (childLC.isKeepWithNextPending()) {
  185. //Clear and propagate
  186. context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
  187. childLC.clearKeepWithNextPending();
  188. }
  189. prevLM = curLM;
  190. }
  191. primaryGridUnit.setKeepWithNext(context.getKeepWithNextPending());
  192. returnedList = new LinkedList();
  193. if (!contentList.isEmpty()) {
  194. wrapPositionElements(contentList, returnList);
  195. } else {
  196. // In relaxed validation mode, table-cells having no children are authorised.
  197. // Add a zero-width block here to not have to take this special case into
  198. // account later
  199. // Copied from BlockStackingLM
  200. returnList.add(new KnuthBox(0, notifyPos(new Position(this)), true));
  201. }
  202. //Space resolution
  203. SpaceResolver.resolveElementList(returnList);
  204. if (((KnuthElement) returnList.get(0)).isForcedBreak()) {
  205. primaryGridUnit.setBreakBefore(((KnuthPenalty) returnList.get(0)).getBreakClass());
  206. returnList.remove(0);
  207. assert !returnList.isEmpty();
  208. }
  209. final KnuthElement lastItem = (KnuthElement) ListUtil
  210. .getLast(returnList);
  211. if (lastItem.isForcedBreak()) {
  212. KnuthPenalty p = (KnuthPenalty) lastItem;
  213. primaryGridUnit.setBreakAfter(p.getBreakClass());
  214. p.setPenalty(0);
  215. }
  216. setFinished(true);
  217. return returnList;
  218. }
  219. /**
  220. * Set the y offset of this cell.
  221. * This offset is used to set the absolute position of the cell.
  222. *
  223. * @param off the y direction offset
  224. */
  225. public void setYOffset(int off) {
  226. yoffset = off;
  227. }
  228. /**
  229. * Set the x offset of this cell (usually the same as its parent row).
  230. * This offset is used to determine the absolute position of the cell.
  231. *
  232. * @param off the x offset
  233. */
  234. public void setXOffset(int off) {
  235. xoffset = off;
  236. }
  237. /**
  238. * Set the content height for this cell. This method is used during
  239. * addAreas() stage.
  240. *
  241. * @param h the height of the contents of this cell
  242. */
  243. public void setContentHeight(int h) {
  244. usedBPD = h;
  245. }
  246. /**
  247. * Sets the total height of this cell on the current page. That is, the cell's bpd
  248. * plus before and after borders and paddings, plus the table's border-separation.
  249. *
  250. * @param h the height of cell
  251. */
  252. public void setTotalHeight(int h) {
  253. totalHeight = h;
  254. }
  255. private void clearRetrieveTableMarkerChildNodes(List<LayoutManager> childrenLMs) {
  256. if (childrenLMs == null) {
  257. return;
  258. }
  259. int n = childrenLMs.size();
  260. for (int j = 0; j < n; j++) {
  261. LayoutManager lm = (LayoutManager) childrenLMs.get(j);
  262. if (lm == null) {
  263. return;
  264. } else if (lm instanceof RetrieveTableMarkerLayoutManager) {
  265. ((AbstractLayoutManager) lm).getFObj().clearChildNodes();
  266. } else {
  267. List<LayoutManager> lms = lm.getChildLMs();
  268. clearRetrieveTableMarkerChildNodes(lms);
  269. }
  270. }
  271. }
  272. /**
  273. * Checks whether the associated table cell of this LM is in a table header or footer.
  274. * @return true if descendant of table header or footer
  275. */
  276. private boolean isDescendantOfTableHeaderOrFooter() {
  277. return (isDescendantOfTableFooter || isDescendantOfTableHeader);
  278. }
  279. private void saveAddAreasArguments(PositionIterator parentIter, LayoutContext layoutContext,
  280. int[] spannedGridRowHeights, int startRow, int endRow, int borderBeforeWhich,
  281. int borderAfterWhich, boolean firstOnPage, boolean lastOnPage, RowPainter painter,
  282. int firstRowHeight) {
  283. // checks for savedAddAreasArguments and isDescendantOfTableHeader were already made but repeat them
  284. if (savedAddAreasArguments) {
  285. return;
  286. }
  287. if (isDescendantOfTableHeader) {
  288. savedAddAreasArguments = true;
  289. savedParentIter = null /* parentIter */;
  290. savedLayoutContext = null /* layoutContext */;
  291. savedSpannedGridRowHeights = spannedGridRowHeights;
  292. savedStartRow = startRow;
  293. savedEndRow = endRow;
  294. savedBorderBeforeWhich = borderBeforeWhich;
  295. savedBorderAfterWhich = borderAfterWhich;
  296. savedFirstOnPage = firstOnPage;
  297. savedLastOnPage = lastOnPage;
  298. savedPainter = painter;
  299. savedFirstRowHeight = firstRowHeight;
  300. TableLayoutManager parentTableLayoutManager = getTableLayoutManager();
  301. parentTableLayoutManager.saveTableHeaderTableCellLayoutManagers(this);
  302. // this saving is done the first time the addArea() is called; since the retrieve-table-markers
  303. // cannot be resolved at this time we do not want to flush the area; the area needs nevertheless
  304. // be built so that space is allocated for it.
  305. flushArea = false;
  306. }
  307. }
  308. private TableLayoutManager getTableLayoutManager() {
  309. LayoutManager parentLM = getParent();
  310. while (!(parentLM instanceof TableLayoutManager)) {
  311. parentLM = parentLM.getParent();
  312. }
  313. TableLayoutManager tlm = (TableLayoutManager) parentLM;
  314. return tlm;
  315. }
  316. /**
  317. * Calls the addAreas() using the original arguments.
  318. */
  319. protected void repeatAddAreas() {
  320. if (savedAddAreasArguments) {
  321. addAreas(savedParentIter, savedLayoutContext, savedSpannedGridRowHeights, savedStartRow,
  322. savedEndRow, savedBorderBeforeWhich, savedBorderAfterWhich, savedFirstOnPage,
  323. savedLastOnPage, savedPainter, savedFirstRowHeight);
  324. // so that the arguments of the next table fragment header can be saved
  325. savedAddAreasArguments = false;
  326. }
  327. }
  328. /**
  329. * Add the areas for the break points. The cell contains block stacking layout
  330. * managers that add block areas.
  331. *
  332. * <p>In the collapsing-border model, the borders of a cell that spans over several
  333. * rows or columns are drawn separately for each grid unit. Therefore we must know the
  334. * height of each grid row spanned over by the cell. Also, if the cell is broken over
  335. * two pages we must know which spanned grid rows are present on the current page.</p>
  336. *
  337. * @param parentIter the iterator of the break positions
  338. * @param layoutContext the layout context for adding the areas
  339. * @param spannedGridRowHeights in collapsing-border model for a spanning cell, height
  340. * of each spanned grid row
  341. * @param startRow first grid row on the current page spanned over by the cell,
  342. * inclusive
  343. * @param endRow last grid row on the current page spanned over by the cell, inclusive
  344. * @param borderBeforeWhich one of {@link ConditionalBorder#NORMAL},
  345. * {@link ConditionalBorder#LEADING_TRAILING} or {@link ConditionalBorder#REST}
  346. * @param borderAfterWhich one of {@link ConditionalBorder#NORMAL},
  347. * {@link ConditionalBorder#LEADING_TRAILING} or {@link ConditionalBorder#REST}
  348. * @param firstOnPage true if the cell will be the very first one on the page, in
  349. * which case collapsed before borders must be drawn in the outer mode
  350. * @param lastOnPage true if the cell will be the very last one on the page, in which
  351. * case collapsed after borders must be drawn in the outer mode
  352. * @param painter painter
  353. * @param firstRowHeight height of the first row spanned by this cell (may be zero if
  354. * this row is placed on a previous page). Used to calculate the placement of the
  355. * row's background image if any
  356. */
  357. public void addAreas( // CSOK: ParameterNumber
  358. PositionIterator parentIter, LayoutContext layoutContext, int[] spannedGridRowHeights,
  359. int startRow, int endRow, int borderBeforeWhich, int borderAfterWhich,
  360. boolean firstOnPage, boolean lastOnPage, RowPainter painter, int firstRowHeight) {
  361. getParentArea(null);
  362. addId();
  363. int borderBeforeWidth = primaryGridUnit.getBeforeBorderWidth(startRow, borderBeforeWhich);
  364. int borderAfterWidth = primaryGridUnit.getAfterBorderWidth(endRow, borderAfterWhich);
  365. CommonBorderPaddingBackground padding = primaryGridUnit.getCell()
  366. .getCommonBorderPaddingBackground();
  367. int paddingRectBPD = totalHeight - borderBeforeWidth - borderAfterWidth;
  368. int cellBPD = paddingRectBPD;
  369. cellBPD -= padding.getPaddingBefore(borderBeforeWhich == ConditionalBorder.REST, this);
  370. cellBPD -= padding.getPaddingAfter(borderAfterWhich == ConditionalBorder.REST, this);
  371. addBackgroundAreas(painter, firstRowHeight, borderBeforeWidth, paddingRectBPD);
  372. if (isSeparateBorderModel()) {
  373. if (!emptyCell || getTableCell().showEmptyCells()) {
  374. if (borderBeforeWidth > 0) {
  375. int halfBorderSepBPD = getTableCell().getTable().getBorderSeparation().getBPD()
  376. .getLength().getValue() / 2;
  377. adjustYOffset(curBlockArea, halfBorderSepBPD);
  378. }
  379. TraitSetter.addBorders(curBlockArea,
  380. getTableCell().getCommonBorderPaddingBackground(),
  381. borderBeforeWidth == 0, borderAfterWidth == 0,
  382. false, false, this);
  383. }
  384. } else {
  385. boolean inFirstColumn = (primaryGridUnit.getColIndex() == 0);
  386. boolean inLastColumn = (primaryGridUnit.getColIndex()
  387. + getTableCell().getNumberColumnsSpanned() == getTable()
  388. .getNumberOfColumns());
  389. if (!primaryGridUnit.hasSpanning()) {
  390. adjustYOffset(curBlockArea, -borderBeforeWidth);
  391. //Can set the borders directly if there's no span
  392. boolean[] outer = new boolean[] {firstOnPage, lastOnPage, inFirstColumn,
  393. inLastColumn};
  394. TraitSetter.addCollapsingBorders(curBlockArea,
  395. primaryGridUnit.getBorderBefore(borderBeforeWhich),
  396. primaryGridUnit.getBorderAfter(borderAfterWhich),
  397. primaryGridUnit.getBorderStart(),
  398. primaryGridUnit.getBorderEnd(), outer);
  399. } else {
  400. adjustYOffset(curBlockArea, borderBeforeWidth);
  401. Block[][] blocks = new Block[getTableCell().getNumberRowsSpanned()][getTableCell()
  402. .getNumberColumnsSpanned()];
  403. GridUnit[] gridUnits = (GridUnit[]) primaryGridUnit.getRows().get(startRow);
  404. for (int x = 0; x < getTableCell().getNumberColumnsSpanned(); x++) {
  405. GridUnit gu = gridUnits[x];
  406. BorderInfo border = gu.getBorderBefore(borderBeforeWhich);
  407. int borderWidth = border.getRetainedWidth() / 2;
  408. if (borderWidth > 0) {
  409. addBorder(blocks, startRow, x, Trait.BORDER_BEFORE, border, firstOnPage);
  410. adjustYOffset(blocks[startRow][x], -borderWidth);
  411. adjustBPD(blocks[startRow][x], -borderWidth);
  412. }
  413. }
  414. gridUnits = (GridUnit[]) primaryGridUnit.getRows().get(endRow);
  415. for (int x = 0; x < getTableCell().getNumberColumnsSpanned(); x++) {
  416. GridUnit gu = gridUnits[x];
  417. BorderInfo border = gu.getBorderAfter(borderAfterWhich);
  418. int borderWidth = border.getRetainedWidth() / 2;
  419. if (borderWidth > 0) {
  420. addBorder(blocks, endRow, x, Trait.BORDER_AFTER, border, lastOnPage);
  421. adjustBPD(blocks[endRow][x], -borderWidth);
  422. }
  423. }
  424. for (int y = startRow; y <= endRow; y++) {
  425. gridUnits = (GridUnit[]) primaryGridUnit.getRows().get(y);
  426. BorderInfo border = gridUnits[0].getBorderStart();
  427. int borderWidth = border.getRetainedWidth() / 2;
  428. if (borderWidth > 0) {
  429. addBorder(blocks, y, 0, Trait.BORDER_START, border, inFirstColumn);
  430. adjustXOffset(blocks[y][0], borderWidth);
  431. adjustIPD(blocks[y][0], -borderWidth);
  432. }
  433. border = gridUnits[gridUnits.length - 1].getBorderEnd();
  434. borderWidth = border.getRetainedWidth() / 2;
  435. if (borderWidth > 0) {
  436. addBorder(blocks, y, gridUnits.length - 1, Trait.BORDER_END, border,
  437. inLastColumn);
  438. adjustIPD(blocks[y][gridUnits.length - 1], -borderWidth);
  439. }
  440. }
  441. int dy = yoffset;
  442. for (int y = startRow; y <= endRow; y++) {
  443. int bpd = spannedGridRowHeights[y - startRow];
  444. int dx = xoffset;
  445. for (int x = 0; x < gridUnits.length; x++) {
  446. int ipd = getTable().getColumn(primaryGridUnit.getColIndex() + x)
  447. .getColumnWidth().getValue(getParent());
  448. if (blocks[y][x] != null) {
  449. Block block = blocks[y][x];
  450. adjustYOffset(block, dy);
  451. adjustXOffset(block, dx);
  452. adjustIPD(block, ipd);
  453. adjustBPD(block, bpd);
  454. parentLayoutManager.addChildArea(block);
  455. }
  456. dx += ipd;
  457. }
  458. dy += bpd;
  459. }
  460. }
  461. }
  462. TraitSetter.addPadding(curBlockArea,
  463. padding,
  464. borderBeforeWhich == ConditionalBorder.REST,
  465. borderAfterWhich == ConditionalBorder.REST,
  466. false, false, this);
  467. //Handle display-align
  468. if (usedBPD < cellBPD) {
  469. if (getTableCell().getDisplayAlign() == EN_CENTER) {
  470. Block space = new Block();
  471. space.setBPD((cellBPD - usedBPD) / 2);
  472. curBlockArea.addBlock(space);
  473. } else if (getTableCell().getDisplayAlign() == EN_AFTER) {
  474. Block space = new Block();
  475. space.setBPD(cellBPD - usedBPD);
  476. curBlockArea.addBlock(space);
  477. }
  478. }
  479. if (isDescendantOfTableHeaderOrFooter()) {
  480. if (hasRetrieveTableMarker) {
  481. if (isDescendantOfTableHeader && !savedAddAreasArguments) {
  482. saveAddAreasArguments(parentIter, layoutContext, spannedGridRowHeights, startRow, endRow,
  483. borderBeforeWhich, borderAfterWhich, firstOnPage, lastOnPage, painter,
  484. firstRowHeight);
  485. }
  486. recreateChildrenLMs();
  487. int displayAlign = ((TableCell) this.getFObj()).getDisplayAlign();
  488. TableCellBreaker breaker = new TableCellBreaker(this, cellIPD, displayAlign);
  489. breaker.doLayout(usedBPD, false);
  490. // this is needed so the next time the LMs are recreated they look like the originals; this
  491. // is due to the fact that during the doLayout() above the FO tree changes when the
  492. // retrieve-table-markers are resolved
  493. clearRetrieveTableMarkerChildNodes(getChildLMs());
  494. }
  495. }
  496. // if hasRetrieveTableMarker == true the areas were already added when the re-layout was done above
  497. if (!hasRetrieveTableMarker) {
  498. AreaAdditionUtil.addAreas(this, parentIter, layoutContext);
  499. }
  500. // Re-adjust the cell's bpd as it may have been modified by the previous call
  501. // for some reason (?)
  502. curBlockArea.setBPD(cellBPD);
  503. // Add background after we know the BPD
  504. if (!isSeparateBorderModel() || !emptyCell || getTableCell().showEmptyCells()) {
  505. TraitSetter.addBackground(curBlockArea,
  506. getTableCell().getCommonBorderPaddingBackground(), this);
  507. }
  508. if (flushArea) {
  509. flush();
  510. } else {
  511. flushArea = true;
  512. }
  513. curBlockArea = null;
  514. notifyEndOfLayout();
  515. }
  516. /** Adds background areas for the column, body and row, if any. */
  517. private void addBackgroundAreas(RowPainter painter, int firstRowHeight, int borderBeforeWidth,
  518. int paddingRectBPD) {
  519. TableColumn column = getTable().getColumn(primaryGridUnit.getColIndex());
  520. if (column.getCommonBorderPaddingBackground().hasBackground()) {
  521. Block colBackgroundArea = getBackgroundArea(paddingRectBPD, borderBeforeWidth);
  522. ((TableLayoutManager) parentLayoutManager).registerColumnBackgroundArea(column,
  523. colBackgroundArea, -startIndent);
  524. }
  525. TablePart body = primaryGridUnit.getTablePart();
  526. if (body.getCommonBorderPaddingBackground().hasBackground()) {
  527. painter.registerPartBackgroundArea(
  528. getBackgroundArea(paddingRectBPD, borderBeforeWidth));
  529. }
  530. TableRow row = primaryGridUnit.getRow();
  531. if (row != null && row.getCommonBorderPaddingBackground().hasBackground()) {
  532. Block rowBackgroundArea = getBackgroundArea(paddingRectBPD, borderBeforeWidth);
  533. ((TableLayoutManager) parentLayoutManager).addBackgroundArea(rowBackgroundArea);
  534. TraitSetter.addBackground(rowBackgroundArea, row.getCommonBorderPaddingBackground(),
  535. parentLayoutManager,
  536. -xoffset - startIndent, -borderBeforeWidth,
  537. parentLayoutManager.getContentAreaIPD(), firstRowHeight);
  538. }
  539. }
  540. private void addBorder(Block[][] blocks, int i, int j, Integer side, BorderInfo border,
  541. boolean outer) {
  542. if (blocks[i][j] == null) {
  543. blocks[i][j] = new Block();
  544. blocks[i][j].addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
  545. blocks[i][j].setPositioning(Block.ABSOLUTE);
  546. }
  547. blocks[i][j].addTrait(side, new BorderProps(border.getStyle(),
  548. border.getRetainedWidth(), border.getColor(),
  549. outer ? BorderProps.COLLAPSE_OUTER : BorderProps.COLLAPSE_INNER));
  550. }
  551. private static void adjustXOffset(Block block, int amount) {
  552. block.setXOffset(block.getXOffset() + amount);
  553. }
  554. private static void adjustYOffset(Block block, int amount) {
  555. block.setYOffset(block.getYOffset() + amount);
  556. }
  557. private static void adjustIPD(Block block, int amount) {
  558. block.setIPD(block.getIPD() + amount);
  559. }
  560. private static void adjustBPD(Block block, int amount) {
  561. block.setBPD(block.getBPD() + amount);
  562. }
  563. private Block getBackgroundArea(int bpd, int borderBeforeWidth) {
  564. CommonBorderPaddingBackground padding = getTableCell().getCommonBorderPaddingBackground();
  565. int paddingStart = padding.getPaddingStart(false, this);
  566. int paddingEnd = padding.getPaddingEnd(false, this);
  567. Block block = new Block();
  568. TraitSetter.setProducerID(block, getTable().getId());
  569. block.setPositioning(Block.ABSOLUTE);
  570. block.setIPD(cellIPD + paddingStart + paddingEnd);
  571. block.setBPD(bpd);
  572. block.setXOffset(xoffset + startIndent - paddingStart);
  573. block.setYOffset(yoffset + borderBeforeWidth);
  574. return block;
  575. }
  576. /**
  577. * Return an Area which can contain the passed childArea. The childArea
  578. * may not yet have any content, but it has essential traits set.
  579. * In general, if the LayoutManager already has an Area it simply returns
  580. * it. Otherwise, it makes a new Area of the appropriate class.
  581. * It gets a parent area for its area by calling its parent LM.
  582. * Finally, based on the dimensions of the parent area, it initializes
  583. * its own area. This includes setting the content IPD and the maximum
  584. * BPD.
  585. *
  586. * @param childArea the child area to get the parent for
  587. * @return the parent area
  588. */
  589. public Area getParentArea(Area childArea) {
  590. if (curBlockArea == null) {
  591. curBlockArea = new Block();
  592. curBlockArea.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
  593. TraitSetter.setProducerID(curBlockArea, getTableCell().getId());
  594. curBlockArea.setPositioning(Block.ABSOLUTE);
  595. curBlockArea.setXOffset(xoffset + startIndent);
  596. curBlockArea.setYOffset(yoffset);
  597. curBlockArea.setIPD(cellIPD);
  598. /*Area parentArea =*/ parentLayoutManager.getParentArea(curBlockArea);
  599. // Get reference IPD from parentArea
  600. setCurrentArea(curBlockArea); // ??? for generic operations
  601. }
  602. return curBlockArea;
  603. }
  604. /**
  605. * Add the child to the cell block area.
  606. *
  607. * @param childArea the child to add to the cell
  608. */
  609. public void addChildArea(Area childArea) {
  610. if (curBlockArea != null) {
  611. curBlockArea.addBlock((Block) childArea);
  612. }
  613. }
  614. /**
  615. * {@inheritDoc}
  616. */
  617. public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
  618. // TODO Auto-generated method stub
  619. return 0;
  620. }
  621. /**
  622. * {@inheritDoc}
  623. */
  624. public void discardSpace(KnuthGlue spaceGlue) {
  625. // TODO Auto-generated method stub
  626. }
  627. /** {@inheritDoc} */
  628. public Keep getKeepTogether() {
  629. // keep-together does not apply to fo:table-cell
  630. return Keep.KEEP_AUTO;
  631. }
  632. /** {@inheritDoc} */
  633. public Keep getKeepWithNext() {
  634. return Keep.KEEP_AUTO; //TODO FIX ME (table-cell has no keep-with-next!)
  635. }
  636. /** {@inheritDoc} */
  637. public Keep getKeepWithPrevious() {
  638. return Keep.KEEP_AUTO; //TODO FIX ME (table-cell has no keep-with-previous!)
  639. }
  640. // --------- Property Resolution related functions --------- //
  641. /**
  642. * Returns the IPD of the content area
  643. * @return the IPD of the content area
  644. */
  645. public int getContentAreaIPD() {
  646. return cellIPD;
  647. }
  648. /**
  649. * Returns the BPD of the content area
  650. * @return the BPD of the content area
  651. */
  652. public int getContentAreaBPD() {
  653. if (curBlockArea != null) {
  654. return curBlockArea.getBPD();
  655. } else {
  656. log.error("getContentAreaBPD called on unknown BPD");
  657. return -1;
  658. }
  659. }
  660. /**
  661. * {@inheritDoc}
  662. */
  663. public boolean getGeneratesReferenceArea() {
  664. return true;
  665. }
  666. /**
  667. * {@inheritDoc}
  668. */
  669. public boolean getGeneratesBlockArea() {
  670. return true;
  671. }
  672. private class TableCellBreaker extends LocalBreaker {
  673. public TableCellBreaker(TableCellLayoutManager lm, int ipd, int displayAlign) {
  674. super(lm, ipd, displayAlign);
  675. }
  676. /**
  677. * {@inheritDoc}
  678. */
  679. protected void observeElementList(List elementList) {
  680. String elementListID = lm.getParent().getFObj().getId() + "-" + lm.getFObj().getId();
  681. ElementListObserver.observe(elementList, "table-cell", elementListID);
  682. }
  683. }
  684. /**
  685. * Registers the FO's markers on the current PageViewport and parent Table.
  686. *
  687. * @param isStarting boolean indicating whether the markers qualify as 'starting'
  688. * @param isFirst boolean indicating whether the markers qualify as 'first'
  689. * @param isLast boolean indicating whether the markers qualify as 'last'
  690. */
  691. protected void registerMarkers(boolean isStarting, boolean isFirst, boolean isLast) {
  692. Map<String, Marker> markers = getTableCell().getMarkers();
  693. if (markers != null) {
  694. getCurrentPV().registerMarkers(markers, isStarting, isFirst, isLast && isLastTrait);
  695. if (!isDescendantOfTableHeaderOrFooter()) {
  696. getTableLayoutManager().registerMarkers(markers, isStarting, isFirst, isLast && isLastTrait);
  697. }
  698. }
  699. }
  700. void setLastTrait(boolean isLast) {
  701. isLastTrait = isLast;
  702. }
  703. /** {@inheritDoc} */
  704. public void setParent(LayoutManager lm) {
  705. this.parentLayoutManager = lm;
  706. if (this.hasRetrieveTableMarker) {
  707. this.getTableLayoutManager().flagAsHavingRetrieveTableMarker();
  708. }
  709. }
  710. }