Skip to content

Commit

Permalink
use consistent nomenclature for outline items
Browse files Browse the repository at this point in the history
  • Loading branch information
mtd91429 committed Jul 23, 2022
1 parent 27702c2 commit dc29db0
Show file tree
Hide file tree
Showing 11 changed files with 450 additions and 284 deletions.
253 changes: 151 additions & 102 deletions PyPDF2/_merger.py

Large diffs are not rendered by default.

101 changes: 56 additions & 45 deletions PyPDF2/_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
TreeObject,
read_object,
)
from .types import OutlinesType, PagemodeType
from .types import OutlineType, PagemodeType
from .xmp import XmpInformation


Expand Down Expand Up @@ -673,19 +673,30 @@ def getNamedDestinations(
return self._get_named_destinations(tree, retval)

@property
def outlines(self) -> OutlinesType:
def outline(self) -> OutlineType:
"""
Read-only property for outlines present in the document.
Read-only property for the outline (i.e., a collection of 'outline items'
which are also known as 'bookmarks') present in the document.
:return: a nested list of :class:`Destinations<PyPDF2.generic.Destination>`.
"""
return self._get_outlines()
return self._get_outline()

def _get_outlines(
self, node: Optional[DictionaryObject] = None, outlines: Optional[Any] = None
) -> OutlinesType:
if outlines is None:
outlines = []
@property
def outlines(self) -> OutlineType:
"""
.. deprecated:: 2.8.0
Use :py:attr:`outline` instead.
"""
deprecate_with_replacement("outlines", "outline")
return self._get_outline()

def _get_outline(
self, node: Optional[DictionaryObject] = None, outline: Optional[Any] = None
) -> OutlineType:
if outline is None:
outline = []
catalog = cast(DictionaryObject, self.trailer[TK.ROOT])

# get the outline dictionary and named destinations
Expand All @@ -696,48 +707,48 @@ def _get_outlines(
# this occurs if the /Outlines object reference is incorrect
# for an example of such a file, see https://unglueit-files.s3.amazonaws.com/ebf/7552c42e9280b4476e59e77acc0bc812.pdf
# so continue to load the file without the Bookmarks
return outlines
return outline

if isinstance(lines, NullObject):
return outlines
return outline

# TABLE 8.3 Entries in the outline dictionary
if lines is not None and "/First" in lines:
node = cast(DictionaryObject, lines["/First"])
self._namedDests = self._get_named_destinations()

if node is None:
return outlines
return outline

# see if there are any more outlines
# see if there are any more outline items
while True:
outline = self._build_outline(node)
if outline:
outlines.append(outline)
outline_obj = self._build_outline_item(node)
if outline_obj:
outline.append(outline_obj)

# check for sub-outlines
# check for sub-outline
if "/First" in node:
sub_outlines: List[Any] = []
self._get_outlines(cast(DictionaryObject, node["/First"]), sub_outlines)
if sub_outlines:
outlines.append(sub_outlines)
sub_outline: List[Any] = []
self._get_outline(cast(DictionaryObject, node["/First"]), sub_outline)
if sub_outline:
outline.append(sub_outline)

if "/Next" not in node:
break
node = cast(DictionaryObject, node["/Next"])

return outlines
return outline

def getOutlines(
self, node: Optional[DictionaryObject] = None, outlines: Optional[Any] = None
) -> OutlinesType: # pragma: no cover
self, node: Optional[DictionaryObject] = None, outline: Optional[Any] = None
) -> OutlineType: # pragma: no cover
"""
.. deprecated:: 1.28.0
Use :py:attr:`outlines` instead.
Use :py:attr:`outline` instead.
"""
deprecate_with_replacement("getOutlines", "outlines")
return self._get_outlines(node, outlines)
deprecate_with_replacement("getOutlines", "outline")
return self._get_outline(node, outline)

def _get_page_number_by_indirect(
self, indirect_ref: Union[None, int, NullObject, IndirectObject]
Expand Down Expand Up @@ -805,7 +816,7 @@ def _build_destination(
array: List[Union[NumberObject, IndirectObject, NullObject, DictionaryObject]],
) -> Destination:
page, typ = None, None
# handle outlines with missing or invalid destination
# handle outline items with missing or invalid destination
if (
isinstance(array, (type(None), NullObject))
or (isinstance(array, ArrayObject) and len(array) == 0)
Expand All @@ -831,8 +842,8 @@ def _build_destination(
title, indirect_ref, TextStringObject("/Fit") # type: ignore
)

def _build_outline(self, node: DictionaryObject) -> Optional[Destination]:
dest, title, outline = None, None, None
def _build_outline_item(self, node: DictionaryObject) -> Optional[Destination]:
dest, title, outline_item = None, None, None

# title required for valid outline
# PDF Reference 1.7: TABLE 8.4 Entries in an outline item dictionary
Expand All @@ -857,38 +868,38 @@ def _build_outline(self, node: DictionaryObject) -> Optional[Destination]:
dest = dest["/D"]

if isinstance(dest, ArrayObject):
outline = self._build_destination(title, dest) # type: ignore
outline_item = self._build_destination(title, dest) # type: ignore
elif isinstance(dest, str):
# named destination, addresses NameObject Issue #193
try:
outline = self._build_destination(title, self._namedDests[dest].dest_array) # type: ignore
outline_item = self._build_destination(title, self._namedDests[dest].dest_array) # type: ignore
except KeyError:
# named destination not found in Name Dict
outline = self._build_destination(title, None)
outline_item = self._build_destination(title, None)
elif isinstance(dest, type(None)):
# outline not required to have destination or action
# outline item not required to have destination or action
# PDFv1.7 Table 153
outline = self._build_destination(title, dest) # type: ignore
outline_item = self._build_destination(title, dest) # type: ignore
else:
if self.strict:
raise PdfReadError(f"Unexpected destination {dest!r}")
outline = self._build_destination(title, None) # type: ignore
outline_item = self._build_destination(title, None) # type: ignore

# if outline created, add color, format, and child count if present
if outline:
# if outline item created, add color, format, and child count if present
if outline_item:
if "/C" in node:
# Color of outline in (R, G, B) with values ranging 0.0-1.0
outline[NameObject("/C")] = ArrayObject(FloatObject(c) for c in node["/C"]) # type: ignore
# Color of outline item font in (R, G, B) with values ranging 0.0-1.0
outline_item[NameObject("/C")] = ArrayObject(FloatObject(c) for c in node["/C"]) # type: ignore
if "/F" in node:
# specifies style characteristics bold and/or italic
# 1=italic, 2=bold, 3=both
outline[NameObject("/F")] = node["/F"]
outline_item[NameObject("/F")] = node["/F"]
if "/Count" in node:
# absolute value = num. visible children
# positive = open/unfolded, negative = closed/folded
outline[NameObject("/Count")] = node["/Count"]
outline_item[NameObject("/Count")] = node["/Count"]

return outline
return outline_item

@property
def pages(self) -> _VirtualList:
Expand Down Expand Up @@ -955,9 +966,9 @@ def page_mode(self) -> Optional[PagemodeType]:
:widths: 50 200
* - /UseNone
- Do not show outlines or thumbnails panels
- Do not show outline or thumbnails panels
* - /UseOutlines
- Show outlines (aka bookmarks) panel
- Show outline (aka bookmarks) panel
* - /UseThumbs
- Show page thumbnails panel
* - /FullScreen
Expand Down

0 comments on commit dc29db0

Please sign in to comment.