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

Handle multilingual strings to improve text shaping results (fix #1187) #1193

Merged
merged 4 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
4 changes: 1 addition & 3 deletions fpdf/fonts.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,9 +270,7 @@ def shaped_text_width(self, text, font_size_pt, text_shaping_parms):
text_width = 0
for pos in glyph_positions:
text_width += (
round(self.scale * (pos.x_advance + pos.x_offset) + 0.001)
* font_size_pt
* 0.001
round(self.scale * pos.x_advance + 0.001) * font_size_pt * 0.001
)
return (len(glyph_positions), text_width)

Expand Down
19 changes: 17 additions & 2 deletions fpdf/fpdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class Image:
from .syntax import DestinationXYZ, PDFArray, PDFDate
from .table import Table, draw_box_borders
from .text_region import TextRegionMixin, TextColumns
from .unicode_script import UnicodeScript, get_unicode_script
from .util import get_scale_factor, Padding

# Public global variables:
Expand Down Expand Up @@ -3379,7 +3380,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 or not self._fallback_font_ids):
if not markdown and not self.is_ttf_font:
yield Fragment(text, self._get_current_graphics_state(), self.k)
return
txt_frag, in_bold, in_italics, in_underline = (
Expand All @@ -3389,9 +3390,10 @@ def _parse_chars(self, text: str, markdown: bool) -> Iterator[Fragment]:
bool(self.underline),
)
current_fallback_font = None
current_text_script = None

def frag():
nonlocal txt_frag, current_fallback_font
nonlocal txt_frag, current_fallback_font, current_text_script
gstate = self._get_current_graphics_state()
gstate["font_style"] = ("B" if in_bold else "") + (
"I" if in_italics else ""
Expand All @@ -3406,6 +3408,7 @@ def frag():
)
gstate["current_font"] = self.fonts[current_fallback_font]
current_fallback_font = None
current_text_script = None
fragment = Fragment(
txt_frag,
gstate,
Expand All @@ -3426,6 +3429,16 @@ def frag():
self.MARKDOWN_UNDERLINE_MARKER,
)
half_marker = text[0]
text_script = get_unicode_script(text[0])
if text_script not in (
UnicodeScript.COMMON,
UnicodeScript.UNKNOWN,
current_text_script,
):
if txt_frag and current_text_script:
yield frag()
current_text_script = text_script

# Check that previous & next characters are not identical to the marker:
if markdown:
if (
Expand Down Expand Up @@ -5132,6 +5145,8 @@ def output(
DeprecationWarning,
stacklevel=get_stack_level(),
)
# Clear cache of cached functions to free up memory after output
get_unicode_script.cache_clear()
# Finish document if necessary:
if not self.buffer:
if self.page == 0:
Expand Down
Loading
Loading