civilytics_logo(): drops patchwork panels, clips captions, and silently rescales fonts #20
Open
opened 2026-08-10 13:02:42 -04:00 by jared
·
0 comments
No Branch/Tag Specified
Labels
Clear labels
bug
documentation
duplicate
enhancement
good first issue
help wanted
invalid
kodor
kodor/feature-proposal
kodor/fix
kodor/needs-review
kodor/triaged
question
wontfix
kodor
kodor/feature-proposal
kodor/fix
kodor/needs-review
kodor/triaged
Something isn't working
Improvements or additions to documentation
This issue or pull request already exists
New feature or request
Good for newcomers
Extra attention is needed
This doesn't seem right
Kodor should process this issue
Kodor has written a feature proposal
Kodor should implement a fix (assigned to Kodor)
Kodor's work or failure needs Jared's review
Kodor has already triaged this issue (skip)
Further information is requested
This will not be worked on
needs
human
Cannot move without a person -- a decision, a check an agent cannot make, something outside the repo
origin
client
Came from a client ask
origin
obligation
Created by a change elsewhere
origin
review
Came from human review
origin
roborev
Promoted from a roborev finding
type
chore
Maintenance with no behaviour change
type
debt
Owed work -- docs, tests, cleanup a change obligated
type
decision
Needs a decision before work can proceed
type
defect
Something is wrong
type
feature
New capability
ws
helpers
Analysis and workflow helpers
ws
logo
Logo and branded output composition
ws
packaging
Package infrastructure and release
ws
quarto
Quarto themes and publishing templates
ws
theme
Themes, palettes, and fonts
Assign a task to kodor
Kodor thinks this needs a feature.
Kodor should fix this
Kodor thinks the user is ready to review this.
Kodor is done with this issue.
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: Civilytics/civilyticsR#20
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
civilytics_logo()/add_logo()silently corrupts figures in three distinct ways. Allthree are invisible at authoring time — the function returns a grob and never warns — and
all three surfaced together in
crdc-arrestswhile preparing 18 figures forcivilytics.com. Every affected chunk there now routes around the function rather than
using it, which is why this is filed upstream.
The root cause of two of the three is that
add_logo()hand-tunes layout with negativepoint constants and heights that sum to more than 1, calibrated against text that was
being drawn at roughly one-third size because
.onLoadenables showtext.Environment
civilyticsas installed 2026-08-10 (renv library,crdc-arrests)raggdevice,dpi = 300Defect 1 — patchwork compositions lose every panel but the last
add_logo()ends with:gridExtra::arrangeGrob()coerces a ggplot viaggplotGrob()→ggplot_build().A
patchworkobject inheritsc("patchwork", "gg", "ggplot"), so it satisfies thedispatch, but patchwork composes at
print()/plot()time and has noggplot_buildmethod.
ggplot_build()therefore builds only the last plot added to the composition.No error, no warning — you get a valid PNG containing one panel of a multi-panel figure.
Repro:
Observed in production: three figures in
crdc-arrestsshipped with 1 of 2, 1 of 4,and 1 of 2 panels respectively. One of them (
zero_arrest_counts_draws) had prosewalking the reader through "the top left" and "the top right panel" of a figure that had
been reduced to a single cell.
Suggested fix: branch on the class before composing.
Defect 2 — captions are pushed off the bottom of the canvas
Two things combine here.
(a) The composed heights sum to 1.03:
The composition is 3% taller than the device, so the bottom 3% — where the caption
sits — falls outside the canvas.
(b) A negative bottom margin proportional to caption line count:
-52pt per caption line is a magic constant that only holds for one font at one size onone device. It appears to have been tuned while showtext was active — and showtext is
enabled by this package's own
.onLoadviacivilytics_load_fonts()→showtext_auto().showtext draws glyphs at its fixed 96 dpi while the device lays out at
res = 300, so at300 dpi text renders at roughly 32% of its requested size. A
-52pt pull is survivableagainst third-size text and destructive against correctly-sized text.
Repro (A/B on identical plots, only the logo call differs):
Measuring ink in the bottom two pixel rows of each file:
no_logo.pngwith_logo.pngObserved in production: 8 of 18 figures shipped with the source attribution cut
roughly in half vertically.
Suggested fix: make the logo band additive rather than subtractive — reserve its
height in the layout so collision is impossible by construction, rather than pulling the
plot down with a negative margin and hoping. Heights should also sum to 1. Sizing the
logo in inches rather than as a fraction would additionally keep its apparent size
stable across 8–24 in canvases.
Defect 3 — the base font is silently rescaled by 10%
Adding a logo changes the typography of the plot. A caller who set
theme_civilytics(font_size = 12)gets 13.2, and any constant tuned against 12 — wrapwidths, annotation offsets, manual
nudge_*values — is now off. This is a surprisingside effect for a function whose documented job is to stamp a logo, and it is not
mentioned at the call site.
Suggested fix: default
font_scale = 1, and if the 1.1 behaviour is wanted somewhere,make it opt-in at the call site.
Why this went unnoticed for so long
The three defects mask each other and mask their own cause:
.onLoadturns showtext on, so all text renders at ~1/3 size.add_logo()were tuned in that regime and look fine there.and a 3% overflow clips only a caption most reviewers don't read.
web content column, where 10pt table text landed at ~5 CSS pixels against 18px body
text.
There is a documented note in
crdc-arrestsfrom an earlier encounter with (2), wherecivilytics_logo()was found to shrink a plot ~7% and collapse a facet. That was workedaround locally at the time rather than reported — hence this issue now covering all of it.
Suggested acceptance criteria
civilytics_logo()renders all its panels and itsplot_annotation()res = 300, at canvas sizes from 8 to 24 inches widefont_scaledefaults to 1independent of whether showtext is active
.onLoadshould callshowtext_auto()at all — it makes correctragg/systemfonts rendering opt-out rather than opt-in, and the two disagree about dpi
Workaround currently in use
crdc-arrestsstamps the logo onto the saved raster instead of composing it into thegrob —
ragg::agg_png()→print(p)→dev.off()→ composite the logo withmagick.That never reflows the plot, so it is immune to all three defects. Helper is
cv_stamp_logo_png()in that repo'sR/branding.Rif it is useful as a starting point.