| 62 |
62 |
|
/// constant rather than on a string typed in two repositories.
|
| 63 |
63 |
|
pub use makeover::{WEBFONT_MONO_FILE, WEBFONT_SANS_FILE};
|
| 64 |
64 |
|
|
|
65 |
+ |
/// Layer 0 of the font model, re-exported for the same one-dependency reason.
|
|
66 |
+ |
///
|
|
67 |
+ |
/// A build script composing an override needs all four names and has no other
|
|
68 |
+ |
/// reason to depend on `makeover` directly.
|
|
69 |
+ |
pub use makeover::{FontFace, FontOverride, FontSlot, Typography};
|
|
70 |
+ |
|
| 65 |
71 |
|
/// Write the themes `makeover` ships into `dir`, as `<id>.toml`.
|
| 66 |
72 |
|
///
|
| 67 |
73 |
|
/// Clears stale `.toml` files first, so a theme removed or renamed upstream
|
| 165 |
171 |
|
///
|
| 166 |
172 |
|
/// If the file cannot be written.
|
| 167 |
173 |
|
pub fn typography_css(path: impl AsRef<Path>, font_url: &str) {
|
|
174 |
+ |
typography_css_from(path, &makeover::Typography::house(font_url));
|
|
175 |
+ |
}
|
|
176 |
+ |
|
|
177 |
+ |
/// [`typography_css`], for a product that overrides a slot.
|
|
178 |
+ |
///
|
|
179 |
+ |
/// Layer 0 of the font model. A product with a brand face declares it here,
|
|
180 |
+ |
/// once, and the generated sheet carries both the `@font-face` and the token —
|
|
181 |
+ |
/// which is what replaces the hand-maintained `@font-face` block plus a
|
|
182 |
+ |
/// `--font-heading` nothing else in the tree knew about:
|
|
183 |
+ |
///
|
|
184 |
+ |
/// ```no_run
|
|
185 |
+ |
/// use makeover_build::{FontFace, FontOverride, FontSlot, Typography};
|
|
186 |
+ |
///
|
|
187 |
+ |
/// makeover_build::typography_css_from(
|
|
188 |
+ |
/// "static/typography.css",
|
|
189 |
+ |
/// &Typography::house("/static/fonts").with_override(
|
|
190 |
+ |
/// FontOverride::new(FontSlot::Display, "\"Young Serif\", serif")
|
|
191 |
+ |
/// .with_face(FontFace::new("Young Serif", ["ysrf.woff2", "ysrf.ttf"])),
|
|
192 |
+ |
/// ),
|
|
193 |
+ |
/// );
|
|
194 |
+ |
/// ```
|
|
195 |
+ |
///
|
|
196 |
+ |
/// The product still ships the face itself, exactly as it does for the house
|
|
197 |
+ |
/// two: this writes the CSS that fetches it and cannot produce a font.
|
|
198 |
+ |
///
|
|
199 |
+ |
/// # Panics
|
|
200 |
+ |
///
|
|
201 |
+ |
/// If the file cannot be written.
|
|
202 |
+ |
pub fn typography_css_from(path: impl AsRef<Path>, typography: &makeover::Typography) {
|
| 168 |
203 |
|
let mut css = String::from(
|
| 169 |
204 |
|
"/* Generated by makeover-build from makeover. Do not edit.\n \
|
| 170 |
205 |
|
Two needs, two names, then a system generic. The faces are cut by\n \
|
| 171 |
206 |
|
quasi-type from Atkinson Hyperlegible plus the house glyph set, and\n \
|
| 172 |
207 |
|
both are variable over wght 200-800 in one file, which is why the\n \
|
| 173 |
|
- |
@font-face rules name the range. The mono face opens at ExtraLight. */\n\n",
|
|
208 |
+ |
@font-face rules name the range. The mono face opens at ExtraLight.\n \
|
|
209 |
+ |
A third token here is this product's own brand face, declared as an\n \
|
|
210 |
+ |
override in its build script. */\n\n",
|
| 174 |
211 |
|
);
|
| 175 |
|
- |
css.push_str(&makeover::font_face_css(font_url));
|
| 176 |
|
- |
css.push_str(&makeover::typography_css_vars());
|
|
212 |
+ |
css.push_str(&typography.css());
|
| 177 |
213 |
|
std::fs::write(path, css).expect("write typography css");
|
| 178 |
214 |
|
}
|
| 179 |
215 |
|
|
| 194 |
230 |
|
manifest_dir: impl AsRef<Path>,
|
| 195 |
231 |
|
opts: &makeover_webview::Emit,
|
| 196 |
232 |
|
explicit_touch: Option<&str>,
|
|
233 |
+ |
) {
|
|
234 |
+ |
tauri_frontend_with(
|
|
235 |
+ |
manifest_dir,
|
|
236 |
+ |
opts,
|
|
237 |
+ |
explicit_touch,
|
|
238 |
+ |
&makeover::Typography::house("../fonts"),
|
|
239 |
+ |
);
|
|
240 |
+ |
}
|
|
241 |
+ |
|
|
242 |
+ |
/// [`tauri_frontend`], for a product that overrides a font slot.
|
|
243 |
+ |
///
|
|
244 |
+ |
/// Separate rather than a fourth parameter on `tauri_frontend` so the three
|
|
245 |
+ |
/// consumers already calling it do not have to move: goingson is held at an
|
|
246 |
+ |
/// older `makeover` by a theming decision unrelated to fonts, and a signature
|
|
247 |
+ |
/// change here would make a font feature it cannot take into a build break it
|
|
248 |
+ |
/// cannot avoid.
|
|
249 |
+ |
///
|
|
250 |
+ |
/// The base URL is the caller's: pass `Typography::house("../fonts")` unless
|
|
251 |
+ |
/// the app serves fonts from somewhere other than the one layout a Tauri
|
|
252 |
+ |
/// frontend has.
|
|
253 |
+ |
///
|
|
254 |
+ |
/// # Panics
|
|
255 |
+ |
///
|
|
256 |
+ |
/// If any file cannot be written.
|
|
257 |
+ |
pub fn tauri_frontend_with(
|
|
258 |
+ |
manifest_dir: impl AsRef<Path>,
|
|
259 |
+ |
opts: &makeover_webview::Emit,
|
|
260 |
+ |
explicit_touch: Option<&str>,
|
|
261 |
+ |
typography: &makeover::Typography,
|
| 197 |
262 |
|
) {
|
| 198 |
263 |
|
let root = manifest_dir.as_ref();
|
| 199 |
264 |
|
let css = root.join("frontend").join("css");
|
| 200 |
265 |
|
themes(root.join("themes"));
|
| 201 |
266 |
|
geometry_css(css.join("geometry.css"), explicit_touch);
|
| 202 |
267 |
|
layout_css(css.join("layout.css"), opts);
|
| 203 |
|
- |
typography_css(css.join("typography.css"), "../fonts");
|
|
268 |
+ |
typography_css_from(css.join("typography.css"), typography);
|
| 204 |
269 |
|
}
|
| 205 |
270 |
|
|
| 206 |
271 |
|
#[cfg(test)]
|
| 284 |
349 |
|
assert!(!css.contains("@layer"));
|
| 285 |
350 |
|
}
|
| 286 |
351 |
|
|
|
352 |
+ |
#[test]
|
|
353 |
+ |
fn an_overridden_slot_reaches_the_same_file_as_the_house_two() {
|
|
354 |
+ |
// Layer 0's whole point: the brand face stops being a hand-maintained
|
|
355 |
+ |
// `@font-face` in the app's own stylesheet and becomes a line in the
|
|
356 |
+ |
// generated one, beside the slots it sits next to.
|
|
357 |
+ |
let dir = scratch("typography-override");
|
|
358 |
+ |
let path = dir.join("typography.css");
|
|
359 |
+ |
typography_css_from(
|
|
360 |
+ |
&path,
|
|
361 |
+ |
&Typography::house("/static/fonts").with_override(
|
|
362 |
+ |
FontOverride::new(FontSlot::Display, "\"Young Serif\", serif")
|
|
363 |
+ |
.with_face(FontFace::new("Young Serif", ["ysrf.woff2", "ysrf.ttf"])),
|
|
364 |
+ |
),
|
|
365 |
+ |
);
|
|
366 |
+ |
let css = std::fs::read_to_string(&path).unwrap();
|
|
367 |
+ |
|
|
368 |
+ |
// `@font-face {`, not `@font-face`: the header comment names the
|
|
369 |
+ |
// at-rule too, and counting that would make this pass for the wrong
|
|
370 |
+ |
// reason the day the comment is reworded.
|
|
371 |
+ |
assert_eq!(css.matches("@font-face {").count(), 3);
|
|
372 |
+ |
assert!(css.contains("--font-display: \"Young Serif\", serif;"));
|
|
373 |
+ |
assert!(css.contains("--font-mono: \"Quasi Mono\", monospace;"));
|
|
374 |
+ |
assert!(css.contains("url(\"/static/fonts/ysrf.ttf\") format(\"truetype\")"));
|
|
375 |
+ |
assert!(!css.contains("@layer"));
|
|
376 |
+ |
}
|
|
377 |
+ |
|
|
378 |
+ |
#[test]
|
|
379 |
+ |
fn the_default_tauri_layout_is_the_house_layer_and_nothing_else() {
|
|
380 |
+ |
// `tauri_frontend` delegating through `tauri_frontend_with` must not
|
|
381 |
+ |
// change a byte for the three consumers already calling it.
|
|
382 |
+ |
let dir = scratch("tauri-default");
|
|
383 |
+ |
let plain = dir.join("plain.css");
|
|
384 |
+ |
let house = dir.join("house.css");
|
|
385 |
+ |
typography_css(&plain, "../fonts");
|
|
386 |
+ |
typography_css_from(&house, &Typography::house("../fonts"));
|
|
387 |
+ |
assert_eq!(
|
|
388 |
+ |
std::fs::read_to_string(&plain).unwrap(),
|
|
389 |
+ |
std::fs::read_to_string(&house).unwrap()
|
|
390 |
+ |
);
|
|
391 |
+ |
}
|
|
392 |
+ |
|
| 287 |
393 |
|
#[test]
|
| 288 |
394 |
|
fn the_geometry_file_carries_the_crates_policy_and_a_banner() {
|
| 289 |
395 |
|
// The policy itself is tested in makeover-geometry. What is this
|