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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  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 List 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. * @param pos ...
  137. * @return true if new child lms were added
  138. */
  139. protected boolean createNextChildLMs(int pos) {
  140. List newLMs = createChildLMs(pos + 1 - listLMs.size());
  141. if (newLMs != null) {
  142. listLMs.addAll(newLMs);
  143. }
  144. return pos < listLMs.size();
  145. }
  146. }
  147. /**
  148. * {@inheritDoc}
  149. */
  150. public boolean createNextChildLMs(int pos) {
  151. while (proxyLMiter.hasNext()) {
  152. LayoutManager lm = (LayoutManager) proxyLMiter.next();
  153. if (lm instanceof InlineLevelLayoutManager) {
  154. LineLayoutManager lineLM = createLineManager(lm);
  155. addChildLM(lineLM);
  156. } else {
  157. addChildLM(lm);
  158. }
  159. if (pos < childLMs.size()) {
  160. return true;
  161. }
  162. }
  163. return false;
  164. }
  165. /**
  166. * Create a new LineLM, and collect all consecutive
  167. * inline generating LMs as its child LMs.
  168. * @param firstlm First LM in new LineLM
  169. * @return the newly created LineLM
  170. */
  171. private LineLayoutManager createLineManager(LayoutManager firstlm) {
  172. LineLayoutManager llm;
  173. llm = new LineLayoutManager(getBlockFO(), lineHeight, lead, follow);
  174. List inlines = new java.util.ArrayList();
  175. inlines.add(firstlm);
  176. while (proxyLMiter.hasNext()) {
  177. LayoutManager lm = (LayoutManager) proxyLMiter.next();
  178. if (lm instanceof InlineLevelLayoutManager) {
  179. inlines.add(lm);
  180. } else {
  181. proxyLMiter.previous();
  182. break;
  183. }
  184. }
  185. llm.addChildLMs(inlines);
  186. return llm;
  187. }
  188. /** {@inheritDoc} */
  189. public int getKeepTogetherStrength() {
  190. KeepProperty keep = getBlockFO().getKeepTogether();
  191. int strength = KeepUtil.getCombinedBlockLevelKeepStrength(keep);
  192. strength = Math.max(strength, getParentKeepTogetherStrength());
  193. return strength;
  194. }
  195. /** {@inheritDoc} */
  196. public int getKeepWithNextStrength() {
  197. return KeepUtil.getCombinedBlockLevelKeepStrength(getBlockFO().getKeepWithNext());
  198. }
  199. /** {@inheritDoc} */
  200. public int getKeepWithPreviousStrength() {
  201. return KeepUtil.getCombinedBlockLevelKeepStrength(getBlockFO().getKeepWithPrevious());
  202. }
  203. /** {@inheritDoc} */
  204. public void addAreas(PositionIterator parentIter,
  205. LayoutContext layoutContext) {
  206. getParentArea(null);
  207. // if this will create the first block area in a page
  208. // and display-align is after or center, add space before
  209. if (layoutContext.getSpaceBefore() > 0) {
  210. addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore()));
  211. }
  212. LayoutManager childLM;
  213. LayoutManager lastLM = null;
  214. LayoutContext lc = new LayoutContext(0);
  215. lc.setSpaceAdjust(layoutContext.getSpaceAdjust());
  216. // set space after in the LayoutContext for children
  217. if (layoutContext.getSpaceAfter() > 0) {
  218. lc.setSpaceAfter(layoutContext.getSpaceAfter());
  219. }
  220. PositionIterator childPosIter;
  221. // "unwrap" the NonLeafPositions stored in parentIter
  222. // and put them in a new list;
  223. LinkedList positionList = new LinkedList();
  224. Position pos;
  225. boolean bSpaceBefore = false;
  226. boolean bSpaceAfter = false;
  227. Position firstPos = null;
  228. Position lastPos = null;
  229. while (parentIter.hasNext()) {
  230. pos = (Position) parentIter.next();
  231. //log.trace("pos = " + pos.getClass().getName() + "; " + pos);
  232. if (pos.getIndex() >= 0) {
  233. if (firstPos == null) {
  234. firstPos = pos;
  235. }
  236. lastPos = pos;
  237. }
  238. Position innerPosition = pos;
  239. if (pos instanceof NonLeafPosition) {
  240. //Not all elements are wrapped
  241. innerPosition = pos.getPosition();
  242. }
  243. if (innerPosition == null) {
  244. // pos was created by this BlockLM and was inside an element
  245. // representing space before or after
  246. // this means the space was not discarded
  247. if (positionList.size() == 0) {
  248. // pos was in the element representing space-before
  249. bSpaceBefore = true;
  250. //log.trace(" space before");
  251. } else {
  252. // pos was in the element representing space-after
  253. bSpaceAfter = true;
  254. //log.trace(" space-after");
  255. }
  256. } else if (innerPosition.getLM() == this
  257. && !(innerPosition instanceof MappingPosition)) {
  258. // pos was created by this BlockLM and was inside a penalty
  259. // allowing or forbidding a page break
  260. // nothing to do
  261. //log.trace(" penalty");
  262. } else {
  263. // innerPosition was created by another LM
  264. positionList.add(innerPosition);
  265. lastLM = innerPosition.getLM();
  266. //log.trace(" " + innerPosition.getClass().getName());
  267. }
  268. }
  269. addId();
  270. addMarkersToPage(true, isFirst(firstPos), isLast(lastPos));
  271. if (bpUnit == 0) {
  272. // the Positions in positionList were inside the elements
  273. // created by the LineLM
  274. childPosIter = new StackingIter(positionList.listIterator());
  275. } else {
  276. // the Positions in positionList were inside the elements
  277. // created by the BlockLM in the createUnitElements() method
  278. //if (((Position) positionList.getLast()) instanceof
  279. // LeafPosition) {
  280. // // the last item inside positionList is a LeafPosition
  281. // // (a LineBreakPosition, more precisely); this means that
  282. // // the whole paragraph is on the same page
  283. // childPosIter = new KnuthPossPosIter(storedList, 0,
  284. // storedList.size());
  285. //} else {
  286. // // the last item inside positionList is a Position;
  287. // // this means that the paragraph has been split
  288. // // between consecutive pages
  289. LinkedList splitList = new LinkedList();
  290. int splitLength = 0;
  291. int iFirst = ((MappingPosition) positionList.getFirst()).getFirstIndex();
  292. int iLast = ((MappingPosition) positionList.getLast()).getLastIndex();
  293. // copy from storedList to splitList all the elements from
  294. // iFirst to iLast
  295. ListIterator storedListIterator = storedList.listIterator(iFirst);
  296. while (storedListIterator.nextIndex() <= iLast) {
  297. KnuthElement element = (KnuthElement) storedListIterator
  298. .next();
  299. // some elements in storedList (i.e. penalty items) were created
  300. // by this BlockLM, and must be ignored
  301. if (element.getLayoutManager() != this) {
  302. splitList.add(element);
  303. splitLength += element.getW();
  304. lastLM = element.getLayoutManager();
  305. }
  306. }
  307. //log.debug("Adding areas from " + iFirst + " to " + iLast);
  308. //log.debug("splitLength= " + splitLength
  309. // + " (" + neededUnits(splitLength) + " units') "
  310. // + (neededUnits(splitLength) * bpUnit - splitLength)
  311. // + " spacing");
  312. // add space before and / or after the paragraph
  313. // to reach a multiple of bpUnit
  314. if (bSpaceBefore && bSpaceAfter) {
  315. foSpaceBefore = new SpaceVal(getBlockFO()
  316. .getCommonMarginBlock().spaceBefore, this).getSpace();
  317. foSpaceAfter = new SpaceVal(getBlockFO()
  318. .getCommonMarginBlock().spaceAfter, this).getSpace();
  319. adjustedSpaceBefore = (neededUnits(splitLength
  320. + foSpaceBefore.min
  321. + foSpaceAfter.min)
  322. * bpUnit - splitLength) / 2;
  323. adjustedSpaceAfter = neededUnits(splitLength
  324. + foSpaceBefore.min
  325. + foSpaceAfter.min)
  326. * bpUnit - splitLength - adjustedSpaceBefore;
  327. } else if (bSpaceBefore) {
  328. adjustedSpaceBefore = neededUnits(splitLength
  329. + foSpaceBefore.min)
  330. * bpUnit - splitLength;
  331. } else {
  332. adjustedSpaceAfter = neededUnits(splitLength
  333. + foSpaceAfter.min)
  334. * bpUnit - splitLength;
  335. }
  336. //log.debug("spazio prima = " + adjustedSpaceBefore
  337. // + " spazio dopo = " + adjustedSpaceAfter + " totale = " +
  338. // (adjustedSpaceBefore + adjustedSpaceAfter + splitLength));
  339. childPosIter = new KnuthPossPosIter(splitList, 0, splitList
  340. .size());
  341. //}
  342. }
  343. while ((childLM = childPosIter.getNextChildLM()) != null) {
  344. // set last area flag
  345. lc.setFlags(LayoutContext.LAST_AREA,
  346. (layoutContext.isLastArea() && childLM == lastLM));
  347. lc.setStackLimitBP(layoutContext.getStackLimitBP());
  348. // Add the line areas to Area
  349. childLM.addAreas(childPosIter, lc);
  350. }
  351. addMarkersToPage(false, isFirst(firstPos), isLast(lastPos));
  352. TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(),
  353. effSpaceBefore, effSpaceAfter);
  354. flush();
  355. curBlockArea = null;
  356. resetSpaces();
  357. //Notify end of block layout manager to the PSLM
  358. checkEndOfLayout(lastPos);
  359. }
  360. /**
  361. * Return an Area which can contain the passed childArea. The childArea
  362. * may not yet have any content, but it has essential traits set.
  363. * In general, if the LayoutManager already has an Area it simply returns
  364. * it. Otherwise, it makes a new Area of the appropriate class.
  365. * It gets a parent area for its area by calling its parent LM.
  366. * Finally, based on the dimensions of the parent area, it initializes
  367. * its own area. This includes setting the content IPD and the maximum
  368. * BPD.
  369. * @param childArea area to get the parent area for
  370. * @return the parent area
  371. */
  372. public Area getParentArea(Area childArea) {
  373. if (curBlockArea == null) {
  374. curBlockArea = new Block();
  375. curBlockArea.setIPD(super.getContentAreaIPD());
  376. TraitSetter.addBreaks(curBlockArea,
  377. getBlockFO().getBreakBefore(), getBlockFO().getBreakAfter());
  378. // Must get dimensions from parent area
  379. //Don't optimize this line away. It can have ugly side-effects.
  380. /*Area parentArea =*/ parentLM.getParentArea(curBlockArea);
  381. // set traits
  382. TraitSetter.setProducerID(curBlockArea, getBlockFO().getId());
  383. TraitSetter.addBorders(curBlockArea,
  384. getBlockFO().getCommonBorderPaddingBackground(),
  385. discardBorderBefore, discardBorderAfter, false, false, this);
  386. TraitSetter.addPadding(curBlockArea,
  387. getBlockFO().getCommonBorderPaddingBackground(),
  388. discardPaddingBefore, discardPaddingAfter, false, false, this);
  389. TraitSetter.addMargins(curBlockArea,
  390. getBlockFO().getCommonBorderPaddingBackground(),
  391. startIndent, endIndent,
  392. this);
  393. setCurrentArea(curBlockArea); // ??? for generic operations
  394. }
  395. return curBlockArea;
  396. }
  397. /**
  398. * {@inheritDoc}
  399. */
  400. public void addChildArea(Area childArea) {
  401. if (curBlockArea != null) {
  402. if (childArea instanceof LineArea) {
  403. curBlockArea.addLineArea((LineArea) childArea);
  404. } else {
  405. curBlockArea.addBlock((Block) childArea);
  406. }
  407. }
  408. }
  409. /**
  410. * Force current area to be added to parent area.
  411. * {@inheritDoc}
  412. */
  413. protected void flush() {
  414. if (curBlockArea != null) {
  415. TraitSetter.addBackground(curBlockArea,
  416. getBlockFO().getCommonBorderPaddingBackground(),
  417. this);
  418. super.flush();
  419. }
  420. }
  421. /**
  422. * convenience method that returns the Block node
  423. * @return the block node
  424. */
  425. protected org.apache.fop.fo.flow.Block getBlockFO() {
  426. return (org.apache.fop.fo.flow.Block) fobj;
  427. }
  428. // --------- Property Resolution related functions --------- //
  429. /**
  430. * Returns the IPD of the content area
  431. * @return the IPD of the content area
  432. */
  433. public int getContentAreaIPD() {
  434. if (curBlockArea != null) {
  435. return curBlockArea.getIPD();
  436. }
  437. return super.getContentAreaIPD();
  438. }
  439. /**
  440. * Returns the BPD of the content area
  441. * @return the BPD of the content area
  442. */
  443. public int getContentAreaBPD() {
  444. if (curBlockArea != null) {
  445. return curBlockArea.getBPD();
  446. }
  447. return -1;
  448. }
  449. /**
  450. * {@inheritDoc}
  451. */
  452. public boolean getGeneratesBlockArea() {
  453. return true;
  454. }
  455. /** {@inheritDoc} */
  456. public void notifySpace(RelSide side, MinOptMax effectiveLength) {
  457. if (RelSide.BEFORE == side) {
  458. if (log.isDebugEnabled()) {
  459. log.debug(this + ": Space " + side + ", "
  460. + this.effSpaceBefore + "-> " + effectiveLength);
  461. }
  462. this.effSpaceBefore = effectiveLength;
  463. } else {
  464. if (log.isDebugEnabled()) {
  465. log.debug(this + ": Space " + side + ", "
  466. + this.effSpaceAfter + "-> " + effectiveLength);
  467. }
  468. this.effSpaceAfter = effectiveLength;
  469. }
  470. }
  471. /** {@inheritDoc} */
  472. public void notifyBorder(RelSide side, MinOptMax effectiveLength) {
  473. if (effectiveLength == null) {
  474. if (RelSide.BEFORE == side) {
  475. this.discardBorderBefore = true;
  476. } else {
  477. this.discardBorderAfter = true;
  478. }
  479. }
  480. if (log.isDebugEnabled()) {
  481. log.debug(this + ": Border " + side + " -> " + effectiveLength);
  482. }
  483. }
  484. /** {@inheritDoc} */
  485. public void notifyPadding(RelSide side, MinOptMax effectiveLength) {
  486. if (effectiveLength == null) {
  487. if (RelSide.BEFORE == side) {
  488. this.discardPaddingBefore = true;
  489. } else {
  490. this.discardPaddingAfter = true;
  491. }
  492. }
  493. if (log.isDebugEnabled()) {
  494. log.debug(this + ": Padding " + side + " -> " + effectiveLength);
  495. }
  496. }
  497. }