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.

PageSequence.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  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.fo.pagination;
  8. // FOP
  9. import org.apache.fop.fo.*;
  10. import org.apache.fop.fo.properties.*;
  11. import org.apache.fop.fo.flow.Flow;
  12. import org.apache.fop.fo.flow.StaticContent;
  13. import org.apache.fop.layout.Area;
  14. import org.apache.fop.layout.AreaContainer;
  15. import org.apache.fop.layout.BodyAreaContainer;
  16. import org.apache.fop.layout.AreaTree;
  17. import org.apache.fop.layout.Page;
  18. import org.apache.fop.layout.PageMaster;
  19. import org.apache.fop.apps.FOPException;
  20. // Java
  21. import java.util.HashMap;
  22. import java.util.ArrayList;
  23. /**
  24. * Class modeling the fo:page-sequence object. Provides pagination of flows.
  25. * Much of the logic for paginating flows is contained in this class. The main
  26. * entry point is the format method.
  27. *
  28. * @see <a href="@XSLFO-STD@#fo_page-sequence" target="_xslfostd">@XSLFO-STDID@
  29. * &para;6.4.5</a>
  30. */
  31. public class PageSequence extends FObj {
  32. //
  33. // Factory methods
  34. //
  35. public static class Maker extends FObj.Maker {
  36. public FObj make(FObj parent,
  37. PropertyList propertyList) throws FOPException {
  38. return new PageSequence(parent, propertyList);
  39. }
  40. }
  41. public static FObj.Maker maker() {
  42. return new PageSequence.Maker();
  43. }
  44. //
  45. // intial-page-number types
  46. //
  47. private static final int EXPLICIT = 0;
  48. private static final int AUTO = 1;
  49. private static final int AUTO_EVEN = 2;
  50. private static final int AUTO_ODD = 3;
  51. /**
  52. * The parent root object
  53. */
  54. private Root root;
  55. /**
  56. * the set of layout masters (provided by the root object)
  57. */
  58. private LayoutMasterSet layoutMasterSet;
  59. // There doesn't seem to be anything in the spec requiring flows
  60. // to be in the order given, only that they map to the regions
  61. // defined in the page sequence, so all we need is this one hashtable
  62. // the set of flows includes StaticContent flows also
  63. /**
  64. * Map of flows to their flow name (flow-name, Flow)
  65. * Does only contain flows for static content!
  66. */
  67. private HashMap flowMap;
  68. // according to communication from Paul Grosso (XSL-List,
  69. // 001228, Number 406), confusion in spec section 6.4.5 about
  70. // multiplicity of fo:flow in XSL 1.0 is cleared up - one (1)
  71. // fo:flow per fo:page-sequence only.
  72. private Flow flow = null;
  73. /**
  74. * the "master-reference" attribute,
  75. * which specifies the name of the page-sequence-master or
  76. * page-master to be used to create pages in the sequence
  77. */
  78. private String masterName;
  79. // page number and related formatting variables
  80. private int firstPageNumber = 0;
  81. private PageNumberGenerator pageNumberGenerator;
  82. private int pageCount = 0;
  83. private int currentPageNumber;
  84. /**
  85. * specifies page numbering type (auto|auto-even|auto-odd|explicit)
  86. */
  87. private int pageNumberType;
  88. private int forcePageCountType;
  89. /**
  90. * the current page master
  91. */
  92. private SimplePageMaster currentSimplePageMaster;
  93. private PageSequenceMaster pageSequenceMaster;
  94. protected PageSequence(FObj parent,
  95. PropertyList propertyList) throws FOPException {
  96. super(parent, propertyList);
  97. if (parent.getName().equals("fo:root")) {
  98. this.root = (Root)parent;
  99. }
  100. else {
  101. throw new FOPException("page-sequence must be child of root, not "
  102. + parent.getName());
  103. }
  104. layoutMasterSet = root.getLayoutMasterSet();
  105. // best time to run some checks on LayoutMasterSet
  106. layoutMasterSet.checkRegionNames();
  107. flowMap = new HashMap();
  108. String ipnValue = this.properties.get("initial-page-number").getString();
  109. if (ipnValue.equals("auto")) {
  110. pageNumberType = AUTO;
  111. this.firstPageNumber = 1;
  112. } else if (ipnValue.equals("auto-even")) {
  113. pageNumberType = AUTO_EVEN;
  114. this.firstPageNumber = 2;
  115. } else if (ipnValue.equals("auto-odd")) {
  116. pageNumberType = AUTO_ODD;
  117. this.firstPageNumber = 1;
  118. } else {
  119. pageNumberType = EXPLICIT;
  120. try {
  121. int pageStart = new Integer(ipnValue).intValue();
  122. this.firstPageNumber = (pageStart > 0) ? pageStart : 1;
  123. } catch (NumberFormatException nfe) {
  124. throw new FOPException("The value '" + ipnValue
  125. + "' is not valid for initial-page-number");
  126. }
  127. }
  128. masterName = this.properties.get("master-reference").getString();
  129. // get the 'format' properties
  130. this.pageNumberGenerator =
  131. new PageNumberGenerator(this.properties.get("format").getString(),
  132. this.properties.get("grouping-separator").getCharacter(),
  133. this.properties.get("grouping-size").getNumber().intValue(),
  134. this.properties.get("letter-value").getEnum());
  135. this.forcePageCountType =
  136. this.properties.get("force-page-count").getEnum();
  137. // this.properties.get("country");
  138. // this.properties.get("language");
  139. // this.properties.get("id");
  140. }
  141. public String getName() {
  142. return "fo:page-sequence";
  143. }
  144. public void addFlow(Flow flow) throws FOPException {
  145. if (this.flow!=null) {
  146. throw new FOPException("Only a single fo:flow permitted per fo:page-sequence");
  147. }
  148. if (flowMap.containsKey(flow.getFlowName())) {
  149. throw new FOPException("flow-names must be unique within an fo:page-sequence");
  150. }
  151. this.flow = flow;
  152. }
  153. public void addStaticContent(StaticContent staticContent) throws FOPException {
  154. if (this.flow!=null) {
  155. throw new FOPException("Static content ('"
  156. + staticContent.getFlowName()
  157. + "') is not allowed after fo:flow");
  158. }
  159. if (flowMap.containsKey(staticContent.getFlowName())) {
  160. throw new FOPException("flow-names must be unique within an fo:page-sequence");
  161. }
  162. if (!this.layoutMasterSet.regionNameExists(staticContent.getFlowName())) {
  163. log.error("region-name '"
  164. + staticContent.getFlowName()
  165. + "' doesn't exist in the layout-master-set.");
  166. }
  167. flowMap.put(staticContent.getFlowName(), staticContent);
  168. }
  169. /**
  170. * Runs the formatting of this page sequence into the given area tree
  171. */
  172. public void format(AreaTree areaTree) throws FOPException {
  173. if (flow == null) {
  174. throw new FOPException("No flow in page-sequence");
  175. }
  176. PageSequence previousPageSequence=this.root.getPageSequence();
  177. if( previousPageSequence!=null ) {
  178. if (previousPageSequence.forcePageCountType == ForcePageCount.AUTO) {
  179. if (pageNumberType == AUTO_ODD) {
  180. if (previousPageSequence.currentPageNumber % 2 == 0) {
  181. previousPageSequence.makePage(areaTree,true,null);
  182. }
  183. currentPageNumber = previousPageSequence.currentPageNumber;
  184. } else if (pageNumberType == AUTO_EVEN) {
  185. if (previousPageSequence.currentPageNumber % 2 == 1) {
  186. previousPageSequence.makePage(areaTree,true,null);
  187. }
  188. currentPageNumber = previousPageSequence.currentPageNumber;
  189. } else if (pageNumberType == EXPLICIT){
  190. if ((previousPageSequence.currentPageNumber % 2)
  191. != (firstPageNumber % 2)) {
  192. previousPageSequence.makePage(areaTree,true,null);
  193. }
  194. currentPageNumber = firstPageNumber;
  195. } else {
  196. currentPageNumber = previousPageSequence.currentPageNumber;
  197. }
  198. } else {
  199. currentPageNumber = previousPageSequence.currentPageNumber;
  200. if (pageNumberType == AUTO_ODD) {
  201. if (currentPageNumber % 2 == 0) {
  202. currentPageNumber++;
  203. }
  204. } else if (pageNumberType == AUTO_EVEN) {
  205. if (currentPageNumber % 2 == 1) {
  206. currentPageNumber++;
  207. }
  208. } else if (pageNumberType == EXPLICIT){
  209. currentPageNumber = firstPageNumber;
  210. }
  211. }
  212. } else {
  213. currentPageNumber = firstPageNumber;
  214. }
  215. previousPageSequence = null;
  216. this.root.setPageSequence(this);
  217. this.currentSimplePageMaster =
  218. this.layoutMasterSet.getSimplePageMaster(masterName);
  219. if (this.currentSimplePageMaster==null) {
  220. this.pageSequenceMaster =
  221. this.layoutMasterSet.getPageSequenceMaster(masterName);
  222. if (this.pageSequenceMaster==null) {
  223. throw new FOPException("master-reference '" + masterName
  224. + "' for fo:page-sequence matches no simple-page-master or page-sequence-master");
  225. }
  226. pageSequenceMaster.reset();
  227. } else {
  228. Region region = currentSimplePageMaster
  229. .getRegion(RegionBody.REGION_CLASS);
  230. if (!flow.getFlowName().equals(region.getRegionName())) {
  231. throw new FOPException("Flow '" + flow.getFlowName()
  232. + "' does not map to the region-body in page-master '"
  233. + currentSimplePageMaster.getMasterName() + "'");
  234. }
  235. }
  236. // make pages and layout content
  237. Status status = new Status(Status.OK);
  238. Page currentPage = null;
  239. do {
  240. boolean isBlankPage = false;
  241. // for this calculation we are already on the
  242. // blank page
  243. if (status.getCode() == Status.FORCE_PAGE_BREAK_EVEN) {
  244. if ((currentPageNumber % 2) == 1) {
  245. isBlankPage = true;
  246. }
  247. } else if (status.getCode() == Status.FORCE_PAGE_BREAK_ODD) {
  248. if ((currentPageNumber % 2) == 0) {
  249. isBlankPage = true;
  250. }
  251. }
  252. currentPage = makePage(areaTree, isBlankPage, currentPage);
  253. status = flow.getStatus();
  254. } while (status.isIncomplete());
  255. // handle cases of 'force-page-count' which do not depend
  256. // on the presence of a following page sequence
  257. if (this.forcePageCountType == ForcePageCount.EVEN) {
  258. if (this.pageCount % 2 != 0) {
  259. makePage(areaTree,true, null);
  260. }
  261. } else if (this.forcePageCountType == ForcePageCount.ODD) {
  262. if (this.pageCount % 2 != 1) {
  263. makePage(areaTree,true, null);
  264. }
  265. } else if (this.forcePageCountType == ForcePageCount.END_ON_EVEN) {
  266. if (this.currentPageNumber % 2 == 0) {
  267. makePage(areaTree,true, null);
  268. }
  269. } else if (this.forcePageCountType == ForcePageCount.END_ON_ODD) {
  270. if (this.currentPageNumber % 2 == 1) {
  271. makePage(areaTree,true, null);
  272. }
  273. }
  274. }
  275. /**
  276. * Creates a new page area for the given parameters
  277. * @param areaTree the area tree the page should be contained in
  278. * @param isBlankPage true if this page will be empty (e.g. forced even or odd break, or forced page count)
  279. * @return a Page layout object based on the page master selected from the params
  280. */
  281. private Page makePage(AreaTree areaTree,
  282. boolean isBlankPage,
  283. Page currentPage)
  284. throws FOPException {
  285. if (this.pageSequenceMaster!=null) {
  286. this.currentSimplePageMaster = this.pageSequenceMaster
  287. .getNextSimplePageMaster(((this.currentPageNumber % 2)==1),
  288. isBlankPage);
  289. Region region = currentSimplePageMaster
  290. .getRegion(RegionBody.REGION_CLASS);
  291. if (!flow.getFlowName().equals(region.getRegionName())) {
  292. throw new FOPException("Flow '" + flow.getFlowName()
  293. + "' does not map to the region-body in page-master '"
  294. + currentSimplePageMaster.getMasterName() + "'");
  295. }
  296. }
  297. Page newPage = this.currentSimplePageMaster.getPageMaster()
  298. .makePage(areaTree);
  299. if (currentPage != null) {
  300. ArrayList foots = currentPage.getPendingFootnotes();
  301. newPage.setPendingFootnotes(foots);
  302. }
  303. newPage.setNumber(this.currentPageNumber);
  304. String formattedPageNumber =
  305. pageNumberGenerator.makeFormattedPageNumber(this.currentPageNumber);
  306. newPage.setFormattedNumber(formattedPageNumber);
  307. newPage.setPageSequence(this);
  308. if (!isBlankPage) {
  309. log.info("[" + currentPageNumber + "]");
  310. BodyAreaContainer bodyArea = newPage.getBody();
  311. bodyArea.setIDReferences(areaTree.getIDReferences());
  312. flow.layout(bodyArea);
  313. } else {
  314. log.info("[" + currentPageNumber + " (blank)]");
  315. }
  316. // because of markers, do after fo:flow (likely also
  317. // justifiable because of spec)
  318. formatStaticContent(areaTree, newPage);
  319. areaTree.addPage(newPage);
  320. this.currentPageNumber++;
  321. this.pageCount++;
  322. return newPage;
  323. }
  324. /**
  325. * Formats the static content of the current page
  326. */
  327. private void formatStaticContent(AreaTree areaTree, Page page)
  328. throws FOPException {
  329. SimplePageMaster simpleMaster = currentSimplePageMaster;
  330. if (simpleMaster.getRegion(RegionBefore.REGION_CLASS) != null
  331. && (page.getBefore() != null)) {
  332. StaticContent staticFlow =
  333. (StaticContent)flowMap.get(simpleMaster.getRegion(RegionBefore.REGION_CLASS).getRegionName());
  334. if (staticFlow != null) {
  335. AreaContainer beforeArea = page.getBefore();
  336. beforeArea.setIDReferences(areaTree.getIDReferences());
  337. layoutStaticContent(staticFlow,
  338. simpleMaster.getRegion(RegionBefore.REGION_CLASS),
  339. beforeArea);
  340. }
  341. }
  342. if (simpleMaster.getRegion(RegionAfter.REGION_CLASS) != null
  343. && (page.getAfter() != null)) {
  344. StaticContent staticFlow =
  345. (StaticContent)flowMap.get(simpleMaster.getRegion(RegionAfter.REGION_CLASS).getRegionName());
  346. if (staticFlow != null) {
  347. AreaContainer afterArea = page.getAfter();
  348. afterArea.setIDReferences(areaTree.getIDReferences());
  349. layoutStaticContent(staticFlow,
  350. simpleMaster.getRegion(RegionAfter.REGION_CLASS),
  351. afterArea);
  352. }
  353. }
  354. if (simpleMaster.getRegion(RegionStart.REGION_CLASS) != null
  355. && (page.getStart() != null)) {
  356. StaticContent staticFlow =
  357. (StaticContent)flowMap.get(simpleMaster.getRegion(RegionStart.REGION_CLASS).getRegionName());
  358. if (staticFlow != null) {
  359. AreaContainer startArea = page.getStart();
  360. startArea.setIDReferences(areaTree.getIDReferences());
  361. layoutStaticContent(staticFlow,
  362. simpleMaster.getRegion(RegionStart.REGION_CLASS),
  363. startArea);
  364. }
  365. }
  366. if (simpleMaster.getRegion(RegionEnd.REGION_CLASS) != null
  367. && (page.getEnd() != null)) {
  368. StaticContent staticFlow =
  369. (StaticContent)flowMap.get(simpleMaster.getRegion(RegionEnd.REGION_CLASS).getRegionName());
  370. if (staticFlow != null) {
  371. AreaContainer endArea = page.getEnd();
  372. endArea.setIDReferences(areaTree.getIDReferences());
  373. layoutStaticContent(staticFlow,
  374. simpleMaster.getRegion(RegionEnd.REGION_CLASS),
  375. endArea);
  376. }
  377. }
  378. }
  379. private void layoutStaticContent(StaticContent flow, Region region,
  380. AreaContainer area) throws FOPException {
  381. flow.layout(area, region);
  382. // log.error("The region '" + region.getRegionName()
  383. // + "' only supports static-content. Cannot use flow named '"
  384. // + flow.getFlowName() + "'");
  385. }
  386. public int getCurrentPageNumber() {
  387. return currentPageNumber;
  388. }
  389. public int getPageCount() {
  390. return pageCount;
  391. }
  392. }