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

Improve performance working with unicode fonts (#907) #1158

Merged
merged 9 commits into from
Jun 27, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ This can also be enabled programmatically with `warnings.simplefilter('default',
### Changed
* [`FPDF.table()`](https://py-pdf.github.io/fpdf2/Tables.html) now raises an error when a single row is too high to be rendered on a single page
* [`FPDF.write_html()`](https://py-pdf.github.io/fpdf2/fpdf/fpdf.html#fpdf.fpdf.FPDF.write_html): `tag_indents` can now be non-integer. Indentation of HTML elements is now independent of font size and bullet strings.
* improved performance of font glyph selection by using functools cache

## [2.7.9] - 2024-05-17
### Added
Expand Down
12 changes: 5 additions & 7 deletions fpdf/fonts.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from bisect import bisect_left
from collections import defaultdict
from dataclasses import dataclass, replace
from functools import lru_cache
from typing import List, Optional, Tuple, Union

from fontTools import ttLib
Expand Down Expand Up @@ -447,8 +448,6 @@ def __init__(self, font: TTFFont, identities: List[int]):
glyph = self.get_glyph(unicode=x)
if glyph:
self._char_id_per_glyph[glyph] = int(x)
# This is a cache to speed things up:
self._char_id_per_unicode = {}

def __repr__(self):
return (
Expand All @@ -463,10 +462,9 @@ def items(self):
for glyph, char_id in self._char_id_per_glyph.items():
yield glyph, char_id

# pylint: disable=method-cache-max-size-none
@lru_cache(maxsize=None)
def pick(self, unicode: int):
cache_hit = self._char_id_per_unicode.get(unicode)
if cache_hit:
return cache_hit
glyph = self.get_glyph(unicode=unicode)
if glyph is None and unicode not in self.font.missing_glyphs:
self.font.missing_glyphs.append(unicode)
Expand All @@ -482,10 +480,10 @@ def pick_glyph(self, glyph):
char_id = self._next
self._char_id_per_glyph[glyph] = char_id
self._next += 1
# Fill cache:
self._char_id_per_unicode[glyph.unicode] = char_id
return char_id

# pylint: disable=method-cache-max-size-none
@lru_cache(maxsize=None)
def get_glyph(
self, glyph=None, unicode=None, glyph_name=None, glyph_width=None
) -> Glyph:
Expand Down
2 changes: 1 addition & 1 deletion fpdf/fpdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -3437,7 +3437,7 @@ def get_fallback_font(self, char, style=""):

def _parse_chars(self, text: str, markdown: bool) -> Iterator[Fragment]:
"Split text into fragments"
if not markdown and not self.is_ttf_font:
if not markdown and not self.text_shaping and not self._fallback_font_ids:
yield Fragment(text, self._get_current_graphics_state(), self.k)
return
txt_frag, in_bold, in_italics, in_underline = (
Expand Down
2 changes: 2 additions & 0 deletions fpdf/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,8 @@ def format_code(unicode):
self._add_pdf_obj(font_file_cs_obj, "fonts")
font_descriptor_obj.font_file2 = font_file_cs_obj

font.subset.pick.cache_clear()
font.subset.get_glyph.cache_clear()
font.close()

return font_objs_per_index
Expand Down
2 changes: 1 addition & 1 deletion test/text/test_cell.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ def test_cell_deprecated_txt_arg():
pdf.cell(txt="Lorem ipsum Ut nostrud irure")


@ensure_exec_time_below(seconds=24)
@ensure_exec_time_below(seconds=18)
@ensure_rss_memory_below(mib=1)
def test_cell_speed_with_long_text(): # issue #907
pdf = FPDF()
Expand Down
Binary file modified test/text_shaping/bidi_paragraph_direction.pdf
Binary file not shown.
1 change: 1 addition & 0 deletions test/text_shaping/test_bidirectional.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ def test_bidi_paragraph_direction(tmp_path):
pdf.add_page()
pdf.add_font(family="SBL_Hbrw", fname=HERE / "SBL_Hbrw.ttf")
pdf.set_font("SBL_Hbrw", "", 18)
pdf.set_text_shaping(True)

text1 = "אנגלית (באנגלית: English)" # first char is RTL
text2 = "The test is: אנגלית (באנגלית: English)" # first char is LTR
Expand Down
Loading