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.

BlockLayoutManager.java 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  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;
  19. import java.util.LinkedList;
  20. import java.util.List;
  21. import java.util.ListIterator;
  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.LineArea;
  27. import org.apache.fop.datatypes.Length;
  28. import org.apache.fop.fo.properties.KeepProperty;
  29. import org.apache.fop.fonts.Font;
  30. import org.apache.fop.fonts.FontInfo;
  31. import org.apache.fop.fonts.FontTriplet;
  32. import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager;
  33. import org.apache.fop.layoutmgr.inline.LineLayoutManager;
  34. import org.apache.fop.traits.MinOptMax;
  35. import org.apache.fop.traits.SpaceVal;
  36. /**
  37. * LayoutManager for a block FO.
  38. */
  39. public class BlockLayoutManager extends BlockStackingLayoutManager
  40. implements ConditionalElementListener {
  41. /**
  42. * logging instance
  43. */
  44. private static Log log = LogFactory.getLog(BlockLayoutManager.class);
  45. private Block curBlockArea;
  46. /** Iterator over the child layout managers. */
  47. protected ListIterator proxyLMiter;
  48. private int lead = 12000;
  49. private Length lineHeight;
  50. private int follow = 2000;
  51. //private int middleShift = 0;
  52. private boolean discardBorderBefore;
  53. private boolean discardBorderAfter;
  54. private boolean discardPaddingBefore;
  55. private boolean discardPaddingAfter;
  56. private MinOptMax effSpaceBefore;
  57. private MinOptMax effSpaceAfter;
  58. /** The list of child BreakPoss instances. */
  59. protected List childBreaks = new java.util.ArrayList();
  60. /**
  61. * Creates a new BlockLayoutManager.
  62. * @param inBlock the block FO object to create the layout manager for.
  63. */
  64. public BlockLayoutManager(org.apache.fop.fo.flow.Block inBlock) {
  65. super(inBlock);
  66. proxyLMiter = new ProxyLMiter();
  67. }
  68. /** {@inheritDoc} */
  69. public void initialize() {
  70. super.initialize();
  71. FontInfo fi = getBlockFO().getFOEventHandler().getFontInfo();
  72. FontTriplet[] fontkeys = getBlockFO().getCommonFont().getFontState(fi);
  73. Font initFont = fi.getFontInstance(fontkeys[0],
  74. getBlockFO().getCommonFont().fontSize.getValue(this));
  75. lead = initFont.getAscender();
  76. follow = -initFont.getDescender();
  77. //middleShift = -fs.getXHeight() / 2;
  78. lineHeight = getBlockFO().getLineHeight().getOptimum(this).getLength();
  79. startIndent = getBlockFO().getCommonMarginBlock().startIndent.getValue(this);
  80. endIndent = getBlockFO().getCommonMarginBlock().endIndent.getValue(this);
  81. foSpaceBefore = new SpaceVal(getBlockFO().getCommonMarginBlock().spaceBefore, this)
  82. .getSpace();
  83. foSpaceAfter = new SpaceVal(getBlockFO().getCommonMarginBlock().spaceAfter, this)
  84. .getSpace();
  85. bpUnit = 0; // non-standard extension
  86. if (bpUnit == 0) {
  87. // use optimum space values
  88. adjustedSpaceBefore = getBlockFO().getCommonMarginBlock().spaceBefore.getSpace()
  89. .getOptimum(this).getLength().getValue(this);
  90. adjustedSpaceAfter = getBlockFO().getCommonMarginBlock().spaceAfter.getSpace()
  91. .getOptimum(this).getLength().getValue(this);
  92. } else {
  93. // use minimum space values
  94. adjustedSpaceBefore = getBlockFO().getCommonMarginBlock().spaceBefore.getSpace()
  95. .getMinimum(this).getLength().getValue(this);
  96. adjustedSpaceAfter = getBlockFO().getCommonMarginBlock().spaceAfter.getSpace()
  97. .getMinimum(this).getLength().getValue(this);
  98. }
  99. }
  100. /** {@inheritDoc} */
  101. public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
  102. resetSpaces();
  103. return super.getNextKnuthElements(context, alignment);
  104. }
  105. private void resetSpaces() {
  106. this.discardBorderBefore = false;
  107. this.discardBorderAfter = false;
  108. this.discardPaddingBefore = false;
  109. this.discardPaddingAfter = false;
  110. this.effSpaceBefore = null;
  111. this.effSpaceAfter = null;
  112. }
  113. /**
  114. * Proxy iterator for Block LM.
  115. * This iterator creates and holds the complete list
  116. * of child LMs.
  117. * It uses fobjIter as its base iterator.
  118. * Block LM's createNextChildLMs uses this iterator
  119. * as its base iterator.
  120. */
  121. protected class ProxyLMiter extends LMiter {
  122. /**
  123. * Constructs a proxy iterator for Block LM.
  124. */
  125. public ProxyLMiter() {
  126. super(BlockLayoutManager.this);
  127. listLMs = new java.util.ArrayList(10);
  128. }
  129. /**
  130. * @return true if there are more child lms
  131. */
  132. public boolean hasNext() {
  133. return (curPos < listLMs.size()) || createNextChildLMs(curPos);
  134. }
  135. /**
  136. * @return true if new child lms were added
  137. */
  138. protected boolean createNextChildLMs(int pos) {
  139. List newLMs = createChildLMs(pos + 1 - listLMs.size());
  140. if (newLMs != null) {
  141. listLMs.addAll(newLMs);
  142. }
  143. return pos < listLMs.size();
  144. }
  145. }
  146. /**
  147. * {@inheritDoc}
  148. */
  149. public boolean createNextChildLMs(int pos) {
  150. while (proxyLMiter.hasNext()) {
  151. LayoutManager lm = (LayoutManager) proxyLMiter.next();
  152. if (lm instanceof InlineLevelLayoutManager) {
  153. LineLayoutManager lineLM = createLineManager(lm);
  154. addChildLM(lineLM);
  155. } else {
  156. addChildLM(lm);
  157. }
  158. if (pos < childLMs.size()) {
  159. return true;
  160. }
  161. }
  162. return false;
  163. }
  164. /**
  165. * Create a new LineLM, and collect all consecutive
  166. * inline generating LMs as its child LMs.
  167. * @param firstlm First LM in new LineLM
  168. * @return the newly created LineLM
  169. */
  170. private LineLayoutManager createLineManager(LayoutManager firstlm) {
  171. LineLayoutManager llm;
  172. llm = new LineLayoutManager(getBlockFO(), lineHeight, lead, follow);
  173. List inlines = new java.util.ArrayList();
  174. inlines.add(firstlm);
  175. while (proxyLMiter.hasNext()) {
  176. LayoutManager lm = (LayoutManager) proxyLMiter.next();
  177. if (lm instanceof InlineLevelLayoutManager) {
  178. inlines.add(lm);
  179. } else {
  180. proxyLMiter.previous();
  181. break;
  182. }
  183. }
  184. llm.addChildLMs(inlines);
  185. return llm;
  186. }
  187. /** {@inheritDoc} */
  188. public int getKeepTogetherStrength() {
  189. KeepProperty keep = getBlockFO().getKeepTogether();
  190. int strength = KeepUtil.getCombinedBlockLevelKeepStrength(keep);
  191. strength = Math.max(strength, getParentKeepTogetherStrength());
  192. return strength;
  193. }
  194. /** {@inheritDoc} */
  195. public int getKeepWithNextStrength() {
  196. return KeepUtil.getCombinedBlockLevelKeepStrength(getBlockFO().getKeepWithNext());
  197. }
  198. /** {@inheritDoc} */
  199. public int getKeepWithPreviousStrength() {
  200. return KeepUtil.getCombinedBlockLevelKeepStrength(getBlockFO().getKeepWithPrevious());
  201. }
  202. /** {@inheritDoc} */
  203. public void addAreas(PositionIterator parentIter,
  204. LayoutContext layoutContext) {
  205. getParentArea(null);
  206. // if this will create the first block area in a page
  207. // and display-align is after or center, add space before
  208. if (layoutContext.getSpaceBefore() > 0) {
  209. addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore()));
  210. }
  211. LayoutManager childLM;
  212. LayoutManager lastLM = null;
  213. LayoutContext lc = new LayoutContext(0);
  214. lc.setSpaceAdjust(layoutContext.getSpaceAdjust());
  215. // set space after in the LayoutContext for children
  216. if (layoutContext.getSpaceAfter() > 0) {
  217. lc.setSpaceAfter(layoutContext.getSpaceAfter());
  218. }
  219. PositionIterator childPosIter;
  220. // "unwrap" the NonLeafPositions stored in parentIter
  221. // and put them in a new list;
  222. LinkedList positionList = new LinkedList();
  223. Position pos;
  224. boolean bSpaceBefore = false;
  225. boolean bSpaceAfter = false;
  226. Position firstPos = null;
  227. Position lastPos = null;
  228. while (parentIter.hasNext()) {
  229. pos = (Position) parentIter.next();
  230. //log.trace("pos = " + pos.getClass().getName() + "; " + pos);
  231. if (pos.getIndex() >= 0) {
  232. if (firstPos == null) {
  233. firstPos = pos;
  234. }
  235. lastPos = pos;
  236. }
  237. Position innerPosition = pos;
  238. if (pos instanceof NonLeafPosition) {
  239. //Not all elements are wrapped
  240. innerPosition = pos.getPosition();
  241. }
  242. if (innerPosition == null) {
  243. // pos was created by this BlockLM and was inside an element
  244. // representing space before or after
  245. // this means the space was not discarded
  246. if (positionList.size() == 0) {
  247. // pos was in the element representing space-before
  248. bSpaceBefore = true;
  249. //log.trace(" space before");
  250. } else {
  251. // pos was in the element representing space-after
  252. bSpaceAfter = true;
  253. //log.trace(" space-after");
  254. }
  255. } else if (innerPosition.getLM() == this
  256. && !(innerPosition instanceof MappingPosition)) {
  257. // pos was created by this BlockLM and was inside a penalty
  258. // allowing or forbidding a page break
  259. // nothing to do
  260. //log.trace(" penalty");
  261. } else {
  262. // innerPosition was created by another LM
  263. positionList.add(innerPosition);
  264. lastLM = innerPosition.getLM();
  265. //log.trace(" " + innerPosition.getClass().getName());
  266. }
  267. }
  268. addId();
  269. addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
  270. if (bpUnit == 0) {
  271. // the Positions in positionList were inside the elements
  272. // created by the LineLM
  273. childPosIter = new StackingIter(positionList.listIterator());
  274. } else {
  275. // the Positions in positionList were inside the elements
  276. // created by the BlockLM in the createUnitElements() method
  277. //if (((Position) positionList.getLast()) instanceof
  278. // LeafPosition) {
  279. // // the last item inside positionList is a LeafPosition
  280. // // (a LineBreakPosition, more precisely); this means that
  281. // // the whole paragraph is on the same page
  282. // childPosIter = new KnuthPossPosIter(storedList, 0,
  283. // storedList.size());
  284. //} else {
  285. // // the last item inside positionList is a Position;
  286. // // this means that the paragraph has been split
  287. // // between consecutive pages
  288. LinkedList splitList = new LinkedList();
  289. int splitLength = 0;
  290. int iFirst = ((MappingPosition) positionList.getFirst()).getFirstIndex();
  291. int iLast = ((MappingPosition) positionList.getLast()).getLastIndex();
  292. // copy from storedList to splitList all the elements from
  293. // iFirst to iLast
  294. ListIterator storedListIterator = storedList.listIterator(iFirst);
  295. while (storedListIterator.nextIndex() <= iLast) {
  296. KnuthElement element = (KnuthElement) storedListIterator
  297. .next();
  298. // some elements in storedList (i.e. penalty items) were created
  299. // by this BlockLM, and must be ignored
  300. if (element.getLayoutManager() != this) {
  301. splitList.add(element);
  302. splitLength += element.getW();
  303. lastLM = element.getLayoutManager();
  304. }
  305. }
  306. //log.debug("Adding areas from " + iFirst + " to " + iLast);
  307. //log.debug("splitLength= " + splitLength
  308. // + " (" + neededUnits(splitLength) + " units') "
  309. // + (neededUnits(splitLength) * bpUnit - splitLength)
  310. // + " spacing");
  311. // add space before and / or after the paragraph
  312. // to reach a multiple of bpUnit
  313. if (bSpaceBefore && bSpaceAfter) {
  314. foSpaceBefore = new SpaceVal(getBlockFO()
  315. .getCommonMarginBlock().spaceBefore, this).getSpace();
  316. foSpaceAfter = new SpaceVal(getBlockFO()
  317. .getCommonMarginBlock().spaceAfter, this).getSpace();
  318. adjustedSpaceBefore = (neededUnits(splitLength
  319. + foSpaceBefore.min
  320. + foSpaceAfter.min)
  321. * bpUnit - splitLength) / 2;
  322. adjustedSpaceAfter = neededUnits(splitLength
  323. + foSpaceBefore.min
  324. + foSpaceAfter.min)
  325. * bpUnit - splitLength - adjustedSpaceBefore;
  326. } else if (bSpaceBefore) {
  327. adjustedSpaceBefore = neededUnits(splitLength
  328. + foSpaceBefore.min)
  329. * bpUnit - splitLength;
  330. } else {
  331. adjustedSpaceAfter = neededUnits(splitLength
  332. + foSpaceAfter.min)
  333. * bpUnit - splitLength;
  334. }
  335. //log.debug("spazio prima = " + adjustedSpaceBefore
  336. // + " spazio dopo = " + adjustedSpaceAfter + " totale = " +
  337. // (adjustedSpaceBefore + adjustedSpaceAfter + splitLength));
  338. childPosIter = new KnuthPossPosIter(splitList, 0, splitList
  339. .size());
  340. //}
  341. }
  342. while ((childLM = childPosIter.getNextChildLM()) != null) {
  343. // set last area flag
  344. lc.setFlags(LayoutContext.LAST_AREA,
  345. (layoutContext.isLastArea() && childLM == lastLM));
  346. lc.setStackLimitBP(layoutContext.getStackLimitBP());
  347. // Add the line areas to Area
  348. childLM.addAreas(childPosIter, lc);
  349. }
  350. addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
  351. TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(),
  352. effSpaceBefore, effSpaceAfter);
  353. flush();
  354. curBlockArea = null;
  355. resetSpaces();
  356. // Notify end of block layout manager to the PSLM
  357. getPSLM().notifyEndOfLayout(getBlockFO().getId());
  358. }
  359. /**
  360. * Return an Area which can contain the passed childArea. The childArea
  361. * may not yet have any content, but it has essential traits set.
  362. * In general, if the LayoutManager already has an Area it simply returns
  363. * it. Otherwise, it makes a new Area of the appropriate class.
  364. * It gets a parent area for its area by calling its parent LM.
  365. * Finally, based on the dimensions of the parent area, it initializes
  366. * its own area. This includes setting the content IPD and the maximum
  367. * BPD.
  368. * @param childArea area to get the parent area for
  369. * @return the parent area
  370. */
  371. public Area getParentArea(Area childArea) {
  372. if (curBlockArea == null) {
  373. curBlockArea = new Block();
  374. curBlockArea.setIPD(super.getContentAreaIPD());
  375. TraitSetter.addBreaks(curBlockArea,
  376. getBlockFO().getBreakBefore(), getBlockFO().getBreakAfter());
  377. // Must get dimensions from parent area
  378. //Don't optimize this line away. It can have ugly side-effects.
  379. /*Area parentArea =*/ parentLM.getParentArea(curBlockArea);
  380. // set traits
  381. TraitSetter.setProducerID(curBlockArea, getBlockFO().getId());
  382. TraitSetter.addBorders(curBlockArea,
  383. getBlockFO().getCommonBorderPaddingBackground(),
  384. discardBorderBefore, discardBorderAfter, false, false, this);
  385. TraitSetter.addPadding(curBlockArea,
  386. getBlockFO().getCommonBorderPaddingBackground(),
  387. discardPaddingBefore, discardPaddingAfter, false, false, this);
  388. TraitSetter.addMargins(curBlockArea,
  389. getBlockFO().getCommonBorderPaddingBackground(),
  390. startIndent, endIndent,
  391. this);
  392. setCurrentArea(curBlockArea); // ??? for generic operations
  393. }
  394. return curBlockArea;
  395. }
  396. /**
  397. * {@inheritDoc}
  398. */
  399. public void addChildArea(Area childArea) {
  400. if (curBlockArea != null) {
  401. if (childArea instanceof LineArea) {
  402. curBlockArea.addLineArea((LineArea) childArea);
  403. } else {
  404. curBlockArea.addBlock((Block) childArea);
  405. }
  406. }
  407. }
  408. /**
  409. * Force current area to be added to parent area.
  410. * {@inheritDoc}
  411. */
  412. protected void flush() {
  413. if (curBlockArea != null) {
  414. TraitSetter.addBackground(curBlockArea,
  415. getBlockFO().getCommonBorderPaddingBackground(),
  416. this);
  417. super.flush();
  418. }
  419. }
  420. /**
  421. * convenience method that returns the Block node
  422. * @return the block node
  423. */
  424. protected org.apache.fop.fo.flow.Block getBlockFO() {
  425. return (org.apache.fop.fo.flow.Block) fobj;
  426. }
  427. // --------- Property Resolution related functions --------- //
  428. /**
  429. * Returns the IPD of the content area
  430. * @return the IPD of the content area
  431. */
  432. public int getContentAreaIPD() {
  433. if (curBlockArea != null) {
  434. return curBlockArea.getIPD();
  435. }
  436. return super.getContentAreaIPD();
  437. }
  438. /**
  439. * Returns the BPD of the content area
  440. * @return the BPD of the content area
  441. */
  442. public int getContentAreaBPD() {
  443. if (curBlockArea != null) {
  444. return curBlockArea.getBPD();
  445. }
  446. return -1;
  447. }
  448. /**
  449. * {@inheritDoc}
  450. */
  451. public boolean getGeneratesBlockArea() {
  452. return true;
  453. }
  454. /** {@inheritDoc} */
  455. public void notifySpace(RelSide side, MinOptMax effectiveLength) {
  456. if (RelSide.BEFORE == side) {
  457. if (log.isDebugEnabled()) {
  458. log.debug(this + ": Space " + side + ", "
  459. + this.effSpaceBefore + "-> " + effectiveLength);
  460. }
  461. this.effSpaceBefore = effectiveLength;
  462. } else {
  463. if (log.isDebugEnabled()) {
  464. log.debug(this + ": Space " + side + ", "
  465. + this.effSpaceAfter + "-> " + effectiveLength);
  466. }
  467. this.effSpaceAfter = effectiveLength;
  468. }
  469. }
  470. /** {@inheritDoc} */
  471. public void notifyBorder(RelSide side, MinOptMax effectiveLength) {
  472. if (effectiveLength == null) {
  473. if (RelSide.BEFORE == side) {
  474. this.discardBorderBefore = true;
  475. } else {
  476. this.discardBorderAfter = true;
  477. }
  478. }
  479. if (log.isDebugEnabled()) {
  480. log.debug(this + ": Border " + side + " -> " + effectiveLength);
  481. }
  482. }
  483. /** {@inheritDoc} */
  484. public void notifyPadding(RelSide side, MinOptMax effectiveLength) {
  485. if (effectiveLength == null) {
  486. if (RelSide.BEFORE == side) {
  487. this.discardPaddingBefore = true;
  488. } else {
  489. this.discardPaddingAfter = true;
  490. }
  491. }
  492. if (log.isDebugEnabled()) {
  493. log.debug(this + ": Padding " + side + " -> " + effectiveLength);
  494. }
  495. }
  496. }