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.

StaticContentLayoutManager.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  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.RegionReference;
  27. import org.apache.fop.fo.Constants;
  28. import org.apache.fop.fo.FObj;
  29. import org.apache.fop.fo.pagination.PageSequence;
  30. import org.apache.fop.fo.pagination.SideRegion;
  31. import org.apache.fop.fo.pagination.StaticContent;
  32. import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener;
  33. import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager;
  34. import org.apache.fop.layoutmgr.inline.TextLayoutManager;
  35. import org.apache.fop.traits.MinOptMax;
  36. import org.apache.fop.util.ListUtil;
  37. /**
  38. * LayoutManager for an fo:flow object.
  39. * Its parent LM is the PageSequenceLayoutManager.
  40. * This LM is responsible for getting columns of the appropriate size
  41. * and filling them with block-level areas generated by its children.
  42. */
  43. public class StaticContentLayoutManager extends BlockStackingLayoutManager {
  44. /**
  45. * logging instance
  46. */
  47. private static Log log = LogFactory.getLog(StaticContentLayoutManager.class);
  48. private RegionReference targetRegion;
  49. private Block targetBlock;
  50. private SideRegion regionFO;
  51. private int contentAreaIPD = 0;
  52. private int contentAreaBPD = -1;
  53. /**
  54. * Creates a new StaticContentLayoutManager.
  55. * @param pslm PageSequenceLayoutManager this layout manager belongs to
  56. * @param node static-content FO
  57. * @param reg side region to layout into
  58. */
  59. public StaticContentLayoutManager(PageSequenceLayoutManager pslm,
  60. StaticContent node, SideRegion reg) {
  61. super(node);
  62. setParent(pslm);
  63. regionFO = reg;
  64. targetRegion = getCurrentPV().getRegionReference(regionFO.getNameId());
  65. }
  66. /**
  67. * Creates a new StaticContentLayoutManager.
  68. * @param pslm PageSequenceLayoutManager this layout manager belongs to
  69. * @param node static-content FO
  70. * @param block the block to layout into
  71. */
  72. public StaticContentLayoutManager(PageSequenceLayoutManager pslm,
  73. StaticContent node, Block block) {
  74. super(node);
  75. setParent(pslm);
  76. targetBlock = block;
  77. }
  78. /** {@inheritDoc} */
  79. public List getNextKnuthElements(LayoutContext context, int alignment) {
  80. if (true) {
  81. throw new UnsupportedOperationException(
  82. "Shouldn't this method be emptied because it's never called at all?");
  83. }
  84. //TODO Empty this method?!?
  85. // set layout dimensions
  86. setContentAreaIPD(context.getRefIPD());
  87. setContentAreaBPD(context.getStackLimitBP().opt);
  88. //TODO Copied from elsewhere. May be worthwhile to factor out the common parts.
  89. // currently active LM
  90. BlockLevelLayoutManager curLM;
  91. BlockLevelLayoutManager prevLM = null;
  92. MinOptMax stackSize = new MinOptMax();
  93. List returnedList;
  94. List returnList = new LinkedList();
  95. while ((curLM = ((BlockLevelLayoutManager) getChildLM())) != null) {
  96. if (curLM instanceof InlineLevelLayoutManager) {
  97. log.error("inline area not allowed under flow - ignoring");
  98. curLM.setFinished(true);
  99. continue;
  100. }
  101. // Set up a LayoutContext
  102. MinOptMax bpd = context.getStackLimitBP();
  103. LayoutContext childLC = new LayoutContext(0);
  104. childLC.setStackLimitBP(MinOptMax.subtract(bpd, stackSize));
  105. childLC.setRefIPD(context.getRefIPD());
  106. // get elements from curLM
  107. returnedList = curLM.getNextKnuthElements(childLC, alignment);
  108. //log.debug("FLM.getNextKnuthElements> returnedList.size() = "
  109. // + returnedList.size());
  110. // "wrap" the Position inside each element
  111. List tempList = returnedList;
  112. KnuthElement tempElement;
  113. returnedList = new LinkedList();
  114. ListIterator listIter = tempList.listIterator();
  115. while (listIter.hasNext()) {
  116. tempElement = (KnuthElement)listIter.next();
  117. tempElement.setPosition(new NonLeafPosition(this, tempElement.getPosition()));
  118. returnedList.add(tempElement);
  119. }
  120. if (returnedList.size() == 1
  121. && ((KnuthElement)returnedList.get(0)).isPenalty()
  122. && ((KnuthPenalty)returnedList.get(0)).getP() == -KnuthElement.INFINITE) {
  123. // a descendant of this flow has break-before
  124. returnList.addAll(returnedList);
  125. return returnList;
  126. } else {
  127. if (!returnList.isEmpty()) {
  128. // there is a block before this one
  129. if (prevLM.mustKeepWithNext()
  130. || curLM.mustKeepWithPrevious()) {
  131. // add an infinite penalty to forbid a break between blocks
  132. returnList.add(new KnuthPenalty(0,
  133. KnuthElement.INFINITE, false,
  134. new Position(this), false));
  135. } else if (!((KnuthElement) ListUtil.getLast(returnList))
  136. .isGlue()) {
  137. // add a null penalty to allow a break between blocks
  138. returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
  139. }
  140. }
  141. /*LF*/ if (!returnedList.isEmpty()) { // controllare!
  142. returnList.addAll(returnedList);
  143. final KnuthElement last = (KnuthElement) ListUtil
  144. .getLast(returnedList);
  145. if (last.isPenalty()
  146. && ((KnuthPenalty) last).getP() == -KnuthElement.INFINITE) {
  147. // a descendant of this flow has break-after
  148. /*LF*/ //log.debug("FLM - break after!!");
  149. return returnList;
  150. }
  151. /*LF*/ }
  152. }
  153. prevLM = curLM;
  154. }
  155. setFinished(true);
  156. if (returnList.isEmpty()) {
  157. return null;
  158. } else {
  159. return returnList;
  160. }
  161. }
  162. /**
  163. * {@inheritDoc}
  164. */
  165. public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
  166. AreaAdditionUtil.addAreas(this, parentIter, layoutContext);
  167. flush();
  168. targetRegion = null;
  169. }
  170. /**
  171. * Add child area to a the correct container, depending on its
  172. * area class. A Flow can fill at most one area container of any class
  173. * at any one time. The actual work is done by BlockStackingLM.
  174. * {@inheritDoc}
  175. */
  176. public void addChildArea(Area childArea) {
  177. if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator")) {
  178. targetBlock.addBlock((Block)childArea);
  179. } else {
  180. targetRegion.addBlock((Block)childArea);
  181. }
  182. }
  183. /**
  184. * {@inheritDoc}
  185. */
  186. public Area getParentArea(Area childArea) {
  187. if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator")) {
  188. return targetBlock;
  189. } else {
  190. return targetRegion;
  191. }
  192. }
  193. /**
  194. * Does the layout for a side region. Called by PageSequenceLayoutManager.
  195. */
  196. public void doLayout() {
  197. int targetIPD = 0;
  198. int targetBPD = 0;
  199. int targetAlign = EN_AUTO;
  200. boolean autoHeight = false;
  201. StaticContentBreaker breaker;
  202. if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator")) {
  203. targetIPD = targetBlock.getIPD();
  204. targetBPD = targetBlock.getBPD();
  205. if (targetBPD == 0) {
  206. autoHeight = true;
  207. }
  208. targetAlign = EN_BEFORE;
  209. } else {
  210. targetIPD = targetRegion.getIPD();
  211. targetBPD = targetRegion.getBPD();
  212. targetAlign = regionFO.getDisplayAlign();
  213. }
  214. setContentAreaIPD(targetIPD);
  215. setContentAreaBPD(targetBPD);
  216. breaker = new StaticContentBreaker(this, targetIPD, targetAlign);
  217. breaker.doLayout(targetBPD, autoHeight);
  218. if (breaker.isOverflow()) {
  219. if (!autoHeight) {
  220. String page = getPSLM().getCurrentPage().getPageViewport().getPageNumberString();
  221. BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
  222. getStaticContentFO().getUserAgent().getEventBroadcaster());
  223. boolean canRecover = (regionFO.getOverflow() != EN_ERROR_IF_OVERFLOW);
  224. boolean needClip = (regionFO.getOverflow() == Constants.EN_HIDDEN
  225. || regionFO.getOverflow() == Constants.EN_ERROR_IF_OVERFLOW);
  226. eventProducer.regionOverflow(this, regionFO.getName(),
  227. page,
  228. breaker.getOverflowAmount(), needClip, canRecover,
  229. getStaticContentFO().getLocator());
  230. }
  231. }
  232. }
  233. /**
  234. * Convenience method that returns the Static Content node.
  235. * @return the static content node
  236. */
  237. protected StaticContent getStaticContentFO() {
  238. return (StaticContent) fobj;
  239. }
  240. private class StaticContentBreaker extends AbstractBreaker {
  241. private StaticContentLayoutManager lm;
  242. private int displayAlign;
  243. private int ipd;
  244. private int overflow = 0;
  245. public StaticContentBreaker(StaticContentLayoutManager lm, int ipd,
  246. int displayAlign) {
  247. this.lm = lm;
  248. this.ipd = ipd;
  249. this.displayAlign = displayAlign;
  250. }
  251. /** {@inheritDoc} */
  252. protected void observeElementList(List elementList) {
  253. String elementListID = getStaticContentFO().getFlowName();
  254. String pageSequenceID = ((PageSequence)lm.getParent().getFObj()).getId();
  255. if (pageSequenceID != null && pageSequenceID.length() > 0) {
  256. elementListID += "-" + pageSequenceID;
  257. }
  258. ElementListObserver.observe(elementList, "static-content", elementListID);
  259. }
  260. /** {@inheritDoc} */
  261. protected boolean isPartOverflowRecoveryActivated() {
  262. //For side regions, this must be disabled because of wanted overflow.
  263. return false;
  264. }
  265. public boolean isOverflow() {
  266. return (this.overflow != 0);
  267. }
  268. public int getOverflowAmount() {
  269. return this.overflow;
  270. }
  271. /** {@inheritDoc} */
  272. protected PageBreakingLayoutListener createLayoutListener() {
  273. return new PageBreakingLayoutListener() {
  274. public void notifyOverflow(int part, int amount, FObj obj) {
  275. if (StaticContentBreaker.this.overflow == 0) {
  276. StaticContentBreaker.this.overflow = amount;
  277. }
  278. }
  279. };
  280. }
  281. protected LayoutManager getTopLevelLM() {
  282. return lm;
  283. }
  284. protected LayoutContext createLayoutContext() {
  285. LayoutContext lc = super.createLayoutContext();
  286. lc.setRefIPD(ipd);
  287. return lc;
  288. }
  289. protected List getNextKnuthElements(LayoutContext context, int alignment) {
  290. LayoutManager curLM; // currently active LM
  291. List returnList = new LinkedList();
  292. while ((curLM = getChildLM()) != null) {
  293. LayoutContext childLC = new LayoutContext(0);
  294. childLC.setStackLimitBP(context.getStackLimitBP());
  295. childLC.setRefIPD(context.getRefIPD());
  296. childLC.setWritingMode(context.getWritingMode());
  297. List returnedList = null;
  298. //The following is a HACK! Ignore leading and trailing white space
  299. boolean ignore = curLM instanceof TextLayoutManager;
  300. if (!curLM.isFinished()) {
  301. returnedList = curLM.getNextKnuthElements(childLC, alignment);
  302. }
  303. if (returnedList != null && !ignore) {
  304. lm.wrapPositionElements(returnedList, returnList);
  305. }
  306. }
  307. SpaceResolver.resolveElementList(returnList);
  308. setFinished(true);
  309. return returnList;
  310. }
  311. protected int getCurrentDisplayAlign() {
  312. return displayAlign;
  313. }
  314. protected boolean hasMoreContent() {
  315. return !lm.isFinished();
  316. }
  317. protected void addAreas(PositionIterator posIter, LayoutContext context) {
  318. AreaAdditionUtil.addAreas(lm, posIter, context);
  319. }
  320. protected void doPhase3(PageBreakingAlgorithm alg, int partCount,
  321. BlockSequence originalList, BlockSequence effectiveList) {
  322. if (partCount > 1) {
  323. PageBreakPosition pos = (PageBreakPosition)alg.getPageBreaks().getFirst();
  324. int firstPartLength = ElementListUtils.calcContentLength(effectiveList,
  325. effectiveList.ignoreAtStart, pos.getLeafPos());
  326. overflow += alg.totalWidth - firstPartLength;
  327. }
  328. //Rendering all parts (not just the first) at once for the case where the parts that
  329. //overflow should be visible.
  330. alg.removeAllPageBreaks();
  331. //Directly add areas after finding the breaks
  332. this.addAreas(alg, 1, originalList, effectiveList);
  333. }
  334. protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) {
  335. //nop for static content
  336. }
  337. protected LayoutManager getCurrentChildLM() {
  338. return null; //TODO NYI
  339. }
  340. }
  341. /**
  342. * Returns the IPD of the content area
  343. * @return the IPD of the content area
  344. */
  345. public int getContentAreaIPD() {
  346. return contentAreaIPD;
  347. }
  348. /** {@inheritDoc} */
  349. protected void setContentAreaIPD(int contentAreaIPD) {
  350. this.contentAreaIPD = contentAreaIPD;
  351. }
  352. /**
  353. * Returns the BPD of the content area
  354. * @return the BPD of the content area
  355. */
  356. public int getContentAreaBPD() {
  357. return contentAreaBPD;
  358. }
  359. private void setContentAreaBPD(int contentAreaBPD) {
  360. this.contentAreaBPD = contentAreaBPD;
  361. }
  362. /** {@inheritDoc} */
  363. public Keep getKeepTogether() {
  364. return Keep.KEEP_AUTO;
  365. }
  366. /** {@inheritDoc} */
  367. public Keep getKeepWithNext() {
  368. return Keep.KEEP_AUTO;
  369. }
  370. /** {@inheritDoc} */
  371. public Keep getKeepWithPrevious() {
  372. return Keep.KEEP_AUTO;
  373. }
  374. }