From 25cba33f88c6708ebc50169808f02b80e96fb0ab Mon Sep 17 00:00:00 2001 From: mtd91429 Date: Mon, 18 Jul 2022 00:45:36 -0500 Subject: [PATCH] ENH: Add `outline_count` property (#1129) Enables retrieval of "/Count" attribute of outline item in PdfReader.outlines by implementing property outline_count. Closes #1122 --- PyPDF2/_reader.py | 6 ++++- PyPDF2/generic.py | 10 ++++++++ tests/test_reader.py | 58 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/PyPDF2/_reader.py b/PyPDF2/_reader.py index 43c79292e..ac92e4e16 100644 --- a/PyPDF2/_reader.py +++ b/PyPDF2/_reader.py @@ -838,7 +838,7 @@ def _build_outline(self, node: DictionaryObject) -> Optional[Destination]: else: raise PdfReadError(f"Unexpected destination {dest!r}") - # if outline created, add color and format if present + # if outline created, add color, format, and child count if present if outline: if "/C" in node: # Color of outline in (R, G, B) with values ranging 0.0-1.0 @@ -847,6 +847,10 @@ def _build_outline(self, node: DictionaryObject) -> Optional[Destination]: # specifies style characteristics bold and/or italic # 1=italic, 2=bold, 3=both outline[NameObject("/F")] = node["/F"] + if "/Count" in node: + # absolute value = num. visible children + # positive = open/unfolded, negative = closed/folded + outline[NameObject("/Count")] = node["/Count"] return outline diff --git a/PyPDF2/generic.py b/PyPDF2/generic.py index aa5444620..ff416d145 100644 --- a/PyPDF2/generic.py +++ b/PyPDF2/generic.py @@ -1901,6 +1901,16 @@ def font_format(self) -> Optional[OutlineFontFlag]: """Read-only property accessing the font type. 1=italic, 2=bold, 3=both""" return self.get("/F", 0) + @property + def outline_count(self) -> Optional[int]: + """ + Read-only property accessing the outline count. + positive = expanded + negative = collapsed + absolute value = number of visible descendents at all levels + """ + return self.get("/Count", None) + class Bookmark(Destination): def write_to_stream( diff --git a/tests/test_reader.py b/tests/test_reader.py index 013bb0ed4..f163e8de7 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -933,3 +933,61 @@ def get_titles_only(outlines, results=None): "Twenty-seventh", ], ] + + +def test_outline_count(): + reader = PdfReader(EXTERNAL_ROOT / "014-outlines/mistitled_outlines_example.pdf") + + def get_counts_only(outlines, results=None): + if results is None: + results = [] + if isinstance(outlines, list): + for outline in outlines: + if isinstance(outline, Destination): + results.append(outline.outline_count) + else: + results.append(get_counts_only(outline)) + else: + raise ValueError(f"got {type(outlines)}") + return results + assert get_counts_only(reader.outlines) == [ + 5, + [ + None, + None, + 2, + [ + None, + None, + ], + -2, + [ + None, + None, + ], + ], + 4, + [ + None, + None, + None, + None, + ], + -2, + [ + None, + None, + ], + None, + 8, + [ + None, + None, + None, + None, + None, + None, + None, + None, + ], + ]