0

I am creating multiple multipage PDF reports using PdfPages by running a for loop through a dataframe. I have everything ready, except I need to include some hyperlinks (between 0 and 3), preferably on msr_line4, but if they need to be on individual lines, I can make that work.

import pandas as pd
import numpy as np
import pickle
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import matplotlib.lines as mlines
from matplotlib.pyplot import figure
from matplotlib.backends.backend_pdf import PdfPages
import seaborn as sns
import matplotlib.image as mpimg
import mpl_toolkits.axisartist as axisartist

## Text

msr_line1 = r'$\bf{' + 'Name: ' + '}$' + 'Calls name from df'
msr_line2 = r'$\bf{' + 'Measure: ' + '}$' + 'Calls measure from df'
msr_line3 = r'$\bf{' + 'Direction: ' + '}$' + 'Calls direction from df'
msr_line4 = r'$\bf{' + 'Link\ to\ Resources: ' + '}$' + "NEED TO INSERT HYPERLINK HERE"

with PdfPages('msr.pdf') as pdf:
    plt.figure(figsize=(11, 8.5))

    ## Header text
    ax2 = plt.subplot2grid((9, 5), (1, 0), rowspan=1, colspan=2)
    ax2.text(0, .9, msr_line1, fontsize=9)
    ax2.text(0, 0.6, msr_line2, fontsize=9)
    ax2.text(0, 0.3, msr_line3, fontsize=9)
    ax2.text(0, 0, msr_line4, fontsize=9)
    plt.axis('off')
    pdf.savefig()
    plt.close

EDIT/UPDATE Libraries now being used:

import pandas as pd
import numpy as np
import pickle
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import matplotlib.lines as mlines
from matplotlib.pyplot import figure
import seaborn as sns
import matplotlib.image as mpimg
import mpl_toolkits.axisartist as axisartist
from matplotlib import rcParams

import matplotlib
matplotlib.use('pgf')
from PyPDF2 import PdfFileMerger
import os

Parameters:

plt.rc('text', usetex=True)
rcParams['font.family'] = 'serif'
rcParams['font.serif'] = ['Georgia']
plt.rcParams['pgf.preamble'] = [r'\usepackage{hyperref} \hypersetup{hidelinks,' 
                                'colorlinks=true, urlcolor=cyan}', ]

ax2 = plt.subplot2grid((9, 5), (1, 0), rowspan=1, colspan=1)
plt.text(0, .9, msr_line1, fontsize=9)
plt.text(0, 0.6, msr_line2, fontsize=9)
plt.text(0, 0.3, msr_line3, fontsize=9)
plt.text(0, 0, r'\href{https://stackoverflow.com/questions/}{StackOverflow}', fontsize=9)
plt.axis('off')

The error that I am now getting is:

CalledProcessError                        Traceback (most recent call last)
~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\texmanager.py in _run_checked_subprocess(self, command, tex)
    303                                              cwd=self.texcache,
--> 304                                              stderr=subprocess.STDOUT)
    305         except FileNotFoundError as exc:

~\AppData\Local\Continuum\anaconda3\lib\subprocess.py in check_output(timeout, *popenargs, **kwargs)
    394     return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
--> 395                **kwargs).stdout
    396 

~\AppData\Local\Continuum\anaconda3\lib\subprocess.py in run(input, capture_output, timeout, check, *popenargs, **kwargs)
    486             raise CalledProcessError(retcode, process.args,
--> 487                                      output=stdout, stderr=stderr)
    488     return CompletedProcess(process.args, retcode, stdout, stderr)

CalledProcessError: Command '['latex', '-interaction=nonstopmode', '--halt-on-error', 
'C:\\Users\\KrumlinZ\\.matplotlib\\tex.cache\\2d92c6482fbb9d5f9ece1213452d403d.tex']' returned non-zero exit status 1.

The above exception was the direct cause of the following exception:

RuntimeError                              Traceback (most recent call last)
<ipython-input-13-c8cf5db9d20c> in <module>
    226 
    227     measure_page = str(ProviderNumber) + str(msr) + '_msr.pdf'
--> 228     plt.savefig(measure_page)
    229     merger.append(measure_page)
    230     #os.remove(measure_page)

~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\pyplot.py in savefig(*args, **kwargs)
    720 def savefig(*args, **kwargs):
    721     fig = gcf()
--> 722     res = fig.savefig(*args, **kwargs)
    723     fig.canvas.draw_idle()   # need this if 'transparent=True' to reset colors
    724     return res

~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\figure.py in savefig(self, fname, transparent, **kwargs)
   2178             self.patch.set_visible(frameon)
   2179 
-> 2180         self.canvas.print_figure(fname, **kwargs)
   2181 
   2182         if frameon:

~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, **kwargs)
   2080                     orientation=orientation,
   2081                     bbox_inches_restore=_bbox_inches_restore,
-> 2082                     **kwargs)
   2083             finally:
   2084                 if bbox_inches and restore_bbox:

~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\backends\backend_pdf.py in print_pdf(self, filename, dpi, bbox_inches_restore, metadata, **kwargs)
   2501                 RendererPdf(file, dpi, height, width),
   2502                 bbox_inches_restore=bbox_inches_restore)
-> 2503             self.figure.draw(renderer)
   2504             renderer.finalize()
   2505             if not isinstance(filename, PdfPages):

~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     36                 renderer.start_filter()
     37 
---> 38             return draw(artist, renderer, *args, **kwargs)
     39         finally:
     40             if artist.get_agg_filter() is not None:

~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\figure.py in draw(self, renderer)
   1707             self.patch.draw(renderer)
   1708             mimage._draw_list_compositing_images(
-> 1709                 renderer, self, artists, self.suppressComposite)
   1710 
   1711             renderer.close_group('figure')

~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    133     if not_composite or not has_images:
    134         for a in artists:
--> 135             a.draw(renderer)
    136     else:
    137         # Composite any adjacent images together

~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     36                 renderer.start_filter()
     37 
---> 38             return draw(artist, renderer, *args, **kwargs)
     39         finally:
     40             if artist.get_agg_filter() is not None:

~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\axes\_base.py in draw(self, renderer, inframe)
   2645             renderer.stop_rasterizing()
   2646 
-> 2647         mimage._draw_list_compositing_images(renderer, self, artists)
   2648 
   2649         renderer.close_group('axes')

~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    133     if not_composite or not has_images:
    134         for a in artists:
--> 135             a.draw(renderer)
    136     else:
    137         # Composite any adjacent images together

~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
     36                 renderer.start_filter()
     37 
---> 38             return draw(artist, renderer, *args, **kwargs)
     39         finally:
     40             if artist.get_agg_filter() is not None:

~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\text.py in draw(self, renderer)
    668 
    669         with _wrap_text(self) as textobj:
--> 670             bbox, info, descent = textobj._get_layout(renderer)
    671             trans = textobj.get_transform()
    672 

~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\text.py in _get_layout(self, renderer)
    296             if clean_line:
    297                 w, h, d = renderer.get_text_width_height_descent(
--> 298                     clean_line, self._fontproperties, ismath=ismath)
    299             else:
    300                 w = h = d = 0

~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\backends\_backend_pdf_ps.py in get_text_width_height_descent(self, s, prop, ismath)
     45             fontsize = prop.get_size_in_points()
     46             w, h, d = texmanager.get_text_width_height_descent(
---> 47                 s, fontsize, renderer=self)
     48             return w, h, d
     49         elif ismath:

~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\texmanager.py in get_text_width_height_descent(self, tex, fontsize, renderer)
    446         else:
    447             # use dviread. It sometimes returns a wrong descent.
--> 448             dvifile = self.make_dvi(tex, fontsize)
    449             with dviread.Dvi(dvifile, 72 * dpi_fraction) as dvi:
    450                 page, = dvi

~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\texmanager.py in make_dvi(self, tex, fontsize)
    336                 self._run_checked_subprocess(
    337                     ["latex", "-interaction=nonstopmode", "--halt-on-error",
--> 338                      texfile], tex)
    339             for fname in glob.glob(basefile + '*'):
    340                 if not fname.endswith(('dvi', 'tex')):

~\AppData\Local\Continuum\anaconda3\lib\site-packages\matplotlib\texmanager.py in _run_checked_subprocess(self, command, tex)
    315                     prog=command[0],
    316                     tex=tex.encode('unicode_escape'),
--> 317                     exc=exc.output.decode('utf-8'))) from exc
    318         _log.debug(report)
    319         return report

RuntimeError: latex was not able to process the following string:
b'\\\\href{https://stackoverflow.com/questions/}{StackOverflow}'

Here is the full report generated by latex:
This is pdfTeX, Version 3.14159265-2.6-1.40.20 (MiKTeX 2.9.7250 64-bit)
entering extended mode
(C:/Users/KrumlinZ/.matplotlib/tex.cache/2d92c6482fbb9d5f9ece1213452d403d.tex
LaTeX2e <2019-10-01> patch level 3

("C:\Users\KrumlinZ\AppData\Local\Programs\MiKTeX 2.9\tex/latex/base\article.cl
s"
Document Class: article 2019/10/25 v1.4k Standard LaTeX document class
("C:\Users\KrumlinZ\AppData\Local\Programs\MiKTeX 2.9\tex/latex/base\size10.clo
"))
("C:\Users\KrumlinZ\AppData\Local\Programs\MiKTeX 2.9\tex/latex/type1cm\type1cm
.sty")
("C:\Users\KrumlinZ\AppData\Local\Programs\MiKTeX 2.9\tex/latex/base\textcomp.s
ty"
("C:\Users\KrumlinZ\AppData\Local\Programs\MiKTeX 2.9\tex/latex/base\ts1enc.def
"
("C:\Users\KrumlinZ\AppData\Local\Programs\MiKTeX 2.9\tex/latex/base\ts1enc.dfu
")))
("C:\Users\KrumlinZ\AppData\Local\Programs\MiKTeX 2.9\tex/latex/base\inputenc.s
ty")
("C:\Users\KrumlinZ\AppData\Local\Programs\MiKTeX 2.9\tex/latex/geometry\geomet
ry.sty"
("C:\Users\KrumlinZ\AppData\Local\Programs\MiKTeX 2.9\tex/latex/graphics\keyval
.sty")
("C:\Users\KrumlinZ\AppData\Local\Programs\MiKTeX 2.9\tex/generic/iftex\ifvtex.
sty"
("C:\Users\KrumlinZ\AppData\Local\Programs\MiKTeX 2.9\tex/generic/iftex\iftex.s
ty"))
("C:\Users\KrumlinZ\AppData\Local\Programs\MiKTeX 2.9\tex/latex/geometry\geomet
ry.cfg")

Package geometry Warning: Over-specification in `h'-direction.
    `width' (5058.9pt) is ignored.


Package geometry Warning: Over-specification in `v'-direction.
    `height' (5058.9pt) is ignored.

) (2d92c6482fbb9d5f9ece1213452d403d.aux)
("C:\Users\KrumlinZ\AppData\Local\Programs\MiKTeX 2.9\tex/latex/base\ts1cmr.fd"
)
*geometry* driver: auto-detecting
*geometry* detected driver: dvips
! Undefined control sequence.
l.14 ...tsize{9.000000}{11.250000}{\rmfamily \href
                                                  {https://stackoverflow.com...
No pages of output.
Transcript written on 2d92c6482fbb9d5f9ece1213452d403d.log.


Error in callback <function install_repl_displayhook.<locals>.post_execute at 0x000001EAC6E4CA68> (for post_execute):

1 Answers1

2

You will need to use LaTex and PGF to accomplish this (unless outputting SVG is an option). This will require a LaTex installation (TexLive, MikTex, etc). Unfortunately this is incompatible with the PdfPages backend. However, you can concatenate the output PDFs on the fly fairly easily.

Here is an working example:

import matplotlib
matplotlib.use('pgf')
import matplotlib.pyplot as plt
from PyPDF2 import PdfFileMerger
import os

plt.rc('text', usetex=True)
plt.rc('font', family='serif')
plt.rcParams['pgf.preamble'] = [r'\usepackage{hyperref} \hypersetup{hidelinks,' 
                                'colorlinks=true, urlcolor=cyan}', ]

merger = PdfFileMerger()
for i in range(5):
    plt.figure()
    plt.text(0.5, 0.5, 
             r'\href{https://stackoverflow.com/questions/}{StackOverflow '+str(i)+'}')

    out = './out.'+str(i)+'.pdf'
    plt.savefig(out)
    merger.append(out)
    os.remove(out)

merger.write('./out.pdf')
merger.close()
William Miller
  • 9,839
  • 3
  • 25
  • 46
  • I am able to run your example fine, but then when I try to run the exact program in my actual program I get an extremely long error, "latex was not able to process the following string: b'\\\\href{https://stackoverflow.com/questions/}{StackOverflow}'" I have all the libraries and parameters set up in both and am using the PdfFileMerger() as well. At the end of my error, there is this also "! Undefined control sequence. l.14 ...tsize{9.000000}{11.250000}{\rmfamily \href {https://stackoverflow.com..." Any suggestions? – Zakary Krumlinde Apr 01 '20 at 18:33
  • @ZakaryKrumlinde That error is arising from an issue with the backend, can you edit your question to include the exact code which produces that error? I will be able to tell you precisely what needs to be fixed if you do – William Miller Apr 02 '20 at 02:32
  • just edited, I think everything you would need to know is in there, let me know otherwise. – Zakary Krumlinde Apr 02 '20 at 13:01
  • @ZakaryKrumlinde How are you running your code? I.e. self contained script, from an iPython kernel, Jupyter Notebook, Spyder? – William Miller Apr 09 '20 at 00:51
  • I have attempted in both Jupyter and in Spyder. I think that my issue has to do with changing font weights. I can add the hyperlink if I do not use any bold or italics font. That is my new investigation. – Zakary Krumlinde Apr 09 '20 at 12:18
  • @ZakaryKrumlinde Have you tried using LaTex for the bolding/italicizing? (i.e. `\href{https://stackoverflow.com}{\textit{link}}`) – William Miller Apr 09 '20 at 12:21
  • I am trying to bold/italicize other text in the document. I have tried about everything that I have come across. I am either able to bold nothing or the all the text on the page with 'plt.rcParams['font.weight'] = 'bold'' – Zakary Krumlinde Apr 09 '20 at 15:49