Introduce override list to FontInfo (#3228)

This commit is contained in:
Peng Guanwen 2024-02-01 21:45:33 +08:00 committed by GitHub
parent 7d33436e55
commit 464a15bdca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 186 additions and 56 deletions

15
Cargo.lock generated
View File

@ -1638,6 +1638,7 @@ version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_macros",
"phf_shared",
]
@ -1661,6 +1662,19 @@ dependencies = [
"rand",
]
[[package]]
name = "phf_macros"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "phf_shared"
version = "0.11.2"
@ -2541,6 +2555,7 @@ dependencies = [
"log",
"once_cell",
"palette",
"phf",
"rayon",
"regex",
"roxmltree 0.19.0",

View File

@ -72,6 +72,7 @@ palette = { version = "0.7.3", default-features = false, features = ["approx", "
parking_lot = "0.12.1"
pathdiff = "0.2"
pdf-writer = "0.9.2"
phf = { version = "0.11", features = ["macros"] }
pixglyph = "0.3"
proc-macro2 = "1"
pulldown-cmark = "0.9"

View File

@ -41,6 +41,7 @@ lipsum = { workspace = true }
log = { workspace = true }
once_cell = { workspace = true }
palette = { workspace = true }
phf = { workspace = true }
rayon = { workspace = true }
regex = { workspace = true }
roxmltree = { workspace = true }

View File

@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
use ttf_parser::{name_id, PlatformId, Tag};
use unicode_segmentation::UnicodeSegmentation;
use super::exceptions::find_exception;
use crate::text::{Font, FontStretch, FontStyle, FontVariant, FontWeight};
/// Metadata about a collection of fonts.
@ -206,6 +207,8 @@ impl FontInfo {
/// Compute metadata for a single ttf-parser face.
pub(super) fn from_ttf(ttf: &ttf_parser::Face) -> Option<Self> {
let ps_name = find_name(ttf, name_id::POST_SCRIPT_NAME);
let exception = ps_name.as_deref().and_then(find_exception);
// We cannot use Name ID 16 "Typographic Family", because for some
// fonts it groups together more than just Style / Weight / Stretch
// variants (e.g. Display variants of Noto fonts) and then some
@ -222,45 +225,43 @@ impl FontInfo {
// because Name ID 1 "Family" sometimes contains "Display" and
// sometimes doesn't for the Display variants and that mixes things
// up.
let family = {
let mut family = find_name(ttf, name_id::FAMILY)?;
if family.starts_with("Noto")
|| family.starts_with("NewCM")
|| family.starts_with("NewComputerModern")
{
family = find_name(ttf, name_id::FULL_NAME)?;
}
typographic_family(&family).to_string()
};
let family =
exception.and_then(|c| c.family.map(str::to_string)).or_else(|| {
let mut family = find_name(ttf, name_id::FAMILY)?;
if family.starts_with("Noto") {
family = find_name(ttf, name_id::FULL_NAME)?;
}
Some(typographic_family(&family).to_string())
})?;
let variant = {
let mut full = find_name(ttf, name_id::FULL_NAME).unwrap_or_default();
full.make_ascii_lowercase();
let style = exception.and_then(|c| c.style).unwrap_or_else(|| {
let mut full = find_name(ttf, name_id::FULL_NAME).unwrap_or_default();
full.make_ascii_lowercase();
// Some fonts miss the relevant bits for italic or oblique, so
// we also try to infer that from the full name.
let italic = ttf.is_italic() || full.contains("italic");
let oblique =
ttf.is_oblique() || full.contains("oblique") || full.contains("slanted");
// Some fonts miss the relevant bits for italic or oblique, so
// we also try to infer that from the full name.
let italic = ttf.is_italic() || full.contains("italic");
let oblique = ttf.is_oblique()
|| full.contains("oblique")
|| full.contains("slanted");
let style = match (italic, oblique) {
(false, false) => FontStyle::Normal,
(true, _) => FontStyle::Italic,
(_, true) => FontStyle::Oblique,
};
let weight = {
let mut number = ttf.weight().to_number();
if (family.starts_with("NewCM")
|| family.starts_with("New Computer Modern"))
&& full.contains("book")
{
number += 50;
match (italic, oblique) {
(false, false) => FontStyle::Normal,
(true, _) => FontStyle::Italic,
(_, true) => FontStyle::Oblique,
}
FontWeight::from_number(number)
};
});
let weight = exception.and_then(|c| c.weight).unwrap_or_else(|| {
let number = ttf.weight().to_number();
FontWeight::from_number(number)
});
let stretch = exception
.and_then(|c| c.stretch)
.unwrap_or_else(|| FontStretch::from_number(ttf.width().to_number()));
let stretch = FontStretch::from_number(ttf.width().to_number());
FontVariant { style, weight, stretch }
};
@ -355,12 +356,6 @@ fn typographic_family(mut family: &str) -> &str {
"narrow", "condensed", "cond", "cn", "cd", "compressed", "expanded", "exp"
];
let mut extra = [].as_slice();
let newcm = family.starts_with("NewCM") || family.starts_with("NewComputerModern");
if newcm {
extra = &["book"];
}
// Trim spacing and weird leading dots in Apple fonts.
family = family.trim().trim_start_matches('.');
@ -376,7 +371,7 @@ fn typographic_family(mut family: &str) -> &str {
// Find style suffix.
let mut t = trimmed;
let mut shortened = false;
while let Some(s) = SUFFIXES.iter().chain(extra).find_map(|s| t.strip_suffix(s)) {
while let Some(s) = SUFFIXES.iter().find_map(|s| t.strip_suffix(s)) {
shortened = true;
t = s;
}
@ -403,20 +398,7 @@ fn typographic_family(mut family: &str) -> &str {
// Apply style suffix trimming.
family = &family[..len];
if newcm {
family = family.trim_end_matches("10");
}
// Fix bad names.
match family {
"Noto Sans Symbols2" => "Noto Sans Symbols 2",
"NewComputerModern" => "New Computer Modern",
"NewComputerModernMono" => "New Computer Modern Mono",
"NewComputerModernSans" => "New Computer Modern Sans",
"NewComputerModernMath" => "New Computer Modern Math",
"NewCMUncial" | "NewComputerModernUncial" => "New Computer Modern Uncial",
other => other,
}
family
}
/// How many words the two strings share in their prefix.

View File

@ -0,0 +1,130 @@
use serde::Deserialize;
use super::{FontStretch, FontStyle, FontWeight};
#[derive(Debug, Default, Deserialize)]
pub struct Exception {
pub family: Option<&'static str>,
pub style: Option<FontStyle>,
pub weight: Option<FontWeight>,
pub stretch: Option<FontStretch>,
}
impl Exception {
pub const fn new() -> Self {
Self {
family: None,
style: None,
weight: None,
stretch: None,
}
}
const fn family(self, family: &'static str) -> Self {
Self { family: Some(family), ..self }
}
const fn style(self, style: FontStyle) -> Self {
Self { style: Some(style), ..self }
}
const fn weight(self, weight: u16) -> Self {
Self { weight: Some(FontWeight(weight)), ..self }
}
#[allow(unused)] // left for future use
const fn stretch(self, stretch: u16) -> Self {
Self { stretch: Some(FontStretch(stretch)), ..self }
}
}
pub fn find_exception(postscript_name: &str) -> Option<&'static Exception> {
EXCEPTION_MAP.get(postscript_name)
}
/// A map which keys are PostScript name and values are override entries.
static EXCEPTION_MAP: phf::Map<&'static str, Exception> = phf::phf_map! {
"NewCM08-Book" => Exception::new()
.family("New Computer Modern 08")
.weight(450),
"NewCM08-BookItalic" => Exception::new()
.family("New Computer Modern 08")
.weight(450),
"NewCM08-Italic" => Exception::new()
.family("New Computer Modern 08"),
"NewCM08-Regular" => Exception::new()
.family("New Computer Modern 08"),
"NewCM10-Bold" => Exception::new()
.family("New Computer Modern"),
"NewCM10-BoldItalic" => Exception::new()
.family("New Computer Modern"),
"NewCM10-Book" => Exception::new()
.family("New Computer Modern")
.weight(450),
"NewCM10-BookItalic" => Exception::new()
.family("New Computer Modern")
.weight(450),
"NewCM10-Italic" => Exception::new()
.family("New Computer Modern"),
"NewCM10-Regular" => Exception::new()
.family("New Computer Modern"),
"NewCMMath-Book" => Exception::new()
.family("New Computer Modern Math")
.weight(450),
"NewCMMath-Regular" => Exception::new()
.family("New Computer Modern Math"),
"NewCMMono10-Bold" => Exception::new()
.family("New Computer Modern Mono"),
"NewCMMono10-BoldOblique" => Exception::new()
.family("New Computer Modern Mono"),
"NewCMMono10-Book" => Exception::new()
.family("New Computer Modern Mono")
.weight(450),
"NewCMMono10-BookItalic" => Exception::new()
.family("New Computer Modern Mono")
.weight(450),
"NewCMMono10-Italic" => Exception::new()
.family("New Computer Modern Mono"),
"NewCMMono10-Regular" => Exception::new()
.family("New Computer Modern Mono"),
"NewCMSans08-Book" => Exception::new()
.family("New Computer Modern Sans 08")
.weight(450),
"NewCMSans08-BookOblique" => Exception::new()
.family("New Computer Modern Sans 08")
.weight(450),
"NewCMSans08-Oblique" => Exception::new()
.family("New Computer Modern Sans 08"),
"NewCMSans08-Regular" => Exception::new()
.family("New Computer Modern Sans 08"),
"NewCMSans10-Bold" => Exception::new()
.family("New Computer Modern Sans"),
"NewCMSans10-BoldOblique" => Exception::new()
.family("New Computer Modern Sans"),
"NewCMSans10-Book" => Exception::new()
.family("New Computer Modern Sans")
.weight(450),
"NewCMSans10-BookOblique" => Exception::new()
.family("New Computer Modern Sans")
.weight(450)
.style(FontStyle::Oblique),
"NewCMSans10-Oblique" => Exception::new()
.family("New Computer Modern Sans")
.style(FontStyle::Oblique),
"NewCMSans10-Regular" => Exception::new()
.family("New Computer Modern Sans"),
"NewCMUncial08-Bold" => Exception::new()
.family("New Computer Modern Uncial 08"),
"NewCMUncial08-Book" => Exception::new()
.family("New Computer Modern Uncial 08")
.weight(450),
"NewCMUncial08-Regular" => Exception::new()
.family("New Computer Modern Uncial 08"),
"NewCMUncial10-Bold" => Exception::new()
.family("New Computer Modern Uncial"),
"NewCMUncial10-Book" => Exception::new()
.family("New Computer Modern Uncial")
.weight(450),
"NewCMUncial10-Regular" => Exception::new()
.family("New Computer Modern Uncial"),
};

View File

@ -1,6 +1,7 @@
//! Font handling.
mod book;
mod exceptions;
mod variant;
pub use self::book::{Coverage, FontBook, FontFlags, FontInfo};

View File

@ -77,7 +77,7 @@ impl From<usvg::FontStyle> for FontStyle {
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct FontWeight(u16);
pub struct FontWeight(pub(super) u16);
impl FontWeight {
/// Thin weight (100).
@ -180,7 +180,7 @@ cast! {
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Serialize, Deserialize)]
#[serde(transparent)]
pub struct FontStretch(u16);
pub struct FontStretch(pub(super) u16);
impl FontStretch {
/// Ultra-condensed stretch (50%).