Skip to content

Commit

Permalink
Merge branch 'PyFPDF:master' into increased_coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
RedShy authored May 21, 2022
2 parents fa4fa17 + f8b462c commit 3d52b12
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 18 deletions.
9 changes: 9 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,15 @@
"contributions": [
"doc"
]
},
{
"login": "gonzalobarbaran",
"name": "gonzalobarbaran",
"avatar_url": "https://avatars.githubusercontent.com/u/59395855?v=4",
"profile": "https://github.com/gonzalobarbaran",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
Expand Down
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ This can also be enabled programmatically with `warnings.simplefilter('default',
onto [text_annotation()](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.text_annotation)
- allowing correctly parsing of SVG files with CSS styling (`style="..."` attribute), thanks to @RedShy
- [`FPDF.star`](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.star): new method added to draw regular stars, thanks to @digidigital and @RedShy
- allowing to change appearance of [highlight annotations](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.add_highlight) by specifying a [`TextMarkupType`](https://pyfpdf.github.io/fpdf2/fpdf/enums.html#fpdf.enums.TextMarkupType)
- [`FPDF.ink_annotation`](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.ink_annotation): new method added to add path annotations
- allowing to change appearance of [highlight annotations](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.highlight) by specifying a [`TextMarkupType`](https://pyfpdf.github.io/fpdf2/fpdf/enums.html#fpdf.enums.TextMarkupType)
- documentation on how to control objects transparency: [link to docs](https://pyfpdf.github.io/fpdf2/Transparency.html)
- documentation on how to create tables and charts using [pandas](https://pandas.pydata.org/) DataFrames: [link to docs](https://pyfpdf.github.io/fpdf2/Maths.html), thanks to @iwayankurniawan

Expand Down Expand Up @@ -61,7 +62,7 @@ This can also be enabled programmatically with `warnings.simplefilter('default',
## [2.5.2] - 2022-04-13
### Added
- new parameters `new_x` and `new_y` for `cell()` and `multi_cell()`, replacing `ln=0`, thanks to @gmischler
- new `add_highlight()` method to insert highlight annotations: [documentation](https://pyfpdf.github.io/fpdf2/Annotations.html#highlights)
- new `highlight()` method to insert highlight annotations: [documentation](https://pyfpdf.github.io/fpdf2/Annotations.html#highlights)
- new `offset_rendering()` method: [documentation](https://pyfpdf.github.io/fpdf2/PageBreaks.html#unbreakable-sections)
- new `.text_mode` property: [documentation](https://pyfpdf.github.io/fpdf2/TextStyling.html#text_mode)
- the page structure of the documentation has been revised, with a new page about [adding text](https://pyfpdf.github.io/fpdf2/Text.html), thanks to @gmischler
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ This library could only exist thanks to the dedication of many volunteers around
<td align="center"><a href="https://github.com/cgkoutzigiannis"><img src="https://avatars.githubusercontent.com/u/41803093?v=4?s=100" width="100px;" alt=""/><br /><sub><b>cgkoutzigiannis</b></sub></a><br /><a href="https://github.com/PyFPDF/fpdf2/commits?author=cgkoutzigiannis" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/iwayankurniawan"><img src="https://avatars.githubusercontent.com/u/30134645?v=4?s=100" width="100px;" alt=""/><br /><sub><b>I Wayan Kurniawan</b></sub></a><br /><a href="https://github.com/PyFPDF/fpdf2/commits?author=iwayankurniawan" title="Documentation">📖</a></td>
<td align="center"><a href="https://rysta.io"><img src="https://avatars.githubusercontent.com/u/4029642?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sven Eliasson</b></sub></a><br /><a href="https://github.com/PyFPDF/fpdf2/commits?author=comino" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/gonzalobarbaran"><img src="https://avatars.githubusercontent.com/u/59395855?v=4?s=100" width="100px;" alt=""/><br /><sub><b>gonzalobarbaran</b></sub></a><br /><a href="https://github.com/PyFPDF/fpdf2/commits?author=gonzalobarbaran" title="Code">💻</a></td>
</tr>
</table>

Expand Down
22 changes: 20 additions & 2 deletions docs/Annotations.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ from fpdf import FPDF
pdf = FPDF()
pdf.add_page()
pdf.set_font("Helvetica", size=24)
with pdf.add_highlight("Highlight comment"):
with pdf.highlight("Highlight comment"):
pdf.text(50, 50, "Line 1")
pdf.set_y(50)
pdf.multi_cell(w=30, txt="Line 2")
Expand All @@ -46,12 +46,30 @@ pdf.output("highlighted.pdf")
Rendering by Sumatra PDF reader:
![Screenshot of highlight annotation rendered by Sumatra PDF reader](highlighted.png)

Method documentation: [`FPDF.add_highlight`](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.add_highlight)
Method documentation: [`FPDF.highlight`](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.highlight)

The appearance of the "highlight effect" can be controlled through the `type` argument:
it can be `Highlight` (default), `Underline`, `Squiggly` or `StrikeOut`.


## Ink annotations ##

Those annotations allow to draw paths around parts of a document to highlight them:
```python
from fpdf import FPDF

pdf = FPDF()
pdf.ink_annotation([(100, 200), (200, 100), (300, 200), (200, 300), (100, 200)],
title="Lucas", contents="Hello world!")
pdf.output("ink_annotation_demo.pdf")
```

Rendering by Firefox internal PDF viewer:
![Screenshot of ink annotation rendered by Firefox](ink_annotation.png)

Method documentation: [`FPDF.ink_annotation`](https://pyfpdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.ink_annotation)


## Named actions ##

The four standard PDF named actions provide some basic navigation relative to the current page:
Expand Down
Binary file added docs/ink_annotation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
66 changes: 56 additions & 10 deletions fpdf/fpdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ class Image:
from .structure_tree import MarkedContent, StructureTreeBuilder
from .svg import Percent, SVGObject
from .syntax import DestinationXYZ
from .syntax import create_dictionary_string as pdf_d
from .syntax import create_list_string as pdf_l
from .syntax import create_dictionary_string as pdf_dict
from .syntax import create_list_string as pdf_list
from .syntax import create_stream as pdf_stream
from .syntax import iobj_ref as pdf_ref
from .ttfonts import TTFontFile
Expand Down Expand Up @@ -133,6 +133,7 @@ class Annotation(NamedTuple):
page: Optional[int] = None
border_width: int = 0 # PDF readers support: displayed by Acrobat but not Sumatra
name: Optional[AnnotationName] = None # for text annotations
ink_list: Tuple[int] = () # for ink annotations

def serialize(self, fpdf):
"Convert this object dictionnary to a string"
Expand Down Expand Up @@ -176,17 +177,21 @@ def serialize(self, fpdf):

if self.quad_points:
# pylint: disable=not-an-iterable
quad_points = " ".join(
quad_points = pdf_list(
f"{quad_point:.2f}" for quad_point in self.quad_points
)
out += f" /QuadPoints [{quad_points}]"
out += f" /QuadPoints {quad_points}"

if self.page:
out += f" /P {pdf_ref(object_id_for_page(self.page))}"

if self.name:
out += f" /Name {self.name.value.pdf_repr()}"

if self.ink_list:
ink_list = pdf_list(f"{coord:.2f}" for coord in self.ink_list)
out += f" /InkList [{ink_list}]"

return out + ">>"


Expand Down Expand Up @@ -1945,7 +1950,7 @@ def add_action(self, action, x, y, w, h):
return annotation

@contextmanager
def add_highlight(
def highlight(
self, text, title="", type="Highlight", color=(1, 1, 0), modification_time=None
):
"""
Expand All @@ -1962,7 +1967,7 @@ def add_highlight(
modification_time (datetime): date and time when the annotation was most recently modified
"""
if self.record_text_quad_points:
raise FPDFException("add_highlight() cannot be nested")
raise FPDFException("highlight() cannot be nested")
self.record_text_quad_points = True
yield
for page, quad_points in self.text_quad_points.items():
Expand All @@ -1978,6 +1983,8 @@ def add_highlight(
self.text_quad_points = defaultdict(list)
self.record_text_quad_points = False

add_highlight = highlight # For backward compatibilty

@check_page
def add_text_markup_annotation(
self,
Expand Down Expand Up @@ -2031,6 +2038,43 @@ def add_text_markup_annotation(
self.annots[page].append(annotation)
return annotation

@check_page
def ink_annotation(
self, coords, contents="", title="", color=(1, 1, 0), border_width=1
):
"""
Adds add an ink annotation on the page.
Args:
coords (tuple): an iterable of coordinates (pairs of numbers) defining a path
contents (str): textual description
title (str): the text label that shall be displayed in the title bar of the annotation’s
pop-up window when open and active. This entry shall identify the user who added the annotation.
color (tuple): a tuple of numbers in the range 0.0 to 1.0, representing a colour used for
the title bar of the annotation’s pop-up window. Defaults to yellow.
border_width (int): thickness of the path stroke.
"""
ink_list = sum(((x * self.k, (self.h - y) * self.k) for (x, y) in coords), ())
x_min = min(ink_list[0::2])
y_min = min(ink_list[1::2])
x_max = max(ink_list[0::2])
y_max = max(ink_list[1::2])
annotation = Annotation(
"Ink",
x=y_min,
y=y_max,
width=x_max - x_min,
height=y_max - y_min,
ink_list=ink_list,
color=color,
border_width=border_width,
page=self.page,
contents=contents,
title=title,
)
self.annots[self.page].append(annotation)
return annotation

@check_page
def text(self, x, y, txt=""):
"""
Expand Down Expand Up @@ -4115,7 +4159,7 @@ def _putinfo(self):
date_string = f"{datetime.now():%Y%m%d%H%M%S}"
info_d["/CreationDate"] = enclose_in_parens(f"D:{date_string}")

self._out(pdf_d(info_d, open_dict="", close_dict="", has_empty_fields=True))
self._out(pdf_dict(info_d, open_dict="", close_dict="", has_empty_fields=True))

def _putcatalog(self):
catalog_d = {
Expand All @@ -4134,7 +4178,7 @@ def _putcatalog(self):
]
else: # zoom_mode is a number, not one of the allowed strings:
zoom_config = ["/XYZ", "null", "null", str(self.zoom_mode / 100)]
catalog_d["/OpenAction"] = pdf_l(zoom_config)
catalog_d["/OpenAction"] = pdf_list(zoom_config)

if self.page_layout:
catalog_d["/PageLayout"] = self.page_layout.value.pdf_repr()
Expand All @@ -4145,12 +4189,12 @@ def _putcatalog(self):
if self._xmp_metadata_obj_id:
catalog_d["/Metadata"] = pdf_ref(self._xmp_metadata_obj_id)
if self._struct_tree_root_obj_id:
catalog_d["/MarkInfo"] = pdf_d({"/Marked": "true"})
catalog_d["/MarkInfo"] = pdf_dict({"/Marked": "true"})
catalog_d["/StructTreeRoot"] = pdf_ref(self._struct_tree_root_obj_id)
if self._outlines_obj_id:
catalog_d["/Outlines"] = pdf_ref(self._outlines_obj_id)

self._out(pdf_d(catalog_d, open_dict="", close_dict=""))
self._out(pdf_dict(catalog_d, open_dict="", close_dict=""))

def _putheader(self):
if self.page_layout in (PageLayout.TWO_PAGE_LEFT, PageLayout.TWO_PAGE_RIGHT):
Expand Down Expand Up @@ -4681,6 +4725,8 @@ def _is_svg(bytes):
sys.modules[__name__].__class__ = WarnOnDeprecatedModuleAttributes


__pdoc__ = {"FPDF.add_highlight": False} # Replaced by FPDF.highlight

__all__ = [
"FPDF",
"XPos",
Expand Down
Binary file added test/ink_annotation.pdf
Binary file not shown.
19 changes: 15 additions & 4 deletions test/test_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,7 @@ def test_highlighted(tmp_path):
pdf = FPDF()
pdf.add_page()
pdf.set_font("Helvetica", size=24)
with pdf.add_highlight(
"Highlight comment", type="Squiggly", modification_time=EPOCH
):
with pdf.highlight("Highlight comment", type="Squiggly", modification_time=EPOCH):
pdf.text(50, 50, "Line 1")
pdf.set_y(50)
pdf.multi_cell(w=30, txt="Line 2")
Expand All @@ -195,6 +193,19 @@ def test_highlighted_over_page_break(tmp_path):
pdf.set_font("helvetica", size=24)
pdf.write(txt=LOREM_IPSUM)
pdf.ln()
with pdf.add_highlight("Comment", title="Freddy Mercury", modification_time=EPOCH):
with pdf.highlight("Comment", title="Freddy Mercury", modification_time=EPOCH):
pdf.write(txt=LOREM_IPSUM)
assert_pdf_equal(pdf, HERE / "highlighted_over_page_break.pdf", tmp_path)


def test_ink_annotation(tmp_path):
pdf = FPDF()
pdf.add_page()
pdf.set_font("Helvetica", size=24)
pdf.text(50, 50, "Some text")
pdf.ink_annotation(
[(40, 50), (70, 25), (100, 50), (70, 75), (40, 50)],
title="Lucas",
contents="Hello world!",
)
assert_pdf_equal(pdf, HERE / "ink_annotation.pdf", tmp_path)

0 comments on commit 3d52b12

Please sign in to comment.