Why do font sizes change with dpi in Plots?

I would like to save plots as .png files at a certain dpi resolution, import into MS Word, then set the width of the image to the correct length according to the dpi setting. However, changing the dpi with Plots.jl in 3 tested backends also changes the font size (and other attributes).

If font sizes are in units of points, and a point is 0.3528 mm, then shouldn’t font size be constant regardless of dpi settings?

For example I have a function that takes the number of columns I want a figure to occupy and a fraction of the max size allowed in each dimension. It prints out dimensional info as a convenience:

using Plots
pyplot()
backend = "pyplot"

function fig_size_px(;n_columns=n_columns::Int, dpi::Int=300, w_frac::Float64=1.0, h_frac::Float64=1.0)
    MM_PER_IN = 25.4
    w_mm_max = 90 * n_columns
    w_mm = w_mm_max * w_frac
    w_in = w_mm/MM_PER_IN
    w_px::Int = round(Int32,w_in * dpi) 

    h_mm_max = 170
    h_mm = h_mm_max * h_frac
    h_in = h_mm/MM_PER_IN
    h_px::Int = round(Int32,h_in * dpi)

    println("DPI set to $(dpi).\n
        $(n_columns) column wide\n
        Figure width: $(w_px) pixels ($(round(Int32,w_mm_max/MM_PER_IN * dpi)) max), $(w_mm) mm ($(w_mm_max) max), $(round(w_in,digits=3)) in. ($(round(w_mm_max/MM_PER_IN,digits=3)) max)\n
        Figure height: $(h_px) pixels ($(round(h_mm_max/MM_PER_IN * dpi)) max), $(h_mm) mm ($(h_mm_max) max), $(round(h_in,digits=3)) in. ($(round(h_mm_max/MM_PER_IN,digits=3)) max)")

    return w_px, h_px
end

x = (0.0:0.01:2π)
y = sin.(x)

default_font_size = 12
title_font_size = 16
font_family = "Helvetica"

n_cols = 1
dpi_setting = 300
w_px, h_px = fig_size_px(n_columns=n_cols, dpi=dpi_setting, w_frac=0.5, h_frac=0.25)

f1 = plot(size=(w_px, h_px),
    xtickfont = font(default_font_size, font_family),
    ytickfont = font(default_font_size, font_family),
    xlabelfont = font(default_font_size, font_family),
    ylabelfont = font(default_font_size, font_family),
    titlefont = font(default_font_size, font_family),
    legendfont = font(default_font_size, font_family),
    xguidfontsize = default_font_size,
    yguidefontsize = default_font_size,
    margin=5mm,
    dpi=dpi_setting)

plot!(f1[1], x, y,
    xlabel="x",
    ylabel="sin(x)",
    title="y = sin(x)",
    label="y = sin(x)")

savefig("test_$(dpi_setting)-dpi_$(backend).png")

dpi_setting = 600
w_px, h_px = fig_size_px(n_columns=n_cols, dpi=dpi_setting, w_frac=0.5, h_frac=0.25)

f2 = plot(size=(w_px, h_px),
    xtickfont = font(default_font_size, font_family),
    ytickfont = font(default_font_size, font_family),
    xlabelfont = font(default_font_size, font_family),
    ylabelfont = font(default_font_size, font_family),
    titlefont = font(default_font_size, font_family),
    legendfont = font(default_font_size, font_family),
    xguidfontsize = default_font_size,
    yguidefontsize = default_font_size,
    margin=5mm,
    dpi=dpi_setting)

plot!(f2[1], x, y,
    xlabel="x",
    ylabel="sin(x)",
    title="y = sin(x)",
    label="y = sin(x)")

savefig("test_$(dpi_setting)-dpi_$(backend).png")

Testing with pyplot(), gr(), and plotly() (html → png) gives this when importing into Word and scalling the width to 45 mm where the backend labels are typed in Word with the font and font size specified.

… works as expected in plain GR.jl.

There are a handful of open issues debating over what dpi should and should not do on the Plots repository (e.g. dpi on backends · Issue #733 · JuliaPlots/Plots.jl · GitHub, Proposal: user-friendly DPI setting. · Issue #742 · JuliaPlots/Plots.jl · GitHub)