-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathhmtx.rs
95 lines (75 loc) · 3.26 KB
/
hmtx.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
//! The `hmtx` table contains the horizontal metrics for each glyph.
//! All we need to do is to rewrite the table so that it matches the
//! sequence of the new glyphs. A minor pain point is that the table
//! allows omitting the advance width for the last few glyphs if it is
//! the same. In order to keep the code simple, we do not keep this optimization
//! when rewriting the table.
//! While doing so, we also rewrite the `hhea` table, which contains
//! the number of glyphs that contain both, advance width and
//! left side bearing metrics.
// The parsing logic was taken from ttf-parser.
use super::*;
use crate::Error::OverflowError;
pub fn subset(ctx: &mut Context) -> Result<()> {
let hmtx = ctx.expect_table(Tag::HMTX).ok_or(MalformedFont)?;
let new_metrics = {
let mut new_metrics = vec![];
// Extract the number of horizontal metrics from the `hhea` table.
let num_h_metrics = {
let hhea = ctx.expect_table(Tag::HHEA).ok_or(MalformedFont)?;
let mut r = Reader::new(hhea);
r.skip_bytes(34);
r.read::<u16>().ok_or(MalformedFont)?
};
let last_advance_width = {
let index = 4 * num_h_metrics.checked_sub(1).ok_or(OverflowError)? as usize;
let mut r = Reader::new(hmtx.get(index..).ok_or(MalformedFont)?);
r.read::<u16>().ok_or(MalformedFont)?
};
for old_gid in ctx.mapper.remapped_gids() {
let has_advance_width = old_gid < num_h_metrics;
let offset = if has_advance_width {
old_gid as usize * 4
} else {
let num_h_metrics = num_h_metrics as usize;
num_h_metrics * 4 + (old_gid as usize - num_h_metrics) * 2
};
let mut r = Reader::new(hmtx.get(offset..).ok_or(MalformedFont)?);
if has_advance_width {
let adv = r.read::<u16>().ok_or(MalformedFont)?;
let lsb = r.read::<u16>().ok_or(MalformedFont)?;
new_metrics.push((adv, lsb));
} else {
new_metrics
.push((last_advance_width, r.read::<u16>().ok_or(MalformedFont)?));
}
}
new_metrics
};
// Find out the last index we need to include the advance width for.
let mut last_advance_width_index =
u16::try_from(new_metrics.len()).map_err(|_| OverflowError)? - 1;
let last_advance_width = new_metrics[last_advance_width_index as usize].0;
for gid in new_metrics.iter().rev().skip(1) {
if gid.0 == last_advance_width {
last_advance_width_index -= 1;
} else {
break;
}
}
let mut sub_hmtx = Writer::new();
for (index, metric) in new_metrics.iter().enumerate() {
let index = u16::try_from(index).map_err(|_| OverflowError)?;
if index <= last_advance_width_index {
sub_hmtx.write::<u16>(metric.0);
}
sub_hmtx.write::<u16>(metric.1);
}
ctx.push(Tag::HMTX, sub_hmtx.finish());
let hhea = ctx.expect_table(Tag::HHEA).ok_or(MalformedFont)?;
let mut sub_hhea = Writer::new();
sub_hhea.extend(&hhea[0..hhea.len() - 2]);
sub_hhea.write::<u16>(last_advance_width_index + 1);
ctx.push(Tag::HHEA, sub_hhea.finish());
Ok(())
}