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 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  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 org.apache.commons.logging.Log;
  21. import org.apache.commons.logging.LogFactory;
  22. import org.apache.fop.datatypes.PercentBaseContext;
  23. import org.apache.fop.fo.FONode;
  24. import org.apache.fop.fo.flow.Table;
  25. import org.apache.fop.fo.flow.TableCell;
  26. import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
  27. import org.apache.fop.layoutmgr.AreaAdditionUtil;
  28. import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
  29. import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
  30. import org.apache.fop.layoutmgr.BreakElement;
  31. import org.apache.fop.layoutmgr.KnuthElement;
  32. import org.apache.fop.layoutmgr.KnuthGlue;
  33. import org.apache.fop.layoutmgr.KnuthPenalty;
  34. import org.apache.fop.layoutmgr.LayoutContext;
  35. import org.apache.fop.layoutmgr.ListElement;
  36. import org.apache.fop.layoutmgr.PositionIterator;
  37. import org.apache.fop.layoutmgr.Position;
  38. import org.apache.fop.layoutmgr.SpaceResolver;
  39. import org.apache.fop.layoutmgr.TraitSetter;
  40. import org.apache.fop.area.Area;
  41. import org.apache.fop.area.Block;
  42. import org.apache.fop.area.Trait;
  43. import org.apache.fop.traits.MinOptMax;
  44. /**
  45. * LayoutManager for a table-cell FO.
  46. * A cell contains blocks. These blocks fill the cell.
  47. */
  48. public class TableCellLayoutManager extends BlockStackingLayoutManager
  49. implements BlockLevelLayoutManager {
  50. /**
  51. * logging instance
  52. */
  53. private static Log log = LogFactory.getLog(TableCellLayoutManager.class);
  54. private PrimaryGridUnit primaryGridUnit;
  55. private Block curBlockArea;
  56. private int inRowIPDOffset;
  57. private int xoffset;
  58. private int yoffset;
  59. private int cellIPD;
  60. private int rowHeight;
  61. private int usedBPD;
  62. private int borderAndPaddingBPD;
  63. private boolean emptyCell = true;
  64. /**
  65. * Create a new Cell layout manager.
  66. * @param node table-cell FO for which to create the LM
  67. * @param pgu primary grid unit for the cell
  68. */
  69. public TableCellLayoutManager(TableCell node, PrimaryGridUnit pgu) {
  70. super(node);
  71. fobj = node;
  72. this.primaryGridUnit = pgu;
  73. }
  74. /** @return the table-cell FO */
  75. public TableCell getTableCell() {
  76. return (TableCell)this.fobj;
  77. }
  78. private boolean isSeparateBorderModel() {
  79. return getTable().isSeparateBorderModel();
  80. }
  81. /** {@inheritDoc} */
  82. public void initialize() {
  83. borderAndPaddingBPD = 0;
  84. borderAndPaddingBPD += getTableCell()
  85. .getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
  86. borderAndPaddingBPD += getTableCell()
  87. .getCommonBorderPaddingBackground().getBorderAfterWidth(false);
  88. if (!isSeparateBorderModel()) {
  89. borderAndPaddingBPD /= 2;
  90. }
  91. borderAndPaddingBPD += getTableCell().getCommonBorderPaddingBackground()
  92. .getPaddingBefore(false, this);
  93. borderAndPaddingBPD += getTableCell().getCommonBorderPaddingBackground()
  94. .getPaddingAfter(false, this);
  95. }
  96. /**
  97. * @return the table owning this cell
  98. */
  99. public Table getTable() {
  100. FONode node = fobj.getParent();
  101. while (!(node instanceof Table)) {
  102. node = node.getParent();
  103. }
  104. return (Table)node;
  105. }
  106. /** {@inheritDoc} */
  107. protected int getIPIndents() {
  108. int[] startEndBorderWidths = primaryGridUnit.getStartEndBorderWidths();
  109. startIndent = startEndBorderWidths[0];
  110. endIndent = startEndBorderWidths[1];
  111. if (isSeparateBorderModel()) {
  112. int borderSep = getTable().getBorderSeparation().getLengthPair().getIPD().getLength()
  113. .getValue(this);
  114. startIndent += borderSep / 2;
  115. endIndent += borderSep / 2;
  116. } else {
  117. startIndent /= 2;
  118. endIndent /= 2;
  119. }
  120. startIndent += getTableCell().getCommonBorderPaddingBackground().getPaddingStart(false, this);
  121. endIndent += getTableCell().getCommonBorderPaddingBackground().getPaddingEnd(false, this);
  122. return startIndent + endIndent;
  123. }
  124. /**
  125. * {@inheritDoc}
  126. */
  127. public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
  128. MinOptMax stackLimit = new MinOptMax(context.getStackLimit());
  129. referenceIPD = context.getRefIPD();
  130. cellIPD = referenceIPD;
  131. cellIPD -= getIPIndents();
  132. LinkedList returnedList = null;
  133. LinkedList contentList = new LinkedList();
  134. LinkedList returnList = new LinkedList();
  135. BlockLevelLayoutManager curLM; // currently active LM
  136. BlockLevelLayoutManager prevLM = null; // previously active LM
  137. while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
  138. LayoutContext childLC = new LayoutContext(0);
  139. // curLM is a ?
  140. childLC.setStackLimit(MinOptMax.subtract(context
  141. .getStackLimit(), stackLimit));
  142. childLC.setRefIPD(cellIPD);
  143. // get elements from curLM
  144. returnedList = curLM.getNextKnuthElements(childLC, alignment);
  145. if (childLC.isKeepWithNextPending()) {
  146. log.debug("child LM signals pending keep with next");
  147. }
  148. if (contentList.size() == 0 && childLC.isKeepWithPreviousPending()) {
  149. context.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING);
  150. childLC.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING, false);
  151. }
  152. if (prevLM != null) {
  153. // there is a block handled by prevLM
  154. // before the one handled by curLM
  155. if (mustKeepTogether()
  156. || context.isKeepWithNextPending()
  157. || childLC.isKeepWithPreviousPending()) {
  158. //Clear keep pending flag
  159. context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING, false);
  160. childLC.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING, false);
  161. // add an infinite penalty to forbid a break between
  162. // blocks
  163. contentList.add(new BreakElement(
  164. new Position(this), KnuthElement.INFINITE, context));
  165. //contentList.add(new KnuthPenalty(0,
  166. // KnuthElement.INFINITE, false,
  167. // new Position(this), false));
  168. } else if (!(((ListElement) contentList.getLast()).isGlue()
  169. || (((ListElement)contentList.getLast()).isPenalty()
  170. && ((KnuthPenalty)contentList.getLast()).getP() < KnuthElement.INFINITE)
  171. || (contentList.getLast() instanceof BreakElement
  172. && ((BreakElement)contentList.getLast()).getPenaltyValue() < KnuthElement.INFINITE))) {
  173. // TODO vh: this is hacky
  174. // The getNextKnuthElements method of TableCellLM must not be called
  175. // twice, otherwise some settings like indents or borders will be
  176. // counted several times and lead to a wrong output. Anyway the
  177. // getNextKnuthElements methods should be called only once eventually
  178. // (i.e., when multi-threading the code), even when there are forced
  179. // breaks.
  180. // If we add a break possibility after a forced break the
  181. // AreaAdditionUtil.addAreas method will act on a sequence starting
  182. // with a SpaceResolver.SpaceHandlingBreakPosition element, having no
  183. // LM associated to it. Thus it will stop early instead of adding
  184. // areas for following Positions. The above test aims at preventing
  185. // such a situation from occuring. add a null penalty to allow a break
  186. // between blocks
  187. contentList.add(new BreakElement(
  188. new Position(this), 0, context));
  189. //contentList.add(new KnuthPenalty(0, 0, false,
  190. // new Position(this), false));
  191. } else {
  192. // the last element in contentList is a feasible breakpoint, there is
  193. // no need to add a penalty
  194. }
  195. }
  196. contentList.addAll(returnedList);
  197. if (returnedList.size() == 0) {
  198. //Avoid NoSuchElementException below (happens with empty blocks)
  199. continue;
  200. }
  201. if (childLC.isKeepWithNextPending()) {
  202. //Clear and propagate
  203. childLC.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING, false);
  204. context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING);
  205. }
  206. prevLM = curLM;
  207. }
  208. returnedList = new LinkedList();
  209. wrapPositionElements(contentList, returnList);
  210. //Space resolution
  211. SpaceResolver.resolveElementList(returnList);
  212. getPSLM().notifyEndOfLayout(((TableCell)getFObj()).getId());
  213. setFinished(true);
  214. return returnList;
  215. }
  216. /**
  217. * Set the y offset of this cell.
  218. * This offset is used to set the absolute position of the cell.
  219. *
  220. * @param off the y direction offset
  221. */
  222. public void setYOffset(int off) {
  223. yoffset = off;
  224. }
  225. /**
  226. * Set the x offset of this cell (usually the same as its parent row).
  227. * This offset is used to determine the absolute position of the cell.
  228. *
  229. * @param off the x offset
  230. */
  231. public void setXOffset(int off) {
  232. xoffset = off;
  233. }
  234. /**
  235. * Set the IPD offset of this cell inside the table-row.
  236. * This offset is used to determine the absolute position of the cell.
  237. * @param off the IPD offset
  238. */
  239. public void setInRowIPDOffset(int off) {
  240. this.inRowIPDOffset = off;
  241. }
  242. /**
  243. * Set the content height for this cell. This method is used during
  244. * addAreas() stage.
  245. *
  246. * @param h the height of the contents of this cell
  247. */
  248. public void setContentHeight(int h) {
  249. usedBPD = h;
  250. }
  251. /**
  252. * Set the row height that contains this cell. This method is used during
  253. * addAreas() stage.
  254. *
  255. * @param h the height of the row
  256. */
  257. public void setRowHeight(int h) {
  258. rowHeight = h;
  259. }
  260. /**
  261. * Returns the bpd of the given grid unit.
  262. * @param gu a grid unit belonging to this cell
  263. * @return the content height of the grid unit
  264. */
  265. private int getContentHeight(GridUnit gu) {
  266. int bpd = rowHeight;
  267. if (isSeparateBorderModel()) {
  268. bpd -= gu.getPrimary().getBorders().getBorderBeforeWidth(false);
  269. bpd -= gu.getPrimary().getBorders().getBorderAfterWidth(false);
  270. } else {
  271. bpd -= gu.getPrimary().getHalfMaxBorderWidth();
  272. }
  273. CommonBorderPaddingBackground cbpb
  274. = gu.getCell().getCommonBorderPaddingBackground();
  275. bpd -= cbpb.getPaddingBefore(false, this);
  276. bpd -= cbpb.getPaddingAfter(false, this);
  277. bpd -= 2 * ((TableLayoutManager)getParent()).getHalfBorderSeparationBPD();
  278. return bpd;
  279. }
  280. /**
  281. * Add the areas for the break points. The cell contains block stacking layout
  282. * managers that add block areas.
  283. *
  284. * <p>In the collapsing-border model, the borders of a cell that spans over several
  285. * rows or columns are drawn separately for each grid unit. Therefore we must know the
  286. * height of each grid row spanned over by the cell. Also, if the cell is broken over
  287. * two pages we must know which spanned grid rows are present on the current page.</p>
  288. *
  289. * @param parentIter the iterator of the break positions
  290. * @param layoutContext the layout context for adding the areas
  291. * @param spannedGridRowHeights in collapsing-border model for a spanning cell, height
  292. * of each spanned grid row
  293. * @param startRow first grid row on the current page spanned over by the cell,
  294. * inclusive
  295. * @param endRow last grid row on the current page spanned over by the cell, exclusive
  296. */
  297. public void addAreas(PositionIterator parentIter,
  298. LayoutContext layoutContext,
  299. int[] spannedGridRowHeights,
  300. int startRow,
  301. int endRow) {
  302. getParentArea(null);
  303. getPSLM().addIDToPage(getTableCell().getId());
  304. if (isSeparateBorderModel()) {
  305. if (!emptyCell || getTableCell().showEmptyCells()) {
  306. TraitSetter.addBorders(curBlockArea, getTableCell().getCommonBorderPaddingBackground(),
  307. false, false, false, false, this);
  308. TraitSetter.addPadding(curBlockArea, getTableCell().getCommonBorderPaddingBackground(),
  309. false, false, false, false, this);
  310. }
  311. } else {
  312. if (!primaryGridUnit.hasSpanning()) {
  313. //Can set the borders directly if there's no span
  314. boolean[] outer = new boolean[] {
  315. primaryGridUnit.getFlag(GridUnit.FIRST_IN_TABLE),
  316. primaryGridUnit.getFlag(GridUnit.LAST_IN_TABLE),
  317. primaryGridUnit.getFlag(GridUnit.IN_FIRST_COLUMN),
  318. primaryGridUnit.getFlag(GridUnit.IN_LAST_COLUMN)};
  319. TraitSetter.addCollapsingBorders(curBlockArea,
  320. primaryGridUnit.getBorders(), outer, this);
  321. } else {
  322. boolean[] outer = new boolean[4];
  323. int dy = yoffset;
  324. for (int y = startRow; y < endRow; y++) {
  325. GridUnit[] gridUnits = (GridUnit[])primaryGridUnit.getRows().get(y);
  326. int dx = xoffset;
  327. for (int x = 0; x < gridUnits.length; x++) {
  328. GridUnit gu = gridUnits[x];
  329. if (gu.hasBorders()) {
  330. //Blocks for painting grid unit borders
  331. Block block = new Block();
  332. block.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
  333. block.setPositioning(Block.ABSOLUTE);
  334. int bpd = spannedGridRowHeights[y - startRow];
  335. bpd -= gu.getBorders().getBorderBeforeWidth(false) / 2;
  336. bpd -= gu.getBorders().getBorderAfterWidth(false) / 2;
  337. block.setBPD(bpd);
  338. if (log.isTraceEnabled()) {
  339. log.trace("pgu: " + primaryGridUnit + "; gu: " + gu + "; yoffset: "
  340. + (dy - gu.getBorders().getBorderBeforeWidth(false) / 2)
  341. + "; bpd: " + bpd);
  342. }
  343. int ipd = gu.getColumn().getColumnWidth().getValue(
  344. (PercentBaseContext) getParent());
  345. int borderStartWidth = gu.getBorders().getBorderStartWidth(false) / 2;
  346. ipd -= borderStartWidth;
  347. ipd -= gu.getBorders().getBorderEndWidth(false) / 2;
  348. block.setIPD(ipd);
  349. block.setXOffset(dx + borderStartWidth);
  350. block.setYOffset(dy - gu.getBorders().getBorderBeforeWidth(false) / 2);
  351. outer[0] = gu.getFlag(GridUnit.FIRST_IN_TABLE);
  352. outer[1] = gu.getFlag(GridUnit.LAST_IN_TABLE);
  353. outer[2] = gu.getFlag(GridUnit.IN_FIRST_COLUMN);
  354. outer[3] = gu.getFlag(GridUnit.IN_LAST_COLUMN);
  355. TraitSetter.addCollapsingBorders(block, gu.getBorders(), outer, this);
  356. parentLM.addChildArea(block);
  357. }
  358. dx += gu.getColumn().getColumnWidth().getValue(
  359. (PercentBaseContext) getParent());
  360. }
  361. dy += spannedGridRowHeights[y - startRow];
  362. }
  363. }
  364. }
  365. TraitSetter.addPadding(curBlockArea, primaryGridUnit.getBorders(),
  366. false, false, false, false, this);
  367. //Handle display-align
  368. int contentBPD = getContentHeight(primaryGridUnit);
  369. if (usedBPD < contentBPD) {
  370. if (getTableCell().getDisplayAlign() == EN_CENTER) {
  371. Block space = new Block();
  372. space.setBPD((contentBPD - usedBPD) / 2);
  373. curBlockArea.addBlock(space);
  374. } else if (getTableCell().getDisplayAlign() == EN_AFTER) {
  375. Block space = new Block();
  376. space.setBPD((contentBPD - usedBPD));
  377. curBlockArea.addBlock(space);
  378. }
  379. }
  380. AreaAdditionUtil.addAreas(this, parentIter, layoutContext);
  381. curBlockArea.setBPD(contentBPD);
  382. // Add background after we know the BPD
  383. if (isSeparateBorderModel()) {
  384. if (!emptyCell || getTableCell().showEmptyCells()) {
  385. TraitSetter.addBackground(curBlockArea,
  386. getTableCell().getCommonBorderPaddingBackground(),
  387. this);
  388. }
  389. } else {
  390. TraitSetter.addBackground(curBlockArea,
  391. getTableCell().getCommonBorderPaddingBackground(),
  392. this);
  393. }
  394. flush();
  395. curBlockArea = null;
  396. }
  397. /**
  398. * Return an Area which can contain the passed childArea. The childArea
  399. * may not yet have any content, but it has essential traits set.
  400. * In general, if the LayoutManager already has an Area it simply returns
  401. * it. Otherwise, it makes a new Area of the appropriate class.
  402. * It gets a parent area for its area by calling its parent LM.
  403. * Finally, based on the dimensions of the parent area, it initializes
  404. * its own area. This includes setting the content IPD and the maximum
  405. * BPD.
  406. *
  407. * @param childArea the child area to get the parent for
  408. * @return the parent area
  409. */
  410. public Area getParentArea(Area childArea) {
  411. if (curBlockArea == null) {
  412. curBlockArea = new Block();
  413. curBlockArea.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
  414. TraitSetter.setProducerID(curBlockArea, getTableCell().getId());
  415. curBlockArea.setPositioning(Block.ABSOLUTE);
  416. // set position
  417. int borderAdjust = 0;
  418. if (!isSeparateBorderModel()) {
  419. if (primaryGridUnit.hasSpanning()) {
  420. borderAdjust -= primaryGridUnit.getHalfMaxBeforeBorderWidth();
  421. } else {
  422. borderAdjust += primaryGridUnit.getHalfMaxBeforeBorderWidth();
  423. }
  424. } else {
  425. //borderAdjust += primaryGridUnit.getBorders().getBorderBeforeWidth(false);
  426. }
  427. TableLayoutManager tableLM = (TableLayoutManager)getParent();
  428. curBlockArea.setXOffset(xoffset + inRowIPDOffset + startIndent);
  429. curBlockArea.setYOffset(yoffset - borderAdjust
  430. + tableLM.getHalfBorderSeparationBPD());
  431. curBlockArea.setIPD(cellIPD);
  432. //curBlockArea.setHeight();
  433. // Set up dimensions
  434. /*Area parentArea =*/ parentLM.getParentArea(curBlockArea);
  435. // Get reference IPD from parentArea
  436. setCurrentArea(curBlockArea); // ??? for generic operations
  437. }
  438. return curBlockArea;
  439. }
  440. /**
  441. * Add the child to the cell block area.
  442. *
  443. * @param childArea the child to add to the cell
  444. */
  445. public void addChildArea(Area childArea) {
  446. if (curBlockArea != null) {
  447. curBlockArea.addBlock((Block) childArea);
  448. }
  449. }
  450. /**
  451. * Reset the position of the layout.
  452. *
  453. * @param resetPos the position to reset to
  454. */
  455. public void resetPosition(Position resetPos) {
  456. if (resetPos == null) {
  457. reset(null);
  458. }
  459. }
  460. /**
  461. * {@inheritDoc}
  462. */
  463. public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
  464. // TODO Auto-generated method stub
  465. return 0;
  466. }
  467. /**
  468. * {@inheritDoc}
  469. */
  470. public void discardSpace(KnuthGlue spaceGlue) {
  471. // TODO Auto-generated method stub
  472. }
  473. /**
  474. * {@inheritDoc}
  475. */
  476. public boolean mustKeepTogether() {
  477. //TODO Keeps will have to be more sophisticated sooner or later
  478. boolean keep = ((BlockLevelLayoutManager)getParent()).mustKeepTogether();
  479. if (primaryGridUnit.getRow() != null) {
  480. keep |= primaryGridUnit.getRow().mustKeepTogether();
  481. }
  482. return keep;
  483. }
  484. /**
  485. * {@inheritDoc}
  486. */
  487. public boolean mustKeepWithPrevious() {
  488. return false; //TODO FIX ME
  489. /*
  490. return !fobj.getKeepWithPrevious().getWithinPage().isAuto()
  491. || !fobj.getKeepWithPrevious().getWithinColumn().isAuto();
  492. */
  493. }
  494. /**
  495. * {@inheritDoc}
  496. */
  497. public boolean mustKeepWithNext() {
  498. return false; //TODO FIX ME
  499. /*
  500. return !fobj.getKeepWithNext().getWithinPage().isAuto()
  501. || !fobj.getKeepWithNext().getWithinColumn().isAuto();
  502. */
  503. }
  504. // --------- Property Resolution related functions --------- //
  505. /**
  506. * Returns the IPD of the content area
  507. * @return the IPD of the content area
  508. */
  509. public int getContentAreaIPD() {
  510. return cellIPD;
  511. }
  512. /**
  513. * Returns the BPD of the content area
  514. * @return the BPD of the content area
  515. */
  516. public int getContentAreaBPD() {
  517. if (curBlockArea != null) {
  518. return curBlockArea.getBPD();
  519. } else {
  520. log.error("getContentAreaBPD called on unknown BPD");
  521. return -1;
  522. }
  523. }
  524. /**
  525. * {@inheritDoc}
  526. */
  527. public boolean getGeneratesReferenceArea() {
  528. return true;
  529. }
  530. /**
  531. * {@inheritDoc}
  532. */
  533. public boolean getGeneratesBlockArea() {
  534. return true;
  535. }
  536. }