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 18 /* $Id: Root.java 1835810 2018-07-13 10:29:57Z ssteiner $ */ 19 20 package org.apache.fop.fo.pagination; 21 22 // java 23 import java.util.List; 24 import java.util.Locale; 25 26 import org.xml.sax.Locator; 27 28 import org.apache.fop.apps.FOPException; 29 import org.apache.fop.fo.FOEventHandler; 30 import org.apache.fop.fo.FONode; 31 import org.apache.fop.fo.FOTreeBuilderContext; 32 import org.apache.fop.fo.FObj; 33 import org.apache.fop.fo.PropertyList; 34 import org.apache.fop.fo.ValidationException; 35 import org.apache.fop.fo.extensions.destination.Destination; 36 import org.apache.fop.fo.pagination.bookmarks.BookmarkTree; 37 import org.apache.fop.fo.properties.CommonAccessibility; 38 import org.apache.fop.fo.properties.CommonAccessibilityHolder; 39 import org.apache.fop.fo.properties.CommonHyphenation; 40 41 /** 42 * Class modeling the <a href="http://www.w3.org/TR/xsl/#fo_root"> 43 * <code>fo:root</code></a> formatting object. 44 * Contains page masters, page-sequences. 45 */ 46 public class Root extends FObj implements CommonAccessibilityHolder { 47 48 private CommonAccessibility commonAccessibility; 49 50 private int mediaUsage; 51 52 private LayoutMasterSet layoutMasterSet; 53 private Declarations declarations; 54 private BookmarkTree bookmarkTree; 55 private List<Destination> destinationList; 56 private List<PageSequence> pageSequences; 57 private Locale locale; 58 59 // temporary until above list populated 60 private boolean pageSequenceFound; 61 62 /** 63 * Keeps count of page number from over PageSequence instances 64 */ 65 private int endingPageNumberOfPreviousSequence; 66 private int totalPagesGenerated; 67 68 /** 69 * Context class used while building the FO tree. 70 */ 71 private FOTreeBuilderContext builderContext; 72 73 /** 74 * FOEventHandler object for this FO Tree 75 */ 76 private FOEventHandler foEventHandler; 77 78 private PageSequence lastSeq; 79 setLastSeq(PageSequence seq)80 public void setLastSeq(PageSequence seq) { 81 lastSeq = seq; 82 } 83 getLastSeq()84 public PageSequence getLastSeq() { 85 return lastSeq; 86 } 87 88 /** 89 * Base constructor 90 * 91 * @param parent {@link FONode} that is the parent of this object 92 * Note: parent should be null for the fo:root. 93 */ Root(FONode parent)94 public Root(FONode parent) { 95 super(parent); 96 pageSequences = new java.util.ArrayList<PageSequence>(); 97 } 98 99 /** {@inheritDoc} */ bind(PropertyList pList)100 public void bind(PropertyList pList) throws FOPException { 101 super.bind(pList); 102 commonAccessibility = CommonAccessibility.getInstance(pList); 103 mediaUsage = pList.get(PR_MEDIA_USAGE).getEnum(); 104 String language = pList.get(PR_LANGUAGE).getString(); 105 String country = pList.get(PR_COUNTRY).getString(); 106 locale = CommonHyphenation.toLocale(language, country); 107 } 108 109 /** {@inheritDoc} */ startOfNode()110 public void startOfNode() throws FOPException { 111 foEventHandler.startRoot(this); 112 } 113 114 /** {@inheritDoc} */ endOfNode()115 public void endOfNode() throws FOPException { 116 if (!pageSequenceFound || layoutMasterSet == null) { 117 missingChildElementError("(layout-master-set, declarations?, " 118 + "bookmark-tree?, (page-sequence|fox:external-document)+)"); 119 } 120 foEventHandler.endRoot(this); 121 } 122 123 /** 124 * {@inheritDoc} 125 * <br>XSL 1.0 Spec: (layout-master-set,declarations?,page-sequence+) 126 * <br>FOP: (layout-master-set, declarations?, fox:bookmarks?, page-sequence+) 127 */ validateChildNode(Locator loc, String nsURI, String localName)128 protected void validateChildNode(Locator loc, String nsURI, String localName) 129 throws ValidationException { 130 if (FO_URI.equals(nsURI)) { 131 if (localName.equals("layout-master-set")) { 132 if (layoutMasterSet != null) { 133 tooManyNodesError(loc, "fo:layout-master-set"); 134 } 135 } else if (localName.equals("declarations")) { 136 if (layoutMasterSet == null) { 137 nodesOutOfOrderError(loc, "fo:layout-master-set", "fo:declarations"); 138 } else if (declarations != null) { 139 tooManyNodesError(loc, "fo:declarations"); 140 } else if (bookmarkTree != null) { 141 nodesOutOfOrderError(loc, "fo:declarations", "fo:bookmark-tree"); 142 } else if (pageSequenceFound) { 143 nodesOutOfOrderError(loc, "fo:declarations", "fo:page-sequence"); 144 } 145 } else if (localName.equals("bookmark-tree")) { 146 if (layoutMasterSet == null) { 147 nodesOutOfOrderError(loc, "fo:layout-master-set", "fo:bookmark-tree"); 148 } else if (bookmarkTree != null) { 149 tooManyNodesError(loc, "fo:bookmark-tree"); 150 } else if (pageSequenceFound) { 151 nodesOutOfOrderError(loc, "fo:bookmark-tree", "fo:page-sequence"); 152 } 153 } else if (localName.equals("page-sequence")) { 154 if (layoutMasterSet == null) { 155 nodesOutOfOrderError(loc, "fo:layout-master-set", "fo:page-sequence"); 156 } else { 157 pageSequenceFound = true; 158 } 159 } else { 160 invalidChildError(loc, nsURI, localName); 161 } 162 } else { 163 if (FOX_URI.equals(nsURI)) { 164 if ("external-document".equals(localName)) { 165 pageSequenceFound = true; 166 } 167 } 168 //invalidChildError(loc, nsURI, localName); 169 //Ignore non-FO elements under root 170 } 171 } 172 173 174 /** 175 * @param loc location in the source file 176 * @param child the {@link FONode} to validate against 177 * @throws ValidationException if the incoming node is not a valid child for the given FO 178 */ validateChildNode(Locator loc, FONode child)179 protected void validateChildNode(Locator loc, FONode child) throws ValidationException { 180 if (child instanceof AbstractPageSequence) { 181 pageSequenceFound = true; 182 } 183 } 184 185 /** {@inheritDoc} */ getCommonAccessibility()186 public CommonAccessibility getCommonAccessibility() { 187 return commonAccessibility; 188 } 189 190 /** 191 * Sets the FOEventHandler object that this Root is attached to 192 * @param foEventHandler the FOEventHandler object 193 */ setFOEventHandler(FOEventHandler foEventHandler)194 public void setFOEventHandler(FOEventHandler foEventHandler) { 195 this.foEventHandler = foEventHandler; 196 } 197 198 /** 199 * This method overrides the FONode version. The FONode version calls the 200 * method by the same name for the parent object. Since Root is at the top 201 * of the tree, it returns the actual FOEventHandler object. Thus, any FONode 202 * can use this chain to find which FOEventHandler it is being built for. 203 * @return the FOEventHandler implementation that this Root is attached to 204 */ getFOEventHandler()205 public FOEventHandler getFOEventHandler() { 206 return foEventHandler; 207 } 208 209 /** 210 * Sets the builder context for this FO tree. 211 * @param context the builder context to be used 212 */ setBuilderContext(FOTreeBuilderContext context)213 public void setBuilderContext(FOTreeBuilderContext context) { 214 this.builderContext = context; 215 } 216 217 /** {@inheritDoc} */ getBuilderContext()218 public FOTreeBuilderContext getBuilderContext() { 219 return this.builderContext; 220 } 221 222 /** 223 * Gets the last page number generated by the previous page-sequence 224 * @return the last page number, 0 if no page sequences yet generated 225 */ getEndingPageNumberOfPreviousSequence()226 public int getEndingPageNumberOfPreviousSequence() { 227 return endingPageNumberOfPreviousSequence; 228 } 229 230 /** 231 * Returns the total number of pages generated by FOP 232 * (May not equal endingPageNumberOfPreviousSequence due to 233 * initial-page-number property on fo:page-sequences.) 234 * @return the last page number, 0 if no page sequences yet generated 235 */ getTotalPagesGenerated()236 public int getTotalPagesGenerated() { 237 return totalPagesGenerated; 238 } 239 240 /** 241 * Notify additional pages generated to increase the totalPagesGenerated counter 242 * @param lastPageNumber the last page number generated by the sequence 243 * @param additionalPages the total pages generated by the sequence (for statistics) 244 * @throws IllegalArgumentException for negative additional page counts 245 */ notifyPageSequenceFinished(int lastPageNumber, int additionalPages)246 public void notifyPageSequenceFinished(int lastPageNumber, int additionalPages) 247 throws IllegalArgumentException { 248 249 if (additionalPages >= 0) { 250 totalPagesGenerated += additionalPages; 251 endingPageNumberOfPreviousSequence = lastPageNumber; 252 } else { 253 throw new IllegalArgumentException( 254 "Number of additional pages must be zero or greater."); 255 } 256 } 257 258 /** 259 * Returns the number of PageSequence instances. 260 * @return the number of PageSequence instances 261 */ getPageSequenceCount()262 public int getPageSequenceCount() { 263 return pageSequences.size(); 264 } 265 266 /** 267 * Some properties, such as 'force-page-count', require a 268 * page-sequence to know about some properties of the next. 269 * @param current the current PageSequence 270 * @return succeeding PageSequence; null if none 271 */ getSucceedingPageSequence(PageSequence current)272 public PageSequence getSucceedingPageSequence(PageSequence current) { 273 int currentIndex = pageSequences.indexOf(current); 274 if (currentIndex == -1) { 275 return null; 276 } 277 if (currentIndex < (pageSequences.size() - 1)) { 278 return pageSequences.get(currentIndex + 1); 279 } else { 280 return null; 281 } 282 } 283 284 /** 285 * Adds the specified page sequence. 286 * 287 * @param pageSequence The page sequence to add 288 */ addPageSequence(PageSequence pageSequence)289 public void addPageSequence(PageSequence pageSequence) { 290 pageSequences.add(pageSequence); 291 } 292 293 /** 294 * Returns the last page sequence (current while parsing). 295 * 296 * @return The last page sequence or null 297 */ getLastPageSequence()298 public PageSequence getLastPageSequence() { 299 if (getPageSequenceCount() > 0) { 300 return pageSequences.get(getPageSequenceCount() - 1); 301 } else { 302 return null; 303 } 304 } 305 306 /** 307 * Returns the associated LayoutMasterSet. 308 * @return the LayoutMasterSet instance 309 */ getLayoutMasterSet()310 public LayoutMasterSet getLayoutMasterSet() { 311 return this.layoutMasterSet; 312 } 313 314 /** 315 * Sets the associated LayoutMasterSet. 316 * @param layoutMasterSet the LayoutMasterSet to use 317 */ setLayoutMasterSet(LayoutMasterSet layoutMasterSet)318 public void setLayoutMasterSet(LayoutMasterSet layoutMasterSet) { 319 this.layoutMasterSet = layoutMasterSet; 320 } 321 322 /** 323 * Returns the associated Declarations. 324 * @return the Declarations instance 325 */ getDeclarations()326 public Declarations getDeclarations() { 327 return this.declarations; 328 } 329 330 /** 331 * Sets the associated Declarations. 332 * @param declarations the Declarations to use 333 */ setDeclarations(Declarations declarations)334 public void setDeclarations(Declarations declarations) { 335 this.declarations = declarations; 336 } 337 338 /** 339 * Set the BookmarkTree object for this FO 340 * @param bookmarkTree the BookmarkTree object 341 */ setBookmarkTree(BookmarkTree bookmarkTree)342 public void setBookmarkTree(BookmarkTree bookmarkTree) { 343 this.bookmarkTree = bookmarkTree; 344 } 345 346 /** 347 * Add a Destination object to this FO 348 * @param destination the Destination object to add 349 */ addDestination(Destination destination)350 public void addDestination(Destination destination) { 351 if (destinationList == null) { 352 destinationList = new java.util.ArrayList<Destination>(); 353 } 354 destinationList.add(destination); 355 } 356 357 /** 358 * Public accessor for the list of Destination objects for this FO 359 * @return the Destination object 360 */ getDestinationList()361 public List getDestinationList() { 362 return destinationList; 363 } 364 365 /** 366 * Public accessor for the BookmarkTree object for this FO 367 * @return the BookmarkTree object 368 */ getBookmarkTree()369 public BookmarkTree getBookmarkTree() { 370 return bookmarkTree; 371 } 372 373 /** {@inheritDoc} */ getRoot()374 public Root getRoot() { 375 return this; 376 } 377 378 /** {@inheritDoc} */ getLocalName()379 public String getLocalName() { 380 return "root"; 381 } 382 383 /** 384 * {@inheritDoc} 385 * @return {@link org.apache.fop.fo.Constants#FO_ROOT} 386 */ getNameId()387 public int getNameId() { 388 return FO_ROOT; 389 } 390 391 392 /** @return locale proprty. */ getLocale()393 public Locale getLocale() { 394 return locale; 395 } 396 397 } 398