diff --git a/src/bin/diffenator3.rs b/src/bin/diffenator3.rs index 412d8f4..abf6ade 100644 --- a/src/bin/diffenator3.rs +++ b/src/bin/diffenator3.rs @@ -2,12 +2,12 @@ use clap::{builder::ArgAction, Parser}; use colored::Colorize; use diffenator3::{ dfont::DFont, - html::{render_output, CSSFontFace}, + html::{render_output, CSSFontFace, CSSFontStyle}, render::{test_font_glyphs, test_font_words}, ttj::{jsondiff::Substantial, table_diff}, }; use serde_json::Map; -use std::path::PathBuf; +use std::{error::Error, path::PathBuf}; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] @@ -63,6 +63,18 @@ struct Cli { font2: PathBuf, } +fn die(doing: &str, err: impl Error) -> ! { + eprintln!("Error {}: {}", doing, err); + eprintln!(); + eprintln!("Caused by:"); + if let Some(cause) = err.source() { + for (i, e) in std::iter::successors(Some(cause), |e| (*e).source()).enumerate() { + eprintln!(" {}: {}", i, e); + } + } + std::process::exit(1); +} + fn show_map_diff(fields: &Map, indent: usize, succinct: bool) { for (field, diff) in fields.iter() { print!("{}", " ".repeat(indent * 2)); @@ -127,19 +139,24 @@ fn main() { } if cli.words { let word_diff = test_font_words(&font_a, &font_b); - if word_diff.is_something() { - diff.insert("words".into(), word_diff); - } + diff.insert("words".into(), word_diff); } if cli.html { let font_face_old = CSSFontFace::new(cli.font1.to_str().unwrap(), "old", &font_a); let font_face_new = CSSFontFace::new(cli.font2.to_str().unwrap(), "new", &font_b); + let font_style_old = CSSFontStyle::new(&font_a, Some("old")); + let font_style_new = CSSFontStyle::new(&font_b, Some("new")); + let value = serde_json::to_value(&diff).unwrap_or_else(|e| { + die("serializing diff", e); + }); let html = render_output( - &serde_json::to_value(&diff).expect("foo"), + &value, font_face_old, font_face_new, + font_style_old, + font_style_new, ) - .expect("foo"); + .unwrap_or_else(|err| die("rendering HTML", err)); println!("{}", html); std::process::exit(0); } diff --git a/src/dfont.rs b/src/dfont.rs index 3189200..d8a8945 100644 --- a/src/dfont.rs +++ b/src/dfont.rs @@ -9,7 +9,8 @@ use ucd::Codepoint; pub struct DFont { pub backing: Vec, - pub location: Location, + pub location: Vec, + pub normalized_location: Location, pub codepoints: HashSet, } @@ -20,7 +21,8 @@ impl DFont { let mut fnt = DFont { backing, codepoints: HashSet::new(), - location: Location::default(), + normalized_location: Location::default(), + location: vec![], }; let cmap = fnt.fontref().charmap(); fnt.codepoints = cmap.mappings().map(|(cp, _)| cp).collect(); @@ -29,23 +31,31 @@ impl DFont { pub fn set_location(&mut self, variations: &str) -> Result<(), String> { self.location = self.parse_location(variations)?; + self.normalized_location = self.fontref().axes().location(&self.location); Ok(()) } pub fn set_instance(&mut self, instance: &str) -> Result<(), String> { - let font = self.fontref(); - let location = font + let instance = self + .fontref() .named_instances() .iter() .find(|ni| { - font.localized_strings(ni.subfamily_name_id()) + self.fontref() + .localized_strings(ni.subfamily_name_id()) .any(|s| instance == s.chars().collect::>()) }) - .map_or_else( - || Err(format!("No instance named {}", instance)), - |ni| Ok(ni.location()), - ); - self.location = location?; + .ok_or_else(|| format!("No instance named {}", instance))?; + let user_coords = instance.user_coords(); + let location = instance.location(); + self.location = self + .fontref() + .axes() + .iter() + .zip(user_coords) + .map(|(a, v)| (a.tag(), v).into()) + .collect(); + self.normalized_location = location; Ok(()) } @@ -59,6 +69,13 @@ impl DFont { .map_or_else(|| "Unknown".to_string(), |s| s.chars().collect()) } + pub fn style_name(&self) -> String { + self.fontref() + .localized_strings(NameId::SUBFAMILY_NAME) + .english_or_first() + .map_or_else(|| "Regular".to_string(), |s| s.chars().collect()) + } + pub fn is_color(&self) -> bool { self.fontref() .table_directory @@ -91,7 +108,7 @@ impl DFont { .collect() } - fn parse_location(&self, variations: &str) -> Result { + fn parse_location(&self, variations: &str) -> Result, String> { let mut settings: Vec = vec![]; for variation in variations.split(',') { let mut parts = variation.split('='); @@ -102,7 +119,7 @@ impl DFont { .map_err(|_| "Couldn't parse value".to_string())?; settings.push((axis, value).into()); } - Ok(self.fontref().axes().location(&settings)) + Ok(settings) } pub fn supported_scripts(&self) -> HashSet { diff --git a/src/html.rs b/src/html.rs index 1f54d70..00299f6 100644 --- a/src/html.rs +++ b/src/html.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use lazy_static::lazy_static; use serde::Serialize; use serde_json::json; @@ -20,8 +22,8 @@ pub struct CSSFontFace { impl CSSFontFace { pub fn new(filename: &str, suffix: &str, dfont: &DFont) -> Self { let familyname = suffix.to_string() + " " + &dfont.family_name(); - let cssfamilyname = familyname.replace(' ', "-"); - let class_name = cssfamilyname.clone(); + let cssfamilyname = familyname.clone(); + let class_name = cssfamilyname.replace(' ', "-"); let font_weight = "normal".to_string(); let font_width = "normal".to_string(); let font_style = "normal".to_string(); @@ -39,6 +41,53 @@ impl CSSFontFace { } } +#[derive(Debug, Serialize)] +pub struct CSSFontStyle { + suffix: String, + coords: HashMap, + familyname: String, + style_name: String, + cssfamilyname: String, + class_name: String, + font_variation_settings: String, +} + +impl CSSFontStyle { + pub fn new(dfont: &DFont, suffix: Option<&str>) -> Self { + let familyname = dfont.family_name(); + let stylename = dfont.style_name(); + let coords = dfont + .location + .iter() + .map(|setting| (setting.selector.clone().to_string(), setting.value)) + .collect(); + let font_variation_settings = dfont + .location + .iter() + .map(|setting| format!("'{}' {}", setting.selector, setting.value)) + .collect::>() + .join(", "); + let (cssfamilyname, class_name) = if let Some(suffix) = suffix { + ( + format!("{} {}", suffix, familyname), + format!("{}-{}", suffix, stylename).replace(' ', "-"), + ) + } else { + (familyname.to_string(), stylename.replace(' ', "-")) + }; + + CSSFontStyle { + suffix: stylename.to_string(), + familyname: familyname.to_string(), + style_name: stylename.to_string(), + cssfamilyname, + class_name, + coords, + font_variation_settings, + } + } +} + lazy_static! { pub static ref TEMPLATES: Tera = { match Tera::new("templates/*") { @@ -55,18 +104,25 @@ pub fn render_output( value: &serde_json::Value, font_face_old: CSSFontFace, font_face_new: CSSFontFace, + font_style_old: CSSFontStyle, + font_style_new: CSSFontStyle, ) -> Result { TEMPLATES.render( "diffenator.html", &Context::from_serialize(json!({ - "diff": value, + "diff": { + "tables": value.get("tables").unwrap_or(&json!({})), + "glyphs": value.get("glyphs").unwrap_or(&serde_json::Value::Null), + "words": value.get("words").unwrap_or(&serde_json::Value::Null), + }, "font_faces_old": [font_face_old], "font_faces_new": [font_face_new], "font_faces": [], - "font_styles_old": [], - "font_styles_new": [], + "font_styles_old": [font_style_old], + "font_styles_new": [font_style_new], "font_styles": [], "pt_size": 20, + "include_ui": true, }))?, ) } diff --git a/src/render/renderer.rs b/src/render/renderer.rs index 0391022..0be2bd1 100644 --- a/src/render/renderer.rs +++ b/src/render/renderer.rs @@ -42,7 +42,7 @@ impl<'a> Renderer<'a> { font, plan, scale: font_size, - location: (&dfont.location).into(), + location: (&dfont.normalized_location).into(), outlines, } } diff --git a/templates/CSSFontFace.partial.html b/templates/CSSFontFace.partial.html index d907da2..645c431 100644 --- a/templates/CSSFontFace.partial.html +++ b/templates/CSSFontFace.partial.html @@ -1,7 +1,7 @@ @font-face{ - src: url("{{ filename }}"); - font-family: "{{ cssfamilyname }}"; - font-weight: {{ font_weight }}; - {% if font_stretch %}font-stretch: {{ font_stretch }};{% endif %} - font-style: {{ font_style }}; + src: url("{{ font_face.filename }}"); + font-family: "{{ font_face.cssfamilyname }}"; + font-weight: {{ font_face.font_weight }}; + {% if font_face.font_stretch %}font-stretch: {{ font_face.font_stretch }};{% endif %} + font-style: {{ font_face.font_style }}; } \ No newline at end of file diff --git a/templates/CSSFontStyle.partial.html b/templates/CSSFontStyle.partial.html index 56a7428..1e5786c 100644 --- a/templates/CSSFontStyle.partial.html +++ b/templates/CSSFontStyle.partial.html @@ -1,7 +1,7 @@ -.{{class_name}}{ - font-family: "{{ cssfamilyname }}", "Adobe NotDef"; - {% if "wght" in coords %}font-weight: {{coords['wght']}};{% endif %} - {% if "wdth" in coords %}font-stretch: {{coords['wdth']}}%;{% endif %} - {% if "Italic" in stylename %}font-style: italic;{% else %}font-style: normal;{% endif %} - font-variation-settings: {{ font_variation_settings }}; +.{{font_class.class_name}}{ + font-family: "{{font_class.cssfamilyname }}", "Adobe NotDef"; + {% if "wght" in font_class.coords %}font-weight: {{font_class.coords['wght']}};{% endif %} + {% if "wdth" in font_class.coords %}font-stretch: {{font_class.coords['wdth']}}%;{% endif %} + {% if "Italic" in font_class.style_name %}font-style: italic;{% else %}font-style: normal;{% endif %} + {% if font_class.font_variation_settings %} font-variation-settings: {{font_class.font_variation_settings }}; {%endif%} } diff --git a/templates/Glyph.partial.html b/templates/Glyph.partial.html index 867b7c1..3356d24 100644 --- a/templates/Glyph.partial.html +++ b/templates/Glyph.partial.html @@ -1,9 +1,9 @@
- {{ string }}
+ {{ glyph.string }}
- name: {{ name }}
- unicode: {{ unicode }} + name: {{ glyph.name }}
+ unicode: {{ glyph.unicode }}
diff --git a/templates/_base.html b/templates/_base.html index d5c0581..68604b3 100644 --- a/templates/_base.html +++ b/templates/_base.html @@ -140,27 +140,27 @@ } {% for font_face in font_faces_old %} - {{ font_face }} + {% include "CSSFontFace.partial.html" %} {% endfor %} {% for font_face in font_faces_new %} - {{ font_face }} + {% include "CSSFontFace.partial.html" %} {% endfor %} {% for font_face in font_faces %} - {{ font_face }} + {% include "CSSFontFace.partial.html" %} {% endfor %} {% for font_class in font_styles_old %} - {{ font_class }} + {% include "CSSFontStyle.partial.html" %} {% endfor %} {% for font_class in font_styles_new %} - {{ font_class }} + {% include "CSSFontStyle.partial.html" %} {% endfor %} {% for font_class in font_styles %} - {{ font_class }} + {% include "CSSFontStyle.partial.html" %} {% endfor %} {% endblock %} @@ -188,7 +188,7 @@

{% block content_name %}{% endblock %}

{% block content %} {% endblock %} - + + + diff --git a/templates/diffenator.html b/templates/diffenator.html index c3a94f2..92f4b83 100644 --- a/templates/diffenator.html +++ b/templates/diffenator.html @@ -36,10 +36,12 @@ {% endblock %} {% block content %} {% for font_class in font_styles_old %} + {% if diff["tables"] %}
-
Tables
+
Tables
+ {% endif %} {% if diff["glyphs"].new %}
@@ -47,7 +49,7 @@
{% for glyph in diff["glyphs"].new %}
- {{ glyph }} + {% include "Glyph.partial.html" %}
{% endfor %}
@@ -61,7 +63,7 @@
{% for glyph in diff["glyphs"].missing %}
- {{ glyph }} + {% include "Glyph.partial.html" %}
{% endfor %}
@@ -74,24 +76,27 @@
{% for glyph in diff["glyphs"].modified %}
- {{ glyph }} + {% include "Glyph.partial.html" %}
{% endfor %}
{% endif %} - {% for script in diff["words"] %} + {% if diff["words"] %} + {% for script, words in diff["words"] %}
Misshapen {{ script }} words
- {% for word in diff["words"][script] %} + {% for word in words %} {{ word }} {% endfor %}
{% endfor %} + {% endif %} + {% if diff["strings"] %}
Misshapen user strings
@@ -100,18 +105,19 @@ {% endfor %}
+ {% endif %} - - {% if diff.features %} + {% if diff["features"] %}
Fea
{{ diff.features }}
{% endif %} - - {% endfor %} -{{ diff.tables }} + + {% endblock %} {% block js %} class Table {