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

MAINT: Cleanup of annotations #1745

Merged
merged 29 commits into from
Jul 29, 2023
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8b3ced1
MAINT: Cleanup of annotations
MartinThoma Mar 25, 2023
0d619e6
Merge branch 'main' into annotations
MartinThoma Mar 26, 2023
d26b44b
Merge branch 'main' into annotations
MartinThoma Mar 29, 2023
6dc5f24
Restructure
MartinThoma Mar 29, 2023
b5e09c3
/Text should be /FreeText in FreeText class
MartinThoma Mar 30, 2023
9b63511
Use kwargs and super
MartinThoma Mar 30, 2023
3273d5f
Default for title bar
MartinThoma Mar 30, 2023
72951e2
Fix title_bar attribute - thanks pubpub-zz!
MartinThoma Mar 30, 2023
3c10fa0
Make MarkupAnnotation abstract + add docstring
MartinThoma Mar 30, 2023
ccb49ca
Expose markup annotation
MartinThoma Mar 30, 2023
a8a85e1
Annotation module docstring
MartinThoma Mar 30, 2023
182735e
Adjust deprecation messages
MartinThoma Mar 31, 2023
a93c271
Move popup-annotation
MartinThoma Mar 31, 2023
c32a298
Add deprections
MartinThoma Mar 31, 2023
bad1ceb
Adjust tests
MartinThoma Mar 31, 2023
6fcd6a5
Make flags a property
MartinThoma Mar 31, 2023
56959dd
Export AnnotationDictionary
MartinThoma Mar 31, 2023
0087edc
Fix flags
MartinThoma Mar 31, 2023
deca45b
Fix tests
MartinThoma Apr 1, 2023
5291312
Merge branch 'main' into annotations
MartinThoma Apr 3, 2023
1f0d485
BUG: Fix x_max / y_max
MartinThoma Apr 8, 2023
e41e238
Merge branch 'main' into annotations
MartinThoma Jun 11, 2023
b14199b
Merge branch 'main' into annotations
MartinThoma Jun 25, 2023
59f724b
Merge branch 'main' into annotations
MartinThoma Jul 6, 2023
0df6da0
Apply suggestions from code review
MartinThoma Jul 9, 2023
92af890
Merge branch 'main' into annotations
MartinThoma Jul 17, 2023
98a3178
Merge branch 'main' into annotations
MartinThoma Jul 29, 2023
dd5aaa9
Update test
MartinThoma Jul 29, 2023
e7ccd15
Make title_bar default to None instead of empty string
MartinThoma Jul 29, 2023
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
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ You can contribute to `pypdf on GitHub <https://github.com/py-pdf/pypdf>`_.
modules/RectangleObject
modules/Field
modules/PageRange
modules/AnnotationBuilder
modules/annotations
modules/Fit
modules/PaperSize

Expand Down
7 changes: 0 additions & 7 deletions docs/modules/AnnotationBuilder.rst

This file was deleted.

7 changes: 7 additions & 0 deletions docs/modules/annotations.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
The annotations module
----------------------

.. automodule:: pypdf.annotations
:members:
:undoc-members:
:show-inheritance:
72 changes: 50 additions & 22 deletions docs/user/adding-pdf-annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ If you want to add text in a box like this

![](free-text-annotation.png)

you can use the {py:class}`AnnotationBuilder <pypdf.generic.AnnotationBuilder>`:
you can use the {py:class}`FreeText <pypdf.annotations.FreeText>`:

```python
from pypdf import PdfReader, PdfWriter
from pypdf.generic import AnnotationBuilder
from pypdf.annotations import FreeText

# Fill the writer with the pages you want
pdf_path = os.path.join(RESOURCE_ROOT, "crazyones.pdf")
Expand All @@ -36,8 +36,8 @@ writer = PdfWriter()
writer.add_page(page)

# Create the annotation and add it
annotation = AnnotationBuilder.free_text(
"Hello World\nThis is the second line!",
annotation = FreeText(
text="Hello World\nThis is the second line!",
rect=(50, 550, 200, 650),
font="Arial",
bold=True,
Expand Down Expand Up @@ -66,17 +66,20 @@ If you want to add a line like this:

![](annotation-line.png)

you can use the {py:class}`AnnotationBuilder <pypdf.generic.AnnotationBuilder>`:
you can use {py:class}`Line <pypdf.annotations.Line>`:

```python
from pypdf import PdfReader, PdfWriter
from pypdf.annotations import Line

pdf_path = os.path.join(RESOURCE_ROOT, "crazyones.pdf")
reader = PdfReader(pdf_path)
page = reader.pages[0]
writer = PdfWriter()
writer.add_page(page)

# Add the line
annotation = AnnotationBuilder.line(
annotation = Line(
text="Hello World\nLine2",
rect=(50, 550, 200, 650),
p1=(50, 550),
Expand All @@ -95,17 +98,20 @@ If you want to add a line like this:

![](annotation-polyline.png)

you can use the {py:class}`AnnotationBuilder <pypdf.generic.AnnotationBuilder>`:
you can use {py:class}`PolyLine <pypdf.annotations.PolyLine>`:

```python
from pypdf import PdfReader, PdfWriter
from pypdf.annotations import Polyline

pdf_path = os.path.join(RESOURCE_ROOT, "crazyones.pdf")
reader = PdfReader(pdf_path)
page = reader.pages[0]
writer = PdfWriter()
writer.add_page(page)

# Add the polyline
annotation = AnnotationBuilder.polyline(
annotation = Polyline(
vertices=[(50, 550), (200, 650), (70, 750), (50, 700)],
)
writer.add_annotation(page_number=0, annotation=annotation)
Expand All @@ -121,17 +127,20 @@ If you want to add a rectangle like this:

![](annotation-square.png)

you can use the {py:class}`AnnotationBuilder <pypdf.generic.AnnotationBuilder>`:
you can use {py:class}`Rectangle <pypdf.annotations.Rectangle>`:

```python
from pypdf import PdfReader, PdfWriter
from pypdf.annotations import Rectangle

pdf_path = os.path.join(RESOURCE_ROOT, "crazyones.pdf")
reader = PdfReader(pdf_path)
page = reader.pages[0]
writer = PdfWriter()
writer.add_page(page)

# Add the rectangle
annotation = AnnotationBuilder.rectangle(
annotation = Rectangle(
rect=(50, 550, 200, 650),
)
writer.add_annotation(page_number=0, annotation=annotation)
Expand All @@ -152,15 +161,20 @@ If you want to add a circle like this:

![](annotation-circle.png)

you can use {py:class}`Ellipse <pypdf.annotations.Ellipse>`:

```python
from pypdf import PdfReader, PdfWriter
from pypdf.annotations import Ellipse

pdf_path = os.path.join(RESOURCE_ROOT, "crazyones.pdf")
reader = PdfReader(pdf_path)
page = reader.pages[0]
writer = PdfWriter()
writer.add_page(page)

# Add the rectangle
annotation = AnnotationBuilder.ellipse(
annotation = Ellipse(
rect=(50, 550, 200, 650),
)
writer.add_annotation(page_number=0, annotation=annotation)
Expand All @@ -176,17 +190,20 @@ If you want to add a polygon like this:

![](annotation-polygon.png)

you can use the {py:class}`AnnotationBuilder <pypdf.generic.AnnotationBuilder>`:
you can use {py:class}`Polygon <pypdf.annotations.Polygon>`:

```python
from pypdf import PdfReader, PdfWriter
from pypdf.annotations import Polygon

pdf_path = os.path.join(RESOURCE_ROOT, "crazyones.pdf")
reader = PdfReader(pdf_path)
page = reader.pages[0]
writer = PdfWriter()
writer.add_page(page)

# Add the line
annotation = AnnotationBuilder.polygon(
annotation = Polygon(
vertices=[(50, 550), (200, 650), (70, 750), (50, 700)],
)
writer.add_annotation(page_number=0, annotation=annotation)
Expand All @@ -202,26 +219,28 @@ Manage the Popup windows for markups. looks like this:

![](annotation-popup.png)

you can use the {py:class}`AnnotationBuilder <pypdf.generic.AnnotationBuilder>`:
you can use the {py:class}`Popup <pypdf.annotations.Popup>`:

you have to use the returned result from add_annotation() to fill-up the

```python
from pypdf.annotations import Popup, Text

# Arrange
writer = pypdf.PdfWriter()
writer.append(os.path.join(RESOURCE_ROOT, "crazyones.pdf"), [0])

# Act
text_annotation = writer.add_annotation(
0,
AnnotationBuilder.text(
Text(
text="Hello World\nThis is the second line!",
rect=(50, 550, 200, 650),
open=True,
),
)

popup_annotation = AnnotationBuilder.popup(
popup_annotation = Popup(
rect=(50, 550, 200, 650),
open=True,
parent=text_annotation, # use the output of add_annotation
Expand All @@ -233,17 +252,20 @@ writer.write("annotated-pdf-popup.pdf")
## Link

If you want to add a link, you can use
the {py:class}`AnnotationBuilder <pypdf.generic.AnnotationBuilder>`:
{py:class}`Link <pypdf.annotations.Link>`:

```python
from pypdf import PdfReader, PdfWriter
from pypdf.annotations import Link

pdf_path = os.path.join(RESOURCE_ROOT, "crazyones.pdf")
reader = PdfReader(pdf_path)
page = reader.pages[0]
writer = PdfWriter()
writer.add_page(page)

# Add the line
annotation = AnnotationBuilder.link(
annotation = Link(
rect=(50, 550, 200, 650),
url="https://martin-thoma.com/",
)
Expand All @@ -257,14 +279,17 @@ with open("annotated-pdf.pdf", "wb") as fp:
You can also add internal links:

```python
from pypdf import PdfReader, PdfWriter
from pypdf.annotations import Link

pdf_path = os.path.join(RESOURCE_ROOT, "crazyones.pdf")
reader = PdfReader(pdf_path)
page = reader.pages[0]
writer = PdfWriter()
writer.add_page(page)

# Add the line
annotation = AnnotationBuilder.link(
annotation = Link(
rect=(50, 550, 200, 650), target_page_index=3, fit="/FitH", fit_args=(123,)
)
writer.add_annotation(page_number=0, annotation=annotation)
Expand All @@ -287,17 +312,20 @@ If you want to highlight text like this:

![](annotation-highlight.png)

you can use the {py:class}`AnnotationBuilder <pypdf.generic.AnnotationBuilder>`:
you can use the {py:class}`Highlight <pypdf.annotations.Highlight>`:

```python
from pypdf import PdfReader, PdfWriter
from pypdf.annotations import Hihglight
MartinThoma marked this conversation as resolved.
Show resolved Hide resolved

pdf_path = os.path.join(RESOURCE_ROOT, "crazyones.pdf")
reader = PdfReader(pdf_path)
page = reader.pages[0]
writer = PdfWriter()
writer.add_page(page)

# Add the line
annotation = AnnotationBuilder.polygon(
# Add the highlight
annotation = Hihglight(
MartinThoma marked this conversation as resolved.
Show resolved Hide resolved
vertices=[(50, 550), (200, 650), (70, 750), (50, 700)],
)
writer.add_annotation(page_number=0, annotation=annotation)
Expand Down
8 changes: 4 additions & 4 deletions pypdf/_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
deprecation_with_replacement,
logger_warning,
)
from .annotations import Link
from .constants import AnnotationDictionaryAttributes as AA
from .constants import CatalogAttributes as CA
from .constants import (
Expand All @@ -89,7 +90,6 @@
from .constants import TrailerKeys as TK
from .generic import (
PAGE_FIT,
AnnotationBuilder,
ArrayObject,
BooleanObject,
ByteStringObject,
Expand Down Expand Up @@ -2308,7 +2308,7 @@ def add_link(
*args: ZoomArgType,
) -> DictionaryObject:
deprecation_with_replacement(
"add_link", "add_annotation(AnnotationBuilder.link(...))"
"add_link", "add_annotation(pypdf.annotations.Link(...))"
)

if isinstance(rect, str):
Expand All @@ -2321,7 +2321,7 @@ def add_link(
else:
rect = RectangleObject(rect)

annotation = AnnotationBuilder.link(
annotation = Link(
rect=rect,
border=border,
target_page_index=page_destination,
Expand All @@ -2344,7 +2344,7 @@ def addLink(
.. deprecated:: 1.28.0
"""
deprecate_with_replacement(
"addLink", "add_annotation(AnnotationBuilder.link(...))", "4.0.0"
"addLink", "add_annotation(pypdf.annotations.Link(...))", "4.0.0"
)
self.add_link(pagenum, page_destination, rect, border, fit, *args)

Expand Down
46 changes: 46 additions & 0 deletions pypdf/annotations/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""
PDF specifies several annotation types which pypdf makes available here.

The names of the annotations and their attributes do not reflect the names in
the specification in all cases. For example, the PDF standard defines a
'Square' annotation that does not actually need to be square. For this reason,
pypdf calls it 'Rectangle'.

At their core, all annotation types are DictionaryObjects. That means if pypdf
does not implement a feature, users can easily extend the given functionality.
"""


from ._base import NO_FLAGS, AnnotationDictionary
from ._markup_annotations import (
Ellipse,
FreeText,
Highlight,
Line,
Link,
MarkupAnnotation,
Polygon,
PolyLine,
Rectangle,
Text,
)
from ._non_markup_annotations import Popup

__all__ = [
"NO_FLAGS",
# Export abstract base classes so that they are shown in the docs
"AnnotationDictionary",
"MarkupAnnotation",
# markup annotations
"Ellipse",
"FreeText",
"Highlight",
"Line",
"Link",
"Polygon",
"PolyLine",
"Rectangle",
"Text",
# Non-markup annotations
"Popup",
]
27 changes: 27 additions & 0 deletions pypdf/annotations/_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from abc import ABC

from ..constants import AnnotationFlag
from ..generic import NameObject, NumberObject
from ..generic._data_structures import DictionaryObject


class AnnotationDictionary(DictionaryObject, ABC):
def __init__(self) -> None:
from ..generic._base import NameObject

# "rect" should not be added here as PolyLine can automatically set it
self[NameObject("/Type")] = NameObject("/Annot")
# The flags was NOT added to the constructor on purpose: We expect that
# most users don't want to change the default. If they want, they
# can use the property. The default is 0.

@property
def flags(self) -> AnnotationFlag:
return self.get(NameObject("/F"), AnnotationFlag(0))

@flags.setter
def flags(self, value: AnnotationFlag) -> None:
self[NameObject("/F")] = NumberObject(value)


NO_FLAGS = AnnotationFlag(0)
Loading