Skip to content

Commit 74c0930

Browse files
committed
explicitly test physical and pixel size separately
1 parent 7e0b52c commit 74c0930

File tree

7 files changed

+85
-39
lines changed

7 files changed

+85
-39
lines changed

src/server.jl

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,7 +1338,7 @@ function process_results(dict::Dict{String,@NamedTuple{error::Bool, data::Vector
13381338
return (; data, metadata, errors)
13391339
end
13401340

1341-
function png_image_metadata(bytes::Vector{UInt8})
1341+
function png_image_metadata(bytes::Vector{UInt8}; phys_correction = true)
13421342
if @view(bytes[1:8]) != b"\x89PNG\r\n\x1a\n"
13431343
throw(ArgumentError("Not a png file"))
13441344
end
@@ -1371,25 +1371,27 @@ function png_image_metadata(bytes::Vector{UInt8})
13711371
width = Int(_load(UInt32, chunk.data, 1))
13721372
height = Int(_load(UInt32, chunk.data, 5))
13731373

1374-
# if the png reports a physical pixel size, i.e., it has a pHYs chunk
1375-
# with the pixels per meter unit flag set, correct the basic width and height
1376-
# by those physical pixel sizes so that quarto receives the intended size
1377-
# in CSS pixels
1378-
while true
1379-
chunk = read_chunk!()
1380-
chunk === nothing && break
1381-
chunk.type == b"IDAT" && break
1382-
if chunk.type == b"pHYs"
1383-
is_in_meters = Bool(_load(UInt8, chunk.data, 9))
1384-
is_in_meters || break
1385-
x_px_per_meter = _load(UInt32, chunk.data, 1)
1386-
y_px_per_meter = _load(UInt32, chunk.data, 5)
1387-
# it seems sensible to round the final image size to full CSS pixels,
1388-
# especially given that png doesn't store dpi but px per meter
1389-
# in an integer format, losing some precision
1390-
width = round(Int, width / x_px_per_meter * (96 / 0.0254))
1391-
height = round(Int, height / y_px_per_meter * (96 / 0.0254))
1392-
break
1374+
if phys_correction
1375+
# if the png reports a physical pixel size, i.e., it has a pHYs chunk
1376+
# with the pixels per meter unit flag set, correct the basic width and height
1377+
# by those physical pixel sizes so that quarto receives the intended size
1378+
# in CSS pixels
1379+
while true
1380+
chunk = read_chunk!()
1381+
chunk === nothing && break
1382+
chunk.type == b"IDAT" && break
1383+
if chunk.type == b"pHYs"
1384+
is_in_meters = Bool(_load(UInt8, chunk.data, 9))
1385+
is_in_meters || break
1386+
x_px_per_meter = _load(UInt32, chunk.data, 1)
1387+
y_px_per_meter = _load(UInt32, chunk.data, 5)
1388+
# it seems sensible to round the final image size to full CSS pixels,
1389+
# especially given that png doesn't store dpi but px per meter
1390+
# in an integer format, losing some precision
1391+
width = round(Int, width / x_px_per_meter * (96 / 0.0254))
1392+
height = round(Int, height / y_px_per_meter * (96 / 0.0254))
1393+
break
1394+
end
13931395
end
13941396
end
13951397

test/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[deps]
2+
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
23
JSON3 = "0f8b85d8-7281-11e9-16c2-39a750bddbf1"
34
JSONSchema = "7d188eb4-7ad8-530c-ae41-71a32a6d4692"
45
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"

test/examples/integrations/CairoMakie.qmd

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,3 @@ import CairoMakie
1717
```{julia}
1818
CairoMakie.scatter([1, 2, 3], [1, 2, 3])
1919
```
20-
21-
```{julia}
22-
CairoMakie.Makie.current_default_theme()[:CairoMakie][:px_per_unit][]
23-
```

test/testsets/integrations/CairoMakie.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ test_example(joinpath(@__DIR__, "../../examples/integrations/CairoMakie.qmd")) d
1010
@test cell["outputs"][1]["metadata"]["image/png"] ==
1111
Dict("width" => 4 * px_per_inch, "height" => 3 * px_per_inch)
1212

13-
# the pixel resolution is controlled by px_per_unit and that reflects the dpi that is set
14-
px_per_unit_str = cells[end-1]["outputs"][1]["data"]["text/plain"]
15-
px_per_unit = parse(Float64, px_per_unit_str)
16-
@test px_per_unit == 150 / px_per_inch
13+
pngbytes = Base64.base64decode(cell["outputs"][1]["data"]["image/png"])
14+
@test QuartoNotebookRunner.png_image_metadata(
15+
pngbytes;
16+
phys_correction = false,
17+
) == (; width = 4 * 150, height = 3 * 150)
1718
end

test/testsets/package_integration_hooks.jl

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,31 +26,43 @@ include("../utilities/prelude.jl")
2626
"CairoMakie.qmd";
2727
showprogress = false,
2828
)
29-
return json.cells[end-1].outputs[1].metadata["image/png"]
29+
logical_size = json.cells[end-1].outputs[1].metadata["image/png"]
30+
pngbytes = Base64.base64decode(json.cells[end-1].outputs[1].data["image/png"])
31+
px_size = QuartoNotebookRunner.png_image_metadata(
32+
pngbytes;
33+
phys_correction = false,
34+
)
35+
return (; width_px = px_size.width, height_px = px_size.height, logical_size...)
3036
end
3137

3238
metadata = png_metadata()
33-
@test metadata.width == 4 * 150
34-
@test metadata.height == 3 * 150
39+
@test metadata.width_px == 4 * 150
40+
@test metadata.height_px == 3 * 150
3541

3642
metadata = png_metadata("""
3743
fig-width: 8
3844
fig-height: 6
3945
fig-dpi: 300""")
40-
@test metadata.width == 8 * 300
41-
@test metadata.height == 6 * 300
46+
@test metadata.width_px == 8 * 300
47+
@test metadata.height_px == 6 * 300
48+
@test metadata.width == 8 * 96
49+
@test metadata.height == 6 * 96
4250

4351
metadata = png_metadata("""
4452
fig-width: 5
4553
fig-dpi: 100""")
46-
@test metadata.width == 5 * 100
47-
@test metadata.height == round(5 / 4 * 3 * 100)
54+
@test metadata.width_px == 5 * 100
55+
@test metadata.height_px == round(5 / 4 * 3 * 100)
56+
@test metadata.width == 5 * 96
57+
@test metadata.height == round(5 / 4 * 3 * 96)
4858

4959
metadata = png_metadata("""
5060
fig-height: 5
5161
fig-dpi: 100""")
52-
@test metadata.height == 5 * 100
53-
@test metadata.width == round(5 / 3 * 4 * 100)
62+
@test metadata.height_px == 5 * 100
63+
@test metadata.width_px == round(5 / 3 * 4 * 100)
64+
@test metadata.height == 5 * 96
65+
@test metadata.width == round(5 / 3 * 4 * 96)
5466

5567
# we don't want to rely on hardcoding Makie's own default size for our tests
5668
# but for the dpi-only test we can check that doubling the
@@ -59,8 +71,10 @@ include("../utilities/prelude.jl")
5971
fig-dpi: 96""")
6072
metadata_200dpi = png_metadata("""
6173
fig-dpi: 192""")
62-
@test 2 * metadata_100dpi.height == metadata_200dpi.height
63-
@test 2 * metadata_100dpi.width == metadata_200dpi.width
74+
@test 2 * metadata_100dpi.height_px == metadata_200dpi.height_px
75+
@test 2 * metadata_100dpi.width_px == metadata_200dpi.width_px
76+
@test metadata_100dpi.height == metadata_200dpi.height
77+
@test metadata_100dpi.width == metadata_200dpi.width
6478

6579
# same logic for width and height only
6680
metadata_single = png_metadata("""
@@ -69,6 +83,8 @@ include("../utilities/prelude.jl")
6983
metadata_double = png_metadata("""
7084
fig-width: 6
7185
fig-height: 4""")
86+
@test 2 * metadata_single.height_px == metadata_double.height_px
87+
@test 2 * metadata_single.width_px == metadata_double.width_px
7288
@test 2 * metadata_single.height == metadata_double.height
7389
@test 2 * metadata_single.width == metadata_double.width
7490

test/testsets/png_metadata.jl

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,48 @@ include("../utilities/prelude.jl")
44
@test QuartoNotebookRunner.png_image_metadata(
55
read(joinpath(@__DIR__, "..", "assets", "10x15.png")),
66
) == (; width = 10, height = 15)
7+
@test QuartoNotebookRunner.png_image_metadata(
8+
read(joinpath(@__DIR__, "..", "assets", "10x15.png"));
9+
phys_correction = false,
10+
) == (; width = 10, height = 15)
11+
712
@test QuartoNotebookRunner.png_image_metadata(
813
read(joinpath(@__DIR__, "..", "assets", "15x10.png")),
914
) == (; width = 15, height = 10)
15+
@test QuartoNotebookRunner.png_image_metadata(
16+
read(joinpath(@__DIR__, "..", "assets", "15x10.png"));
17+
phys_correction = false,
18+
) == (; width = 15, height = 10)
19+
1020
@test QuartoNotebookRunner.png_image_metadata(
1121
read(joinpath(@__DIR__, "..", "assets", "black_no_dpi.png")),
1222
) == (; width = 100, height = 100)
23+
@test QuartoNotebookRunner.png_image_metadata(
24+
read(joinpath(@__DIR__, "..", "assets", "black_no_dpi.png"));
25+
phys_correction = false,
26+
) == (; width = 100, height = 100)
27+
1328
@test QuartoNotebookRunner.png_image_metadata(
1429
read(joinpath(@__DIR__, "..", "assets", "black_96_dpi.png")),
1530
) == (; width = 100, height = 100)
31+
@test QuartoNotebookRunner.png_image_metadata(
32+
read(joinpath(@__DIR__, "..", "assets", "black_96_dpi.png"));
33+
phys_correction = false,
34+
) == (; width = 100, height = 100)
35+
1636
@test QuartoNotebookRunner.png_image_metadata(
1737
read(joinpath(@__DIR__, "..", "assets", "black_300_dpi.png")),
1838
) == (; width = 32, height = 32)
39+
@test QuartoNotebookRunner.png_image_metadata(
40+
read(joinpath(@__DIR__, "..", "assets", "black_300_dpi.png"));
41+
phys_correction = false,
42+
) == (; width = 100, height = 100)
43+
1944
@test QuartoNotebookRunner.png_image_metadata(
2045
read(joinpath(@__DIR__, "..", "assets", "black_600_dpi.png")),
2146
) == (; width = 16, height = 16)
47+
@test QuartoNotebookRunner.png_image_metadata(
48+
read(joinpath(@__DIR__, "..", "assets", "black_600_dpi.png"));
49+
phys_correction = false,
50+
) == (; width = 100, height = 100)
2251
end

test/utilities/prelude.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ using Test
22
using Logging
33
using QuartoNotebookRunner
44

5+
import Base64
56
import JSON3
67
import JSONSchema
78
import NodeJS_18_jll

0 commit comments

Comments
 (0)