Skip to content

Commit 4270353

Browse files
authored
ENH: Wrap and align text in flattened PDF forms (#3465)
1 parent 85b53d8 commit 4270353

File tree

6 files changed

+293
-43
lines changed

6 files changed

+293
-43
lines changed

CONTRIBUTORS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ history and [GitHub's 'Contributors' feature](https://github.com/py-pdf/pypdf/gr
1313

1414
* [abyesilyurt](https://github.com/abyesilyurt)
1515
* [ArkieCoder](https://github.com/ArkieCoder)
16+
* [Beers, PJ](https://github.com/PJBrs)
1617
* [Clauss, Christian](https://github.com/cclauss)
1718
* [DL6ER](https://github.com/DL6ER)
1819
* [Duy, Phan Thanh](https://github.com/zuypt)

docs/user/forms.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@ parameter is `True` by default for legacy compatibility, but this flags the PDF
4646
processor to recompute the field's rendering, and may trigger a "save changes"
4747
dialog for users who open the generated PDF.
4848

49+
If you want to flatten your form, that is, keeping all form field contents while
50+
removing the form fields themselves, you can set the `flatten` parameter in
51+
{func}`~pypdf.PdfWriter.update_page_form_field_values` to `True`. This
52+
will convert form field contents to regular PDF content. Afterwards, use
53+
{func}`~pypdf.PdfWriter.remove_annotations` with `subtypes="/Widget"`
54+
to remove all form fields to get an actual flattened PDF.
55+
4956
## Some notes about form fields and annotations
5057

5158
PDF forms have a dual-nature approach to the fields:

pypdf/_font.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
from dataclasses import dataclass, field
2-
from typing import Optional
32

43
from pypdf.generic import DictionaryObject
54

@@ -29,10 +28,16 @@ class FontDescriptor:
2928
character_widths: dict[str, int] = field(default_factory=dict)
3029

3130
@classmethod
32-
def from_font_resource(cls, pdf_font_dict: DictionaryObject) -> "Optional[FontDescriptor]":
31+
def from_font_resource(cls, pdf_font_dict: DictionaryObject) -> "FontDescriptor":
3332
from pypdf._codecs.core_fontmetrics import CORE_FONT_METRICS # noqa: PLC0415
3433
# Prioritize information from the PDF font dictionary
35-
font_name = pdf_font_dict.get("/BaseFont", "Unknown")
36-
if font_name[1:] in CORE_FONT_METRICS:
37-
return CORE_FONT_METRICS.get(font_name[1:])
34+
font_name = pdf_font_dict.get("/BaseFont", "Unknown").removeprefix("/")
35+
if font_name in CORE_FONT_METRICS:
36+
return CORE_FONT_METRICS[font_name]
3837
return cls(name=font_name)
38+
39+
def text_width(self, text: str) -> float:
40+
"""Sum of character widths specified in PDF font for the supplied text."""
41+
return sum(
42+
[self.character_widths.get(char, self.character_widths.get("default", 0)) for char in text], 0.0
43+
)

0 commit comments

Comments
 (0)