Introduce override list to FontInfo (#3228)
This commit is contained in:
parent
7d33436e55
commit
464a15bdca
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -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",
|
||||
|
@ -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"
|
||||
|
@ -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 }
|
||||
|
@ -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.
|
||||
|
130
crates/typst/src/text/font/exceptions.rs
Normal file
130
crates/typst/src/text/font/exceptions.rs
Normal 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"),
|
||||
};
|
@ -1,6 +1,7 @@
|
||||
//! Font handling.
|
||||
|
||||
mod book;
|
||||
mod exceptions;
|
||||
mod variant;
|
||||
|
||||
pub use self::book::{Coverage, FontBook, FontFlags, FontInfo};
|
||||
|
@ -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%).
|
||||
|
Loading…
x
Reference in New Issue
Block a user