blitz-pdf 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +266 -12
- package/THIRD_PARTY_NOTICES.md +4 -2
- package/dist/browser.d.ts +1 -1
- package/dist/browser.d.ts.map +1 -1
- package/dist/document-options.d.ts +36 -0
- package/dist/document-options.d.ts.map +1 -0
- package/dist/document-options.js +236 -0
- package/dist/document-options.js.map +1 -0
- package/dist/node.d.ts +1 -1
- package/dist/node.d.ts.map +1 -1
- package/dist/options.d.ts +35 -1
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +21 -14
- package/dist/options.js.map +1 -1
- package/dist/types.d.ts +71 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/validation.d.ts +2 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +7 -0
- package/dist/validation.js.map +1 -0
- package/examples/zugferd.mjs +41 -0
- package/licenses/RUST_DEPENDENCIES.txt +27 -0
- package/package.json +3 -2
- package/src/browser.ts +1 -1
- package/src/document-options.ts +218 -0
- package/src/node.ts +1 -1
- package/src/options.ts +18 -13
- package/src/types.ts +76 -1
- package/src/validation.ts +6 -0
- package/vendor/README.md +27 -2
- package/vendor/krilla/.cargo-ok +1 -0
- package/vendor/krilla/.cargo_vcs_info.json +6 -0
- package/vendor/krilla/Cargo.lock +1357 -0
- package/vendor/krilla/Cargo.toml +165 -0
- package/vendor/krilla/LICENSE_APACHE +202 -0
- package/vendor/krilla/LICENSE_MIT +21 -0
- package/vendor/krilla/README.md +156 -0
- package/vendor/krilla/examples/basic.rs +117 -0
- package/vendor/krilla/examples/empty_document.rs +25 -0
- package/vendor/krilla/examples/parley.rs +174 -0
- package/vendor/krilla/examples/simple_text.rs +118 -0
- package/vendor/krilla/examples/stream_builder.rs +93 -0
- package/vendor/krilla/icc/LICENSE +116 -0
- package/vendor/krilla/icc/README.md +1 -0
- package/vendor/krilla/icc/sGrey-v2-magic.icc +0 -0
- package/vendor/krilla/icc/sGrey-v4.icc +0 -0
- package/vendor/krilla/icc/sRGB-v2-magic.icc +0 -0
- package/vendor/krilla/icc/sRGB-v4.icc +0 -0
- package/vendor/krilla/src/chunk_container.rs +503 -0
- package/vendor/krilla/src/configure/PDF_A1.md +227 -0
- package/vendor/krilla/src/configure/PDF_A2.md +269 -0
- package/vendor/krilla/src/configure/PDF_A3.md +31 -0
- package/vendor/krilla/src/configure/PDF_A4.md +211 -0
- package/vendor/krilla/src/configure/PDF_UA1.md +202 -0
- package/vendor/krilla/src/configure/README.md +23 -0
- package/vendor/krilla/src/configure/mod.rs +197 -0
- package/vendor/krilla/src/configure/validate.rs +1244 -0
- package/vendor/krilla/src/configure/version.rs +134 -0
- package/vendor/krilla/src/content.rs +1352 -0
- package/vendor/krilla/src/data.rs +49 -0
- package/vendor/krilla/src/document.rs +156 -0
- package/vendor/krilla/src/error.rs +170 -0
- package/vendor/krilla/src/geom.rs +341 -0
- package/vendor/krilla/src/graphics/blend.rs +46 -0
- package/vendor/krilla/src/graphics/color.rs +552 -0
- package/vendor/krilla/src/graphics/graphic.rs +30 -0
- package/vendor/krilla/src/graphics/graphics_state.rs +251 -0
- package/vendor/krilla/src/graphics/icc.rs +218 -0
- package/vendor/krilla/src/graphics/image.rs +835 -0
- package/vendor/krilla/src/graphics/mask.rs +128 -0
- package/vendor/krilla/src/graphics/mod.rs +14 -0
- package/vendor/krilla/src/graphics/paint.rs +434 -0
- package/vendor/krilla/src/graphics/separation.rs +86 -0
- package/vendor/krilla/src/graphics/shading_function.rs +865 -0
- package/vendor/krilla/src/graphics/shading_pattern.rs +60 -0
- package/vendor/krilla/src/graphics/tiling_pattern.rs +119 -0
- package/vendor/krilla/src/graphics/xobject.rs +150 -0
- package/vendor/krilla/src/interactive/action.rs +68 -0
- package/vendor/krilla/src/interactive/annotation.rs +283 -0
- package/vendor/krilla/src/interactive/destination.rs +289 -0
- package/vendor/krilla/src/interactive/mod.rs +3 -0
- package/vendor/krilla/src/interchange/embed.rs +240 -0
- package/vendor/krilla/src/interchange/metadata.rs +564 -0
- package/vendor/krilla/src/interchange/mod.rs +4 -0
- package/vendor/krilla/src/interchange/outline.rs +263 -0
- package/vendor/krilla/src/interchange/tagging/fmt.rs +733 -0
- package/vendor/krilla/src/interchange/tagging/generate.toml +474 -0
- package/vendor/krilla/src/interchange/tagging/generated.rs +4754 -0
- package/vendor/krilla/src/interchange/tagging/mod.rs +1324 -0
- package/vendor/krilla/src/interchange/tagging/tag.rs +686 -0
- package/vendor/krilla/src/lib.rs +179 -0
- package/vendor/krilla/src/num.rs +26 -0
- package/vendor/krilla/src/page.rs +612 -0
- package/vendor/krilla/src/pdf.rs +313 -0
- package/vendor/krilla/src/resource.rs +436 -0
- package/vendor/krilla/src/serialize.rs +1101 -0
- package/vendor/krilla/src/stream.rs +368 -0
- package/vendor/krilla/src/surface.rs +664 -0
- package/vendor/krilla/src/text/cid.rs +488 -0
- package/vendor/krilla/src/text/font.rs +351 -0
- package/vendor/krilla/src/text/glyph/bitmap.rs +81 -0
- package/vendor/krilla/src/text/glyph/colr.rs +581 -0
- package/vendor/krilla/src/text/glyph/mod.rs +205 -0
- package/vendor/krilla/src/text/glyph/outline.rs +72 -0
- package/vendor/krilla/src/text/glyph/svg.rs +34 -0
- package/vendor/krilla/src/text/group.rs +463 -0
- package/vendor/krilla/src/text/mod.rs +232 -0
- package/vendor/krilla/src/text/shape.rs +102 -0
- package/vendor/krilla/src/text/type3.rs +514 -0
- package/vendor/krilla/src/util.rs +286 -0
- package/vendor/stylo/properties/longhands.toml +0 -1
- package/vendor/stylo/stylesheets/rule_parser.rs +1 -1
- package/wasm/blitz_pdf.js +10 -5
- package/wasm/blitz_pdf_bg.wasm +0 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
use std::fmt::{Debug, Formatter};
|
|
2
|
+
use std::hash::{Hash, Hasher};
|
|
3
|
+
use std::ops::Deref;
|
|
4
|
+
use std::sync::Arc;
|
|
5
|
+
|
|
6
|
+
use skrifa::instance::{Location, LocationRef, Size};
|
|
7
|
+
use skrifa::metrics::GlyphMetrics;
|
|
8
|
+
use skrifa::raw::types::NameId;
|
|
9
|
+
use skrifa::raw::TableProvider;
|
|
10
|
+
use skrifa::{FontRef, MetadataProvider, OutlineGlyphCollection};
|
|
11
|
+
use tiny_skia_path::FiniteF32;
|
|
12
|
+
use yoke::{Yoke, Yokeable};
|
|
13
|
+
|
|
14
|
+
use crate::geom::Rect;
|
|
15
|
+
use crate::text::GlyphId;
|
|
16
|
+
use crate::util::Prehashed;
|
|
17
|
+
use crate::Data;
|
|
18
|
+
|
|
19
|
+
/// An OpenType font. Can be a TrueType, OpenType font or a TrueType collection.
|
|
20
|
+
/// It holds a reference to the underlying data as well as some basic information
|
|
21
|
+
/// about the font.
|
|
22
|
+
///
|
|
23
|
+
/// Cloning and hashing this type is cheap.
|
|
24
|
+
#[derive(Clone, Hash, Eq, PartialEq)]
|
|
25
|
+
pub struct Font(Arc<Prehashed<Repr>>);
|
|
26
|
+
|
|
27
|
+
impl Font {
|
|
28
|
+
/// Create a new font from some data. If you want to create a variable font at a specific
|
|
29
|
+
/// location, use [`Font::new_variable`] instead.
|
|
30
|
+
///
|
|
31
|
+
/// The `index` indicates the index that should be
|
|
32
|
+
/// associated with this font for TrueType collections, otherwise this value should be
|
|
33
|
+
/// set to 0.
|
|
34
|
+
///
|
|
35
|
+
/// Returns `None` if the index is invalid or the font couldn't be read.
|
|
36
|
+
pub fn new(data: Data, index: u32) -> Option<Self> {
|
|
37
|
+
Self::new_variable(data, index, &[])
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/// Like [`Font::new`], creates a new font from some data, but allows you to specify
|
|
41
|
+
/// variation coordinates in case the font is variable.
|
|
42
|
+
pub fn new_variable(data: Data, index: u32, variation_coords: &[(Tag, f32)]) -> Option<Self> {
|
|
43
|
+
let font_info = FontInfo::new(data.as_ref(), index, variation_coords)?;
|
|
44
|
+
|
|
45
|
+
Font::new_with_info(data.clone(), Arc::new(font_info))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
pub(crate) fn new_with_info(data: Data, font_info: Arc<FontInfo>) -> Option<Self> {
|
|
49
|
+
let yoke_data = YokeData {
|
|
50
|
+
data: data.0.clone(),
|
|
51
|
+
location: font_info.location.clone(),
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
let font_ref_yoke = Yoke::<FontRefYoke<'static>, Box<YokeData>>::attach_to_cart(
|
|
55
|
+
Box::new(yoke_data),
|
|
56
|
+
|data| {
|
|
57
|
+
let font_ref =
|
|
58
|
+
FontRef::from_index(data.data.as_ref().as_ref(), font_info.index).unwrap();
|
|
59
|
+
FontRefYoke {
|
|
60
|
+
font_ref: font_ref.clone(),
|
|
61
|
+
glyph_metrics: font_ref.glyph_metrics(Size::unscaled(), &data.location),
|
|
62
|
+
outline_glyphs: font_ref.outline_glyphs(),
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
Some(Font(Arc::new(Prehashed::new(Repr {
|
|
68
|
+
font_data: data,
|
|
69
|
+
font_ref_yoke,
|
|
70
|
+
font_info,
|
|
71
|
+
}))))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
pub(crate) fn postscript_name(&self) -> Option<&str> {
|
|
75
|
+
self.0.font_info.postscript_name.as_deref()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// Return the index of the font.
|
|
79
|
+
pub(crate) fn index(&self) -> u32 {
|
|
80
|
+
self.font_info().index
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
pub(crate) fn font_info(&self) -> Arc<FontInfo> {
|
|
84
|
+
self.0.font_info.clone()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
pub(crate) fn variation_coordinates(&self) -> &[(Tag, FiniteF32)] {
|
|
88
|
+
&self.0.font_info.var_coords
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
pub(crate) fn cap_height(&self) -> Option<f32> {
|
|
92
|
+
self.0.font_info.cap_height.map(|n| n.get())
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
pub(crate) fn ascent(&self) -> f32 {
|
|
96
|
+
self.0.font_info.ascent.get()
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
pub(crate) fn weight(&self) -> f32 {
|
|
100
|
+
self.0.font_info.weight.get()
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
pub(crate) fn stretch(&self) -> f32 {
|
|
104
|
+
self.0.font_info.stretch.get()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
pub(crate) fn descent(&self) -> f32 {
|
|
108
|
+
self.0.font_info.descent.get()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
pub(crate) fn num_glyphs(&self) -> u32 {
|
|
112
|
+
self.0.font_info.num_glyphs
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
pub(crate) fn is_monospaced(&self) -> bool {
|
|
116
|
+
self.0.font_info.is_monospaced
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
pub(crate) fn italic_angle(&self) -> f32 {
|
|
120
|
+
self.0.font_info.italic_angle.get()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/// The units per em of the font.
|
|
124
|
+
pub fn units_per_em(&self) -> f32 {
|
|
125
|
+
self.0.font_info.units_per_em as f32
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
pub(crate) fn bbox(&self) -> Rect {
|
|
129
|
+
self.0.font_info.global_bbox
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
pub(crate) fn location_ref(&self) -> LocationRef<'_> {
|
|
133
|
+
(&self.0.font_info.location).into()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
pub(crate) fn font_ref(&self) -> &FontRef<'_> {
|
|
137
|
+
&self.0.font_ref_yoke.get().font_ref
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
pub(crate) fn glyph_metrics(&self) -> &GlyphMetrics<'_> {
|
|
141
|
+
&self.0.font_ref_yoke.get().glyph_metrics
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
pub(crate) fn outline_glyphs(&self) -> &OutlineGlyphCollection<'_> {
|
|
145
|
+
&self.0.font_ref_yoke.get().outline_glyphs
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
pub(crate) fn font_data(&self) -> Data {
|
|
149
|
+
self.0.font_data.clone()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
#[inline]
|
|
153
|
+
pub(crate) fn advance_width(&self, glyph_id: GlyphId) -> Option<f32> {
|
|
154
|
+
self.glyph_metrics().advance_width(glyph_id.to_skrifa())
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/// A 4-byte OpenType tag.
|
|
159
|
+
#[derive(Copy, Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
|
160
|
+
pub struct Tag([u8; 4]);
|
|
161
|
+
|
|
162
|
+
impl Tag {
|
|
163
|
+
/// Create a new tag.
|
|
164
|
+
pub fn new(tag: &[u8; 4]) -> Self {
|
|
165
|
+
Self(*tag)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/// Try to create a new tag from a string.
|
|
169
|
+
///
|
|
170
|
+
/// Return `None` if the string is not 4 bytes in size.
|
|
171
|
+
pub fn try_from_str(s: &str) -> Option<Self> {
|
|
172
|
+
let tag: [u8; 4] = s.as_bytes().try_into().ok()?;
|
|
173
|
+
|
|
174
|
+
Some(Self(tag))
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/// Return the value of the tag.
|
|
178
|
+
pub fn get(&self) -> &[u8; 4] {
|
|
179
|
+
&self.0
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
impl Debug for Font {
|
|
184
|
+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
185
|
+
write!(f, "Font {{..}}")
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
struct YokeData {
|
|
190
|
+
data: Arc<dyn AsRef<[u8]> + Send + Sync>,
|
|
191
|
+
location: Location,
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
impl Deref for YokeData {
|
|
195
|
+
type Target = YokeData;
|
|
196
|
+
|
|
197
|
+
fn deref(&self) -> &Self::Target {
|
|
198
|
+
self
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/// `FontInfo` holds basic information about the font which is necessary
|
|
203
|
+
/// to distinguish a `Font` object from others. The `Hash` implementation
|
|
204
|
+
/// of the `Font` struct solely depends on its `FontInfo` object. The reason
|
|
205
|
+
/// we do this is to avoid hashing the whole font, which can be dozens of megabytes.
|
|
206
|
+
/// Instead, we parse the most basic information as well as additional distinguishing
|
|
207
|
+
/// information, such as the font name and the checksum, and has this instead.
|
|
208
|
+
/// This is much faster, and since we also include the checksum, the odds of two
|
|
209
|
+
/// different fonts ending up with the same hash is pretty much zero.
|
|
210
|
+
#[derive(Debug, Hash, Eq, PartialEq)]
|
|
211
|
+
pub(crate) struct FontInfo {
|
|
212
|
+
index: u32,
|
|
213
|
+
checksum: u32,
|
|
214
|
+
data_len: usize,
|
|
215
|
+
// `location` is derived from `var_coords`, but we need to store `var_coords` it explicitly
|
|
216
|
+
// so we can later pass it to the subsetter.
|
|
217
|
+
var_coords: Vec<(Tag, FiniteF32)>,
|
|
218
|
+
location: Location,
|
|
219
|
+
units_per_em: u16,
|
|
220
|
+
global_bbox: Rect,
|
|
221
|
+
num_glyphs: u32,
|
|
222
|
+
postscript_name: Option<String>,
|
|
223
|
+
ascent: FiniteF32,
|
|
224
|
+
descent: FiniteF32,
|
|
225
|
+
cap_height: Option<FiniteF32>,
|
|
226
|
+
is_monospaced: bool,
|
|
227
|
+
italic_angle: FiniteF32,
|
|
228
|
+
weight: FiniteF32,
|
|
229
|
+
has_glyf: bool,
|
|
230
|
+
has_cff: bool,
|
|
231
|
+
has_cff2: bool,
|
|
232
|
+
stretch: FiniteF32,
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
struct Repr {
|
|
236
|
+
font_info: Arc<FontInfo>,
|
|
237
|
+
font_data: Data,
|
|
238
|
+
font_ref_yoke: Yoke<FontRefYoke<'static>, Box<YokeData>>,
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
impl Hash for Repr {
|
|
242
|
+
fn hash<H: Hasher>(&self, state: &mut H) {
|
|
243
|
+
// We assume that if the font info is distinct, the font itself is distinct as well. This
|
|
244
|
+
// doesn't have to be the case: while the font does have a checksum, it's "only" a
|
|
245
|
+
// u32. The proper way would be to hash the whole font data, but this is just too expensive.
|
|
246
|
+
// However, the odds of the checksum AND all font metrics (including font name) being the same
|
|
247
|
+
// with the font being different is diminishingly low.
|
|
248
|
+
self.font_info.hash(state);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
impl FontInfo {
|
|
253
|
+
pub(crate) fn new(data: &[u8], index: u32, var_coords: &[(Tag, f32)]) -> Option<Self> {
|
|
254
|
+
let font_ref = FontRef::from_index(data, index).ok()?;
|
|
255
|
+
let location = font_ref.axes().location(
|
|
256
|
+
var_coords
|
|
257
|
+
.iter()
|
|
258
|
+
.map(|i| (skrifa::Tag::new(i.0.get()), i.1)),
|
|
259
|
+
);
|
|
260
|
+
let data_len = data.len();
|
|
261
|
+
let checksum = font_ref.head().ok()?.checksum_adjustment();
|
|
262
|
+
let num_glyphs = font_ref.glyph_names().num_glyphs();
|
|
263
|
+
|
|
264
|
+
let metrics = font_ref.metrics(Size::unscaled(), &location);
|
|
265
|
+
let os_2 = font_ref.os2().ok();
|
|
266
|
+
let ascent = FiniteF32::new(
|
|
267
|
+
os_2.as_ref()
|
|
268
|
+
.map(|s| s.s_typo_ascender() as f32)
|
|
269
|
+
.unwrap_or(metrics.ascent),
|
|
270
|
+
)?;
|
|
271
|
+
let descent = FiniteF32::new(
|
|
272
|
+
os_2.as_ref()
|
|
273
|
+
.map(|s| s.s_typo_descender() as f32)
|
|
274
|
+
.unwrap_or(metrics.descent),
|
|
275
|
+
)?;
|
|
276
|
+
let is_monospaced = metrics.is_monospace;
|
|
277
|
+
let cap_height = metrics.cap_height.map(|n| FiniteF32::new(n).unwrap());
|
|
278
|
+
let italic_angle = FiniteF32::new(metrics.italic_angle).unwrap();
|
|
279
|
+
let weight = FiniteF32::new(font_ref.attributes().weight.value())?;
|
|
280
|
+
let stretch = FiniteF32::new(font_ref.attributes().stretch.ratio())?;
|
|
281
|
+
let units_per_em = metrics.units_per_em;
|
|
282
|
+
|
|
283
|
+
let global_bbox = metrics
|
|
284
|
+
.bounds
|
|
285
|
+
.and_then(|b| Rect::from_ltrb(b.x_min, b.y_min, b.x_max, b.y_max))
|
|
286
|
+
.unwrap_or(Rect::from_xywh(
|
|
287
|
+
0.0,
|
|
288
|
+
0.0,
|
|
289
|
+
units_per_em as f32,
|
|
290
|
+
units_per_em as f32,
|
|
291
|
+
)?);
|
|
292
|
+
|
|
293
|
+
let postscript_name = {
|
|
294
|
+
if let Ok(name) = font_ref.name() {
|
|
295
|
+
name.name_record().iter().find_map(|n| {
|
|
296
|
+
if n.name_id.get() == NameId::POSTSCRIPT_NAME {
|
|
297
|
+
if let Ok(string) = n.string(name.string_data()) {
|
|
298
|
+
return Some(string.to_string());
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
None
|
|
303
|
+
})
|
|
304
|
+
} else {
|
|
305
|
+
None
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
let has_glyf = font_ref.glyf().is_ok();
|
|
310
|
+
let has_cff = font_ref.cff().is_ok();
|
|
311
|
+
let has_cff2 = font_ref.cff2().is_ok();
|
|
312
|
+
|
|
313
|
+
Some(FontInfo {
|
|
314
|
+
index,
|
|
315
|
+
data_len,
|
|
316
|
+
checksum,
|
|
317
|
+
var_coords: var_coords
|
|
318
|
+
.iter()
|
|
319
|
+
.map(|v| (v.0, FiniteF32::new(v.1).unwrap_or_default()))
|
|
320
|
+
.collect(),
|
|
321
|
+
location,
|
|
322
|
+
num_glyphs,
|
|
323
|
+
units_per_em,
|
|
324
|
+
postscript_name,
|
|
325
|
+
ascent,
|
|
326
|
+
cap_height,
|
|
327
|
+
has_glyf,
|
|
328
|
+
has_cff,
|
|
329
|
+
has_cff2,
|
|
330
|
+
descent,
|
|
331
|
+
is_monospaced,
|
|
332
|
+
weight,
|
|
333
|
+
stretch,
|
|
334
|
+
italic_angle,
|
|
335
|
+
global_bbox,
|
|
336
|
+
})
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
pub(crate) fn can_be_cid_font(&self) -> bool {
|
|
340
|
+
self.has_cff || self.has_glyf || self.has_cff2
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/// A yoke so that we can attach a `FontRef` object to the corresponding `Font`,
|
|
345
|
+
/// without running into lifetime issues.
|
|
346
|
+
#[derive(Yokeable, Clone)]
|
|
347
|
+
struct FontRefYoke<'a> {
|
|
348
|
+
pub font_ref: FontRef<'a>,
|
|
349
|
+
pub glyph_metrics: GlyphMetrics<'a>,
|
|
350
|
+
pub outline_glyphs: OutlineGlyphCollection<'a>,
|
|
351
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
use skrifa::bitmap::{BitmapData, BitmapFormat, BitmapGlyph, Origin};
|
|
2
|
+
use skrifa::MetadataProvider;
|
|
3
|
+
|
|
4
|
+
use crate::geom::{Size, Transform};
|
|
5
|
+
use crate::graphics::image::Image;
|
|
6
|
+
use crate::surface::Surface;
|
|
7
|
+
use crate::text::Font;
|
|
8
|
+
use crate::text::GlyphId;
|
|
9
|
+
|
|
10
|
+
pub(crate) fn has_bitmap_data(font: &Font, glyph: GlyphId) -> bool {
|
|
11
|
+
// We only support PNG.
|
|
12
|
+
get_bitmap_glyph(font, glyph).is_some_and(|b| matches!(b.data, BitmapData::Png(_)))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
pub(crate) fn get_bitmap_glyph(font: &Font, glyph: GlyphId) -> Option<BitmapGlyph<'_>> {
|
|
16
|
+
let bitmap_strikes = font.font_ref().bitmap_strikes();
|
|
17
|
+
bitmap_strikes.glyph_for_size(skrifa::instance::Size::unscaled(), glyph.to_skrifa())
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/// Draw a bitmap-based glyph on a surface.
|
|
21
|
+
pub(crate) fn draw_glyph(font: Font, glyph: GlyphId, surface: &mut Surface) -> Option<()> {
|
|
22
|
+
let metrics = font
|
|
23
|
+
.font_ref()
|
|
24
|
+
.metrics(skrifa::instance::Size::unscaled(), font.location_ref());
|
|
25
|
+
|
|
26
|
+
let bitmap_strikes = font.font_ref().bitmap_strikes();
|
|
27
|
+
let bitmap_glyph =
|
|
28
|
+
bitmap_strikes.glyph_for_size(skrifa::instance::Size::unscaled(), glyph.to_skrifa())?;
|
|
29
|
+
|
|
30
|
+
let upem = metrics.units_per_em as f32;
|
|
31
|
+
|
|
32
|
+
match bitmap_glyph.data {
|
|
33
|
+
BitmapData::Png(data) => {
|
|
34
|
+
let image = Image::from_png(data.to_vec().into(), false).ok()?;
|
|
35
|
+
let size = Size::from_wh(image.size().0 as f32, image.size().1 as f32).unwrap();
|
|
36
|
+
|
|
37
|
+
// Adapted from vello.
|
|
38
|
+
let scale_factor = upem / (bitmap_glyph.ppem_y);
|
|
39
|
+
let mut transform =
|
|
40
|
+
Transform::from_translate(-bitmap_glyph.bearing_x, bitmap_glyph.bearing_y)
|
|
41
|
+
.pre_concat(Transform::from_scale(scale_factor, scale_factor))
|
|
42
|
+
.pre_concat(Transform::from_translate(
|
|
43
|
+
-bitmap_glyph.inner_bearing_x,
|
|
44
|
+
-bitmap_glyph.inner_bearing_y,
|
|
45
|
+
));
|
|
46
|
+
|
|
47
|
+
transform = match bitmap_glyph.placement_origin {
|
|
48
|
+
Origin::TopLeft => transform,
|
|
49
|
+
Origin::BottomLeft => {
|
|
50
|
+
transform.pre_concat(Transform::from_translate(0.0, -(image.size().1 as f32)))
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
transform = if let Some(format) = bitmap_strikes.format() {
|
|
55
|
+
if format == BitmapFormat::Sbix {
|
|
56
|
+
// For unknown reasons, using Apple Color Emoji will lead to a vertical shift on MacOS, but this shift
|
|
57
|
+
// doesn't seem to be coming from the font and most likely is somehow hardcoded. On Windows,
|
|
58
|
+
// this shift will not be applied. However, if this shift is not applied the emojis are a bit
|
|
59
|
+
// too high up when being together with other text, so we try to imitate this.
|
|
60
|
+
// See also https://github.com/harfbuzz/harfbuzz/issues/2679#issuecomment-1345595425
|
|
61
|
+
// We approximate this vertical shift that seems to be produced by it.
|
|
62
|
+
// This value seems to be pretty close to what is happening on MacOS.
|
|
63
|
+
transform
|
|
64
|
+
.pre_concat(Transform::from_translate(0.0, 0.128 * upem / scale_factor))
|
|
65
|
+
} else {
|
|
66
|
+
transform
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
transform
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
surface.push_transform(&transform);
|
|
73
|
+
surface.draw_image(image, size);
|
|
74
|
+
surface.pop();
|
|
75
|
+
|
|
76
|
+
Some(())
|
|
77
|
+
}
|
|
78
|
+
BitmapData::Bgra(_) => None,
|
|
79
|
+
BitmapData::Mask(_) => None,
|
|
80
|
+
}
|
|
81
|
+
}
|