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 package org.apache.pdfbox.pdmodel; 18 19 import java.io.IOException; 20 import java.util.ArrayList; 21 import java.util.List; 22 23 import org.apache.pdfbox.cos.COSArray; 24 import org.apache.pdfbox.cos.COSBase; 25 import org.apache.pdfbox.cos.COSDictionary; 26 import org.apache.pdfbox.cos.COSName; 27 import org.apache.pdfbox.cos.COSStream; 28 import org.apache.pdfbox.cos.COSString; 29 30 import org.apache.pdfbox.pdmodel.common.COSArrayList; 31 import org.apache.pdfbox.pdmodel.common.COSObjectable; 32 import org.apache.pdfbox.pdmodel.common.PDDestinationOrAction; 33 import org.apache.pdfbox.pdmodel.common.PDMetadata; 34 import org.apache.pdfbox.pdmodel.common.PDPageLabels; 35 import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDMarkInfo; 36 import org.apache.pdfbox.pdmodel.documentinterchange.logicalstructure.PDStructureTreeRoot; 37 import org.apache.pdfbox.pdmodel.interactive.action.type.PDURIDictionary; 38 import org.apache.pdfbox.pdmodel.interactive.action.PDActionFactory; 39 import org.apache.pdfbox.pdmodel.interactive.action.PDDocumentCatalogAdditionalActions; 40 import org.apache.pdfbox.pdmodel.interactive.documentnavigation.destination.PDDestination; 41 import org.apache.pdfbox.pdmodel.interactive.documentnavigation.outline.PDDocumentOutline; 42 import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm; 43 44 import org.apache.pdfbox.pdmodel.interactive.pagenavigation.PDThread; 45 import org.apache.pdfbox.pdmodel.interactive.viewerpreferences.PDViewerPreferences; 46 47 /** 48 * This class represents the acroform of a PDF document. 49 * 50 * @author <a href="mailto:ben@benlitchfield.com">Ben Litchfield</a> 51 * @version $Revision: 1.21 $ 52 */ 53 public class PDDocumentCatalog implements COSObjectable 54 { 55 private COSDictionary root; 56 private PDDocument document; 57 58 private PDAcroForm acroForm = null; 59 60 /** 61 * Page mode where neither the outline nor the thumbnails 62 * are displayed. 63 */ 64 public static final String PAGE_MODE_USE_NONE = "UseNone"; 65 /** 66 * Show bookmarks when pdf is opened. 67 */ 68 public static final String PAGE_MODE_USE_OUTLINES = "UseOutlines"; 69 /** 70 * Show thumbnails when pdf is opened. 71 */ 72 public static final String PAGE_MODE_USE_THUMBS = "UseThumbs"; 73 /** 74 * Full screen mode with no menu bar, window controls. 75 */ 76 public static final String PAGE_MODE_FULL_SCREEN = "FullScreen"; 77 /** 78 * Optional content group panel is visible when opened. 79 */ 80 public static final String PAGE_MODE_USE_OPTIONAL_CONTENT = "UseOC"; 81 /** 82 * Attachments panel is visible. 83 */ 84 public static final String PAGE_MODE_USE_ATTACHMENTS = "UseAttachments"; 85 86 /** 87 * Display one page at a time. 88 */ 89 public static final String PAGE_LAYOUT_SINGLE_PAGE = "SinglePage"; 90 /** 91 * Display the pages in one column. 92 */ 93 public static final String PAGE_LAYOUT_ONE_COLUMN = "OneColumn"; 94 /** 95 * Display the pages in two columns, with odd numbered pagse on the left. 96 */ 97 public static final String PAGE_LAYOUT_TWO_COLUMN_LEFT = "TwoColumnLeft"; 98 /** 99 * Display the pages in two columns, with odd numbered pagse on the right. 100 */ 101 public static final String PAGE_LAYOUT_TWO_COLUMN_RIGHT ="TwoColumnRight"; 102 /** 103 * Display the pages two at a time, with odd-numbered pages on the left. 104 * @since PDF Version 1.5 105 */ 106 public static final String PAGE_LAYOUT_TWO_PAGE_LEFT = "TwoPageLeft"; 107 /** 108 * Display the pages two at a time, with odd-numbered pages on the right. 109 * @since PDF Version 1.5 110 */ 111 public static final String PAGE_LAYOUT_TWO_PAGE_RIGHT = "TwoPageRight"; 112 113 114 115 /** 116 * Constructor. 117 * 118 * @param doc The document that this catalog is part of. 119 */ 120 public PDDocumentCatalog( PDDocument doc ) 121 { 122 document = doc; 123 root = new COSDictionary(); 124 root.setItem( COSName.TYPE, new COSString( "Catalog" ) ); 125 document.getDocument().getTrailer().setItem( COSName.ROOT, root ); 126 } 127 128 /** 129 * Constructor. 130 * 131 * @param doc The document that this catalog is part of. 132 * @param rootDictionary The root dictionary that this object wraps. 133 */ 134 public PDDocumentCatalog( PDDocument doc, COSDictionary rootDictionary ) 135 { 136 document = doc; 137 root = rootDictionary; 138 } 139 140 /** 141 * Convert this standard java object to a COS object. 142 * 143 * @return The cos object that matches this Java object. 144 */ 145 public COSBase getCOSObject() 146 { 147 return root; 148 } 149 150 /** 151 * Convert this standard java object to a COS object. 152 * 153 * @return The cos object that matches this Java object. 154 */ 155 public COSDictionary getCOSDictionary() 156 { 157 return root; 158 } 159 160 /** 161 * This will get the documents acroform. This will return null if 162 * no acroform is part of the document. 163 * 164 * @return The documents acroform. 165 */ 166 public PDAcroForm getAcroForm() 167 { 168 if( acroForm == null ) 169 { 170 COSDictionary acroFormDic = 171 (COSDictionary)root.getDictionaryObject( COSName.ACRO_FORM ); 172 if( acroFormDic != null ) 173 { 174 acroForm = new PDAcroForm( document, acroFormDic ); 175 } 176 } 177 return acroForm; 178 } 179 180 /** 181 * Set the acro form for this catalog. 182 * 183 * @param acro The new acro form. 184 */ 185 public void setAcroForm( PDAcroForm acro ) 186 { 187 root.setItem( COSName.ACRO_FORM, acro ); 188 } 189 190 /** 191 * This will get the root node for the pages. 192 * 193 * @return The parent page node. 194 */ 195 public PDPageNode getPages() 196 { 197 return new PDPageNode( (COSDictionary)root.getDictionaryObject( COSName.PAGES ) ); 198 } 199 200 /** 201 * The PDF document contains a hierarchical structure of PDPageNode and PDPages, which 202 * is mostly just a way to store this information. This method will return a flat list 203 * of all PDPage objects in this document. 204 * 205 * @return A list of PDPage objects. 206 */ 207 public List getAllPages() 208 { 209 List retval = new ArrayList(); 210 PDPageNode rootNode = getPages(); 211 //old (slower): 212 //getPageObjects( rootNode, retval ); 213 rootNode.getAllKids(retval); 214 return retval; 215 } 216 217 /** 218 * Get the viewer preferences associated with this document or null if they 219 * do not exist. 220 * 221 * @return The document's viewer preferences. 222 */ 223 public PDViewerPreferences getViewerPreferences() 224 { 225 PDViewerPreferences retval = null; 226 COSDictionary dict = (COSDictionary)root.getDictionaryObject( COSName.VIEWER_PREFERENCES ); 227 if( dict != null ) 228 { 229 retval = new PDViewerPreferences( dict ); 230 } 231 232 return retval; 233 } 234 235 /** 236 * Set the viewer preferences. 237 * 238 * @param prefs The new viewer preferences. 239 */ 240 public void setViewerPreferences( PDViewerPreferences prefs ) 241 { 242 root.setItem( COSName.VIEWER_PREFERENCES, prefs ); 243 } 244 245 /** 246 * Get the outline associated with this document or null if it 247 * does not exist. 248 * 249 * @return The document's outline. 250 */ 251 public PDDocumentOutline getDocumentOutline() 252 { 253 PDDocumentOutline retval = null; 254 COSDictionary dict = (COSDictionary)root.getDictionaryObject( COSName.OUTLINES ); 255 if( dict != null ) 256 { 257 retval = new PDDocumentOutline( dict ); 258 } 259 260 return retval; 261 } 262 263 /** 264 * Set the document outlines. 265 * 266 * @param outlines The new document outlines. 267 */ 268 public void setDocumentOutline( PDDocumentOutline outlines ) 269 { 270 root.setItem( COSName.OUTLINES, outlines ); 271 } 272 273 /** 274 * Get the list threads for this pdf document. 275 * 276 * @return A list of PDThread objects. 277 */ 278 public List getThreads() 279 { 280 COSArray array = (COSArray)root.getDictionaryObject( COSName.THREADS ); 281 if( array == null ) 282 { 283 array = new COSArray(); 284 root.setItem( COSName.THREADS, array ); 285 } 286 List pdObjects = new ArrayList(); 287 for( int i=0; i<array.size(); i++ ) 288 { 289 pdObjects.add( new PDThread( (COSDictionary)array.getObject( i ) ) ); 290 } 291 return new COSArrayList( pdObjects, array ); 292 } 293 294 /** 295 * Set the list of threads for this pdf document. 296 * 297 * @param threads The list of threads, or null to clear it. 298 */ 299 public void setThreads( List threads ) 300 { 301 root.setItem( COSName.THREADS, COSArrayList.converterToCOSArray( threads ) ); 302 } 303 304 /** 305 * Get the metadata that is part of the document catalog. This will 306 * return null if there is no meta data for this object. 307 * 308 * @return The metadata for this object. 309 */ 310 public PDMetadata getMetadata() 311 { 312 PDMetadata retval = null; 313 COSStream stream = (COSStream)root.getDictionaryObject( COSName.METADATA ); 314 if( stream != null ) 315 { 316 retval = new PDMetadata( stream ); 317 } 318 return retval; 319 } 320 321 /** 322 * Set the metadata for this object. This can be null. 323 * 324 * @param meta The meta data for this object. 325 */ 326 public void setMetadata( PDMetadata meta ) 327 { 328 root.setItem( COSName.METADATA, meta ); 329 } 330 331 /** 332 * Set the Document Open Action for this object. 333 * 334 * @param action The action you want to perform. 335 */ 336 public void setOpenAction( PDDestinationOrAction action ) 337 { 338 root.setItem( COSName.OPEN_ACTION, action ); 339 } 340 341 /** 342 * Get the Document Open Action for this object. 343 * 344 * @return The action to perform when the document is opened. 345 * 346 * @throws IOException If there is an error creating the destination 347 * or action. 348 */ 349 public PDDestinationOrAction getOpenAction() throws IOException 350 { 351 PDDestinationOrAction action = null; 352 COSBase actionObj = root.getDictionaryObject(COSName.OPEN_ACTION); 353 354 if( actionObj == null ) 355 { 356 //no op 357 } 358 else if( actionObj instanceof COSDictionary ) 359 { 360 action = PDActionFactory.createAction((COSDictionary)actionObj); 361 } 362 else if( actionObj instanceof COSArray ) 363 { 364 action = PDDestination.create( actionObj ); 365 } 366 else 367 { 368 throw new IOException( "Unknown OpenAction " + actionObj ); 369 } 370 371 return action; 372 } 373 /** 374 * @return The Additional Actions for this Document 375 */ 376 public PDDocumentCatalogAdditionalActions getActions() 377 { 378 COSDictionary addAct = (COSDictionary) root.getDictionaryObject( COSName.AA ); 379 if (addAct == null) 380 { 381 addAct = new COSDictionary(); 382 root.setItem(COSName.AA, addAct); 383 } 384 return new PDDocumentCatalogAdditionalActions(addAct); 385 } 386 387 /** 388 * Set the additional actions for the document. 389 * 390 * @param actions The actions that are associated with this document. 391 */ 392 public void setActions( PDDocumentCatalogAdditionalActions actions ) 393 { 394 root.setItem(COSName.AA, actions ); 395 } 396 397 /** 398 * @return The names dictionary for this document or null if none exist. 399 */ 400 public PDDocumentNameDictionary getNames() 401 { 402 PDDocumentNameDictionary nameDic = null; 403 COSDictionary names = (COSDictionary) root.getDictionaryObject(COSName.NAMES); 404 if(names != null) 405 { 406 nameDic = new PDDocumentNameDictionary(this,names); 407 } 408 return nameDic; 409 } 410 411 /** 412 * Set the names dictionary for the document. 413 * 414 * @param names The names dictionary that is associated with this document. 415 */ 416 public void setNames( PDDocumentNameDictionary names ) 417 { 418 root.setItem(COSName.NAMES, names ); 419 } 420 421 /** 422 * Get info about doc's usage of tagged features. This will return 423 * null if there is no information. 424 * 425 * @return The new mark info. 426 */ 427 public PDMarkInfo getMarkInfo() 428 { 429 PDMarkInfo retval = null; 430 COSDictionary dic = (COSDictionary)root.getDictionaryObject( COSName.MARK_INFO ); 431 if( dic != null ) 432 { 433 retval = new PDMarkInfo( dic ); 434 } 435 return retval; 436 } 437 438 /** 439 * Set information about the doc's usage of tagged features. 440 * 441 * @param markInfo The new MarkInfo data. 442 */ 443 public void setMarkInfo( PDMarkInfo markInfo ) 444 { 445 root.setItem( COSName.MARK_INFO, markInfo ); 446 } 447 448 /** 449 * Set the page display mode, see the PAGE_MODE_XXX constants. 450 * @return A string representing the page mode. 451 */ 452 public String getPageMode() 453 { 454 return root.getNameAsString( COSName.PAGE_MODE, PAGE_MODE_USE_NONE ); 455 } 456 457 /** 458 * Set the page mode. See the PAGE_MODE_XXX constants for valid values. 459 * @param mode The new page mode. 460 */ 461 public void setPageMode( String mode ) 462 { 463 root.setName( COSName.PAGE_MODE, mode ); 464 } 465 466 /** 467 * Set the page layout, see the PAGE_LAYOUT_XXX constants. 468 * @return A string representing the page layout. 469 */ 470 public String getPageLayout() 471 { 472 return root.getNameAsString( COSName.PAGE_LAYOUT, PAGE_LAYOUT_SINGLE_PAGE ); 473 } 474 475 /** 476 * Set the page layout. See the PAGE_LAYOUT_XXX constants for valid values. 477 * @param layout The new page layout. 478 */ 479 public void setPageLayout( String layout ) 480 { 481 root.setName( COSName.PAGE_LAYOUT, layout ); 482 } 483 484 /** 485 * Document level information in the URI. 486 * @return Document level URI. 487 */ 488 public PDURIDictionary getURI() 489 { 490 PDURIDictionary retval = null; 491 COSDictionary uri = (COSDictionary)root.getDictionaryObject( COSName.URI ); 492 if( uri != null ) 493 { 494 retval = new PDURIDictionary( uri ); 495 } 496 return retval; 497 } 498 499 /** 500 * Set the document level uri. 501 * @param uri The new document level uri. 502 */ 503 public void setURI( PDURIDictionary uri ) 504 { 505 root.setItem( COSName.URI, uri ); 506 } 507 508 /** 509 * Get the document's structure tree root. 510 * 511 * @return The document's structure tree root or null if none exists. 512 */ 513 public PDStructureTreeRoot getStructureTreeRoot() 514 { 515 PDStructureTreeRoot treeRoot = null; 516 COSDictionary dic = (COSDictionary)root.getDictionaryObject( COSName.STRUCT_TREE_ROOT ); 517 if( dic != null ) 518 { 519 treeRoot = new PDStructureTreeRoot( dic ); 520 } 521 return treeRoot; 522 } 523 524 /** 525 * Set the document's structure tree root. 526 * 527 * @param treeRoot The new structure tree. 528 */ 529 public void setStructureTreeRoot( PDStructureTreeRoot treeRoot ) 530 { 531 root.setItem( COSName.STRUCT_TREE_ROOT, treeRoot ); 532 } 533 534 /** 535 * The language for the document. 536 * 537 * @return The language for the document. 538 */ 539 public String getLanguage() 540 { 541 return root.getString( COSName.LANG ); 542 } 543 544 /** 545 * Set the Language for the document. 546 * 547 * @param language The new document language. 548 */ 549 public void setLanguage( String language ) 550 { 551 root.setString( COSName.LANG, language ); 552 } 553 554 /** 555 * Returns the page labels descriptor of the document. 556 * 557 * @return the page labels descriptor of the document. 558 * 559 * @throws IOException If there is a problem retrieving the page labels. 560 */ 561 public PDPageLabels getPageLabels() throws IOException 562 { 563 PDPageLabels labels = null; 564 COSDictionary dict = (COSDictionary) root.getDictionaryObject(COSName.PAGE_LABELS); 565 if (dict != null) 566 { 567 labels = new PDPageLabels(document, dict); 568 } 569 return labels; 570 } 571 572 /** 573 * Set the page label descriptor for the document. 574 * 575 * @param labels the new page label descriptor to set. 576 */ 577 public void setPageLabels(PDPageLabels labels) 578 { 579 root.setItem(COSName.PAGE_LABELS, labels); 580 } 581 582 }