-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
/
font_template.rs
183 lines (159 loc) · 6.14 KB
/
font_template.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::{Error as IoError, Read};
use std::ops::Deref;
use std::path::Path;
use std::sync::{Arc, Mutex, RwLock};
use app_units::Au;
use core_graphics::data_provider::CGDataProvider;
use core_graphics::font::CGFont;
use core_text::font::CTFont;
use serde::de::{Error, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use webrender_api::NativeFontHandle;
use crate::font_cache_thread::FontIdentifier;
/// Platform specific font representation for MacOS. CTFont object is cached here for use
/// by the paint functions that create CGFont references.
#[derive(Deserialize, Serialize)]
pub struct FontTemplateData {
// If you add members here, review the Debug impl below
/// The `CTFont` object, if present. This is cached here so that we don't have to keep creating
/// `CTFont` instances over and over. It can always be recreated from the `identifier` and/or
/// `font_data` fields.
///
/// When sending a `FontTemplateData` instance across processes, this will be cleared out on
/// the other side, because `CTFont` instances cannot be sent across processes. This is
/// harmless, however, because it can always be recreated.
ctfont: CachedCTFont,
pub identifier: FontIdentifier,
pub font_data: RwLock<Option<Arc<Vec<u8>>>>,
}
impl fmt::Debug for FontTemplateData {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("FontTemplateData")
.field("ctfont", &self.ctfont)
.field("identifier", &self.identifier)
.field(
"font_data",
&self
.font_data
.read()
.unwrap()
.as_ref()
.map(|bytes| format!("[{} bytes]", bytes.len())),
)
.finish()
}
}
unsafe impl Send for FontTemplateData {}
unsafe impl Sync for FontTemplateData {}
impl FontTemplateData {
pub fn new(
identifier: FontIdentifier,
font_data: Option<Vec<u8>>,
) -> Result<FontTemplateData, IoError> {
Ok(FontTemplateData {
ctfont: CachedCTFont(Mutex::new(HashMap::new())),
identifier,
font_data: RwLock::new(font_data.map(Arc::new)),
})
}
/// Retrieves the Core Text font instance, instantiating it if necessary.
pub fn ctfont(&self, pt_size: f64) -> Option<CTFont> {
let mut ctfonts = self.ctfont.lock().unwrap();
let entry = ctfonts.entry(Au::from_f64_px(pt_size));
match entry {
Entry::Occupied(entry) => return Some(entry.get().clone()),
Entry::Vacant(_) => {},
}
// If you pass a zero font size to one of the Core Text APIs, it'll replace it with
// 12.0. We don't want that! (Issue #10492.)
let clamped_pt_size = pt_size.max(0.01);
let provider = CGDataProvider::from_buffer(self.bytes());
let cgfont = CGFont::from_data_provider(provider).ok()?;
let ctfont = core_text::font::new_from_CGFont(&cgfont, clamped_pt_size);
// Cache the newly created CTFont font.
entry.or_insert(ctfont.clone());
Some(ctfont)
}
/// Returns a reference to the data in this font. This may be a hugely expensive
/// operation (depending on the platform) which performs synchronous disk I/O
/// and should never be done lightly.
pub fn bytes(&self) -> Arc<Vec<u8>> {
self.font_data
.write()
.unwrap()
.get_or_insert_with(|| {
let path = match &self.identifier {
FontIdentifier::Local(local) => local.path.clone(),
FontIdentifier::Web(_) => unreachable!("Web fonts should always have data."),
};
let mut bytes = Vec::new();
File::open(Path::new(&*path))
.expect("Couldn't open font file!")
.read_to_end(&mut bytes)
.unwrap();
Arc::new(bytes)
})
.clone()
}
/// Returns a reference to the bytes in this font if they are in memory.
/// This function never performs disk I/O.
pub fn bytes_if_in_memory(&self) -> Option<Arc<Vec<u8>>> {
self.font_data.read().unwrap().as_ref().cloned()
}
/// Returns the native font that underlies this font template, if applicable.
pub fn native_font(&self) -> Option<NativeFontHandle> {
let local_identifier = match &self.identifier {
FontIdentifier::Local(local_identifier) => local_identifier,
FontIdentifier::Web(_) => return None,
};
Some(NativeFontHandle {
name: local_identifier.postscript_name.to_string(),
path: local_identifier.path.to_string(),
})
}
}
#[derive(Debug)]
pub struct CachedCTFont(Mutex<HashMap<Au, CTFont>>);
impl Deref for CachedCTFont {
type Target = Mutex<HashMap<Au, CTFont>>;
fn deref(&self) -> &Mutex<HashMap<Au, CTFont>> {
&self.0
}
}
impl Serialize for CachedCTFont {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_none()
}
}
impl<'de> Deserialize<'de> for CachedCTFont {
fn deserialize<D>(deserializer: D) -> Result<CachedCTFont, D::Error>
where
D: Deserializer<'de>,
{
struct NoneOptionVisitor;
impl<'de> Visitor<'de> for NoneOptionVisitor {
type Value = CachedCTFont;
fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "none")
}
#[inline]
fn visit_none<E>(self) -> Result<CachedCTFont, E>
where
E: Error,
{
Ok(CachedCTFont(Mutex::new(HashMap::new())))
}
}
deserializer.deserialize_option(NoneOptionVisitor)
}
}