mirror of
https://github.com/typst/typst
synced 2025-05-19 19:45:29 +08:00
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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"phf_macros",
|
||||||
"phf_shared",
|
"phf_shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1661,6 +1662,19 @@ dependencies = [
|
|||||||
"rand",
|
"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]]
|
[[package]]
|
||||||
name = "phf_shared"
|
name = "phf_shared"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
@ -2541,6 +2555,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"palette",
|
"palette",
|
||||||
|
"phf",
|
||||||
"rayon",
|
"rayon",
|
||||||
"regex",
|
"regex",
|
||||||
"roxmltree 0.19.0",
|
"roxmltree 0.19.0",
|
||||||
|
@ -72,6 +72,7 @@ palette = { version = "0.7.3", default-features = false, features = ["approx", "
|
|||||||
parking_lot = "0.12.1"
|
parking_lot = "0.12.1"
|
||||||
pathdiff = "0.2"
|
pathdiff = "0.2"
|
||||||
pdf-writer = "0.9.2"
|
pdf-writer = "0.9.2"
|
||||||
|
phf = { version = "0.11", features = ["macros"] }
|
||||||
pixglyph = "0.3"
|
pixglyph = "0.3"
|
||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
pulldown-cmark = "0.9"
|
pulldown-cmark = "0.9"
|
||||||
|
@ -41,6 +41,7 @@ lipsum = { workspace = true }
|
|||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
once_cell = { workspace = true }
|
once_cell = { workspace = true }
|
||||||
palette = { workspace = true }
|
palette = { workspace = true }
|
||||||
|
phf = { workspace = true }
|
||||||
rayon = { workspace = true }
|
rayon = { workspace = true }
|
||||||
regex = { workspace = true }
|
regex = { workspace = true }
|
||||||
roxmltree = { workspace = true }
|
roxmltree = { workspace = true }
|
||||||
|
@ -6,6 +6,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use ttf_parser::{name_id, PlatformId, Tag};
|
use ttf_parser::{name_id, PlatformId, Tag};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
|
use super::exceptions::find_exception;
|
||||||
use crate::text::{Font, FontStretch, FontStyle, FontVariant, FontWeight};
|
use crate::text::{Font, FontStretch, FontStyle, FontVariant, FontWeight};
|
||||||
|
|
||||||
/// Metadata about a collection of fonts.
|
/// Metadata about a collection of fonts.
|
||||||
@ -206,6 +207,8 @@ impl FontInfo {
|
|||||||
|
|
||||||
/// Compute metadata for a single ttf-parser face.
|
/// Compute metadata for a single ttf-parser face.
|
||||||
pub(super) fn from_ttf(ttf: &ttf_parser::Face) -> Option<Self> {
|
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
|
// We cannot use Name ID 16 "Typographic Family", because for some
|
||||||
// fonts it groups together more than just Style / Weight / Stretch
|
// fonts it groups together more than just Style / Weight / Stretch
|
||||||
// variants (e.g. Display variants of Noto fonts) and then some
|
// 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
|
// because Name ID 1 "Family" sometimes contains "Display" and
|
||||||
// sometimes doesn't for the Display variants and that mixes things
|
// sometimes doesn't for the Display variants and that mixes things
|
||||||
// up.
|
// up.
|
||||||
let family = {
|
let family =
|
||||||
|
exception.and_then(|c| c.family.map(str::to_string)).or_else(|| {
|
||||||
let mut family = find_name(ttf, name_id::FAMILY)?;
|
let mut family = find_name(ttf, name_id::FAMILY)?;
|
||||||
if family.starts_with("Noto")
|
if family.starts_with("Noto") {
|
||||||
|| family.starts_with("NewCM")
|
|
||||||
|| family.starts_with("NewComputerModern")
|
|
||||||
{
|
|
||||||
family = find_name(ttf, name_id::FULL_NAME)?;
|
family = find_name(ttf, name_id::FULL_NAME)?;
|
||||||
}
|
}
|
||||||
typographic_family(&family).to_string()
|
Some(typographic_family(&family).to_string())
|
||||||
};
|
})?;
|
||||||
|
|
||||||
let variant = {
|
let variant = {
|
||||||
|
let style = exception.and_then(|c| c.style).unwrap_or_else(|| {
|
||||||
let mut full = find_name(ttf, name_id::FULL_NAME).unwrap_or_default();
|
let mut full = find_name(ttf, name_id::FULL_NAME).unwrap_or_default();
|
||||||
full.make_ascii_lowercase();
|
full.make_ascii_lowercase();
|
||||||
|
|
||||||
// Some fonts miss the relevant bits for italic or oblique, so
|
// Some fonts miss the relevant bits for italic or oblique, so
|
||||||
// we also try to infer that from the full name.
|
// we also try to infer that from the full name.
|
||||||
let italic = ttf.is_italic() || full.contains("italic");
|
let italic = ttf.is_italic() || full.contains("italic");
|
||||||
let oblique =
|
let oblique = ttf.is_oblique()
|
||||||
ttf.is_oblique() || full.contains("oblique") || full.contains("slanted");
|
|| full.contains("oblique")
|
||||||
|
|| full.contains("slanted");
|
||||||
|
|
||||||
let style = match (italic, oblique) {
|
match (italic, oblique) {
|
||||||
(false, false) => FontStyle::Normal,
|
(false, false) => FontStyle::Normal,
|
||||||
(true, _) => FontStyle::Italic,
|
(true, _) => FontStyle::Italic,
|
||||||
(_, true) => FontStyle::Oblique,
|
(_, 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;
|
|
||||||
}
|
}
|
||||||
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 }
|
FontVariant { style, weight, stretch }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -355,12 +356,6 @@ fn typographic_family(mut family: &str) -> &str {
|
|||||||
"narrow", "condensed", "cond", "cn", "cd", "compressed", "expanded", "exp"
|
"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.
|
// Trim spacing and weird leading dots in Apple fonts.
|
||||||
family = family.trim().trim_start_matches('.');
|
family = family.trim().trim_start_matches('.');
|
||||||
|
|
||||||
@ -376,7 +371,7 @@ fn typographic_family(mut family: &str) -> &str {
|
|||||||
// Find style suffix.
|
// Find style suffix.
|
||||||
let mut t = trimmed;
|
let mut t = trimmed;
|
||||||
let mut shortened = false;
|
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;
|
shortened = true;
|
||||||
t = s;
|
t = s;
|
||||||
}
|
}
|
||||||
@ -403,20 +398,7 @@ fn typographic_family(mut family: &str) -> &str {
|
|||||||
// Apply style suffix trimming.
|
// Apply style suffix trimming.
|
||||||
family = &family[..len];
|
family = &family[..len];
|
||||||
|
|
||||||
if newcm {
|
family
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// How many words the two strings share in their prefix.
|
/// 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.
|
//! Font handling.
|
||||||
|
|
||||||
mod book;
|
mod book;
|
||||||
|
mod exceptions;
|
||||||
mod variant;
|
mod variant;
|
||||||
|
|
||||||
pub use self::book::{Coverage, FontBook, FontFlags, FontInfo};
|
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(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct FontWeight(u16);
|
pub struct FontWeight(pub(super) u16);
|
||||||
|
|
||||||
impl FontWeight {
|
impl FontWeight {
|
||||||
/// Thin weight (100).
|
/// Thin weight (100).
|
||||||
@ -180,7 +180,7 @@ cast! {
|
|||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
#[serde(transparent)]
|
#[serde(transparent)]
|
||||||
pub struct FontStretch(u16);
|
pub struct FontStretch(pub(super) u16);
|
||||||
|
|
||||||
impl FontStretch {
|
impl FontStretch {
|
||||||
/// Ultra-condensed stretch (50%).
|
/// Ultra-condensed stretch (50%).
|
||||||
|
Loading…
x
Reference in New Issue
Block a user