Skip to content

Add kerning to single-byte strings in PDFs #19582

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

Merged
merged 2 commits into from
Mar 4, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
24 changes: 13 additions & 11 deletions lib/matplotlib/backends/backend_pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2287,24 +2287,26 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
}
self.file._annotations[-1][1].append(link_annotation)

# If fonttype != 3 or there are no multibyte characters, emit the whole
# string at once.
if fonttype != 3 or all(ord(char) <= 255 for char in s):
# If fonttype != 3 emit the whole string at once without manual
# kerning.
if fonttype != 3:
self.file.output(Op.begin_text,
self.file.fontName(prop), fontsize, Op.selectfont)
self._setup_textpos(x, y, angle)
self.file.output(self.encode_string(s, fonttype), Op.show,
Op.end_text)
self.file.output(self.encode_string(s, fonttype),
Op.show, Op.end_text)

# There is no way to access multibyte characters of Type 3 fonts, as
# they cannot have a CIDMap. Therefore, in this case we break the
# string into chunks, where each chunk contains either a string of
# consecutive 1-byte characters or a single multibyte character. Each
# chunk is emitted with a separate command: 1-byte characters use the
# regular text show command (Tj), whereas multibyte characters use
# the XObject command (Do). (If using Type 42 fonts, all of this
# complication is avoided, but of course, those fonts can not be
# subsetted.)
# consecutive 1-byte characters or a single multibyte character.
# A sequence of 1-byte characters is broken into multiple chunks to
# adjust the kerning between adjacent chunks. Each chunk is emitted
# with a separate command: 1-byte characters use the regular text show
# command (TJ) with appropriate kerning between chunks, whereas
# multibyte characters use the XObject command (Do). (If using Type
# 42 fonts, all of this complication is avoided, but of course,
# subsetting those fonts is complex/hard to implement.)
else:
# List of (start_x, [prev_kern, char, char, ...]), w/o zero kerns.
singlebyte_chunks = []
Expand Down
Binary file not shown.
6 changes: 6 additions & 0 deletions lib/matplotlib/tests/test_text.py
Original file line number Diff line number Diff line change
Expand Up @@ -714,3 +714,9 @@ def test_update_mutate_input():
def test_invalid_color():
with pytest.raises(ValueError):
plt.figtext(.5, .5, "foo", c="foobar")


@image_comparison(['text_pdf_kerning.pdf'], style='mpl20')
def test_pdf_kerning():
plt.figure()
plt.figtext(0.1, 0.5, "ATATATATATATATATATA", size=30)