1- # R/sponsors.R ---------------------------------------------------------------
21
32read_sponsors <- function (csv_path ) {
43 read.csv(csv_path , stringsAsFactors = FALSE )
54}
65
7- # Helper: join prefix + path safely (works for URLs and relative paths)
86url_join <- function (prefix , path ) {
97 if (is.null(prefix ) || prefix == " " ) return (path )
8+
9+ # Keep "/" as site-root prefix
10+ if (identical(prefix , " /" )) {
11+ path <- sub(" ^/+" , " " , path )
12+ return (paste0(" /" , path ))
13+ }
14+
1015 prefix <- sub(" /+$" , " " , prefix )
1116 path <- sub(" ^/+" , " " , path )
1217 paste0(prefix , " /" , path )
1318}
1419
15- # ---- (1) Harmonize one logo into a fixed canvas and save it ----------------
20+
21+ # ---- (1) Harmonize one logo into a fixed canvas and save it
1622harmonize_logo <- function (input_fs ,
1723 output_fs ,
1824 canvas_w = 800 ,
@@ -21,23 +27,23 @@ harmonize_logo <- function(input_fs,
2127 if (! requireNamespace(" magick" , quietly = TRUE )) {
2228 stop(" Package 'magick' is required for harmonization. Install with install.packages('magick')." )
2329 }
24-
30+
2531 img <- magick :: image_read(input_fs )
26-
32+
2733 # Scale logo to fit inside (canvas - padding) while preserving aspect ratio
2834 target_w <- max(1 , canvas_w - 2 * padding )
2935 target_h <- max(1 , canvas_h - 2 * padding )
30-
36+
3137 img2 <- magick :: image_resize(img , geometry = paste0(target_w , " x" , target_h , " >" ))
32-
38+
3339 # Create transparent canvas and center the resized logo
3440 canvas <- magick :: image_blank(width = canvas_w , height = canvas_h , color = " none" )
3541 composed <- magick :: image_composite(canvas , img2 , operator = " over" , gravity = " center" )
36-
42+
3743 # Ensure output directory exists
3844 out_dir <- dirname(output_fs )
3945 if (! dir.exists(out_dir )) dir.create(out_dir , recursive = TRUE , showWarnings = FALSE )
40-
46+
4147 magick :: image_write(composed , path = output_fs , format = " png" )
4248 invisible (output_fs )
4349}
@@ -50,50 +56,57 @@ harmonize_sponsor_logos <- function(df,
5056 canvas_h = 240 ,
5157 padding = 24 ) {
5258 stopifnot(" image" %in% names(df ))
53-
59+
60+ # If magick missing, do nothing (keep original df$image)
5461 if (! requireNamespace(" magick" , quietly = TRUE )) {
5562 warning(" Package 'magick' not installed; skipping harmonization." )
5663 return (df )
5764 }
58-
59- # Create a copy so we can overwrite df$image to point to harmonized images
65+
6066 df2 <- df
61-
67+
6268 for (i in seq_len(nrow(df2 ))) {
6369 in_rel <- df2 $ image [i ]
64- in_fs <- url_join(fs_prefix , in_rel )
65-
66- # Make stable output filename
70+
71+ # filesystem input (prefix applies)
72+ in_fs <- file.path(fs_prefix , in_rel )
73+
74+ # stable output name
6775 base <- tools :: file_path_sans_ext(basename(in_rel ))
68- out_rel <- file.path(harmonized_dir , paste0(base , " .png" ))
69- out_fs <- out_rel
70-
71- # Only (re)render if missing, or input is newer
72- need <- TRUE
73- if (file.exists(out_fs ) && file.exists(in_fs )) {
74- need <- file.info(out_fs )$ mtime < file.info(in_fs )$ mtime
75- } else if (file.exists(out_fs ) && ! file.exists(in_fs )) {
76- # If input_fs is not readable in this working dir, don't overwrite
77- need <- FALSE
78- }
79-
80- if (need && file.exists(in_fs )) {
81- harmonize_logo(
82- input_fs = in_fs ,
83- output_fs = out_fs ,
84- canvas_w = canvas_w ,
85- canvas_h = canvas_h ,
86- padding = padding
87- )
76+ out_rel <- file.path(harmonized_dir , paste0(base , " .png" )) # WEB PATH (no fs_prefix)
77+ out_fs <- file.path(fs_prefix , out_rel ) # FILESYSTEM PATH
78+
79+ # Only harmonize if we can read the input
80+ if (file.exists(in_fs )) {
81+
82+ # render if missing or stale
83+ need <- ! file.exists(out_fs ) ||
84+ (file.info(out_fs )$ mtime < file.info(in_fs )$ mtime )
85+
86+ if (need ) {
87+ harmonize_logo(
88+ input_fs = in_fs ,
89+ output_fs = out_fs ,
90+ canvas_w = canvas_w ,
91+ canvas_h = canvas_h ,
92+ padding = padding
93+ )
94+ }
95+
96+ # Only switch to harmonized image if the file actually exists
97+ if (file.exists(out_fs )) {
98+ df2 $ image [i ] <- out_rel
99+ }
100+ } else {
101+ # input not found -> keep original df2$image[i]
102+ # (this is the critical difference vs your current code)
88103 }
89-
90- # Point to the harmonized relative path for rendering
91- df2 $ image [i ] <- out_rel
92104 }
93-
105+
94106 df2
95107}
96108
109+
97110# ---- (2) Render grid; optionally harmonize and USE harmonized images --------
98111render_sponsor_grid <- function (df ,
99112 ncol = 4 ,
@@ -104,15 +117,17 @@ render_sponsor_grid <- function(df,
104117 harmonized_dir = " images/partners_harmonized" ,
105118 canvas_w = 800 ,
106119 canvas_h = 240 ,
107- padding = 24 ) {
120+ padding = 24 ,
121+ left_margin = 10 ,
122+ inner_width = 80 ) {
108123 stopifnot(all(c(" image" , " website" ) %in% names(df )))
109124 if (nrow(df ) == 0 ) return (invisible (NULL ))
110-
111- ncol <- min(ncol , nrow(df ))
125+
126+ ncol <- min(ncol , nrow(df ))
112127 width <- floor(100 / ncol )
113-
114- # Harmonize first (updates df$image to harmonized paths )
115- if (harmonize ) {
128+
129+ # If you want to harmonize on render time, do it here (optional )
130+ if (isTRUE( harmonize ) ) {
116131 df <- harmonize_sponsor_logos(
117132 df ,
118133 fs_prefix = fs_prefix ,
@@ -121,62 +136,72 @@ render_sponsor_grid <- function(df,
121136 canvas_h = canvas_h ,
122137 padding = padding
123138 )
139+ } else {
140+ # Even if we don't harmonize now, prefer an already-existing harmonized image
141+ for (i in seq_len(nrow(df ))) {
142+ base <- tools :: file_path_sans_ext(basename(df $ image [i ]))
143+ harm_rel <- file.path(harmonized_dir , paste0(base , " .png" ))
144+ harm_fs <- file.path(fs_prefix , harm_rel )
145+ if (file.exists(harm_fs )) df $ image [i ] <- harm_rel
146+ }
124147 }
125-
126- out <- c()
148+
149+ out <- character ()
150+
127151 out <- c(out , " :::: {.columns}" )
128- out <- c(out , " ::: {.column width=\" 10% \" }" )
152+ out <- c(out , sprintf( " ::: {.column width=\" %s%% \" }" , left_margin ) )
129153 out <- c(out , " :::" )
130- out <- c(out , " ::: {.column width=\" 90% \" }" , " " )
154+ out <- c(out , sprintf( " ::: {.column width=\" %s%% \" }" , inner_width ) , " " )
131155 out <- c(out , " :::: {.columns}" )
132-
156+
133157 for (i in seq_len(nrow(df ))) {
134- # df$image is now the harmonized relative path (or original if harmonize=FALSE)
135158 img_src <- url_join(web_prefix , df $ image [i ])
136-
159+ alt_txt <- " "
160+
137161 out <- c(out , sprintf(" ::: {.column width=\" %s%%\" }" , width ))
138-
162+
163+ # Quarto supports target="_blank" on markdown links with an attribute block
164+ # If your setup doesn’t, you can remove `{target="_blank"}`
139165 out <- c(out , sprintf(
140- " <a href='%s' target='_blank' class='sponsor-link'>%s</a>" ,
141- df $ website [i ],
142- sprintf(
143- " <img src='%s' alt='%s' class='sponsor-logo'>" ,
144- img_src ,
145- if (" name" %in% names(df )) df $ name [i ] else " Sponsor logo"
146- )
166+ " [](%s){target=\" _blank\" }" ,
167+ alt_txt , img_src , df $ website [i ]
147168 ))
148-
149- if (show_name && (" name" %in% names(df )) && nzchar(df $ name [i ])) {
150- out <- c(out , sprintf( " <div class='sponsor-name'>%s</div> " , df $ name [i ]) )
169+
170+ if (isTRUE( show_name ) && (" name" %in% names(df )) && nzchar(df $ name [i ])) {
171+ out <- c(out , " " , df $ name [i ])
151172 }
152-
173+
153174 out <- c(out , " :::" )
154175 }
155-
176+
156177 out <- c(out , " ::::" , " " )
178+ out <- c(out , " :::" ) # close inner width column
179+ out <- c(out , sprintf(" ::: {.column width=\" %s%%\" }" , left_margin ))
157180 out <- c(out , " :::" )
158- out <- c(out , " ::: {.column width=\" 10%\" }" )
159- out <- c(out , " :::" )
160- out <- c(out , " ::::" )
161-
181+ out <- c(out , " ::::" ) # close outer columns
182+
162183 cat(paste(out , collapse = " \n " ))
163184}
164185
186+
165187render_sponsors_home <- function (csv_path , title = " " , ncol = 4 ,
188+ fs_prefix = " " ,
189+ web_prefix = " " ,
166190 harmonize = TRUE ,
167191 harmonized_dir = " images/partners_harmonized" ) {
168192 df <- read_sponsors(csv_path )
169- cat(sprintf( " # %s \n\n " , title ))
193+
170194 render_sponsor_grid(
171195 df ,
172196 ncol = ncol ,
173- fs_prefix = " " ,
174- web_prefix = " " ,
197+ fs_prefix = fs_prefix ,
198+ web_prefix = web_prefix ,
175199 harmonize = harmonize ,
176200 harmonized_dir = harmonized_dir
177201 )
178202}
179203
204+
180205render_sponsors_by_level <- function (
181206 csv_path ,
182207 level_order = c(" Diamond" , " Gold" , " Silver" , " Bronze" , " Supporter" ),
@@ -188,28 +213,28 @@ render_sponsors_by_level <- function(
188213 harmonized_dir = " images/partners_harmonized"
189214) {
190215 heading <- match.arg(heading )
191-
216+
192217 df <- read_sponsors(csv_path )
193218 if (! (" level" %in% names(df ))) stop(" CSV must include a 'level' column." )
194-
219+
195220 df $ level <- factor (df $ level , levels = level_order )
196-
221+
197222 levels_present <- unique(as.character(df $ level ))
198223 levels_present <- level_order [level_order %in% levels_present ]
199-
224+
200225 for (lvl in levels_present ) {
201226 df_lvl <- df [as.character(df $ level ) == lvl , , drop = FALSE ]
202227 if (nrow(df_lvl ) == 0 ) next
203-
228+
204229 if (heading == " h2" ) {
205230 cat(sprintf(" \n ## %s\n\n " , lvl ))
206231 } else {
207232 cat(sprintf(" \n **%s**\n\n " , lvl ))
208233 }
209-
234+
210235 ncol <- unname(ncol_by_level [lvl ])
211236 if (is.na(ncol )) ncol <- 4
212-
237+
213238 render_sponsor_grid(
214239 df_lvl ,
215240 ncol = ncol ,
0 commit comments