Fix icon resolution: imshow + interpolation="none" embeds icons unsampled
The OffsetImage path composites at figure dpi, so the icons were going into the PDF as 39x39 bitmaps regardless of source resolution (pdfimages-verified; this is why the SVG switch changed nothing visually). Placing them as AxesImage with interpolation="none" embeds the full 1024 px rasterisation with a scaling transform: 4719 ppi at the placed size, print-crisp. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BkRLcc18rwT2Lysu6PbG7v
This commit is contained in:
parent
c9c4d927ad
commit
438931d67b
3 changed files with 10 additions and 5 deletions
Binary file not shown.
Binary file not shown.
|
|
@ -54,7 +54,7 @@ def _icon(svg_name: str):
|
||||||
svg = OUT / "icons" / svg_name
|
svg = OUT / "icons" / svg_name
|
||||||
with tempfile.NamedTemporaryFile(suffix=".png") as f:
|
with tempfile.NamedTemporaryFile(suffix=".png") as f:
|
||||||
try:
|
try:
|
||||||
subprocess.run(["rsvg-convert", "-w", "2048", "-h", "2048", "-o", f.name, str(svg)],
|
subprocess.run(["rsvg-convert", "-w", "1024", "-h", "1024", "-o", f.name, str(svg)],
|
||||||
check=True, capture_output=True)
|
check=True, capture_output=True)
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
raise RuntimeError("rsvg-convert (librsvg) is required to rasterise the icon SVGs "
|
raise RuntimeError("rsvg-convert (librsvg) is required to rasterise the icon SVGs "
|
||||||
|
|
@ -122,8 +122,6 @@ def fig1a():
|
||||||
xs = [x0, x0 + cw + sep, x0 + 2 * cw + sep]
|
xs = [x0, x0 + cw + sep, x0 + 2 * cw + sep]
|
||||||
row_h, row_top = 0.157, 0.805
|
row_h, row_top = 0.157, 0.805
|
||||||
|
|
||||||
from matplotlib.offsetbox import AnnotationBbox, OffsetImage
|
|
||||||
|
|
||||||
for j2, (name, arch, guarantee, edge, face, headfill, textcol, icon) in enumerate(TIERS):
|
for j2, (name, arch, guarantee, edge, face, headfill, textcol, icon) in enumerate(TIERS):
|
||||||
x = xs[j2]
|
x = xs[j2]
|
||||||
xc = x + cw / 2 - 0.02 # text centred left of the icon slot
|
xc = x + cw / 2 - 0.02 # text centred left of the icon slot
|
||||||
|
|
@ -135,9 +133,16 @@ def fig1a():
|
||||||
linespacing=1.3, color=textcol)
|
linespacing=1.3, color=textcol)
|
||||||
ax.text(xc, 0.833, guarantee, ha="center", va="bottom", fontsize=6.4,
|
ax.text(xc, 0.833, guarantee, ha="center", va="bottom", fontsize=6.4,
|
||||||
style="italic", color=textcol, alpha=0.85)
|
style="italic", color=textcol, alpha=0.85)
|
||||||
|
# Reason: imshow + interpolation="none" embeds the icon unsampled in the PDF (an
|
||||||
|
# OffsetImage is always composited at figure dpi, i.e. ~39 px, whatever the source).
|
||||||
img = _icon(icon)
|
img = _icon(icon)
|
||||||
ax.add_artist(AnnotationBbox(OffsetImage(img, zoom=28.0 / img.shape[0]),
|
iw = 28.0 / 1140.0 # 28 display px on an 11.4in/100dpi fig
|
||||||
(x + cw - gap - 0.024, 0.902), frameon=False))
|
ih = iw * 11.4 / 5.3
|
||||||
|
icx, icy = x + cw - gap - 0.024, 0.902
|
||||||
|
ax.imshow(img, extent=(icx - iw / 2, icx + iw / 2, icy - ih / 2, icy + ih / 2),
|
||||||
|
interpolation="none", aspect="auto", zorder=5)
|
||||||
|
ax.set_xlim(0, 1)
|
||||||
|
ax.set_ylim(0, 1)
|
||||||
|
|
||||||
for i2, (label, definition, cells) in enumerate(ROWS):
|
for i2, (label, definition, cells) in enumerate(ROWS):
|
||||||
tags = TAGS[i2]
|
tags = TAGS[i2]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue