Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BUG: Handle empty root outline #2239

Merged
merged 4 commits into from
Oct 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions pypdf/_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,13 +295,13 @@
obj: PdfObject,
) -> PdfObject:
if isinstance(indirect_reference, IndirectObject):
assert indirect_reference.pdf == self
if indirect_reference.pdf != self:
raise ValueError("pdf must be self")

Check warning on line 299 in pypdf/_writer.py

View check run for this annotation

Codecov / codecov/patch

pypdf/_writer.py#L299

Added line #L299 was not covered by tests
indirect_reference = indirect_reference.idnum
gen = self._objects[indirect_reference - 1].indirect_reference.generation # type: ignore
self._objects[indirect_reference - 1] = obj
obj.indirect_reference = IndirectObject(indirect_reference, gen, self)
return self._objects[indirect_reference - 1]
if indirect_reference.pdf != self:
raise ValueError("pdf must be self")
return self._objects[indirect_reference.idnum - 1]

def _add_page(
self,
Expand Down Expand Up @@ -1623,6 +1623,10 @@
if CO.OUTLINES in self._root_object:
# TABLE 3.25 Entries in the catalog dictionary
outline = cast(TreeObject, self._root_object[CO.OUTLINES])
if not isinstance(outline, TreeObject):
t = TreeObject(outline)
self._replace_object(outline.indirect_reference.idnum, t)
outline = t
idnum = self._objects.index(outline) + 1
outline_ref = IndirectObject(idnum, 0, self)
assert outline_ref.get_object() == outline
Expand Down Expand Up @@ -1718,7 +1722,7 @@

def add_outline_item_destination(
self,
page_destination: Union[None, PageObject, TreeObject] = None,
page_destination: Union[None, IndirectObject, PageObject, TreeObject] = None,
parent: Union[None, TreeObject, IndirectObject] = None,
before: Union[None, TreeObject, IndirectObject] = None,
is_open: bool = True,
Expand All @@ -1744,6 +1748,7 @@
# argument is only Optional due to deprecated argument.
raise ValueError("page_destination may not be None")

page_destination = cast(PageObject, page_destination.get_object())
if isinstance(page_destination, PageObject):
return self.add_outline_item_destination(
Destination(
Expand Down Expand Up @@ -1928,7 +1933,9 @@
}
)
)
outline_item = _create_outline_item(action_ref, title, color, italic, bold)
outline_item = self._add_object(
_create_outline_item(action_ref, title, color, italic, bold)
)

if parent is None:
parent = self.get_outline_root()
Expand Down
4 changes: 3 additions & 1 deletion pypdf/generic/_data_structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -548,8 +548,10 @@ def readFromStream(


class TreeObject(DictionaryObject):
def __init__(self) -> None:
def __init__(self, dct: Optional[DictionaryObject] = None) -> None:
DictionaryObject.__init__(self)
if dct:
self.update(dct)

def hasChildren(self) -> bool: # deprecated
deprecate_with_replacement("hasChildren", "has_children", "4.0.0")
Expand Down
81 changes: 81 additions & 0 deletions tests/test_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import subprocess
from io import BytesIO
from pathlib import Path
from typing import Any

import pytest

Expand Down Expand Up @@ -1861,6 +1862,86 @@ def test_object_contains_indirect_reference_to_self():
writer.append(reader)


@pytest.mark.enable_socket()
def test_add_outlines_on_empty_dict():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test also passes with the current code

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the copy/paste of the URL of the test file failed and reported an other file.
thanks for notice.
this should be fixed.

"""Cf #2233"""

def _get_parent_bookmark(current_indent, history_indent, bookmarks) -> Any:
"""The parent of A is the nearest bookmark whose indent is smaller than A's"""
assert len(history_indent) == len(bookmarks)
if current_indent == 0:
return None
for i in range(len(history_indent) - 1, -1, -1):
# len(history_indent) - 1 ===> 0
if history_indent[i] < current_indent:
return bookmarks[i]
return None

bookmark_lines = """1 FUNDAMENTALS OF RADIATIVE TRANSFER 1
1.1 The Electromagnetic Spectrum; Elementary Properties of Radiation 1
1.2 Radiative Flux 2
Macroscopic Description of the Propagation of Radiation 2
Flux from an Isotropic Source-The Inverse Square Law 2
1.3 The Specific Intensity and Its Moments 3
Definition of Specific Intensity or Brightness 3
Net Flux and Momentum Flux 4
Radiative Energy Density 5
Radiation Pressure in an Enclosure Containing an Isotropic Radiation Field 6
Constancy of Specific Zntensiw Along Rays in Free Space 7
Proof of the Inverse Square Law for a Uniformly Bright Sphere 7
1.4 Radiative Transfer 8
Emission 9
Absorption 9
The Radiative Transfer Equation 11
Optical Depth and Source Function 12
Mean Free Path 14
Radiation Force 15
1.5 Thermal Radiation 15
Blackbody Radiation 15
Kirchhof's Law for Thermal Emission 16
Thermodynamics of Blackbody Radiation 17
The Planck Spectrum 20
Properties of the Planck Law 23
Characteristic Temperatures Related to Planck Spectrum 25
1.6 The Einstein Coefficients 27
Definition of Coefficients 27
Relations between Einstein Coefficients 29
Absorption and Emission Coefficients in Terms of Einstein Coefficients 30
1.7 Scattering Effects; Random Walks 33
Pure Scattering 33
Combined Scattering and Absorption 36
1.8 Radiative Diffusion 39
The Rosseland Approximation 39
The Eddington Approximation; Two-Stream Approximation 42
PROBLEMS 45
REFERENCES 50
2 BASIC THEORY OF RADIATION FIELDS 51
2.1 Review of Maxwell’s Equations 51
2.2 Plane Electromagnetic Waves 55
2.3 The Radiation Spectrum 58
2.4 Polarization and Stokes Parameters 62
Monochromatic Waves 62
Quasi-monochromatic Waves 65
2.5 Electromagnetic Potentials 69
2.6 Applicability of Transfer Theory and the Geometrical Optics Limit 72
PROBLEMS 74
REFERENCES 76"""
url = "https://github.com/py-pdf/pypdf/files/12797067/test-12.pdf"
name = "iss2233.pdf"
reader = PdfReader(BytesIO(get_data_from_url(url, name=name)))
writer = PdfWriter(clone_from=reader)

bookmarks, history_indent = [], []
for line in bookmark_lines.split("\n"):
line2 = re.split(r"\s+", line.strip())
indent_size = len(line) - len(line.lstrip())
parent = _get_parent_bookmark(indent_size, history_indent, bookmarks)
history_indent.append(indent_size)
title, page = " ".join(line2[:-1]), int(line2[-1]) - 1
new_bookmark = writer.add_outline_item(title, page, parent=parent)
bookmarks.append(new_bookmark)


def test_merging_many_temporary_files():
def create_number_pdf(n) -> BytesIO:
from fpdf import FPDF
Expand Down
Loading