Luca Clissa
Luca Clissa

Reputation: 908

How to export text annotations with links using plotly python?

I am using python plotly '5.1.0' for a bubble plot. What I would like to do is to also add some text annotations containing links to web pages. All works fine when I show the figure in jupyter notebook, however I haven't managed to save the plot with working links as a picture (my favorite format would be pdf but I tried basically all the available ones).

Is this possible with plotly? Are there alternatives to achieve that with other libraries?

Edit: the final goal is to insert the picture in a pdf using latex, so solution directly compatible with this usage are preferred :)

Here same toy code to reproduce the behavior:

import plotly.express as px
df = px.data.gapminder()

fig = px.scatter(df.query("year==2007"), x="gdpPercap", y="lifeExp",
                 size="pop", color="continent",
                 hover_name="country", log_x=True, size_max=60)
fig.add_annotation(text="""here is my <a href="https://rdcu.be/cB1Ds"><b>paper</b></a>""", x=4, y=50)
fig.show()

The result correctly displays a clickable link: notebook preview: the link works

However, when I save it the link disappears:

fig.write_image("fig1.pdf", engine="kaleido") # I also tried "orca" but doesn't help

enter image description here

Upvotes: 0

Views: 1009

Answers (2)

rosa b.
rosa b.

Reputation: 2146

Short answer

Though not elegant, I would suggest to create an image without a link and then use tikz and hyperref to add the link to the image manually in latex:

\documentclass{article}
\usepackage{tikz}
\usepackage{hyperref}
\begin{document}

\begin{tikzpicture}
\begin{scope}
    \node[anchor=south west,inner sep=0] (image) at (0,0){\includegraphics[width=0.7\textwidth]{scatter_without_link.png}};
\begin{scope}[x={(image.south east)},y={(image.north west)}]
    \node [anchor=west] (note) at (0.6,0.70) {\href{https://stackoverflow.com}{Link}};
\end{scope}
\end{scope}
\end{tikzpicture}

\end{document}

Long answer

You could create an svg with clickable links. A minimum working example using matplotlib is

import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.scatter([1, 2, 3], [4, 5, 6])
text = ax.annotate("Link", xy=(2,5), xytext=(2.2,5.5),
                url='https://stackoverflow.com', 
                bbox=dict(color='w', alpha=1e-6, url='https://stackoverflow.com'))
fig.savefig('scatter.svg')

Then convert the svg to pdf, e.g. using inkscape and the command line:

inkscape -D --export-type="pdf" scatter.svg --export-latex

This results in a pdf with the scatter plot and a clickable hyperlink.

However: Preserving the hyperlink when embedding the pdf in your latex file (e.g. using the svg package or simply \input{scatter.pdf_tex}) is a more difficult problem as pdftex seems to drop the annotations from the included file (see this 10-year-old question How to preserve hyperlinks in included PDF?).

Note: If you want to use the svg package, make sure that (1) Inkscape is installed and on the system path and (2) latex runs with shell-escape (--shell-escape) enabled.

Note 2: For displaying the matplotlib example in jupyter notebook:

%config InlineBackend.figure_formats = ['svg']
import matplotlib.pyplot as plt
%matplotlib inline
fig, ax = plt.subplots()
ax.scatter([1, 2, 3], [4, 5, 6])
text = ax.annotate("Link", ... )  # same as above
plt.plot()

Upvotes: 1

Bilal Qandeel
Bilal Qandeel

Reputation: 727

It seems that the available solutions are not always trusted. That, perhaps, explains why there is no standard one. I was about to suggest @rosa b.'s exact solution, it just did not work for me when I tried to replicate it at my end. Maybe because I used a challenging svg sample that contained two different types of hyperlinks: one around a shape and another around a text to cover all possibilities as follows:

links.svg

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"> 
 <!-- A link around a shape -->
 <a href="http://www.example.com">
   <circle cx="50" cy="40" r="35" stroke="green" fill="red"/>
     <text x="50" y="40" text-anchor="middle" font-size="0.5em">
       www.example.com
     </text>
 </a>

 <!-- A link around a text -->
 <a href="http://www.foo.bar">
   <text x="50" y="90" text-anchor="middle">
     Foo.Bar
   </text>
 </a>
</svg>

When I carried out rosa b.'s solution, using the following command under a Windows environment:

inkscape.com --export-area-drawing --export-type="pdf" --export-filename=links.pdf --export-latex links.svg

It indeed generated a links.tex_pdf file that, also, did not contain the link to the text hyperlink correctly suggesting adding it manually using \href{}.

I tried another solution by printing the svg to a pdf using Google Chrome since many PDF transformers filter off those hyperlinks as unwanted annotations. The print has to happen without headers or footers and at a 0 margin. This kept the csv's hyperlinks intact. Now coming to the second challenge to include this good pdf into LaTeX since, again, LaTeX inclusion of pdf do filter off the annotations, again!

I learned about a package called pax that you can include to better add the pdfs with links. I followed their recommendation and ended up with the following:

links.tex

\documentclass[a4paper,12pt,landscape]{article}
\usepackage{hyperref}
\usepackage{pdfpages}
% pdfpages loads graphicx

\usepackage{pax}

\iffalse
  \usepackage{hyperref}
  \hypersetup{
    filebordercolor={1 1 0},
  }
\fi

\begin{document}
  \includepdf[pages=-]{links}
\end{document}

I gave it a shot using the online LaTex editor Overleaf here and now we ended up with a clickable text inside of the circle, but not the outer one.

I hope this one would work for your specific svg.

Upvotes: 1

Related Questions