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: PDFRoot.java 1863870 2019-07-27 13:23:59Z matthias $ */ 19 20 package org.apache.fop.pdf; 21 22 import java.io.IOException; 23 import java.io.OutputStream; 24 import java.util.Locale; 25 26 import org.apache.fop.util.LanguageTags; 27 28 /** 29 * Class representing a Root (/Catalog) object. 30 */ 31 public class PDFRoot extends PDFDictionary { 32 33 /** 34 * Use no page mode setting, default 35 */ 36 public static final int PAGEMODE_USENONE = 0; 37 38 /** 39 * Use outlines page mode to show bookmarks 40 */ 41 public static final int PAGEMODE_USEOUTLINES = 1; 42 43 /** 44 * Use thumbs page mode to show thumbnail images 45 */ 46 public static final int PAGEMODE_USETHUMBS = 2; 47 48 /** 49 * Full screen page mode 50 */ 51 public static final int PAGEMODE_FULLSCREEN = 3; 52 53 private final PDFDocument document; 54 55 private PDFDPartRoot dPartRoot; 56 private PDFArray af; 57 58 private static final PDFName[] PAGEMODE_NAMES = new PDFName[] { 59 new PDFName("UseNone"), 60 new PDFName("UseOutlines"), 61 new PDFName("UseThumbs"), 62 new PDFName("FullScreen"), 63 }; 64 65 /** 66 * create a Root (/Catalog) object. NOTE: The PDFRoot 67 * object must be created before the PDF document is 68 * generated, but it is not assigned an object ID until 69 * it is about to be written (immediately before the xref 70 * table as part of the trailer). (mark-fop@inomial.com) 71 * 72 * @param document TODO 73 * @param pages the PDFPages object 74 */ PDFRoot(PDFDocument document, PDFPages pages)75 public PDFRoot(PDFDocument document, PDFPages pages) { 76 this.document = document; 77 setObjectNumber(document); 78 put("Type", new PDFName("Catalog")); 79 setRootPages(pages); 80 setLanguage("x-unknown"); 81 } 82 83 /** {@inheritDoc} */ output(OutputStream stream)84 public int output(OutputStream stream) throws IOException { 85 if (document.getProfile().getPDFUAMode().isEnabled()) { 86 PDFDictionary d = new PDFDictionary(); 87 d.put("DisplayDocTitle", true); 88 put("ViewerPreferences", d); 89 } 90 getDocument().getProfile().verifyTaggedPDF(); 91 return super.output(stream); 92 } 93 94 /** 95 * Set the page mode for the PDF document. 96 * 97 * @param mode the page mode (one of PAGEMODE_*) 98 */ setPageMode(int mode)99 public void setPageMode(int mode) { 100 put("PageMode", PAGEMODE_NAMES[mode]); 101 } 102 103 /** 104 * Returns the currently active /PageMode. 105 * @return the /PageMode (one of PAGEMODE_*) 106 */ getPageMode()107 public int getPageMode() { 108 PDFName mode = (PDFName)get("PageMode"); 109 if (mode != null) { 110 for (int i = 0; i < PAGEMODE_NAMES.length; i++) { 111 if (PAGEMODE_NAMES[i].equals(mode)) { 112 return i; 113 } 114 } 115 throw new IllegalStateException("Unknown /PageMode encountered: " + mode); 116 } else { 117 return PAGEMODE_USENONE; 118 } 119 } 120 121 /** 122 * add a /Page object to the root /Pages object 123 * 124 * @param page the /Page object to add 125 */ addPage(PDFPage page)126 public void addPage(PDFPage page) { 127 PDFPages pages = getRootPages(); 128 pages.addPage(page); 129 } 130 131 /** 132 * set the root /Pages object 133 * 134 * @param pages the /Pages object to set as root 135 */ setRootPages(PDFPages pages)136 public void setRootPages(PDFPages pages) { 137 put("Pages", pages.makeReference()); 138 } 139 140 /** 141 * Returns the /PageLabels object. 142 * @return the /PageLabels object if set, null otherwise. 143 * @since PDF 1.3 144 */ getRootPages()145 public PDFPages getRootPages() { 146 PDFReference ref = (PDFReference)get("Pages"); 147 return (ref != null ? (PDFPages)ref.getObject() : null); 148 } 149 150 /** 151 * Sets the /PageLabels object. 152 * @param pageLabels the /PageLabels object 153 */ setPageLabels(PDFPageLabels pageLabels)154 public void setPageLabels(PDFPageLabels pageLabels) { 155 put("PageLabels", pageLabels.makeReference()); 156 } 157 158 /** 159 * Returns the /PageLabels object. 160 * @return the /PageLabels object if set, null otherwise. 161 * @since PDF 1.3 162 */ getPageLabels()163 public PDFPageLabels getPageLabels() { 164 PDFReference ref = (PDFReference)get("PageLabels"); 165 return (ref != null ? (PDFPageLabels)ref.getObject() : null); 166 } 167 168 /** 169 * Set the root outline for the PDF document. 170 * 171 * @param out the root PDF Outline 172 */ setRootOutline(PDFOutline out)173 public void setRootOutline(PDFOutline out) { 174 put("Outlines", out.makeReference()); 175 176 //Set /PageMode to /UseOutlines by default if no other mode has been set 177 PDFName mode = (PDFName)get("PageMode"); 178 if (mode == null) { 179 setPageMode(PAGEMODE_USEOUTLINES); 180 } 181 } 182 183 /** 184 * Get the root PDF outline for the document. 185 * 186 * @return the root PDF Outline 187 */ getRootOutline()188 public PDFOutline getRootOutline() { 189 PDFReference ref = (PDFReference)get("Outlines"); 190 return (ref != null ? (PDFOutline)ref.getObject() : null); 191 } 192 193 /** 194 * Set the /Names object. 195 * @param names the Names object 196 * @since PDF 1.2 197 */ setNames(PDFNames names)198 public void setNames(PDFNames names) { 199 put("Names", names.makeReference()); 200 } 201 202 /** 203 * Returns the /Names object. 204 * @return the Names object if set, null otherwise. 205 * @since PDF 1.2 206 */ getNames()207 public PDFNames getNames() { 208 PDFReference ref = (PDFReference)get("Names"); 209 return (ref != null ? (PDFNames)ref.getObject() : null); 210 } 211 212 /** 213 * Set the optional Metadata object. 214 * @param meta the Metadata object 215 * @since PDF 1.4 216 */ setMetadata(PDFMetadata meta)217 public void setMetadata(PDFMetadata meta) { 218 if (getDocumentSafely().getPDFVersion().compareTo(Version.V1_4) >= 0) { 219 put("Metadata", meta.makeReference()); 220 } 221 } 222 223 /** 224 * Returns the /Metadata object 225 * @return the /Metadata object if set, null otherwise. 226 * @since PDF 1.4 227 */ getMetadata()228 public PDFMetadata getMetadata() { 229 PDFReference ref = (PDFReference)get("Metadata"); 230 return (ref != null ? (PDFMetadata)ref.getObject() : null); 231 } 232 233 /** 234 * Returns the /OutputIntents array. 235 * @return the /OutputIntents array or null if it doesn't exist 236 * @since PDF 1.4 237 */ getOutputIntents()238 public PDFArray getOutputIntents() { 239 return (PDFArray)get("OutputIntents"); 240 } 241 242 /** 243 * Adds an OutputIntent to the PDF 244 * @param outputIntent the OutputIntent dictionary 245 * @since PDF 1.4 246 */ addOutputIntent(PDFOutputIntent outputIntent)247 public void addOutputIntent(PDFOutputIntent outputIntent) { 248 if (getDocumentSafely().getPDFVersion().compareTo(Version.V1_4) >= 0) { 249 PDFArray outputIntents = getOutputIntents(); 250 if (outputIntents == null) { 251 outputIntents = new PDFArray(this); 252 put("OutputIntents", outputIntents); 253 } 254 outputIntents.add(outputIntent); 255 } 256 } 257 258 /** 259 * Sets the "Version" entry. If this version is greater than that specified in the header, this 260 * version takes precedence. 261 * 262 * @param version the PDF document version 263 * @since PDF 1.4 264 */ setVersion(Version version)265 void setVersion(Version version) { 266 put("Version", new PDFName(version.toString())); 267 } 268 269 /** 270 * Returns the language identifier of the document. 271 * @return the language identifier of the document (or null if not set or undefined) 272 * @since PDF 1.4 273 */ getLanguage()274 public String getLanguage() { 275 return (String)get("Lang"); 276 } 277 278 /** 279 * Sets the locale of the document. 280 * @param locale the locale of the document. 281 */ setLanguage(Locale locale)282 public void setLanguage(Locale locale) { 283 if (locale == null) { 284 throw new NullPointerException("locale must not be null"); 285 } 286 setLanguage(LanguageTags.toLanguageTag(locale)); 287 } 288 setLanguage(String lang)289 private void setLanguage(String lang) { 290 put("Lang", lang); 291 } 292 293 /** 294 * Sets the StructTreeRoot object. Used for accessibility. 295 * @param structTreeRoot of this document 296 */ setStructTreeRoot(PDFStructTreeRoot structTreeRoot)297 public void setStructTreeRoot(PDFStructTreeRoot structTreeRoot) { 298 if (structTreeRoot == null) { 299 throw new NullPointerException("structTreeRoot must not be null"); 300 } 301 put("StructTreeRoot", structTreeRoot); 302 } 303 304 /** 305 * Returns the StructTreeRoot object. 306 * @return the structure tree root (or null if accessibility is not enabled) 307 */ getStructTreeRoot()308 public PDFStructTreeRoot getStructTreeRoot() { 309 return (PDFStructTreeRoot)get("StructTreeRoot"); 310 } 311 312 /** 313 * Marks this document as conforming to the Tagged PDF conventions. 314 */ makeTagged()315 public void makeTagged() { 316 PDFDictionary dict = new PDFDictionary(); 317 dict.put("Marked", Boolean.TRUE); 318 put("MarkInfo", dict); //new PDFMarkInfo() 319 } 320 321 /** 322 * Returns the MarkInfo dictionary. 323 * @return the MarkInfo dictionary (or null if it's not present) 324 */ getMarkInfo()325 public PDFDictionary getMarkInfo() { 326 return (PDFDictionary)get("MarkInfo"); 327 } 328 getDPartRoot()329 public PDFDPartRoot getDPartRoot() { 330 if (dPartRoot == null) { 331 dPartRoot = getDocument().getFactory().makeDPartRoot(); 332 put("DPartRoot", dPartRoot.makeReference()); 333 } 334 return dPartRoot; 335 } 336 addAF(PDFFileSpec fileSpec)337 public void addAF(PDFFileSpec fileSpec) { 338 if (af == null) { 339 af = new PDFArray(); 340 put("AF", af); 341 } 342 af.add(fileSpec); 343 fileSpec.put("AFRelationship", new PDFName("Data")); 344 } 345 } 346