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.

AbstractPageSequenceLayoutManager.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  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.List;
  20. import org.apache.commons.logging.Log;
  21. import org.apache.commons.logging.LogFactory;
  22. import org.apache.fop.area.AreaTreeHandler;
  23. import org.apache.fop.area.AreaTreeModel;
  24. import org.apache.fop.area.IDTracker;
  25. import org.apache.fop.area.PageViewport;
  26. import org.apache.fop.area.Resolvable;
  27. import org.apache.fop.datatypes.Numeric;
  28. import org.apache.fop.fo.Constants;
  29. import org.apache.fop.fo.flow.Marker;
  30. import org.apache.fop.fo.flow.RetrieveMarker;
  31. import org.apache.fop.fo.pagination.AbstractPageSequence;
  32. /**
  33. * Abstract base class for a page sequence layout manager.
  34. */
  35. public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutManager
  36. implements TopLevelLayoutManager {
  37. private static Log log = LogFactory.getLog(AbstractPageSequenceLayoutManager.class);
  38. /**
  39. * AreaTreeHandler which activates the PSLM and controls
  40. * the rendering of its pages.
  41. */
  42. protected AreaTreeHandler areaTreeHandler;
  43. /** ID tracker supplied by the AreaTreeHandler */
  44. protected IDTracker idTracker;
  45. /** page sequence formatting object being processed by this class */
  46. protected AbstractPageSequence pageSeq;
  47. /** Current page with page-viewport-area being filled by the PSLM. */
  48. protected Page curPage;
  49. /** the current page number */
  50. protected int currentPageNum;
  51. /** The stating page number */
  52. protected int startPageNum;
  53. /**
  54. * Constructor
  55. *
  56. * @param ath the area tree handler object
  57. * @param pseq fo:page-sequence to process
  58. */
  59. public AbstractPageSequenceLayoutManager(AreaTreeHandler ath, AbstractPageSequence pseq) {
  60. super(pseq);
  61. this.areaTreeHandler = ath;
  62. this.idTracker = ath.getIDTracker();
  63. this.pageSeq = pseq;
  64. }
  65. /**
  66. * @return the LayoutManagerMaker object associated to the areaTreeHandler
  67. */
  68. public LayoutManagerMaker getLayoutManagerMaker() {
  69. return areaTreeHandler.getLayoutManagerMaker();
  70. }
  71. /**
  72. * Provides access to the current page.
  73. * @return the current Page
  74. */
  75. public Page getCurrentPage() {
  76. return curPage;
  77. }
  78. /**
  79. * Provides access for setting the current page.
  80. * @param currentPage the new current Page
  81. */
  82. protected void setCurrentPage(Page currentPage) {
  83. this.curPage = currentPage;
  84. }
  85. /**
  86. * Provides access to the current page number
  87. * @return the current page number
  88. */
  89. protected int getCurrentPageNum() {
  90. return currentPageNum;
  91. }
  92. /** {@inheritDoc} */
  93. public void initialize() {
  94. startPageNum = pageSeq.getStartingPageNumber();
  95. currentPageNum = startPageNum - 1;
  96. curPage = null;
  97. }
  98. /**
  99. * This returns the first PageViewport that contains an id trait
  100. * matching the idref argument, or null if no such PV exists.
  101. *
  102. * @param idref the idref trait needing to be resolved
  103. * @return the first PageViewport that contains the ID trait
  104. */
  105. public PageViewport getFirstPVWithID(String idref) {
  106. List list = idTracker.getPageViewportsContainingID(idref);
  107. if (list != null && list.size() > 0) {
  108. return (PageViewport) list.get(0);
  109. }
  110. return null;
  111. }
  112. /**
  113. * This returns the last PageViewport that contains an id trait
  114. * matching the idref argument, or null if no such PV exists.
  115. *
  116. * @param idref the idref trait needing to be resolved
  117. * @return the last PageViewport that contains the ID trait
  118. */
  119. public PageViewport getLastPVWithID(String idref) {
  120. List list = idTracker.getPageViewportsContainingID(idref);
  121. if (list != null && list.size() > 0) {
  122. return (PageViewport) list.get(list.size() - 1);
  123. }
  124. return null;
  125. }
  126. /**
  127. * Add an ID reference to the current page.
  128. * When adding areas the area adds its ID reference.
  129. * For the page layout manager it adds the id reference
  130. * with the current page to the area tree.
  131. *
  132. * @param id the ID reference to add
  133. */
  134. public void addIDToPage(String id) {
  135. if (id != null && id.length() > 0) {
  136. idTracker.associateIDWithPageViewport(id, curPage.getPageViewport());
  137. }
  138. }
  139. /**
  140. * Add an id reference of the layout manager in the AreaTreeHandler,
  141. * if the id hasn't been resolved yet
  142. * @param id the id to track
  143. * @return a boolean indicating if the id has already been resolved
  144. * TODO Maybe give this a better name
  145. */
  146. public boolean associateLayoutManagerID(String id) {
  147. if (log.isDebugEnabled()) {
  148. log.debug("associateLayoutManagerID(" + id + ")");
  149. }
  150. if (!idTracker.alreadyResolvedID(id)) {
  151. idTracker.signalPendingID(id);
  152. return false;
  153. } else {
  154. return true;
  155. }
  156. }
  157. /**
  158. * Notify the areaTreeHandler that the LayoutManagers containing
  159. * idrefs have finished creating areas
  160. * @param id the id for which layout has finished
  161. */
  162. public void notifyEndOfLayout(String id) {
  163. idTracker.signalIDProcessed(id);
  164. }
  165. /**
  166. * Identify an unresolved area (one needing an idref to be
  167. * resolved, e.g. the internal-destination of an fo:basic-link)
  168. * for both the AreaTreeHandler and PageViewport object.
  169. *
  170. * The IDTracker keeps a document-wide list of idref's
  171. * and the PV's needing them to be resolved. It uses this to
  172. * send notifications to the PV's when an id has been resolved.
  173. *
  174. * The PageViewport keeps lists of id's needing resolving, along
  175. * with the child areas (page-number-citation, basic-link, etc.)
  176. * of the PV needing their resolution.
  177. *
  178. * @param id the ID reference to add
  179. * @param res the resolvable object that needs resolving
  180. */
  181. public void addUnresolvedArea(String id, Resolvable res) {
  182. curPage.getPageViewport().addUnresolvedIDRef(id, res);
  183. idTracker.addUnresolvedIDRef(id, curPage.getPageViewport());
  184. }
  185. /**
  186. * Bind the RetrieveMarker to the corresponding Marker subtree.
  187. * If the boundary is page then it will only check the
  188. * current page. For page-sequence and document it will
  189. * lookup preceding pages from the area tree and try to find
  190. * a marker.
  191. * If we retrieve a marker from a preceding page,
  192. * then the containing page does not have a qualifying area,
  193. * and all qualifying areas have ended.
  194. * Therefore we use last-ending-within-page (Constants.EN_LEWP)
  195. * as the position.
  196. *
  197. * @param rm the RetrieveMarker instance whose properties are to
  198. * used to find the matching Marker.
  199. * @return a bound RetrieveMarker instance, or null if no Marker
  200. * could be found.
  201. */
  202. public RetrieveMarker resolveRetrieveMarker(RetrieveMarker rm) {
  203. AreaTreeModel areaTreeModel = areaTreeHandler.getAreaTreeModel();
  204. String name = rm.getRetrieveClassName();
  205. int boundary = rm.getRetrieveBoundary();
  206. // get marker from the current markers on area tree
  207. Marker mark = getCurrentPV().resolveMarker(rm);
  208. if (mark == null && boundary != EN_PAGE) {
  209. // go back over pages until mark found
  210. // if document boundary then keep going
  211. boolean doc = (boundary == EN_DOCUMENT);
  212. int seq = areaTreeModel.getPageSequenceCount();
  213. int page = areaTreeModel.getPageCount(seq) - 1;
  214. while (page < 0 && doc && seq > 1) {
  215. seq--;
  216. page = areaTreeModel.getPageCount(seq) - 1;
  217. }
  218. while (page >= 0) {
  219. PageViewport pv = areaTreeModel.getPage(seq, page);
  220. int originalPosition = rm.getPosition();
  221. rm.changePositionTo(Constants.EN_LEWP);
  222. mark = pv.resolveMarker(rm);
  223. // this is probably not necessary since the RM will not be used again, but to be safe...
  224. rm.changePositionTo(originalPosition);
  225. if (mark != null) {
  226. break;
  227. }
  228. page--;
  229. if (page < 0 && doc && seq > 1) {
  230. seq--;
  231. page = areaTreeModel.getPageCount(seq) - 1;
  232. }
  233. }
  234. }
  235. if (mark == null) {
  236. log.debug("found no marker with name: " + name);
  237. return null;
  238. } else {
  239. rm.bindMarker(mark);
  240. return rm;
  241. }
  242. }
  243. /**
  244. * Creates and returns a new page.
  245. * @param pageNumber the page number
  246. * @param isBlank true if it's a blank page
  247. * @return the newly created page
  248. */
  249. protected abstract Page createPage(int pageNumber, boolean isBlank);
  250. /**
  251. * Makes a new page
  252. *
  253. * @param isBlank whether this page is blank or not
  254. * @return a new page
  255. */
  256. protected Page makeNewPage(boolean isBlank) {
  257. if (curPage != null) {
  258. finishPage();
  259. }
  260. currentPageNum++;
  261. curPage = createPage(currentPageNum, isBlank);
  262. if (log.isDebugEnabled()) {
  263. log.debug("[" + curPage.getPageViewport().getPageNumberString()
  264. + (isBlank ? "*" : "") + "]");
  265. }
  266. addIDToPage(pageSeq.getRoot().getId());
  267. addIDToPage(pageSeq.getId());
  268. return curPage;
  269. }
  270. /**
  271. * Finishes a page in preparation for a new page.
  272. */
  273. protected void finishPage() {
  274. if (log.isTraceEnabled()) {
  275. curPage.getPageViewport().dumpMarkers();
  276. }
  277. // Try to resolve any unresolved IDs for the current page.
  278. //
  279. idTracker.tryIDResolution(curPage.getPageViewport());
  280. // Queue for ID resolution and rendering
  281. areaTreeHandler.getAreaTreeModel().addPage(curPage.getPageViewport());
  282. if (log.isDebugEnabled()) {
  283. log.debug("page finished: " + curPage.getPageViewport().getPageNumberString()
  284. + ", current num: " + currentPageNum);
  285. }
  286. curPage = null;
  287. }
  288. /** {@inheritDoc} */
  289. public void doForcePageCount(Numeric nextPageSeqInitialPageNumber) {
  290. int forcePageCount = pageSeq.getForcePageCount();
  291. // xsl-spec version 1.0 (15.oct 2001)
  292. // auto | even | odd | end-on-even | end-on-odd | no-force | inherit
  293. // auto:
  294. // Force the last page in this page-sequence to be an odd-page
  295. // if the initial-page-number of the next page-sequence is even.
  296. // Force it to be an even-page
  297. // if the initial-page-number of the next page-sequence is odd.
  298. // If there is no next page-sequence
  299. // or if the value of its initial-page-number is "auto" do not force any page.
  300. // if force-page-count is auto then set the value of forcePageCount
  301. // depending on the initial-page-number of the next page-sequence
  302. if (nextPageSeqInitialPageNumber != null && forcePageCount == Constants.EN_AUTO) {
  303. if (nextPageSeqInitialPageNumber.getEnum() != 0) {
  304. // auto | auto-odd | auto-even
  305. int nextPageSeqPageNumberType = nextPageSeqInitialPageNumber.getEnum();
  306. if (nextPageSeqPageNumberType == Constants.EN_AUTO_ODD) {
  307. forcePageCount = Constants.EN_END_ON_EVEN;
  308. } else if (nextPageSeqPageNumberType == Constants.EN_AUTO_EVEN) {
  309. forcePageCount = Constants.EN_END_ON_ODD;
  310. } else { // auto
  311. forcePageCount = Constants.EN_NO_FORCE;
  312. }
  313. } else { // <integer> for explicit page number
  314. int nextPageSeqPageStart = nextPageSeqInitialPageNumber.getValue();
  315. // spec rule
  316. nextPageSeqPageStart = (nextPageSeqPageStart > 0) ? nextPageSeqPageStart : 1;
  317. if (nextPageSeqPageStart % 2 == 0) { // explicit even startnumber
  318. forcePageCount = Constants.EN_END_ON_ODD;
  319. } else { // explicit odd startnumber
  320. forcePageCount = Constants.EN_END_ON_EVEN;
  321. }
  322. }
  323. }
  324. if (forcePageCount == Constants.EN_EVEN) {
  325. if ((currentPageNum - startPageNum + 1) % 2 != 0) { // we have an odd number of pages
  326. curPage = makeNewPage(true);
  327. }
  328. } else if (forcePageCount == Constants.EN_ODD) {
  329. if ((currentPageNum - startPageNum + 1) % 2 == 0) { // we have an even number of pages
  330. curPage = makeNewPage(true);
  331. }
  332. } else if (forcePageCount == Constants.EN_END_ON_EVEN) {
  333. if (currentPageNum % 2 != 0) { // we are now on an odd page
  334. curPage = makeNewPage(true);
  335. }
  336. } else if (forcePageCount == Constants.EN_END_ON_ODD) {
  337. if (currentPageNum % 2 == 0) { // we are now on an even page
  338. curPage = makeNewPage(true);
  339. }
  340. } /* else if (forcePageCount == Constants.EN_NO_FORCE) {
  341. // i hope: nothing special at all
  342. } */
  343. if (curPage != null) {
  344. finishPage();
  345. }
  346. while (forcePageCount != Constants.EN_NO_FORCE && getCurrentPageNum() < getLastPageNumber()) {
  347. curPage = makeNewPage(true);
  348. finishPage();
  349. }
  350. }
  351. /** {@inheritDoc} */
  352. public void reset() {
  353. throw new IllegalStateException();
  354. }
  355. protected int getLastPageNumber() {
  356. return currentPageNum;
  357. }
  358. }