refactor changes

This commit is contained in:
pj committed 2026-08-30 16:58:02 +05:30
1 parent c1c47bc513
commit f7a536b2c3
58 files changed
+2524 -320

No files matched your search

+39
View File
@@ -141,3 +141,42 @@ fn collect_installed(
.map(|family| (*family).to_string())
.collect()
}
// ------------------------------------------------------------------------------------------------
// The document's own face
//
// Everything above is about the two families the bundle is missing. What follows is about the one
// the author chose: src/model/fonts.ts offers six bundled families and whatever this machine has,
// and an export has to be handed the bytes of whichever it names.
// ------------------------------------------------------------------------------------------------
/// Every family this machine has, for the "System" group of the font picker.
///
/// Sorted and deduplicated here rather than in the UI, because a font book has one entry per face
/// and the picker wants one per family: a machine with the full Helvetica Neue family would
/// otherwise show it nine times.
#[tauri::command(async)]
pub fn fonts_list_system() -> Vec<String> {
let db = system_db();
let mut names: Vec<String> = db
.faces()
.filter_map(|face| face.families.first().map(|(name, _)| name.clone()))
.collect();
names.sort();
names.dedup();
names
}
/// The upright and italic cuts of one installed family, for a document set in a face off the
/// machine.
///
/// Four queries and not two, because a family whose bold is a separate file gives fontdb a
/// different id for it and the collection has to be loaded either way. `seen` is what stops a `.ttc`
/// holding all four from being read four times.
pub fn system_faces(family: &str) -> Vec<Vec<u8>> {
let db = system_db();
let mut fonts = Vec::new();
let mut seen = HashSet::new();
faces_for(&db, family, &mut fonts, &mut seen);
fonts
}
+1
View File
@@ -297,6 +297,7 @@ pub fn run() {
pdf::pdf_write,
grammar::grammar_available,
grammar::grammar_check,
fonts::fonts_list_system,
])
.run(context)
.expect("error while running Margin Docs");
+70 -2
View File
@@ -41,6 +41,46 @@ static FACES: [&[u8]; 9] = [
include_bytes!("../fonts/HankenGrotesk-BoldItalic.ttf"),
];
// The other four families src/model/fonts.ts offers, in the variable files public/fonts already
// ships for the editor, and loaded only when a document is actually set in one.
//
// These carry the caveat the nine above exist to avoid, and it is not worth cutting eight more
// static instances to fix. Typst lays a variable file out at its default instance, so a document
// set in EB Garamond exports with its headings at the same weight as its body: the face is right,
// the hierarchy is flatter than the screen. The two the app defaults to are the two that export
// perfectly, and choosing one of these is choosing a face rather than a fallback.
static EB_GARAMOND: [&[u8]; 2] = [
include_bytes!("../../public/fonts/EBGaramond-VF.ttf"),
include_bytes!("../../public/fonts/EBGaramond-Italic-VF.ttf"),
];
static LORA: [&[u8]; 2] = [
include_bytes!("../../public/fonts/Lora-VF.ttf"),
include_bytes!("../../public/fonts/Lora-Italic-VF.ttf"),
];
static SOURCE_SERIF: [&[u8]; 2] = [
include_bytes!("../../public/fonts/SourceSerif4-VF.ttf"),
include_bytes!("../../public/fonts/SourceSerif4-Italic-VF.ttf"),
];
static FRAUNCES: [&[u8]; 2] = [
include_bytes!("../../public/fonts/Fraunces-VF.ttf"),
include_bytes!("../../public/fonts/Fraunces-Italic-VF.ttf"),
];
/// The bytes behind one `BundledFont` id from src/model/fonts.ts.
///
/// Literata and Hanken Grotesk are absent on purpose rather than missing: `FACES` already holds
/// their nine static instances and they are loaded for every export, so naming either here would
/// hand Typst the same family twice and let the variable copy win.
fn bundled_faces(id: &str) -> &'static [&'static [u8]] {
match id {
"eb-garamond" => &EB_GARAMOND,
"lora" => &LORA,
"source-serif" => &SOURCE_SERIF,
"fraunces" => &FRAUNCES,
_ => &[],
}
}
// mitex 0.2.5, vendored under src-tauri/vendor/mitex with its LICENSE, and served as ordinary
// paths under `/mitex/` rather than through Typst's package system. A package spec would mean a
// download on first export and a cache directory to keep, for a dependency that is 380K and never
@@ -242,7 +282,7 @@ fn compile_once(
Ok((bytes, warnings.into_iter().collect()))
}
/// Compiles Typst source to PDF bytes, with whatever the compiler had to work around.
/// Compiles Typst source to PDF bytes, in the app's default pair of faces.
///
/// Separate from the command so a test can reach it: a `#[tauri::command]` taking an `AppHandle`
/// needs a running app, and none of the work below wants one.
@@ -250,6 +290,22 @@ pub fn compile(
source: String,
images: &[ImageInput],
root_paths: &[String],
) -> Result<(Vec<u8>, Vec<PdfWarning>), String> {
compile_in(source, images, root_paths, &[], &[])
}
/// The same, for a document set in something other than the default.
///
/// `bundled` are ids from src/model/fonts.ts and `system` are family names off this machine, both
/// as the converter named them in the preamble it wrote. Naming a face Typst has no bytes for is a
/// warning and a fallback rather than a failure, so an id this build does not know and a family
/// that has since been uninstalled both end in a PDF.
pub fn compile_in(
source: String,
images: &[ImageInput],
root_paths: &[String],
bundled: &[String],
system: &[String],
) -> Result<(Vec<u8>, Vec<PdfWarning>), String> {
let mut warnings: Vec<PdfWarning> = Vec::new();
@@ -273,6 +329,16 @@ pub fn compile(
let mut fonts: Vec<Vec<u8>> = FACES.iter().map(|face| face.to_vec()).collect();
fonts.append(&mut fallbacks.fonts);
// The face the author chose, if it is not one of the two already in `FACES`. A system family is
// read off this machine, which is the one place an export can differ between two computers, and
// is the caveat the picker states next to the "System" group.
for id in bundled {
fonts.extend(bundled_faces(id).iter().map(|face| face.to_vec()));
}
for family in system {
fonts.extend(crate::fonts::system_faces(family));
}
let source = format!("{}{source}", font_preamble(&fallbacks));
let failure = match compile_once(&source, MITEX_LIB, &binaries, &fonts) {
@@ -322,6 +388,8 @@ pub fn pdf_compile(
app: tauri::AppHandle,
source: String,
images: Vec<ImageInput>,
bundled_fonts: Vec<String>,
system_fonts: Vec<String>,
) -> Result<tauri::ipc::Response, String> {
// The same gate `fs::checked` puts in front of every other read, reached the same way it is:
// the open roots out of shared state, then `resolve_in_roots`. The lock is dropped before the
@@ -332,7 +400,7 @@ pub fn pdf_compile(
open.iter().map(|root| root.path.clone()).collect()
};
let (bytes, warnings) = compile(source, &images, &root_paths)?;
let (bytes, warnings) = compile_in(source, &images, &root_paths, &bundled_fonts, &system_fonts)?;
if !warnings.is_empty() {
app.emit("pdf-warnings", warnings).ok();
}