8000 pdf: Support setting URLs on Text objects. · matplotlib/matplotlib@830316e · GitHub
[go: up one dir, main page]

Skip to content

Commit 830316e

Browse files
committed
pdf: Support setting URLs on Text objects.
1 parent 450c589 commit 830316e

File tree

3 files changed

+94
-0
lines changed

3 files changed

+94
-0
lines changed

doc/users/next_whats_new/pdf_urls.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
PDF supports URLs on ``Text`` artists
2+
-------------------------------------
3+
4+
URLs on `.text.Text` artists (i.e., from `.Artist.set_url`) will now be saved
5+
in PDF files.

lib/matplotlib/backends/backend_pdf.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2062,6 +2062,19 @@ def draw_mathtext(self, gc, x, y, s, prop, angle):
20622062
width, height, descent, glyphs, rects = \
20632063
self._text2path.mathtext_parser.parse(s, 72, prop)
20642064

2065+
if gc.get_url() is not None:
2066+
link_annotation = {
2067+
'Type': Name('Annot'),
2068+
'Subtype': Name('Link'),
2069+
'Rect': (x, y, x + width, y + height),
2070+
'Border': [0, 0, 0],
2071+
'A': {
2072+
'S': Name('URI'),
2073+
'URI': gc.get_url(),
2074+
},
2075+
}
2076+
self.file._annotations[-1][1].append(link_annotation)
2077+
20652078
# When using Type 3 fonts, we can't use character codes higher
20662079
# than 255, so we use the "Do" command to render those
20672080
# instead.
@@ -2136,6 +2149,19 @@ def draw_tex(self, gc, x, y, s, prop, angle, ismath='TeX!', mtext=None):
21362149
with dviread.Dvi(dvifile, 72) as dvi:
21372150
page, = dvi
21382151

2152+
if gc.get_url() is not None:
2153+
link_annotation = {
2154+
'Type': Name('Annot'),
2155+
'Subtype': Name('Link'),
2156+
'Rect': (x, y, x + page.width, y + page.height),
2157+
'Border': [0, 0, 0],
2158+
'A': {
2159+
'S': Name('URI'),
2160+
'URI': gc.get_url(),
2161+
},
2162+
}
2163+
self.file._annotations[-1][1].append(link_annotation)
2164+
21392165
# Gather font information and do some setup for combining
21402166
# characters into strings. The variable seq will contain a
21412167
# sequence of font and text entries. A font entry is a list
@@ -2235,6 +2261,21 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
22352261
if is_opentype_cff_font(font.fname):
22362262
fonttype = 42
22372263

2264+
if gc.get_url() is not None:
2265+
font.set_text(s)
2266+
width, height = font.get_width_height()
2267+
link_annotation = {
2268+
'Type': Name('Annot'),
2269+
'Subtype': Name('Link'),
2270+
'Rect': (x, y, x + width / 64, y + height / 64),
2271+
'Border': [0, 0, 0],
2272+
'A': {
2273+
'S': Name('URI'),
2274+
'URI': gc.get_url(),
2275+
},
2276+
}
2277+
self.file._annotations[-1][1].append(link_annotation)
2278+
22382279
# If fonttype != 3 or there are no multibyte characters, emit the whole
22392280
# string at once.
22402281
if fonttype != 3 or all(ord(char) <= 255 for char in s):

lib/matplotlib/tests/test_backend_pdf.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import datetime
2+
import decimal
23
import io
34
import os
45
from pathlib import Path
@@ -198,6 +199,53 @@ def test_multipage_metadata(monkeypatch):
198199
}
199200

200201

202+
def test_text_urls():
203+
pikepdf = pytest.importorskip('pikepdf')
204+
205+
test_url = 'https://test_text_urls.matplotlib.org/'
206+
207+
fig = plt.figure(figsize=(2, 1))
208+
fig.text(0.1, 0.1, 'test plain 123', url=f'{test_url}plain')
209+
fig.text(0.1, 0.4, 'test mathtext $123$', url=f'{test_url}mathtext')
210+
211+
with io.BytesIO() as fd:
212+
fig.savefig(fd, format='pdf')
213+
214+
with pikepdf.Pdf.open(fd) as pdf:
215+
annots = pdf.pages[0].Annots
216+
217+
for y, fragment in [('0.1', 'plain'), ('0.4', 'mathtext')]:
218+
annot = next(
219+
(a for a in annots if a.A.URI == f'{test_url}{fragment}'),
220+
None)
221+
assert annot is not None
222+
# Positions in points (72 per inch.)
223+
assert annot.Rect[1] == decimal.Decimal(y) * 72
224+
225+
226+
@needs_usetex
227+
def test_text_urls_tex():
228+
pikepdf = pytest.importorskip('pikepdf')
229+
230+
test_url = 'https://test_text_urls.matplotlib.org/'
231+
232+
fig = plt.figure(figsize=(2, 1))
233+
fig.text(0.1, 0.7, 'test tex $123$', usetex=True, url=f'{test_url}tex')
234+
235+
with io.BytesIO() as fd:
236+
fig.savefig(fd, format='pdf')
237+
238+
with pikepdf.Pdf.open(fd) as pdf:
239+
annots = pdf.pages[0].Annots
240+
241+
annot = next(
242+
(a for a in annots if a.A.URI == f'{test_url}tex'),
243+
None)
244+
assert annot is not None
245+
# Positions in points (72 per inch.)
246+
assert annot.Rect[1] == decimal.Decimal('0.7') * 72
247+
248+
201249
def test_pdfpages_fspath():
202250
with PdfPages(Path(os.devnull)) as pdf:
203251
pdf.savefig(plt.figure())

0 commit comments

Comments
 (0)
0