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
184
|
/* 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 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 servo_atoms::Atom;
use servo_url::ServoUrl;
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::fmt;
use std::fs::File;
use std::io::{Error as IoError, Read};
use std::ops::Deref;
use std::sync::{Arc, Mutex};
use webrender_api::NativeFontHandle;
/// Platform specific font representation for mac.
/// The identifier is a PostScript font name. The
/// 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: Atom,
pub font_data: 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
.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: Atom, font_data: Option<Vec<u8>>) -> Result<FontTemplateData, IoError> {
Ok(FontTemplateData {
ctfont: CachedCTFont(Mutex::new(HashMap::new())),
identifier: identifier.to_owned(),
font_data: 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 pt_size_key = Au::from_f64_px(pt_size);
if !ctfonts.contains_key(&pt_size_key) {
// 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 ctfont = match self.font_data {
Some(ref bytes) => {
let fontprov = CGDataProvider::from_buffer(bytes.clone());
let cgfont_result = CGFont::from_data_provider(fontprov);
match cgfont_result {
Ok(cgfont) => {
Some(core_text::font::new_from_CGFont(&cgfont, clamped_pt_size))
},
Err(_) => None,
}
},
None => core_text::font::new_from_name(&*self.identifier, clamped_pt_size).ok(),
};
if let Some(ctfont) = ctfont {
ctfonts.insert(pt_size_key, ctfont);
}
}
ctfonts.get(&pt_size_key).map(|ctfont| (*ctfont).clone())
}
/// Returns a clone of 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) -> Vec<u8> {
if let Some(font_data) = self.bytes_if_in_memory() {
return font_data;
}
let path = ServoUrl::parse(
&*self
.ctfont(0.0)
.expect("No Core Text font available!")
.url()
.expect("No URL for Core Text font!")
.get_string()
.to_string(),
)
.expect("Couldn't parse Core Text font URL!")
.as_url()
.to_file_path()
.expect("Core Text font didn't name a path!");
let mut bytes = Vec::new();
File::open(path)
.expect("Couldn't open font file!")
.read_to_end(&mut bytes)
.unwrap();
bytes
}
/// Returns a clone of 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<Vec<u8>> {
self.font_data.as_ref().map(|bytes| (**bytes).clone())
}
/// Returns the native font that underlies this font template, if applicable.
pub fn native_font(&self) -> Option<NativeFontHandle> {
self.ctfont(0.0)
.map(|ctfont| NativeFontHandle(ctfont.copy_to_CGFont()))
}
}
#[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)
}
}
|