@@ -282,9 +282,7 @@ class PdfReader:
282
282
@property
283
283
def viewer_preferences (self ) -> Optional [ViewerPreferences ]:
284
284
"""Returns the existing ViewerPreferences as an overloaded dictionary."""
285
- o = cast (DictionaryObject , self .trailer ["/Root" ]).get (
286
- CD .VIEWER_PREFERENCES , None
287
- )
285
+ o = self .root_object .get (CD .VIEWER_PREFERENCES , None )
288
286
if o is None :
289
287
return None
290
288
o = o .get_object ()
@@ -344,6 +342,33 @@ def __init__(
344
342
elif password is not None :
345
343
raise PdfReadError ("Not encrypted file" )
346
344
345
+ @property
346
+ def root_object (self ) -> DictionaryObject :
347
+ """Provide access to "/Root". standardized with PdfWriter."""
348
+ return cast (DictionaryObject , self .trailer [TK .ROOT ].get_object ())
349
+
350
+ @property
351
+ def _info (self ) -> Optional [DictionaryObject ]:
352
+ """
353
+ Provide access to "/Info". standardized with PdfWriter.
354
+
355
+ Returns:
356
+ /Info Dictionary ; None if the entry does not exists
357
+ """
358
+ info = self .trailer .get (TK .INFO , None )
359
+ return None if info is None else cast (DictionaryObject , info .get_object ())
360
+
361
+ @property
362
+ def _ID (self ) -> Optional [ArrayObject ]:
363
+ """
364
+ Provide access to "/ID". standardized with PdfWriter.
365
+
366
+ Returns:
367
+ /ID array ; None if the entry does not exists
368
+ """
369
+ id = self .trailer .get (TK .ID , None )
370
+ return None if id is None else cast (ArrayObject , id .get_object ())
371
+
347
372
def _repr_mimebundle_ (
348
373
self ,
349
374
include : Union [None , Iterable [str ]] = None ,
@@ -400,21 +425,20 @@ def metadata(self) -> Optional[DocumentInformation]:
400
425
"""
401
426
if TK .INFO not in self .trailer :
402
427
return None
403
- obj = self .trailer [TK .INFO ]
404
428
retval = DocumentInformation ()
405
- if isinstance (obj , type (None )):
429
+ if isinstance (self . _info , type (None )):
406
430
raise PdfReadError (
407
431
"trailer not found or does not point to document information directory"
408
432
)
409
- retval .update (obj ) # type: ignore
433
+ retval .update (self . _info ) # type: ignore
410
434
return retval
411
435
412
436
@property
413
437
def xmp_metadata (self ) -> Optional [XmpInformation ]:
414
438
"""XMP (Extensible Metadata Platform) data."""
415
439
try :
416
440
self ._override_encryption = True
417
- return self .trailer [ TK . ROOT ] .xmp_metadata # type: ignore
441
+ return self .root_object .xmp_metadata # type: ignore
418
442
finally :
419
443
self ._override_encryption = False
420
444
@@ -433,7 +457,7 @@ def _get_num_pages(self) -> int:
433
457
# the PDF file's page count is used in this case. Otherwise,
434
458
# the original method (flattened page count) is used.
435
459
if self .is_encrypted :
436
- return self .trailer [ TK . ROOT ] ["/Pages" ]["/Count" ] # type: ignore
460
+ return self .root_object ["/Pages" ]["/Count" ] # type: ignore
437
461
else :
438
462
if self .flattened_pages is None :
439
463
self ._flatten ()
@@ -493,7 +517,7 @@ def get_fields(
493
517
field_attributes .update (CheckboxRadioButtonAttributes .attributes_dict ())
494
518
if retval is None :
495
519
retval = {}
496
- catalog = cast ( DictionaryObject , self .trailer [ TK . ROOT ])
520
+ catalog = self .root_object
497
521
# get the AcroForm tree
498
522
if CD .ACRO_FORM in catalog :
499
523
tree = cast (Optional [TreeObject ], catalog [CD .ACRO_FORM ])
@@ -755,7 +779,7 @@ def _get_named_destinations(
755
779
"""
756
780
if retval is None :
757
781
retval = {}
758
- catalog = cast ( DictionaryObject , self .trailer [ TK . ROOT ])
782
+ catalog = self .root_object
759
783
760
784
# get the name tree
761
785
if CA .DESTS in catalog :
@@ -822,7 +846,7 @@ def _get_outline(
822
846
) -> OutlineType :
823
847
if outline is None :
824
848
outline = []
825
- catalog = cast ( DictionaryObject , self .trailer [ TK . ROOT ])
849
+ catalog = self .root_object
826
850
827
851
# get the outline dictionary and named destinations
828
852
if CO .OUTLINES in catalog :
@@ -868,7 +892,7 @@ def threads(self) -> Optional[ArrayObject]:
868
892
It's an array of dictionaries with "/F" and "/I" properties or
869
893
None if there are no articles.
870
894
"""
871
- catalog = cast ( DictionaryObject , self .trailer [ TK . ROOT ])
895
+ catalog = self .root_object
872
896
if CO .THREADS in catalog :
873
897
return cast ("ArrayObject" , catalog [CO .THREADS ])
874
898
else :
@@ -1071,9 +1095,8 @@ def page_layout(self) -> Optional[str]:
1071
1095
* - /TwoPageRight
1072
1096
- Show two pages at a time, odd-numbered pages on the right
1073
1097
"""
1074
- trailer = cast (DictionaryObject , self .trailer [TK .ROOT ])
1075
- if CD .PAGE_LAYOUT in trailer :
1076
- return cast (NameObject , trailer [CD .PAGE_LAYOUT ])
1098
+ if CD .PAGE_LAYOUT in self .root_object :
1099
+ return cast (NameObject , self .root_object [CD .PAGE_LAYOUT ])
1077
1100
return None
1078
1101
1079
1102
@property
@@ -1098,7 +1121,7 @@ def page_mode(self) -> Optional[PagemodeType]:
1098
1121
- Show attachments panel
1099
1122
"""
1100
1123
try :
1101
- return self .trailer [ TK . ROOT ] ["/PageMode" ] # type: ignore
1124
+ return self .root_object ["/PageMode" ] # type: ignore
1102
1125
except KeyError :
1103
1126
return None
1104
1127
@@ -1119,12 +1142,12 @@ def _flatten(
1119
1142
if pages is None :
1120
1143
# Fix issue 327: set flattened_pages attribute only for
1121
1144
# decrypted file
1122
- catalog = self .trailer [ TK . ROOT ]. get_object ()
1123
- pages = catalog ["/Pages" ].get_object () # type: ignore
1145
+ catalog = self .root_object
1146
+ pages = cast ( DictionaryObject , catalog ["/Pages" ].get_object ())
1124
1147
self .flattened_pages = []
1125
1148
1126
1149
if PA .TYPE in pages :
1127
- t = pages [PA .TYPE ]
1150
+ t = cast ( str , pages [PA .TYPE ])
1128
1151
# if pdf has no type, considered as a page if /Kids is missing
1129
1152
elif PA .KIDS not in pages :
1130
1153
t = "/Page"
@@ -1925,7 +1948,7 @@ def is_encrypted(self) -> bool:
1925
1948
def xfa (self ) -> Optional [Dict [str , Any ]]:
1926
1949
tree : Optional [TreeObject ] = None
1927
1950
retval : Dict [str , Any ] = {}
1928
- catalog = cast ( DictionaryObject , self .trailer [ TK . ROOT ])
1951
+ catalog = self .root_object
1929
1952
1930
1953
if "/AcroForm" not in catalog or not catalog ["/AcroForm" ]:
1931
1954
return None
@@ -1955,7 +1978,7 @@ def add_form_topname(self, name: str) -> Optional[DictionaryObject]:
1955
1978
Returns:
1956
1979
The created object. ``None`` means no object was created.
1957
1980
"""
1958
- catalog = cast ( DictionaryObject , self .trailer [ TK . ROOT ])
1981
+ catalog = self .root_object
1959
1982
1960
1983
if "/AcroForm" not in catalog or not isinstance (
1961
1984
catalog ["/AcroForm" ], DictionaryObject
@@ -1997,7 +2020,7 @@ def rename_form_topname(self, name: str) -> Optional[DictionaryObject]:
1997
2020
Returns:
1998
2021
The modified object. ``None`` means no object was modified.
1999
2022
"""
2000
- catalog = cast ( DictionaryObject , self .trailer [ TK . ROOT ])
2023
+ catalog = self .root_object
2001
2024
2002
2025
if "/AcroForm" not in catalog or not isinstance (
2003
2026
catalog ["/AcroForm" ], DictionaryObject
@@ -2030,7 +2053,7 @@ def _list_attachments(self) -> List[str]:
2030
2053
Returns:
2031
2054
list of filenames
2032
2055
"""
2033
- catalog = cast ( DictionaryObject , self .trailer [ "/Root" ])
2056
+ catalog = self .root_object
2034
2057
# From the catalog get the embedded file names
2035
2058
try :
2036
2059
filenames = cast (
@@ -2068,7 +2091,7 @@ def _get_attachments(
2068
2091
dictionary of filename -> Union[bytestring or List[ByteString]]
2069
2092
if the filename exists multiple times a List of the different version will be provided
2070
2093
"""
2071
- catalog = cast ( DictionaryObject , self .trailer [ "/Root" ])
2094
+ catalog = self .root_object
2072
2095
# From the catalog get the embedded file names
2073
2096
try :
2074
2097
filenames = cast (
0 commit comments