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.

PageLayoutManager.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. /*
  2. * $Id$
  3. * Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
  4. * For details on use and redistribution please refer to the
  5. * LICENSE file included with these sources.
  6. */
  7. package org.apache.fop.layoutmgr;
  8. import org.apache.fop.apps.FOPException;
  9. import org.apache.fop.area.*;
  10. import org.apache.fop.fo.flow.StaticContent;
  11. import org.apache.fop.fo.pagination.PageSequence;
  12. import org.apache.fop.fo.pagination.Region;
  13. import org.apache.fop.fo.pagination.SimplePageMaster;
  14. import org.apache.fop.fo.properties.Constants;
  15. import java.util.ArrayList;
  16. import java.util.List;
  17. /**
  18. * LayoutManager for a PageSequence and its flow.
  19. * It manages all page-related layout.
  20. */
  21. public class PageLayoutManager extends AbstractLayoutManager implements Runnable {
  22. private static class BlockBreakPosition extends LeafPosition {
  23. BreakPoss breakps;
  24. BlockBreakPosition(LayoutManager lm, BreakPoss bp) {
  25. super(lm, 0);
  26. breakps = bp;
  27. }
  28. }
  29. /** True if haven't yet laid out any pages.*/
  30. private boolean bFirstPage;
  31. /** Current page being worked on. */
  32. private PageViewport curPage;
  33. /** Body region of the current page */
  34. private BodyRegion curBody;
  35. /** Current span being filled */
  36. private Span curSpan;
  37. /** Number of columns in current span area. */
  38. private int curSpanColumns;
  39. /** Current flow-reference-area (column) being filled. */
  40. private Flow curFlow;
  41. private int flowBPD = 0;
  42. private int flowIPD = 0;
  43. /** Manager which handles a queue of all pages which are completely
  44. * laid out and ready for rendering, except for resolution of ID
  45. * references?
  46. */
  47. private AreaTree areaTree;
  48. private PageSequence pageSequence;
  49. private int pageCount = 0;
  50. /**
  51. * This is the top level layout manager.
  52. * It is created by the PageSequence FO.
  53. */
  54. public PageLayoutManager(AreaTree areaTree, PageSequence pageseq) {
  55. super(pageseq);
  56. this.areaTree = areaTree;
  57. pageSequence = pageseq;
  58. }
  59. /**
  60. * The layout process is designed to be able to be run in a thread.
  61. * In theory it can run at the same
  62. * time as FO tree generation, once the layout-master-set has been read.
  63. * We can arrange it so that the iterator over the fobj children waits
  64. * until the next child is available.
  65. * As it produces pages, it adds them to the AreaTree, where the
  66. * rendering process can also run in a parallel thread.
  67. */
  68. public void run() {
  69. doLayout();
  70. flush();
  71. }
  72. public void doLayout() {
  73. // this should be done another way
  74. makeNewPage(false, false);
  75. createBodyMainReferenceArea();
  76. createSpan(1);
  77. flowIPD = curFlow.getIPD();
  78. BreakPoss bp;
  79. LayoutContext childLC = new LayoutContext(0);
  80. while (!isFinished()) {
  81. pageCount++;
  82. if ((bp = getNextBreakPoss(childLC)) != null) {
  83. addAreas((BlockBreakPosition)bp.getPosition());
  84. // add static areas and resolve any new id areas
  85. // finish page and add to area tree
  86. finishPage();
  87. }
  88. }
  89. }
  90. public BreakPoss getNextBreakPoss(LayoutContext context) {
  91. LayoutManager curLM ; // currently active LM
  92. while ((curLM = getChildLM()) != null) {
  93. BreakPoss bp = null;
  94. LayoutContext childLC = new LayoutContext(0);
  95. childLC.setStackLimit(new MinOptMax(flowBPD));
  96. childLC.setRefIPD(flowIPD);
  97. if (!curLM.isFinished()) {
  98. bp = curLM.getNextBreakPoss(childLC);
  99. }
  100. if(bp != null) {
  101. return new BreakPoss(
  102. new BlockBreakPosition(curLM, bp));
  103. }
  104. }
  105. setFinished(true);
  106. return null;
  107. }
  108. public String getCurrentPageNumber() {
  109. return "" + pageCount;
  110. }
  111. public PageViewport resolveRefID(String ref) {
  112. List list = areaTree.getIDReferences(ref);
  113. if(list != null && list.size() > 0) {
  114. return (PageViewport)list.get(0);
  115. }
  116. return null;
  117. }
  118. public void addAreas(BlockBreakPosition bbp) {
  119. List list = new ArrayList();
  120. list.add(bbp.breakps);
  121. bbp.getLM().addAreas( new BreakPossPosIter(list, 0,
  122. 1), null);
  123. }
  124. public void addIDToPage(String id) {
  125. areaTree.addIDRef(id, curPage);
  126. }
  127. public void addUnresolvedArea(String id, Resolveable res) {
  128. // add unresolved to tree
  129. // should really add to the page viewport so it can serialize
  130. areaTree.addUnresolvedID(id, res);
  131. }
  132. /**
  133. * For now, only handle normal flow areas.
  134. */
  135. public boolean addChild(Area childArea) {
  136. if (childArea == null)
  137. return false;
  138. if (childArea.getAreaClass() == Area.CLASS_NORMAL) {
  139. return placeFlowRefArea(childArea);
  140. } else
  141. ; // TODO: all the others!
  142. return false;
  143. }
  144. /**
  145. * Place a FlowReferenceArea into the current span. The FlowLM is
  146. * responsible for making sure that it will actually fit in the
  147. * current span area. In fact the area has already been added to the
  148. * current span, so we are just checking to see if the span is "full",
  149. * possibly moving to the next column or to the next page.
  150. */
  151. protected boolean placeFlowRefArea(Area area) {
  152. // assert (curSpan != null);
  153. // assert (area == curFlow);
  154. // assert (curFlow == curSpan.getFlow(curSpan.getColumnCount()-1));
  155. // assert (area.getBPD().min < curSpan.getHeight());
  156. // Last column on this page is filled
  157. // See if the flow is full. The Flow LM can add an area before
  158. // it's full in the case of a break or a span.
  159. // Also in the case of a float to be placed. In that case, there
  160. // may be further material added later.
  161. // The Flow LM sets the "finished" flag on the Flow Area if it has
  162. // completely filled it. In this case, if on the last column
  163. // end the page.
  164. getParentArea(area);
  165. // Alternatively the child LM indicates to parent that it's full?
  166. //System.out.println("size: " + area.getAllocationBPD().max +
  167. // ":" + curSpan.getMaxBPD().min);
  168. if (area.getAllocationBPD().max >= curSpan.getMaxBPD().min) {
  169. // Consider it filled
  170. if (curSpan.getColumnCount() == curSpanColumns) {
  171. finishPage();
  172. return true;
  173. } else
  174. curFlow = null; // Create new flow on next getParentArea()
  175. }
  176. return false;
  177. }
  178. protected void placeAbsoluteArea(Area area) {
  179. }
  180. protected void placeBeforeFloat(Area area) {
  181. }
  182. protected void placeSideFloat(Area area) {
  183. }
  184. protected void placeFootnote(Area area) {
  185. // After doing this, reduce available space on the curSpan.
  186. // This has to be propagated to the curFlow (FlowLM) so that
  187. // it can adjust its limit for composition (or it just asks
  188. // curSpan for BPD before doing the break?)
  189. // If multi-column, we may have to balance to find more space
  190. // for a float. When?
  191. }
  192. private PageViewport makeNewPage(boolean bIsBlank, boolean bIsLast) {
  193. finishPage();
  194. try {
  195. curPage = pageSequence.createPage(bIsBlank, bIsLast);
  196. } catch (FOPException fopex) { /* ???? */
  197. fopex.printStackTrace();
  198. }
  199. curPage.setPageNumber(getCurrentPageNumber());
  200. RegionViewport reg = curPage.getPage().getRegion(
  201. RegionReference.BODY);
  202. curBody = (BodyRegion) reg.getRegion();
  203. flowBPD = (int)reg.getViewArea().getHeight();
  204. return curPage;
  205. }
  206. private void layoutStaticContent(Region region, int regionClass) {
  207. if (region != null ) {
  208. StaticContent flow = pageSequence
  209. .getStaticContent(region.getRegionName());
  210. if (flow != null) {
  211. RegionViewport reg = curPage.getPage()
  212. .getRegion(regionClass);
  213. reg.getRegion().setIPD((int)reg.getViewArea().getWidth());
  214. if (reg == null ) {
  215. System.out.println("no region viewport: shouldn't happen");
  216. }
  217. StaticContentLayoutManager lm = flow.getLayoutManager();
  218. lm.init();
  219. lm.setRegionReference(reg.getRegion());
  220. lm.setParentLM(this);
  221. LayoutContext childLC = new LayoutContext(0);
  222. childLC.setStackLimit(new MinOptMax((int)curPage.getViewArea().getHeight()));
  223. childLC.setRefIPD((int)reg.getViewArea().getWidth());
  224. while (!lm.isFinished()) {
  225. BreakPoss bp = lm.getNextBreakPoss(childLC);
  226. if (bp != null) {
  227. ArrayList vecBreakPoss = new ArrayList();
  228. vecBreakPoss.add(bp);
  229. lm.addAreas( new BreakPossPosIter(vecBreakPoss, 0,
  230. vecBreakPoss.size()), null);
  231. } else {
  232. System.out.println("bp==null cls="+regionClass);
  233. }
  234. }
  235. //lm.flush();
  236. lm.reset(null);
  237. }
  238. }
  239. }
  240. private void finishPage() {
  241. if (curPage != null) {
  242. // Layout static content into the regions
  243. // Need help from pageseq for this
  244. SimplePageMaster spm = pageSequence.getCurrentSimplePageMaster();
  245. layoutStaticContent(spm.getRegion(Region.BEFORE), RegionReference.BEFORE);
  246. layoutStaticContent(spm.getRegion(Region.AFTER), RegionReference.AFTER);
  247. layoutStaticContent(spm.getRegion(Region.START), RegionReference.START);
  248. layoutStaticContent(spm.getRegion(Region.END), RegionReference.END);
  249. // Queue for ID resolution and rendering
  250. areaTree.addPage(curPage);
  251. curPage = null;
  252. curBody = null;
  253. curSpan = null;
  254. curFlow = null;
  255. }
  256. }
  257. /**
  258. * This is called from FlowLayoutManager when it needs to start
  259. * a new flow container (while generating areas).
  260. * @param area The area for which a container is needed. It must be
  261. * some kind of block-level area. It must have area-class, break-before
  262. * and span properties set.
  263. */
  264. public Area getParentArea(Area childArea) {
  265. int aclass = childArea.getAreaClass();
  266. if (aclass == Area.CLASS_NORMAL) {
  267. // TODO: how to get properties from the Area???
  268. // Need span, break
  269. int breakVal = Constants.AUTO; // childArea.getBreakBefore();
  270. if (breakVal != Constants.AUTO) {
  271. // We may be forced to make new page
  272. handleBreak(breakVal);
  273. } else if (curPage == null) {
  274. makeNewPage(false, false);
  275. }
  276. // Now we should be on the right kind of page
  277. boolean bNeedSpan = false;
  278. int span = Constants.NONE; // childArea.getSpan()
  279. int numCols = 1;
  280. if (span == Constants.ALL) {
  281. // Assume the number of columns is stored on the curBody object.
  282. //numCols = curBody.getProperty(NUMBER_OF_COLUMNS);
  283. }
  284. if (curSpan == null) {
  285. createBodyMainReferenceArea();
  286. bNeedSpan = true;
  287. } else if (numCols != curSpanColumns) {
  288. // TODO: BALANCE EXISTING COLUMNS
  289. if (curSpanColumns > 1) {
  290. // balanceColumns();
  291. }
  292. bNeedSpan = true;
  293. }
  294. if (bNeedSpan) {
  295. // Make a new span and the first flow
  296. createSpan(numCols);
  297. } else if (curFlow == null) {
  298. createFlow();
  299. }
  300. return curFlow;
  301. } else {
  302. if (curPage == null) {
  303. makeNewPage(false, false);
  304. }
  305. // Now handle different kinds of areas
  306. if (aclass == Area.CLASS_BEFORE_FLOAT) {
  307. BeforeFloat bf = curBody.getBeforeFloat();
  308. if (bf == null) {
  309. bf = new BeforeFloat();
  310. curBody.setBeforeFloat(bf);
  311. }
  312. return bf;
  313. } else if (aclass == Area.CLASS_FOOTNOTE) {
  314. Footnote fn = curBody.getFootnote();
  315. if (fn == null) {
  316. fn = new Footnote();
  317. curBody.setFootnote(fn);
  318. }
  319. return fn;
  320. }
  321. // TODO!!! other area classes (side-float, absolute, fixed)
  322. return null;
  323. }
  324. }
  325. /**
  326. * Depending on the kind of break condition, make new column
  327. * or page. May need to make an empty page if next page would
  328. * not have the desired "handedness".
  329. */
  330. protected void handleBreak(int breakVal) {
  331. if (breakVal == Constants.COLUMN) {
  332. if (curSpan != null &&
  333. curSpan.getColumnCount() != curSpanColumns) {
  334. // Move to next column
  335. createFlow();
  336. return;
  337. }
  338. // else need new page
  339. breakVal = Constants.PAGE;
  340. }
  341. if (needEmptyPage(breakVal)) {
  342. curPage = makeNewPage(true, false);
  343. }
  344. if (needNewPage(breakVal)) {
  345. curPage = makeNewPage(false, false);
  346. }
  347. }
  348. /**
  349. * If we have already started to layout content on a page,
  350. * and there is a forced break, see if we need to generate
  351. * an empty page.
  352. * Note that if not all content is placed, we aren't sure whether
  353. * it will flow onto another page or not, so we'd probably better
  354. * block until the queue of layoutable stuff is empty!
  355. */
  356. private boolean needEmptyPage(int breakValue) {
  357. return false;
  358. // if (breakValue == Constants.PAGE || curPage.isEmpty()) {
  359. // // any page is OK or we already have an empty page
  360. // return false;
  361. // }
  362. // else {
  363. // /* IF we are on the kind of page we need, we'll need a new page. */
  364. // if (curPage.getPageNumber()%2 != 0) {
  365. // // Current page is odd
  366. // return (breakValue == Constants.ODD_PAGE);
  367. // }
  368. // else {
  369. // return (breakValue == Constants.EVEN_PAGE);
  370. // }
  371. // }
  372. }
  373. /**
  374. * See if need to generate a new page for a forced break condition.
  375. * TODO: methods to see if the current page is empty and to get
  376. * its number.
  377. */
  378. private boolean needNewPage(int breakValue) {
  379. return false;
  380. // if (curPage.isEmpty()) {
  381. // if (breakValue == Constants.PAGE) {
  382. // return false;
  383. // }
  384. // else if (curPage.getPageNumber()%2 != 0) {
  385. // // Current page is odd
  386. // return (breakValue == Constants.EVEN_PAGE);
  387. // }
  388. // else {
  389. // return (breakValue == Constants.ODD_PAGE);
  390. // }
  391. // }
  392. // else {
  393. // return true;
  394. // }
  395. }
  396. private void createBodyMainReferenceArea() {
  397. curBody.setMainReference(new MainReference());
  398. }
  399. private Flow createFlow() {
  400. curFlow = new Flow();
  401. curFlow.setIPD(curSpan.getIPD()); // adjust for columns
  402. //curFlow.setBPD(100000);
  403. // Set IPD and max BPD on the curFlow from curBody
  404. curSpan.addFlow(curFlow);
  405. return curFlow;
  406. }
  407. private void createSpan(int numCols) {
  408. // check number of columns (= all in Body or 1)
  409. // If already have a span, get its size and position (as MinMaxOpt)
  410. // This determines the position of the new span area
  411. // Attention: space calculation between the span areas.
  412. // MinOptMax newpos ;
  413. // if (curSpan != null) {
  414. // newpos = curSpan.getPosition(BPD);
  415. // newpos.add(curSpan.getDimension(BPD));
  416. // }
  417. // else newpos = new MinOptMax();
  418. curSpan = new Span(numCols);
  419. // get Width or Height as IPD for span
  420. curSpan.setIPD( (int) curPage.getPage(). getRegion(
  421. RegionReference.BODY).getViewArea().getWidth());
  422. //curSpan.setPosition(BPD, newpos);
  423. curBody.getMainReference().addSpan(curSpan);
  424. createFlow();
  425. }
  426. // See finishPage...
  427. protected boolean flush() {
  428. finishPage();
  429. return false;
  430. }
  431. }