Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

TableCellLayoutManager.java 34KB

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