pdftract/crates/pdftract-core/build.rs
jedarden 7429a67d08 feat(pdftract-juc): implement Standard 14 font metrics registry
- Add build.rs that generates compile-time std14 metrics from JSON
- Add std14.rs module with Std14Metrics struct and get_std14_metrics()
- Add build/std14-metrics.json with AFM-derived widths for all 14 fonts
- Re-export Std14Metrics, NamedEncoding, get_std14_metrics in lib.rs

Acceptance criteria:
- All 14 Standard fonts (Courier, Helvetica, Times, Symbol, ZapfDingbats
  and their variants) return valid metrics from the registry
- Subset-prefixed names (ABCDEF+Helvetica) resolve via strip_subset_prefix()
- Width tables match Adobe AFM data within rounding tolerance
- Binary footprint < 60 KB (generated source: 20 KB, actual data ~8 KB)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 14:04:02 -04:00

103 lines
3.3 KiB
Rust

use std::env;
use std::fs;
use std::path::Path;
fn main() {
println!("cargo:rerun-if-changed=build/std14-metrics.json");
let out_dir = env::var("OUT_DIR").unwrap();
let metrics_path = Path::new("build/std14-metrics.json");
let json_content = fs::read_to_string(metrics_path)
.expect("Failed to read std14-metrics.json");
let data: serde_json::Value = serde_json::from_str(&json_content)
.expect("Failed to parse std14-metrics.json");
let fonts = data["fonts"].as_object()
.expect("fonts object missing");
let mut metrics_structs = String::new();
for (font_name, font_data) in fonts {
let font_ident = font_name.replace("-", "_");
let weights = font_data["weights"].as_array()
.expect("weights array missing");
let weights_array: Vec<String> = weights.iter()
.map(|v| v.as_u64().unwrap_or(0).to_string())
.collect();
let font_bbox = font_data["font_bbox"].as_array()
.expect("font_bbox array missing");
let font_bbox: Vec<String> = font_bbox.iter()
.map(|v| v.as_i64().unwrap_or(0).to_string())
.collect();
let ascent = font_data["ascent"].as_i64().expect("ascent missing");
let descent = font_data["descent"].as_i64().expect("descent missing");
let italic_angle = font_data["italic_angle"].as_f64().expect("italic_angle missing");
let cap_height = font_data["cap_height"].as_i64().expect("cap_height missing");
let stem_v = font_data["stem_v"].as_i64().expect("stem_v missing");
let encoding_str = font_data["encoding"].as_str().expect("encoding missing");
let encoding = match encoding_str {
"StandardEncoding" => "NamedEncoding::Standard",
"SymbolEncoding" => "NamedEncoding::Symbol",
"ZapfDingbatsEncoding" => "NamedEncoding::ZapfDingbats",
_ => "NamedEncoding::Standard",
};
metrics_structs.push_str(&format!(r#"
static {}_WIDTHS: &[u16; 256] = &[{}];
static {}_METRICS: Std14Metrics = Std14Metrics {{
widths: &{}_WIDTHS,
ascent: {},
descent: {},
italic_angle: {}f32,
font_bbox: [{}],
cap_height: {},
stem_v: {},
encoding: {},
}};
"#,
font_ident.to_uppercase(),
weights_array.join(", "),
font_ident.to_uppercase(),
font_ident.to_uppercase(),
ascent,
descent,
italic_angle,
font_bbox.join(", "),
cap_height,
stem_v,
encoding
));
}
// Build the phf map using phf_codegen
let mut map_builder = phf_codegen::Map::new();
for font_name in fonts.keys() {
let ident = font_name.replace("-", "_");
map_builder.entry(font_name.as_str(), &format!("&{}_METRICS", ident.to_uppercase()));
}
let rust_code = format!(r#"
// Auto-generated Standard 14 font metrics.
// Do not edit manually.
{}
pub fn get_std14_metrics(name: &str) -> Option<&'static Std14Metrics> {{
static METRICS: phf::Map<&'static str, &'static Std14Metrics> = {};
METRICS.get(name).copied()
}}
"#,
metrics_structs,
map_builder.build()
);
fs::write(Path::new(&out_dir).join("std14_registry.rs"), rust_code)
.expect("Failed to write std14_registry.rs");
}