Skip to content

Commit fba4195

Browse files
committed
MAINT: _appearance_stream: Move font_resource parsing
Move the font resource parsing code to TextAppearanceStream, in the hope that, later, one might be able to generate a TextAppearanceStream directly. I wonder, though, where the necessary font resource would come from.
1 parent 3b6a69b commit fba4195

File tree

1 file changed

+40
-42
lines changed

1 file changed

+40
-42
lines changed

pypdf/generic/_appearance_stream.py

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ def _appearance_stream_data(
2525
self,
2626
text: str = "",
2727
selection: Optional[list[str]] = None,
28-
font_glyph_byte_map: Optional[dict[str, bytes]] = None,
2928
rect: Union[RectangleObject, tuple[float, float, float, float]] = (0.0, 0.0, 0.0, 0.0),
29+
font_glyph_byte_map: Optional[dict[str, bytes]] = None,
3030
font_name: str = "/Helv",
3131
font_size: float = 0.0,
3232
font_color: str = "0 g",
@@ -73,17 +73,39 @@ def __init__(
7373
self,
7474
text: str = "",
7575
selection: Optional[list[str]] = None,
76-
font_glyph_byte_map: Optional[dict[str, bytes]] = None,
7776
rect: Union[RectangleObject, tuple[float, float, float, float]] = (0.0, 0.0, 0.0, 0.0),
77+
font_resource: Optional[DictionaryObject] = None,
7878
font_name: str = "/Helv",
7979
font_size: float = 0.0,
8080
font_color: str = "0 g",
8181
multiline: bool = False
8282
) -> None:
83-
font_glyph_byte_map = font_glyph_byte_map or {}
83+
# If a font resource was added, get the font character map
84+
if font_resource:
85+
font_resource = cast(DictionaryObject, font_resource.get_object())
86+
_font_subtype, _, font_encoding, font_map = build_char_map_from_dict(
87+
200, font_resource
88+
)
89+
try: # remove width stored in -1 key
90+
del font_map[-1]
91+
except KeyError:
92+
pass
93+
font_glyph_byte_map: dict[str, bytes]
94+
if isinstance(font_encoding, str):
95+
font_glyph_byte_map = {
96+
v: k.encode(font_encoding) for k, v in font_map.items()
97+
}
98+
else:
99+
font_glyph_byte_map = {v: bytes((k,)) for k, v in font_encoding.items()}
100+
font_encoding_rev = {v: bytes((k,)) for k, v in font_encoding.items()}
101+
for key, value in font_map.items():
102+
font_glyph_byte_map[value] = font_encoding_rev.get(key, key)
103+
else:
104+
logger_warning(f"Font dictionary for {font_name} not found.", __name__)
105+
font_glyph_byte_map = {}
84106

85107
ap_stream_data = self._appearance_stream_data(
86-
text, selection, font_glyph_byte_map, rect, font_name, font_size, font_color, multiline
108+
text, selection, rect, font_glyph_byte_map, font_name, font_size, font_color, multiline
87109
)
88110

89111
super().__init__()
@@ -92,6 +114,19 @@ def __init__(
92114
self[NameObject("/BBox")] = RectangleObject(rect)
93115
self.set_data(ByteStringObject(ap_stream_data))
94116
self[NameObject("/Length")] = NumberObject(len(ap_stream_data))
117+
# Update Resources with font information if necessary
118+
if font_resource is not None:
119+
self[NameObject("/Resources")] = DictionaryObject(
120+
{
121+
NameObject("/Font"): DictionaryObject(
122+
{
123+
NameObject(font_name): getattr(
124+
font_resource, "indirect_reference", font_resource
125+
)
126+
}
127+
)
128+
}
129+
)
95130

96131
@classmethod
97132
def from_text_annotation(
@@ -158,30 +193,8 @@ def from_text_annotation(
158193
)
159194
document_font_resources = document_resources.get_object().get("/Font", DictionaryObject()).get_object()
160195
font_resource = document_font_resources.get(font_name, None)
161-
162-
# If this annotation has a font resources, get the font character map
163196
if not is_null_or_none(font_resource):
164197
font_resource = cast(DictionaryObject, font_resource.get_object())
165-
_font_subtype, _, font_encoding, font_map = build_char_map_from_dict(
166-
200, font_resource
167-
)
168-
try: # remove width stored in -1 key
169-
del font_map[-1]
170-
except KeyError:
171-
pass
172-
font_glyph_byte_map: dict[str, bytes]
173-
if isinstance(font_encoding, str):
174-
font_glyph_byte_map = {
175-
v: k.encode(font_encoding) for k, v in font_map.items()
176-
}
177-
else:
178-
font_glyph_byte_map = {v: bytes((k,)) for k, v in font_encoding.items()}
179-
font_encoding_rev = {v: bytes((k,)) for k, v in font_encoding.items()}
180-
for key, value in font_map.items():
181-
font_glyph_byte_map[value] = font_encoding_rev.get(key, key)
182-
else:
183-
logger_warning(f"Font dictionary for {font_name} not found.", __name__)
184-
font_glyph_byte_map = {}
185198

186199
# Retrieve field text, selected values and formatting information
187200
multiline = False
@@ -203,26 +216,11 @@ def from_text_annotation(
203216

204217
# Create the TextStreamAppearance instance
205218
new_appearance_stream = cls(
206-
text, selection, font_glyph_byte_map, rect, font_name, font_size, font_color, multiline
219+
text, selection, rect, font_resource, font_name, font_size, font_color, multiline
207220
)
208-
209221
if AnnotationDictionaryAttributes.AP in annotation:
210222
for k, v in cast(DictionaryObject, annotation[AnnotationDictionaryAttributes.AP]).get("/N", {}).items():
211223
if k not in {"/BBox", "/Length", "/Subtype", "/Type", "/Filter"}:
212224
new_appearance_stream[k] = v
213225

214-
# Update Resources with font information if necessary
215-
if font_resource is not None:
216-
new_appearance_stream[NameObject("/Resources")] = DictionaryObject(
217-
{
218-
NameObject("/Font"): DictionaryObject(
219-
{
220-
NameObject(font_name): getattr(
221-
font_resource, "indirect_reference", font_resource
222-
)
223-
}
224-
)
225-
}
226-
)
227-
228226
return new_appearance_stream

0 commit comments

Comments
 (0)