Skip to main content

atmos/kernel/
woff2_glyf.rs

1//! WOFF2 の `glyf` 変換の復元(純粋なバイト列変換)。
2//!
3//! 仕様は `spec/woff2_glyf_transform.md`(WOFF2 §5.1)。
4//!
5//! ## なぜ必要か
6//! Font Awesome のアイコンが箱(☐)として描かれていた。
7//! カスケード側は単体試験で正常と確定済みで、
8//! **フォントの輪郭データが壊れている**ことが原因だった。
9//!
10//! WOFF2 は `glyf` / `loca` を変換して格納する。変換の有無の判定が
11//! 仕様と逆だったうえ、**復元処理そのものが無かった**ため、
12//! 変換済みバイト列がそのまま SFNT の `glyf` として書かれていた。
13//!
14//! ## 不変条件(W-1〜W-4)
15//! - **W-1**: 復元した `glyf`/`loca` は元とバイト一致でなくてよいが、
16//!   輪郭が正しく読めること
17//! - **W-2**: `loca` は復元した `glyf` の実オフセットから作る
18//! - **W-3**: 復元できない入力は `None`。壊れたデータを書かない
19//! - **W-4**: 純粋なバイト列変換。グローバル状態を持たない
20
21extern crate alloc;
22
23use alloc::vec::Vec;
24
25/// WOFF2 の 255UInt16 を読む。
26///
27/// 計算量: **O(1)**。
28///
29/// 仕様(WOFF2 §5.1):
30/// - `253` の次の 2 バイトが値そのもの
31/// - `255` の次の 1 バイト + 253
32/// - `254` の次の 1 バイト + 253*2
33/// - それ以外はその値
34pub fn read_255_u16(data: &[u8], pos: &mut usize) -> Option<u16> {
35    const ONE_MORE: u8 = 255;
36    const WORD_CODE: u8 = 253;
37    const LOWEST: u8 = 254;
38    let code = *data.get(*pos)?;
39    *pos += 1;
40    if code == WORD_CODE {
41        let hi = *data.get(*pos)? as u16;
42        let lo = *data.get(*pos + 1)? as u16;
43        *pos += 2;
44        Some((hi << 8) | lo)
45    } else if code == ONE_MORE {
46        let v = *data.get(*pos)? as u16;
47        *pos += 1;
48        Some(v + 253)
49    } else if code == LOWEST {
50        let v = *data.get(*pos)? as u16;
51        *pos += 1;
52        Some(v + 253 * 2)
53    } else {
54        Some(code as u16)
55    }
56}
57
58fn read_u16(d: &[u8], p: &mut usize) -> Option<u16> {
59    let v = u16::from_be_bytes([*d.get(*p)?, *d.get(*p + 1)?]);
60    *p += 2;
61    Some(v)
62}
63
64fn read_i16(d: &[u8], p: &mut usize) -> Option<i16> {
65    read_u16(d, p).map(|v| v as i16)
66}
67
68fn read_u32(d: &[u8], p: &mut usize) -> Option<u32> {
69    let v = u32::from_be_bytes([
70        *d.get(*p)?,
71        *d.get(*p + 1)?,
72        *d.get(*p + 2)?,
73        *d.get(*p + 3)?,
74    ]);
75    *p += 4;
76    Some(v)
77}
78
79/// 三角符号化された座標差分を読む(WOFF2 §5.1 の表)。
80///
81/// 計算量: **O(1)**。
82///
83/// `flag` の下位 7 ビットが符号化の種類を表す。
84/// 戻り値は `(dx, dy)`。
85fn decode_triplet(flag: u8, g: &[u8], gp: &mut usize) -> Option<(i32, i32)> {
86    let f = flag & 0x7F;
87    if f < 84 {
88        // 1 バイト: dx は 0、dy が ±
89        let b0 = *g.get(*gp)? as i32;
90        *gp += 1;
91        let dy = ((f % 12) as i32) * 256 + b0;
92        let dy = if ((f / 12) % 2) == 0 { -dy } else { dy };
93        Some((0, dy))
94    } else if f < 120 {
95        // 1 バイト: dy は 0、dx が ±
96        let b0 = *g.get(*gp)? as i32;
97        *gp += 1;
98        let f2 = f - 84;
99        let dx = ((f2 % 12) as i32) * 256 + b0;
100        let dx = if ((f2 / 12) % 2) == 0 { -dx } else { dx };
101        Some((dx, 0))
102    } else if f < 124 {
103        // 1 バイト: dx/dy とも 4 ビットずつ
104        let b0 = *g.get(*gp)? as i32;
105        *gp += 1;
106        let f2 = f - 120;
107        let dx = 1 + ((f2 / 2) as i32) * 16 + (b0 >> 4);
108        let dy = 1 + ((f2 % 2) as i32) * 16 + (b0 & 0x0F);
109        // 符号は f2 の上位ビット群で決まる(仕様の表どおり 2 ビット)
110        let dx = if (f2 & 0x02) == 0 { -dx } else { dx };
111        let dy = if (f2 & 0x01) == 0 { -dy } else { dy };
112        Some((dx, dy))
113    } else {
114        // 2 バイト以上
115        let f2 = f - 124;
116        if f2 < 4 {
117            let b0 = *g.get(*gp)? as i32;
118            let b1 = *g.get(*gp + 1)? as i32;
119            *gp += 2;
120            let dx = (b0 << 4) | (b1 >> 4);
121            let dy = ((b1 & 0x0F) << 8) | (*g.get(*gp)? as i32);
122            *gp += 1;
123            let dx = if (f2 & 0x02) == 0 { -dx } else { dx };
124            let dy = if (f2 & 0x01) == 0 { -dy } else { dy };
125            Some((dx, dy))
126        } else {
127            let b0 = *g.get(*gp)? as i32;
128            let b1 = *g.get(*gp + 1)? as i32;
129            let b2 = *g.get(*gp + 2)? as i32;
130            let b3 = *g.get(*gp + 3)? as i32;
131            *gp += 4;
132            let dx = (b0 << 8) | b1;
133            let dy = (b2 << 8) | b3;
134            let f3 = f2 - 4;
135            let dx = if (f3 & 0x02) == 0 { -dx } else { dx };
136            let dy = if (f3 & 0x01) == 0 { -dy } else { dy };
137            Some((dx, dy))
138        }
139    }
140}
141
142/// 変換された `glyf` ストリームから `(glyf, loca, index_format)` を復元する。
143///
144/// 計算量: **O(総点数)**。
145///
146/// 復元できない入力(ストリーム長の不一致など)は `None`(W-3)。
147pub fn reconstruct_glyf(data: &[u8]) -> Option<(Vec<u8>, Vec<u8>, u16)> {
148    let mut p = 0usize;
149    let _version = read_u32(data, &mut p)?;
150    let num_glyphs = read_u16(data, &mut p)? as usize;
151    let index_format = read_u16(data, &mut p)?;
152
153    let n_contour_size = read_u32(data, &mut p)? as usize;
154    let n_points_size = read_u32(data, &mut p)? as usize;
155    let flag_size = read_u32(data, &mut p)? as usize;
156    let glyph_size = read_u32(data, &mut p)? as usize;
157    let composite_size = read_u32(data, &mut p)? as usize;
158    let bbox_size = read_u32(data, &mut p)? as usize;
159    let instruction_size = read_u32(data, &mut p)? as usize;
160
161    let take = |start: usize, len: usize| -> Option<&[u8]> { data.get(start..start + len) };
162    let s0 = p;
163    let n_contour = take(s0, n_contour_size)?;
164    let s1 = s0 + n_contour_size;
165    let n_points = take(s1, n_points_size)?;
166    let s2 = s1 + n_points_size;
167    let flags_s = take(s2, flag_size)?;
168    let s3 = s2 + flag_size;
169    let glyph_s = take(s3, glyph_size)?;
170    let s4 = s3 + glyph_size;
171    let composite_s = take(s4, composite_size)?;
172    let s5 = s4 + composite_size;
173    let bbox_s = take(s5, bbox_size)?;
174    let s6 = s5 + bbox_size;
175    let instr_s = take(s6, instruction_size)?;
176
177    // bbox ビットマップ(立っているグリフだけ明示的な bbox を持つ)
178    let bitmap_len = num_glyphs.div_ceil(8);
179    let bbox_bitmap = bbox_s.get(..bitmap_len)?;
180    let mut bbox_p = bitmap_len;
181
182    let mut glyf: Vec<u8> = Vec::new();
183    let mut offsets: Vec<u32> = Vec::with_capacity(num_glyphs + 1);
184    offsets.push(0);
185
186    let mut ncp = 0usize; // nContourStream
187    let mut npp = 0usize; // nPointsStream
188    let mut fp = 0usize; // flagStream
189    let mut gp = 0usize; // glyphStream
190    let mut cp = 0usize; // compositeStream
191    let mut ip = 0usize; // instructionStream
192
193    for gid in 0..num_glyphs {
194        let n_contours = read_i16(n_contour, &mut ncp)?;
195
196        if n_contours == 0 {
197            // 空グリフ。データ長 0。
198            offsets.push(glyf.len() as u32);
199            continue;
200        }
201
202        let has_bbox = (bbox_bitmap.get(gid / 8)? >> (7 - (gid % 8))) & 1 == 1;
203
204        if n_contours < 0 {
205            // 複合グリフ: compositeStream から丸ごとコピーする。
206            // 複合グリフのデータは無変換なので、終端を自前で走査する。
207            let start = cp;
208            loop {
209                let flags = read_u16(composite_s, &mut cp)?;
210                let _glyph_index = read_u16(composite_s, &mut cp)?;
211                // ARG_1_AND_2_ARE_WORDS
212                if flags & 0x0001 != 0 {
213                    cp += 4;
214                } else {
215                    cp += 2;
216                }
217                // WE_HAVE_A_SCALE / X_AND_Y_SCALE / TWO_BY_TWO
218                if flags & 0x0008 != 0 {
219                    cp += 2;
220                } else if flags & 0x0040 != 0 {
221                    cp += 4;
222                } else if flags & 0x0080 != 0 {
223                    cp += 8;
224                }
225                if cp > composite_s.len() {
226                    return None;
227                }
228                // MORE_COMPONENTS
229                if flags & 0x0020 == 0 {
230                    break;
231                }
232            }
233            let body = composite_s.get(start..cp)?;
234
235            // 複合グリフは bbox が必須。
236            if !has_bbox {
237                return None;
238            }
239            let bb = bbox_s.get(bbox_p..bbox_p + 8)?;
240            bbox_p += 8;
241
242            glyf.extend_from_slice(&n_contours.to_be_bytes());
243            glyf.extend_from_slice(bb);
244            glyf.extend_from_slice(body);
245            while glyf.len() % 4 != 0 {
246                glyf.push(0);
247            }
248            offsets.push(glyf.len() as u32);
249            continue;
250        }
251
252        // 単純グリフ
253        let n_contours = n_contours as usize;
254        let mut end_pts: Vec<u16> = Vec::with_capacity(n_contours);
255        let mut total_points = 0usize;
256        for _ in 0..n_contours {
257            let np = read_255_u16(n_points, &mut npp)? as usize;
258            total_points += np;
259            if total_points == 0 || total_points > u16::MAX as usize + 1 {
260                return None;
261            }
262            end_pts.push((total_points - 1) as u16);
263        }
264
265        // 座標の復元
266        let mut xs: Vec<i32> = Vec::with_capacity(total_points);
267        let mut ys: Vec<i32> = Vec::with_capacity(total_points);
268        let mut on_curve: Vec<bool> = Vec::with_capacity(total_points);
269        let mut x = 0i32;
270        let mut y = 0i32;
271        for _ in 0..total_points {
272            let flag = *flags_s.get(fp)?;
273            fp += 1;
274            on_curve.push(flag & 0x80 == 0);
275            let (dx, dy) = decode_triplet(flag, glyph_s, &mut gp)?;
276            x += dx;
277            y += dy;
278            xs.push(x);
279            ys.push(y);
280        }
281
282        // 命令列
283        let instr_len = read_255_u16(glyph_s, &mut gp)? as usize;
284        let instr = instr_s.get(ip..ip + instr_len)?;
285        ip += instr_len;
286
287        // bbox(無ければ座標から計算する)
288        let (x_min, y_min, x_max, y_max) = if has_bbox {
289            let bb = bbox_s.get(bbox_p..bbox_p + 8)?;
290            bbox_p += 8;
291            (
292                i16::from_be_bytes([bb[0], bb[1]]),
293                i16::from_be_bytes([bb[2], bb[3]]),
294                i16::from_be_bytes([bb[4], bb[5]]),
295                i16::from_be_bytes([bb[6], bb[7]]),
296            )
297        } else {
298            let xmin = xs.iter().copied().min().unwrap_or(0);
299            let ymin = ys.iter().copied().min().unwrap_or(0);
300            let xmax = xs.iter().copied().max().unwrap_or(0);
301            let ymax = ys.iter().copied().max().unwrap_or(0);
302            (xmin as i16, ymin as i16, xmax as i16, ymax as i16)
303        };
304
305        // 標準の glyf グリフを組み立てる
306        glyf.extend_from_slice(&(n_contours as i16).to_be_bytes());
307        glyf.extend_from_slice(&x_min.to_be_bytes());
308        glyf.extend_from_slice(&y_min.to_be_bytes());
309        glyf.extend_from_slice(&x_max.to_be_bytes());
310        glyf.extend_from_slice(&y_max.to_be_bytes());
311        for e in &end_pts {
312            glyf.extend_from_slice(&e.to_be_bytes());
313        }
314        glyf.extend_from_slice(&(instr_len as u16).to_be_bytes());
315        glyf.extend_from_slice(instr);
316
317        // フラグと座標は圧縮せず素直に書く(repeat を使わない)。
318        // サイズは増えるが `ttf_parser` は問題なく読める(W-1)。
319        for (i, &oc) in on_curve.iter().enumerate() {
320            let _ = i;
321            glyf.push(if oc { 0x01 } else { 0x00 });
322        }
323        let mut prev = 0i32;
324        for &v in &xs {
325            let d = v - prev;
326            prev = v;
327            glyf.extend_from_slice(&(d as i16).to_be_bytes());
328        }
329        let mut prev = 0i32;
330        for &v in &ys {
331            let d = v - prev;
332            prev = v;
333            glyf.extend_from_slice(&(d as i16).to_be_bytes());
334        }
335
336        while glyf.len() % 4 != 0 {
337            glyf.push(0);
338        }
339        offsets.push(glyf.len() as u32);
340    }
341
342    // loca を組み立てる(W-2)
343    let mut loca: Vec<u8> = Vec::new();
344    if index_format == 0 {
345        for &o in &offsets {
346            if o % 2 != 0 || o / 2 > u16::MAX as u32 {
347                // short 形式で表せない → long へ切り替える方が安全だが、
348                // ここでは復元不能として扱う(W-3)。
349                return None;
350            }
351            loca.extend_from_slice(&((o / 2) as u16).to_be_bytes());
352        }
353    } else {
354        for &o in &offsets {
355            loca.extend_from_slice(&o.to_be_bytes());
356        }
357    }
358
359    Some((glyf, loca, index_format))
360}