# GLMakie saving Axis

**URL:** <https://discourse.julialang.org/t/glmakie-saving-axis/122358>\
**Category:** General Usage\
**Created:** [November 6, 2024, 9:37pm UTC](https://discourse.julialang.org/t/glmakie-saving-axis/122358 "2024-11-06T21:37:07Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![sransome](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sransome/32/29810_2.png) [@sransome](https://discourse.julialang.org/u/sransome)\
**Post date:** [November 6, 2024, 9:37pm UTC](https://discourse.julialang.org/t/glmakie-saving-axis/122358/1 "2024-11-06T21:37:07Z")

</div>

I have a complex figure with several Axis and I want to save an image of just one Axis. One of the Axis is named SpreadGrid\_ax and I have tried…

`FileIO.save("SpreadGrid_ax.png", SpreadGrid_ax.scene; size = size(SpreadGrid_ax.scene), px_per_unit = 1.0)`

however, the image file this creates contains the entire figure with all Axis rendered. Is it possible to specify just one axis?

Thanks  
Steve

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [November 6, 2024, 11:23pm UTC](https://discourse.julialang.org/t/glmakie-saving-axis/122358/2 "2024-11-06T23:23:12Z")

</div>

This seems to work:

```julia
f = Figure()
for i in 1:3, j in 1:3
    Axis(f[i, j], title = "$i $j")
end

function outer_bbox(ax; padding)
    sbb = ax.layoutobservables.suggestedbbox[]
    prot = ax.layoutobservables.protrusions[]
    o = sbb.origin .- (prot.left, prot.bottom) .- padding
    w = sbb.widths .+ (prot.left + prot.right, prot.bottom + prot.top) .+ 2 * padding
    Rect2f(o, w)
end

Makie.update_state_before_display!(f)

function cropped_colorbuffer(fig, bb; kwargs...)
    full_bb = fig.scene.viewport[]

    mi = bb.origin
    ma = mi .+ bb.widths
    f_mi = (mi .- full_bb.origin) ./ full_bb.widths
    f_ma = (ma .- full_bb.origin) ./ full_bb.widths

    cb = Makie.colorbuffer(fig; kwargs...)
    px_mi = round.(Int, reverse(size(cb)) .* f_mi)
    px_ma = round.(Int, reverse(size(cb)) .* f_ma)
    px_h = size(cb, 1)
    cb[(px_h - px_ma[2]):(px_h - px_mi[2]), px_mi[1]:px_ma[1]]
end

Makie.save("test.png", cropped_colorbuffer(f, outer_bbox(content(f[2, 2]), padding = 10); px_per_unit = 4))

```

 ![test](https://global.discourse-cdn.com/julialang/original/3X/3/5/353f334a7363de4e58b45e00330d01bdb6655ee1.png)

---

<div class="post-metadata">

**Author:** ![sransome](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sransome/32/29810_2.png) [@sransome](https://discourse.julialang.org/u/sransome)\
**Post date:** [November 7, 2024, 12:17am UTC](https://discourse.julialang.org/t/glmakie-saving-axis/122358/3 "2024-11-07T00:17:45Z")

</div>

Thanks Jules, that worked.

When I call Makie.save using…

`Makie.save("test.png", cropped_colorbuffer(fig,outer_bbox(content(fig[1:2,3]), padding = 10); px_per_unit = 2)) `

the image on the screen deteriorates. Its readable but only just. Is there a way to avoid this?

and, I want to save as an svg file so I can print these at good quality. So I added CairoMakie to the project and tried…

`Makie.save("test.svg", cropped_colorbuffer(fig, backend=CairoMakie,outer_bbox(content(fig[1:2,3]), padding = 10); px_per_unit = 2)) `

but it seems that using GLMakie and CarioMakie at the same time causes issues, my plots don’t show and a file display.png opens. Is there a way to have an application based on GLMakie create svg files?

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [November 7, 2024, 6:56am UTC](https://discourse.julialang.org/t/glmakie-saving-axis/122358/4 "2024-11-07T06:56:25Z")

</div>

Hm the quality deteriorating is probably because the screen can only have one px\_per\_unit at the time, although you set 2 so that should usually be sufficient unless you have a very unusual screen setup. Maybe there’s a bug there, though.

With CairoMakie it would still only make sense to save a png with this technique as colorbuffer makes a bitmap. If you had said you wanted an svg I wouldn’t have suggested that solution. I’ll try and see if there’s a similar workaround that makes sense for svg.

---

<div class="post-metadata">

**Author:** ![sdanisch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sdanisch/32/1406_2.png) [@sdanisch](https://discourse.julialang.org/u/sdanisch)\
**Post date:** [November 7, 2024, 11:14am UTC](https://discourse.julialang.org/t/glmakie-saving-axis/122358/5 "2024-11-07T11:14:22Z")

</div>

Oh, this should work but seems to be broken:

```julia
using GLMakie

f = Figure()
for i in 1:3, j in 1:3
    Axis(f[i, j], title="$i $j")
end
Makie.save("test.png", colorbuffer(content(f[2, 2])))

```

Really weird, we even have a reference image test for this, which is totally borked…  
Not sure how this regression got merged without the tests failing - I’m guessing it must have been in a PR where we updated all reference images, since the visual diff must be big enough even for our old thresholds to go off.

---

<div class="post-metadata">

**Author:** ![sransome](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sransome/32/29810_2.png) [@sransome](https://discourse.julialang.org/u/sransome)\
**Post date:** [November 7, 2024, 11:43am UTC](https://discourse.julialang.org/t/glmakie-saving-axis/122358/6 "2024-11-07T11:43:18Z")

</div>

Just to me clear I can generate the png and the image file looks ok on the screen but it’s very poor quality when I print it. I saw a reference suggesting that for printing I should use svg but I’m not wedded to any particular format.

This is an example of one of the Axis that I want to print on it’s own. The others are more conventional charts.

 ![SharedScreenshot](https://global.discourse-cdn.com/julialang/original/3X/2/b/2b2c11bd674ccbe835482e1c51f261e6451909d6.jpeg)

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [November 7, 2024, 12:04pm UTC](https://discourse.julialang.org/t/glmakie-saving-axis/122358/7 "2024-11-07T12:04:31Z")

</div>

Here is a variant that saves svg. You can’t crop an svg by throwing away pixels, so I do it by editing the `width`, `height` and `viewBox` attributes accordingly.

```julia
using CairoMakie

f = Figure()
for i in 1:3, j in 1:3
    Axis(f[i, j], title = "$i $j")
end

function outer_bbox(ax; padding)
    sbb = ax.layoutobservables.suggestedbbox[]
    prot = ax.layoutobservables.protrusions[]
    o = sbb.origin .- (prot.left, prot.bottom) .- padding
    w = sbb.widths .+ (prot.left + prot.right, prot.bottom + prot.top) .+ 2 * padding
    Rect2f(o, w)
end

function save_cropped_svg(file, scene, bbox)
    sw, sh = scene.viewport[].widths
    ox, oy = bbox.origin
    w, h = bbox.widths
    svg = repr(MIME"image/svg+xml"(), scene)
    svg = replace(
        svg,
        r"viewBox=\".*?\"" => "viewBox=\"$ox $(sh - oy - h) $w $h\"",
        r"width=\".*?\"" => "width=\"$w\"",
        r"height=\".*?\"" => "height=\"$h\"",
        count = 3,
    )
    open(file, "w") do io
        write(io, svg)
    end
    return
end

ax = current_axis()

save_cropped_svg("test.svg", ax.blockscene, outer_bbox(ax; padding = 10))

```

---

<div class="post-metadata">

**Author:** ![sransome](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sransome/32/29810_2.png) [@sransome](https://discourse.julialang.org/u/sransome)\
**Post date:** [November 7, 2024, 9:27pm UTC](https://discourse.julialang.org/t/glmakie-saving-axis/122358/8 "2024-11-07T21:27:26Z")

</div>

Thanks Jules, any idea what I might be doing wrong? I have updated all packages and that does not resolve this issue.

When I use GLMakie I get the following error, when I use GLMakie and CairoMakie my app won’t run, I get no errors but a blank display.png file opens.

````julia
MethodError: no method matching backend_show(::GLMakie.Screen{GLFW.Window}, ::IOBuffer, ::MIME{Symbol("image/svg+xml")}, ::Scene)
The function `backend_show` exists, but no method is defined for this 
combination of argument types.

Closest candidates are:
  backend_show(::MakieScreen, ::IO, ::Union{MIME{Symbol("text/html")}, MIME{Symbol("application/vnd.webio.application+html")}, MIME{Symbol("application/prs.juno.plotpane+html")}, MIME{Symbol("juliavscode/html")}}, ::Scene)
   @ Makie C:\Users\steve\.julia\packages\Makie\pFPBw\src\display.jl:497
  backend_show(::MakieScreen, ::IO, ::MIME{Symbol("image/jpeg")}, ::Scene)
   @ Makie C:\Users\steve\.julia\packages\Makie\pFPBw\src\display.jl:491
  backend_show(::MakieScreen, ::IO, ::MIME{Symbol("image/png")}, ::Scene)
   @ Makie C:\Users\steve\.julia\packages\Makie\pFPBw\src\display.jl:485
  ...

Stacktrace:
  [1] show(io::IOBuffer, m::MIME{Symbol("image/svg+xml")}, figlike::Scene)
    @ Makie C:\Users\steve\.julia\packages\Makie\pFPBw\src\display.jl:259
  [2] __binrepr(m::MIME{Symbol("image/svg+xml")}, x::Scene, context::Nothing)
    @ Base.Multimedia .\multimedia.jl:171
  [3] _textrepr(m::MIME{Symbol("image/svg+xml")}, x::Scene, context::Nothing)
    @ Base.Multimedia .\multimedia.jl:163
  [4] repr(m::MIME{Symbol("image/svg+xml")}, x::Scene; context::Nothing)
    @ Base.Multimedia .\multimedia.jl:159
  [5] save_cropped_svg(file::String, scene::Scene, bbox::GeometryBasics.HyperRectangle{2, Float32})
    @ Main c:\Source\Julia\shares\julia_helpers.jl:39
  [6] (::var"#113#115"{Main.mPortfolio.AllPortfolios, String, Date, Int64, Int64})(click::Int64)
    @ Main c:\Source\Julia\shares\shares_events.jl:245```
````

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)\
**Post date:** [November 7, 2024, 9:32pm UTC](https://discourse.julialang.org/t/glmakie-saving-axis/122358/9 "2024-11-07T21:32:18Z")

</div>

GLMakie doesn’t do svg, so you have to use CairoMakie. And `repr` doesn’t have a way to specify a different backend to use. So one has to take a slight detour through `save`:

```julia
using CairoMakie
using GLMakie
GLMakie.activate!()

f = Figure()
for i in 1:3, j in 1:3
    Axis(f[i, j], title = "$i $j")
end
display(f)

function outer_bbox(ax; padding)
    sbb = ax.layoutobservables.suggestedbbox[]
    prot = ax.layoutobservables.protrusions[]
    o = sbb.origin .- (prot.left, prot.bottom) .- padding
    w = sbb.widths .+ (prot.left + prot.right, prot.bottom + prot.top) .+ 2 * padding
    Rect2f(o, w)
end

function save_cropped_svg(file, scene, bbox)
    sw, sh = scene.viewport[].widths
    ox, oy = bbox.origin
    w, h = bbox.widths
    svg = mktempdir() do dir
        save(joinpath(dir, "output.svg"), scene; backend = CairoMakie)
        read(joinpath(dir, "output.svg"), String)
    end
    svg = replace(
        svg,
        r"viewBox=\".*?\"" => "viewBox=\"$ox $(sh - oy - h) $w $h\"",
        r"width=\".*?\"" => "width=\"$w\"",
        r"height=\".*?\"" => "height=\"$h\"",
        count = 3,
    )
    open(file, "w") do io
        write(io, svg)
    end
    return
end

ax = current_axis()

save_cropped_svg("test.svg", ax.blockscene, outer_bbox(ax; padding = 10))

```

---

<div class="post-metadata">

**Author:** ![sransome](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sransome/32/29810_2.png) [@sransome](https://discourse.julialang.org/u/sransome)\
**Post date:** [November 7, 2024, 9:56pm UTC](https://discourse.julialang.org/t/glmakie-saving-axis/122358/10 "2024-11-07T21:56:09Z")

</div>

Thanks Jules, that’s a perfect solution. and thanks to you and the whole team for such an amazing package.

Steve
