Figure and font size when using `Plots`

I am trying to save a plot at a specific size (in physical length units, like inches) and have the correct font size. The idea is that then I could just insert it into my LaTeX document without scaling and have the same font and font size as the rest of my document. I can’t seem to get this right and am extremely confused by the behavior of size and dpi. Any help understanding what is going on is appreciated!

Example:

using Plots

x = 0:0.01:2π;
y = cos.(x); 

p1 = plot(x,y;size=(600,600), dpi=600)
p2 = plot(x,y;size=(600,600), dpi=300)

savefig(p1, "test1.png")
savefig(p2, "test2.png")

I would expect the first image to be 1 inch by 1 inch, and the second to be 2 inches by 2 inches. But when I put them into my LaTeX document they are both larger than that, and even more confusing the second image is smaller than the first.

For reference, this workflow works in matplotlib.

Please check if this thread can help you.

1 Like

No, I don’t think so. Unless I’m completely misunderstanding the fundamental concepts. I tried:

using Plots
x = 0:0.01:2π;
y = cos.(x); 
DPI = 600
width_inches = 3.4
width_px = width_inches * DPI
p = plot(x,y;size=(width_px, width_px), dpi=DPI)
savefig(p, "test.png")

and get this image with tiny text

I would also try using the pyplot backend with a fixed dpi=100: pyplot(dpi=100) and then changing only the size keyword as per linked post.

I can’t get that to work either :frowning:

It seems like Makie can do this and is well documented: Figures | Makie. I will try this!

Matching figure and font sizes to documents

Academic journals usually demand that figures you submit adhere to specific physical dimensions. How can you render Makie figures at exactly the right sizes?

But if anyone knows how to do it in Plots let me know!

Solution using Makie:

Julia:

using CairoMakie

x = 0:0.01:2π
y = cos.(x);

inches_2_pt = 72
pt_per_unit = 1


height = 4 # inches
width = 6 # inches
fontsize = 10 # pt
fonts = (; regular = "Computer Modern")

size =  Int.(round.((width, height) .* (inches_2_pt / pt_per_unit)))

f = Figure(;size, pt_per_unit, fonts, fontsize)
ax = Axis(f[1,1], xlabel="x label", ylabel="y label")
lines!(ax, x, y, label="cosine", color=:tomato)
axislegend(position = :rb)

save("cosine.pdf", f)

LaTeX:

\documentclass{article}
\usepackage{graphicx} 
\usepackage{lipsum} 

\title{Title}
\author{Author Author}
\date{\today}

\begin{document}
\maketitle

\lipsum[2]

\includegraphics{cosine.pdf}

x label cosine \lipsum[2]

\end{document}

and here is the final product:

The font size doesn’t look identical (not sure why) but close enough.

If you put \the\textwidth in somewhere in your latex source, you can see that your column is 345pt wide.

Then with Makie you can just match the width of the figure with your latex by setting size=(345,300) (only the width matters there), and you don’t have to worry about inches conversions etc

Use \includegraphics[width=\textwidth]{cosine.pdf}
edit: or set pt_per_unit = 1 in Makie so the pdf is the correct size

This matches fontsize perfectly with your latex document, confirmed by advanced inspection in paint:

image

1 Like

Ah, my mistake was using pt_per_unit in the wrong place… it should be used when saving the PDF. So it was using the default value (0.75). I can also use one of the default themes instead of specifying the font myself. Here is the solution that works.

using CairoMakie

x = 0:0.01:2π
y = cos.(x);

pt_per_unit = 1
figsizeinches(width, height) = (width, height) .* (72/pt_per_unit)

height = 2.5 # inches
width = 4.5 # inches
fontsize = 10

set_theme!(theme_latexfonts(); fontsize)

f = Figure(; size=figsizeinches(width, height))
ax = Axis(f[1,1], xlabel="x label", ylabel="y label")
lines!(ax, x, y, label="cosine", color=:tomato)
axislegend(position = :rb)

save("cosine.pdf", f; pt_per_unit)

As @p_f mentioned, maybe a better way than inches is basing the size of the figure on the \textwidth of the LaTeX document, and using pt_per_unit=1

The trick is to realize that there is an internal dpi value in Plots.jl and the actual dpi value of the saved figure. The internal value is hardcoded as 100. The actual dpi value of the saved figure is set via plots(...; dpi=DPI). This can be reflected in your code snippet as

using Plots
x = 0:0.01:2π;
y = cos.(x); 
DPI = 600
width_inches = 3.4
width_px = width_inches * 100 # internal dpi=100
p = plot(x,y;size=(width_px, width_px), dpi=DPI)
savefig(p, "test.png")

This gives me the following png file:

Hope this helps!

2 Likes