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.

TableLayoutManager.java 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. /*
  2. * Copyright 1999-2005 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /* $Id$ */
  17. package org.apache.fop.layoutmgr.table;
  18. import org.apache.fop.datatypes.Length;
  19. import org.apache.fop.fo.flow.Table;
  20. import org.apache.fop.fo.flow.TableColumn;
  21. import org.apache.fop.fo.properties.TableColLength;
  22. import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
  23. import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
  24. import org.apache.fop.layoutmgr.ConditionalElementListener;
  25. import org.apache.fop.layoutmgr.KnuthElement;
  26. import org.apache.fop.layoutmgr.KnuthGlue;
  27. import org.apache.fop.layoutmgr.LayoutContext;
  28. import org.apache.fop.layoutmgr.ListElement;
  29. import org.apache.fop.layoutmgr.PositionIterator;
  30. import org.apache.fop.layoutmgr.Position;
  31. import org.apache.fop.layoutmgr.RelSide;
  32. import org.apache.fop.layoutmgr.TraitSetter;
  33. import org.apache.fop.area.Area;
  34. import org.apache.fop.area.Block;
  35. import org.apache.fop.traits.MinOptMax;
  36. import org.apache.fop.traits.SpaceVal;
  37. import java.util.Iterator;
  38. import java.util.LinkedList;
  39. import org.apache.fop.datatypes.LengthBase;
  40. import org.apache.fop.fo.FObj;
  41. /**
  42. * LayoutManager for a table FO.
  43. * A table consists of oldColumns, table header, table footer and multiple
  44. * table bodies.
  45. * The header, footer and body add the areas created from the table cells.
  46. * The table then creates areas for the oldColumns, bodies and rows
  47. * the render background.
  48. */
  49. public class TableLayoutManager extends BlockStackingLayoutManager
  50. implements ConditionalElementListener {
  51. private TableContentLayoutManager contentLM;
  52. private ColumnSetup columns = null;
  53. private Block curBlockArea;
  54. private int contentIPD;
  55. private int referenceBPD;
  56. private double tableUnits;
  57. private boolean autoLayout = true;
  58. private boolean discardBorderBefore;
  59. private boolean discardBorderAfter;
  60. private boolean discardPaddingBefore;
  61. private boolean discardPaddingAfter;
  62. private MinOptMax effSpaceBefore;
  63. private MinOptMax effSpaceAfter;
  64. /**
  65. * Create a new table layout manager.
  66. * @param node the table FO
  67. */
  68. public TableLayoutManager(Table node) {
  69. super(node);
  70. this.columns = new ColumnSetup(node);
  71. }
  72. /** @return the table FO */
  73. public Table getTable() {
  74. return (Table)this.fobj;
  75. }
  76. /**
  77. * @return the column setup for this table.
  78. */
  79. public ColumnSetup getColumns() {
  80. return this.columns;
  81. }
  82. /** @see org.apache.fop.layoutmgr.LayoutManager#initialize() */
  83. public void initialize() {
  84. foSpaceBefore = new SpaceVal(
  85. getTable().getCommonMarginBlock().spaceBefore, this).getSpace();
  86. foSpaceAfter = new SpaceVal(
  87. getTable().getCommonMarginBlock().spaceAfter, this).getSpace();
  88. if (!getTable().isAutoLayout()
  89. && getTable().getInlineProgressionDimension().getOptimum(this).getEnum()
  90. != EN_AUTO) {
  91. autoLayout = false;
  92. }
  93. }
  94. private void resetSpaces() {
  95. this.discardBorderBefore = false;
  96. this.discardBorderAfter = false;
  97. this.discardPaddingBefore = false;
  98. this.discardPaddingAfter = false;
  99. this.effSpaceBefore = foSpaceBefore;
  100. this.effSpaceAfter = foSpaceAfter;
  101. }
  102. private int getIPIndents() {
  103. int iIndents = 0;
  104. iIndents += getTable().getCommonMarginBlock().startIndent.getValue(this);
  105. iIndents += getTable().getCommonMarginBlock().endIndent.getValue(this);
  106. return iIndents;
  107. }
  108. /** @see org.apache.fop.layoutmgr.LayoutManager */
  109. public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
  110. LinkedList returnList = new LinkedList();
  111. if (!bBreakBeforeServed) {
  112. try {
  113. if (addKnuthElementsForBreakBefore(returnList, context)) {
  114. return returnList;
  115. }
  116. } finally {
  117. bBreakBeforeServed = true;
  118. }
  119. }
  120. referenceBPD = context.getStackLimit().opt;
  121. referenceIPD = context.getRefIPD();
  122. if (getTable().getInlineProgressionDimension().getOptimum(this).getEnum() != EN_AUTO) {
  123. referenceIPD = getTable().getInlineProgressionDimension().getOptimum(this)
  124. .getLength().getValue(this);
  125. contentIPD = referenceIPD;
  126. } else {
  127. if (!getTable().isAutoLayout()) {
  128. log.info("table-layout=\"fixed\" and width=\"auto\", "
  129. + "but auto-layout not supported "
  130. + "=> assuming width=\"100%\"");
  131. }
  132. contentIPD = referenceIPD - getIPIndents();
  133. }
  134. if (referenceIPD > context.getRefIPD()) {
  135. log.warn("Allocated IPD exceeds available reference IPD");
  136. }
  137. // either works out table of column widths or if proportional-column-width function
  138. // is used works out total factor, so that value of single unit can be computed.
  139. int sumCols = 0;
  140. float factors = 0;
  141. for (Iterator i = columns.iterator(); i.hasNext();) {
  142. TableColumn column = (TableColumn) i.next();
  143. if (column != null) {
  144. Length width = column.getColumnWidth();
  145. sumCols += width.getValue(this);
  146. if (width instanceof TableColLength) {
  147. factors += ((TableColLength) width).getTableUnits();
  148. }
  149. }
  150. }
  151. // sets TABLE_UNITS in case where one or more oldColumns is defined using
  152. // proportional-column-width
  153. if (sumCols < contentIPD) {
  154. if (tableUnits == 0.0) {
  155. this.tableUnits = (contentIPD - sumCols) / factors;
  156. }
  157. }
  158. addKnuthElementsForSpaceBefore(returnList, alignment);
  159. if (getTable().isSeparateBorderModel()) {
  160. addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed);
  161. firstVisibleMarkServed = true;
  162. }
  163. //Spaces, border and padding to be repeated at each break
  164. addPendingMarks(context);
  165. LinkedList returnedList = null;
  166. LinkedList contentList = new LinkedList();
  167. //Position returnPosition = new NonLeafPosition(this, null);
  168. //Body prevLM = null;
  169. LayoutContext childLC = new LayoutContext(0);
  170. /*
  171. childLC.setStackLimit(
  172. MinOptMax.subtract(context.getStackLimit(),
  173. stackSize));*/
  174. childLC.setRefIPD(context.getRefIPD());
  175. childLC.copyPendingMarksFrom(context);
  176. if (contentLM == null) {
  177. contentLM = new TableContentLayoutManager(this);
  178. }
  179. returnedList = contentLM.getNextKnuthElements(childLC, alignment);
  180. if (childLC.isKeepWithNextPending()) {
  181. log.debug("TableContentLM signals pending keep-with-next");
  182. context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING);
  183. }
  184. if (childLC.isKeepWithPreviousPending()) {
  185. log.debug("TableContentLM signals pending keep-with-previous");
  186. context.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING);
  187. }
  188. //Set index values on elements coming from the content LM
  189. Iterator iter = returnedList.iterator();
  190. while (iter.hasNext()) {
  191. ListElement el = (ListElement)iter.next();
  192. notifyPos(el.getPosition());
  193. }
  194. log.debug(returnedList);
  195. if (returnedList.size() == 1
  196. && ((ListElement)returnedList.getFirst()).isForcedBreak()) {
  197. // a descendant of this block has break-before
  198. if (returnList.size() == 0) {
  199. // the first child (or its first child ...) has
  200. // break-before;
  201. // all this block, including space before, will be put in
  202. // the
  203. // following page
  204. //FIX ME
  205. //bSpaceBeforeServed = false;
  206. }
  207. contentList.addAll(returnedList);
  208. // "wrap" the Position inside each element
  209. // moving the elements from contentList to returnList
  210. returnedList = new LinkedList();
  211. wrapPositionElements(contentList, returnList);
  212. return returnList;
  213. } else {
  214. /*
  215. if (prevLM != null) {
  216. // there is a block handled by prevLM
  217. // before the one handled by curLM
  218. if (mustKeepTogether()
  219. || prevLM.mustKeepWithNext()
  220. || curLM.mustKeepWithPrevious()) {
  221. // add an infinite penalty to forbid a break between
  222. // blocks
  223. contentList.add(new KnuthPenalty(0,
  224. KnuthElement.INFINITE, false,
  225. new Position(this), false));
  226. } else if (!((KnuthElement) contentList.getLast()).isGlue()) {
  227. // add a null penalty to allow a break between blocks
  228. contentList.add(new KnuthPenalty(0, 0, false,
  229. new Position(this), false));
  230. } else {
  231. // the last element in contentList is a glue;
  232. // it is a feasible breakpoint, there is no need to add
  233. // a penalty
  234. }
  235. }*/
  236. contentList.addAll(returnedList);
  237. if (returnedList.size() > 0) {
  238. if (((ListElement)returnedList.getLast()).isForcedBreak()) {
  239. // a descendant of this block has break-after
  240. if (false /*curLM.isFinished()*/) {
  241. // there is no other content in this block;
  242. // it's useless to add space after before a page break
  243. setFinished(true);
  244. }
  245. returnedList = new LinkedList();
  246. wrapPositionElements(contentList, returnList);
  247. return returnList;
  248. }
  249. }
  250. }
  251. wrapPositionElements(contentList, returnList);
  252. if (getTable().isSeparateBorderModel()) {
  253. addKnuthElementsForBorderPaddingAfter(returnList, true);
  254. }
  255. addKnuthElementsForSpaceAfter(returnList, alignment);
  256. addKnuthElementsForBreakAfter(returnList, context);
  257. setFinished(true);
  258. resetSpaces();
  259. return returnList;
  260. }
  261. /**
  262. * The table area is a reference area that contains areas for
  263. * oldColumns, bodies, rows and the contents are in cells.
  264. *
  265. * @param parentIter the position iterator
  266. * @param layoutContext the layout context for adding areas
  267. */
  268. public void addAreas(PositionIterator parentIter,
  269. LayoutContext layoutContext) {
  270. getParentArea(null);
  271. getPSLM().addIDToPage(getTable().getId());
  272. int startXOffset = getTable().getCommonMarginBlock().startIndent.getValue(this);
  273. // add column, body then row areas
  274. int tableHeight = 0;
  275. //Body childLM;
  276. LayoutContext lc = new LayoutContext(0);
  277. lc.setRefIPD(contentIPD);
  278. contentLM.setStartXOffset(startXOffset);
  279. contentLM.addAreas(parentIter, lc);
  280. tableHeight += contentLM.getUsedBPD();
  281. curBlockArea.setBPD(tableHeight);
  282. if (getTable().isSeparateBorderModel()) {
  283. TraitSetter.addBorders(curBlockArea,
  284. getTable().getCommonBorderPaddingBackground(),
  285. discardBorderBefore, discardBorderAfter, false, false, this);
  286. TraitSetter.addPadding(curBlockArea,
  287. getTable().getCommonBorderPaddingBackground(),
  288. discardPaddingBefore, discardPaddingAfter, false, false, this);
  289. }
  290. TraitSetter.addBackground(curBlockArea,
  291. getTable().getCommonBorderPaddingBackground(),
  292. this);
  293. TraitSetter.addMargins(curBlockArea,
  294. getTable().getCommonBorderPaddingBackground(),
  295. getTable().getCommonMarginBlock(),
  296. this);
  297. TraitSetter.addBreaks(curBlockArea,
  298. getTable().getBreakBefore(), getTable().getBreakAfter());
  299. TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(),
  300. effSpaceBefore, effSpaceAfter);
  301. flush();
  302. resetSpaces();
  303. curBlockArea = null;
  304. }
  305. /**
  306. * Return an Area which can contain the passed childArea. The childArea
  307. * may not yet have any content, but it has essential traits set.
  308. * In general, if the LayoutManager already has an Area it simply returns
  309. * it. Otherwise, it makes a new Area of the appropriate class.
  310. * It gets a parent area for its area by calling its parent LM.
  311. * Finally, based on the dimensions of the parent area, it initializes
  312. * its own area. This includes setting the content IPD and the maximum
  313. * BPD.
  314. *
  315. * @param childArea the child area
  316. * @return the parent area of the child
  317. */
  318. public Area getParentArea(Area childArea) {
  319. if (curBlockArea == null) {
  320. curBlockArea = new Block();
  321. // Set up dimensions
  322. // Must get dimensions from parent area
  323. /*Area parentArea =*/ parentLM.getParentArea(curBlockArea);
  324. TraitSetter.setProducerID(curBlockArea, getTable().getId());
  325. curBlockArea.setIPD(contentIPD);
  326. setCurrentArea(curBlockArea);
  327. }
  328. return curBlockArea;
  329. }
  330. /**
  331. * Add the child area to this layout manager.
  332. *
  333. * @param childArea the child area to add
  334. */
  335. public void addChildArea(Area childArea) {
  336. if (curBlockArea != null) {
  337. curBlockArea.addBlock((Block) childArea);
  338. }
  339. }
  340. /**
  341. * Reset the position of this layout manager.
  342. *
  343. * @param resetPos the position to reset to
  344. */
  345. public void resetPosition(Position resetPos) {
  346. if (resetPos == null) {
  347. reset(null);
  348. }
  349. }
  350. /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager */
  351. public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
  352. // TODO Auto-generated method stub
  353. return 0;
  354. }
  355. /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager */
  356. public void discardSpace(KnuthGlue spaceGlue) {
  357. // TODO Auto-generated method stub
  358. }
  359. /**
  360. * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
  361. */
  362. public boolean mustKeepTogether() {
  363. //TODO Keeps will have to be more sophisticated sooner or later
  364. return ((BlockLevelLayoutManager)getParent()).mustKeepTogether()
  365. || !getTable().getKeepTogether().getWithinPage().isAuto()
  366. || !getTable().getKeepTogether().getWithinColumn().isAuto();
  367. }
  368. /**
  369. * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
  370. */
  371. public boolean mustKeepWithPrevious() {
  372. return !getTable().getKeepWithPrevious().getWithinPage().isAuto()
  373. || !getTable().getKeepWithPrevious().getWithinColumn().isAuto();
  374. }
  375. /**
  376. * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
  377. */
  378. public boolean mustKeepWithNext() {
  379. return !getTable().getKeepWithNext().getWithinPage().isAuto()
  380. || !getTable().getKeepWithNext().getWithinColumn().isAuto();
  381. }
  382. // --------- Property Resolution related functions --------- //
  383. /**
  384. * @see org.apache.fop.datatypes.PercentBaseContext#getBaseLength(int, FObj)
  385. */
  386. public int getBaseLength(int lengthBase, FObj fobj) {
  387. // Special handler for TableColumn width specifications
  388. if (fobj instanceof TableColumn && fobj.getParent() == getFObj()) {
  389. switch (lengthBase) {
  390. case LengthBase.CONTAINING_BLOCK_WIDTH:
  391. return getContentAreaIPD();
  392. default:
  393. log.error("Unknown base type for LengthBase.");
  394. return 0;
  395. }
  396. } else {
  397. switch (lengthBase) {
  398. case LengthBase.TABLE_UNITS:
  399. return (int)this.tableUnits;
  400. default:
  401. return super.getBaseLength(lengthBase, fobj);
  402. }
  403. }
  404. }
  405. /**
  406. * Returns the IPD of the content area
  407. * @return the IPD of the content area
  408. */
  409. public int getContentAreaIPD() {
  410. return referenceIPD;
  411. }
  412. /**
  413. * Returns the BPD of the content area
  414. * @return the BPD of the content area
  415. */
  416. public int getContentAreaBPD() {
  417. return referenceBPD;
  418. }
  419. /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
  420. public void notifySpace(RelSide side, MinOptMax effectiveLength) {
  421. if (RelSide.BEFORE == side) {
  422. if (log.isDebugEnabled()) {
  423. log.debug(this + ": Space " + side + ", "
  424. + this.effSpaceBefore + "-> " + effectiveLength);
  425. }
  426. this.effSpaceBefore = effectiveLength;
  427. } else {
  428. if (log.isDebugEnabled()) {
  429. log.debug(this + ": Space " + side + ", "
  430. + this.effSpaceAfter + "-> " + effectiveLength);
  431. }
  432. this.effSpaceAfter = effectiveLength;
  433. }
  434. }
  435. /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
  436. public void notifyBorder(RelSide side, MinOptMax effectiveLength) {
  437. if (effectiveLength == null) {
  438. if (RelSide.BEFORE == side) {
  439. this.discardBorderBefore = true;
  440. } else {
  441. this.discardBorderAfter = true;
  442. }
  443. }
  444. if (log.isDebugEnabled()) {
  445. log.debug(this + ": Border " + side + " -> " + effectiveLength);
  446. }
  447. }
  448. /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
  449. public void notifyPadding(RelSide side, MinOptMax effectiveLength) {
  450. if (effectiveLength == null) {
  451. if (RelSide.BEFORE == side) {
  452. this.discardPaddingBefore = true;
  453. } else {
  454. this.discardPaddingAfter = true;
  455. }
  456. }
  457. if (log.isDebugEnabled()) {
  458. log.debug(this + ": Padding " + side + " -> " + effectiveLength);
  459. }
  460. }
  461. }