Skip to main content

text/
cff.rs

1// cff -- CFF and CFF2 ('CFF ' / 'CFF2' tables of OpenType fonts): Type 2 charstrings -> cubic
2// outlines                                                               [src/text/cff.s]
3//
4//   Cff::open(table, upem) -> context / ERR_*   CFF: the header, Top DICT, CharStrings, global
5//        and local subrs, CID-keyed FDArray / FDSelect (formats 0 and 3), FontMatrix
6//   Cff::open2(table, upem) -> context / ERR_*  CFF2: the header and the Top DICT right after
7//        it (no Name / String INDEX), INDEXes with 32-bit counts, CharStrings, global subrs,
8//        FDArray (each font DICT's Private DICT: Subrs and vsindex; blend in its values),
9//        FDSelect (formats 0, 3 and 4; none: font DICT 0), the VariationStore (vstore),
10//        maxstack, FontMatrix
11//   outline(table, glyph, ol) -> false on error   run the charstring, points into the outline
12//        buffer (OLT_ON / OLT_CUBIC, font units)
13//   outline_var(table, glyph, coords, ol)   the same at a CFF2 instance's normalized
14//        coordinates (F2Dot14 per axis, avar applied: var.rs normalize; empty = the default)
15//
16// Type 2 operators: all path operators (r/h/v moveto, lineto, rrcurveto, hh/vv/hv/vh curveto,
17// rcurveline, rlinecurve, flex, hflex, hflex1, flex1), hint operators (stems are counted for
18// hintmask / cntrmask and skipped), callsubr / callgsubr (bias 107 / 1131 / 32768, nesting
19// <= 10), return, endchar (a seac accent is ignored), the arithmetic / storage operators
20// (abs add sub div neg eq drop put get ifelse random mul sqrt dup exch index roll and or
21// not).  Argument stack 48, transient array 32, at most 200000 operators per glyph.  The
22// width argument is dropped (advances come from hmtx).  Like the assembly, the transient
23// array and the random generator are process-wide.
24//
25// CFF2 charstrings: no width argument, no endchar (the charstring ends with its data, a subr
26// too; return and endchar are still accepted, as are the arithmetic operators, like FreeType),
27// vsindex (before the first blend; else the Private DICT's, default 0) and blend: n values,
28// n x k deltas, n -> the n values default + sum(delta x scalar), k = the regions of that item
29// variation data, their scalars at coords (var.rs ivs_scalars: HarfBuzz's region rules, f32).
30// Argument stack maxstack (default 193, here 193 ..= 513), at most 512 regions per item
31// variation data, a vsindex after a blend is an error.  Advances come from hmtx + HVAR
32// (ttf.rs); hints are skipped (CFF2 outlines are not hinted, like CFF ones).
33
34use alloc::vec;
35use alloc::vec::Vec;
36
37use rt::fx::{cvtt, cvttd, sqrt};
38
39use crate::outline::{Outline, OLT_CUBIC, OLT_ON};
40use crate::ttf::{Tb, ERR_HDR, ERR_UNSUPPORTED};
41use crate::var;
42
43const FD_MAX: u32 = 256;
44const CS_STACK: usize = 48;
45const CS2_STACK: usize = 513; // CFF2: the largest maxstack
46const CS2_DEFSTACK: i64 = 193; // CFF2: maxstack without the operator
47const CS2_REGIONS: usize = 512; // regions of one item variation data a blend may use
48const DICT2_STACK: usize = 513; // CFF2 DICT operands
49const CS_DEPTH: u32 = 10;
50const CS_BUDGET: i32 = 200_000;
51const OPS_MAX: usize = 48; // DICT operands kept
52const F_INV65536: f32 = 0.000_015_258_789_062_5;
53const F_INV8M: f32 = 0.000_000_119_209_29; // 1 / 2^23
54const F_EPS: f32 = 0.000_001;
55
56/// a CFF INDEX: count, offSize, offsets position, data position (table offsets)
57#[derive(Clone, Copy, Default)]
58struct Index {
59    count: u32,
60    offsize: u32,
61    offs: u32,
62    data: u32,
63}
64
65pub struct Cff {
66    /// a CFF2 table (open2)
67    v2: bool,
68    cid: bool, // font DICTs: CID-keyed CFF, every CFF2
69    cs: Index,
70    gs: Index,
71    ls: Index,
72    fdsel: u32, // FDSelect offset (0: none, CFF2 only: font DICT 0)
73    nfd: u32,
74    mat: [f32; 6],
75    hasmat: bool,
76    fdls: Vec<Index>,
77    /// CFF2: the vsindex of each font DICT's Private DICT
78    fdvs: Vec<u16>,
79    /// CFF2: the item variation store (table offset, past vstore's length; 0 = none)
80    vstore: u32,
81    /// CFF2: the argument stack's size
82    maxstack: u32,
83}
84
85// ------------------------------------------------------------------ byte readers
86
87fn c8(t: Tb, off: u32) -> i32 {
88    t.u8(off)
89}
90
91fn c16(t: Tb, off: u32) -> i32 {
92    t.u16(off)
93}
94
95// c_off(off, size 1..4) -> big-endian unsigned value, -1 when out of bounds
96fn c_off(t: Tb, off: u32, size: u32) -> i64 {
97    if off as u64 + size as u64 > t.0.len() as u64 {
98        return -1;
99    }
100    let mut v = 0u32;
101    for i in 0..size {
102        v = (v << 8) | t.0[(off + i) as usize] as u32;
103    }
104    v as i64
105}
106
107// ------------------------------------------------------------------ INDEX
108
109// ix_parse(pos, index) -> position after the INDEX, or -1 (malformed; count = 0)
110fn ix_parse(t: Tb, pos: u32, ix: &mut Index) -> i64 {
111    let count = c16(t, pos);
112    if count < 0 {
113        ix.count = 0;
114        return -1;
115    }
116    ix.count = count as u32;
117    if count == 0 {
118        ix.offsize = 1;
119        ix.offs = pos.wrapping_add(2);
120        ix.data = ix.offs;
121        return ix.offs as i64;
122    }
123    let os = c8(t, pos.wrapping_add(2));
124    if !(1..=4).contains(&os) {
125        ix.count = 0;
126        return -1;
127    }
128    let os = os as u32;
129    ix.offsize = os;
130    ix.offs = pos.wrapping_add(3);
131    ix.data = ix.offs.wrapping_add((count as u32 + 1).wrapping_mul(os));
132    // last offset: the end of the data
133    let last = c_off(t, (count as u32).wrapping_mul(os).wrapping_add(ix.offs), os);
134    if last <= 0 {
135        ix.count = 0;
136        return -1;
137    }
138    let end = ix.data as u64 + last as u64 - 1;
139    if end > t.0.len() as u64 {
140        ix.count = 0;
141        return -1;
142    }
143    end as u32 as i64
144}
145
146// ix_get(index, i) -> (start, length) or None
147fn ix_get(t: Tb, ix: &Index, i: u32) -> Option<(u32, u32)> {
148    if i >= ix.count {
149        return None;
150    }
151    let a = c_off(t, i.wrapping_mul(ix.offsize).wrapping_add(ix.offs), ix.offsize);
152    if a <= 0 {
153        return None;
154    }
155    let b = c_off(t, (i + 1).wrapping_mul(ix.offsize).wrapping_add(ix.offs), ix.offsize);
156    if b <= 0 || b < a {
157        return None;
158    }
159    let len = (b - a) as u64;
160    let start = ix.data as u64 + a as u64 - 1;
161    if start + len > t.0.len() as u64 {
162        return None;
163    }
164    if start as u32 as i32 >= 0 { Some((start as u32, len as u32)) } else { None }
165}
166
167// ix2_parse(pos, index) -> position after a CFF2 INDEX {u32 count, offSize, offsets[count + 1],
168// data} (an empty INDEX is its count alone), or -1 (malformed; count = 0)
169fn ix2_parse(t: Tb, pos: u32, ix: &mut Index) -> i64 {
170    ix.count = 0;
171    let count = c_off(t, pos, 4);
172    if count < 0 {
173        return -1;
174    }
175    let base = pos as u64 + 4; // <= the table's length
176    if count == 0 {
177        ix.offsize = 1;
178        ix.offs = base as u32;
179        ix.data = base as u32;
180        return base as i64;
181    }
182    let os = c8(t, base as u32);
183    if !(1..=4).contains(&os) {
184        return -1;
185    }
186    let os = os as u64;
187    let offs = base + 1;
188    let data = offs + (count as u64 + 1) * os;
189    if data > t.len() as u64 {
190        return -1;
191    }
192    let last = c_off(t, (offs + count as u64 * os) as u32, os as u32);
193    if last <= 0 {
194        return -1;
195    }
196    let end = data + last as u64 - 1;
197    if end > t.len() as u64 {
198        return -1;
199    }
200    ix.count = count as u32;
201    ix.offsize = os as u32;
202    ix.offs = offs as u32;
203    ix.data = data as u32;
204    end as i64
205}
206
207// ------------------------------------------------------------------ CFF2 DICT
208
209// walk a CFF2 DICT: f(operator, operands) for every operator (0..=27 but 12 / 23, or 1200 +
210// the escaped byte); blend (23) leaves the n default values in place of its operands (the
211// values that vary are hint values, which are not used) with k = regions(vsindex) deltas per
212// value, vsindex (22) as the DICT's own sets it -> false: malformed (the walk stopped)
213fn dict2_walk(t: Tb, start: u32, end: u32, regions: &dyn Fn(u32) -> Option<u32>, f: &mut dyn FnMut(u32, &[f64])) -> bool {
214    let mut st: Vec<f64> = Vec::new();
215    let mut vsi = 0u32;
216    let mut p = start;
217    while p < end {
218        let b = c8(t, p);
219        if b < 0 {
220            return false;
221        }
222        let v: f64;
223        if b <= 27 {
224            p += 1;
225            let mut o = b as u32;
226            if o == 12 {
227                let b2 = c8(t, p);
228                if b2 < 0 {
229                    return false;
230                }
231                p += 1;
232                o = 1200 + b2 as u32;
233            }
234            if o == 23 {
235                // blend: n defaults, n x k deltas, n -> the n defaults
236                let Some(nv) = st.pop() else { return false };
237                let n = cvttd(nv);
238                let Some(k) = regions(vsi) else { return false };
239                if n < 0 {
240                    return false;
241                }
242                let need = n as u64 * (k as u64 + 1);
243                if need > st.len() as u64 {
244                    return false;
245                }
246                let keep = st.len() - need as usize + n as usize;
247                st.truncate(keep);
248                continue;
249            }
250            if o == 22 {
251                match st.last().map(|&x| cvttd(x)) {
252                    Some(i) if i >= 0 => vsi = i as u32,
253                    _ => {}
254                }
255            }
256            f(o, &st);
257            st.clear();
258            continue;
259        } else if b == 28 {
260            let w = c16(t, p.wrapping_add(1));
261            if w < 0 {
262                return false;
263            }
264            v = (w as u16 as i16) as f64;
265            p += 3;
266        } else if b == 29 {
267            let w = c_off(t, p.wrapping_add(1), 4);
268            if w < 0 {
269                return false;
270            }
271            v = (w as u32 as i32) as f64;
272            p += 5;
273        } else if b == 30 {
274            p += 1;
275            v = dict_real(t, &mut p);
276        } else if b == 31 || b == 255 {
277            p += 1; // reserved
278            st.clear();
279            continue;
280        } else if b <= 246 {
281            v = (b - 139) as f64;
282            p += 1;
283        } else {
284            let b2 = c8(t, p.wrapping_add(1));
285            if b2 < 0 {
286                return false;
287            }
288            p += 2;
289            v = if b <= 250 { ((b - 247) * 256 + b2 + 108) as f64 } else { (-((b - 251) * 256 + b2 + 108)) as f64 };
290        }
291        if st.len() >= DICT2_STACK {
292            return false;
293        }
294        st.push(v);
295    }
296    true
297}
298
299// dict2_find(start, end, op) -> the operand count of op's first occurrence (its last OPS_MAX
300// operands in ops), -1 = absent
301fn dict2_find(t: Tb, start: u32, end: u32, op: u32, regions: &dyn Fn(u32) -> Option<u32>, ops: &mut [f64; OPS_MAX]) -> i32 {
302    let mut found = -1i32;
303    dict2_walk(t, start, end, regions, &mut |o, a| {
304        if found < 0 && o == op {
305            let a = &a[a.len().saturating_sub(OPS_MAX)..];
306            ops[..a.len()].copy_from_slice(a);
307            found = a.len() as i32;
308        }
309    });
310    found
311}
312
313// CFF2: the local subrs (into ix) and the vsindex of the Private DICT that the font DICT
314// [start, end) names; blend in its values with the regions of the store at vstore
315fn cf2_private(t: Tb, start: u32, end: u32, vstore: u32, ix: &mut Index, ops: &mut [f64; OPS_MAX]) -> u16 {
316    ix.count = 0;
317    let n = dict2_find(t, start, end, 18, &|_| None, ops);
318    if n < 2 {
319        return 0;
320    }
321    let size = cvttd(ops[n as usize - 2]);
322    let off = cvttd(ops[n as usize - 1]);
323    if size <= 0 || off <= 0 || off as u32 >= t.len() {
324        return 0;
325    }
326    let off = off as u32;
327    let e = off.saturating_add(size as u32).min(t.len());
328    let regions = |vsi: u32| if vstore == 0 { None } else { var::ivs_region_count(t, vstore, vsi) };
329    let (mut subrs, mut vsi) = (None, 0u16);
330    dict2_walk(t, off, e, &regions, &mut |o, a| {
331        let last = a.last().map(|&v| cvttd(v));
332        match (o, last) {
333            (19, Some(s)) if subrs.is_none() => subrs = Some(s),
334            (22, Some(v)) if (0..=0xFFFF).contains(&v) => vsi = v as u16,
335            _ => {}
336        }
337    });
338    if let Some(s) = subrs
339        && s > 0
340        && let Some(pos) = (s as u32).checked_add(off)
341    {
342        ix2_parse(t, pos, ix); // Subrs: relative to the Private DICT
343    }
344    vsi
345}
346
347// ------------------------------------------------------------------ DICT
348
349// dict_find(start, end, op) -> operand count (operands in ops as f64), -1 = absent
350// op: 0..21, or 1200 + second byte for escaped operators
351fn dict_find(t: Tb, start: u32, end: u32, op: u32, ops: &mut [f64; OPS_MAX]) -> i32 {
352    let mut p = start;
353    let mut n = 0usize;
354    while p < end {
355        let b = c8(t, p);
356        if b < 0 {
357            return -1;
358        }
359        let v: f64;
360        if b <= 21 {
361            p += 1;
362            let mut o = b as u32;
363            if o == 12 {
364                let b2 = c8(t, p);
365                if b2 < 0 {
366                    return -1;
367                }
368                p += 1;
369                o = 1200 + b2 as u32;
370            }
371            if o == op {
372                return n as i32;
373            }
374            n = 0;
375            continue;
376        } else if b == 28 {
377            let w = c16(t, p.wrapping_add(1));
378            if w < 0 {
379                return -1;
380            }
381            v = (w as u16 as i16) as f64;
382            p = p.wrapping_add(3);
383        } else if b == 29 {
384            let w = c_off(t, p.wrapping_add(1), 4);
385            if w < 0 {
386                return -1;
387            }
388            v = (w as u32 as i32) as f64;
389            p = p.wrapping_add(5);
390        } else if b == 30 {
391            p += 1;
392            v = dict_real(t, &mut p);
393        } else if b < 32 {
394            p += 1;
395            n = 0;
396            continue;
397        } else if b <= 246 {
398            v = (b - 139) as f64;
399            p += 1;
400        } else if b <= 254 {
401            let b2 = c8(t, p.wrapping_add(1));
402            if b2 < 0 {
403                return -1;
404            }
405            p = p.wrapping_add(2);
406            v = if b <= 250 {
407                ((b - 247) * 256 + b2 + 108) as f64
408            } else {
409                (-((b - 251) * 256 + b2 + 108)) as f64
410            };
411        } else {
412            p += 1;
413            n = 0;
414            continue;
415        }
416        if n < OPS_MAX {
417            ops[n] = v;
418            n += 1;
419        }
420    }
421    -1
422}
423
424// a nibble-coded real at p (advanced past it)  [dict_real]
425fn dict_real(t: Tb, p: &mut u32) -> f64 {
426    let mut m = 0u64; // mantissa
427    let mut digits = 0i32; // digits after the point
428    let mut fl = 0u32; // 1 negative, 2 point seen, 4 exponent, 8 exponent negative
429    let mut exp = 0i32;
430    'bytes: while (*p as usize) < t.0.len() {
431        let b = t.0[*p as usize] as u32;
432        *p += 1;
433        for nib in [b >> 4, b & 15] {
434            if nib <= 9 {
435                if fl & 4 != 0 {
436                    if (exp as u32) < 1000 {
437                        exp = exp * 10 + nib as i32;
438                    }
439                } else if m >= 100_000_000_000_000_000 {
440                    if fl & 2 == 0 {
441                        digits -= 1; // integer digit dropped: scale up
442                    }
443                } else {
444                    m = m * 10 + nib as u64;
445                    if fl & 2 != 0 {
446                        digits += 1;
447                    }
448                }
449                continue;
450            }
451            match nib {
452                0xA => fl |= 2,
453                0xB => fl |= 4,
454                0xC => fl |= 4 | 8,
455                0xE => fl |= 1,
456                0xF => break 'bytes,
457                _ => {}
458            }
459        }
460    }
461    // value = mantissa * 10^(exp - digits)
462    let mut v = m as i64 as f64;
463    if fl & 8 != 0 {
464        exp = exp.wrapping_neg();
465    }
466    let e = exp.wrapping_sub(digits);
467    if e > 0 {
468        if e as u32 > 400 {
469            return 0.0;
470        }
471        for _ in 0..e {
472            v = v * 10.0;
473        }
474    } else if e < 0 {
475        let mut c = e.wrapping_neg() as u32;
476        loop {
477            v = v / 10.0;
478            c -= 1;
479            if c == 0 {
480                break;
481            }
482            if c > 400 {
483                return 0.0;
484            }
485        }
486    }
487    if fl & 1 != 0 {
488        v = 0.0 - v;
489    }
490    v
491}
492
493// the local subrs of the Private DICT named by the dict's Private operator  [cf_private]
494fn cf_private(t: Tb, start: u32, end: u32, ix: &mut Index, ops: &mut [f64; OPS_MAX]) {
495    ix.count = 0;
496    let n = dict_find(t, start, end, 18, ops);
497    if n < 2 {
498        return;
499    }
500    let size = rt::fx::cvttd(ops[n as usize - 2]);
501    let off = rt::fx::cvttd(ops[n as usize - 1]);
502    if size <= 0 || off <= 0 || off as u32 >= t.len() {
503        return;
504    }
505    let off = off as u32;
506    let e = off.wrapping_add(size as u32).min(t.len());
507    let n = dict_find(t, off, e, 19, ops); // Subrs (relative to the Private DICT)
508    if n < 1 {
509        return;
510    }
511    let s = rt::fx::cvttd(ops[n as usize - 1]);
512    if s <= 0 {
513        return;
514    }
515    let (pos, carry) = (s as u32).overflowing_add(off);
516    if carry {
517        return;
518    }
519    ix_parse(t, pos, ix);
520}
521
522impl Cff {
523    fn empty(v2: bool) -> Cff {
524        Cff {
525            v2,
526            cid: false,
527            cs: Index::default(),
528            gs: Index::default(),
529            ls: Index::default(),
530            fdsel: 0,
531            nfd: 0,
532            mat: [0.0; 6],
533            hasmat: false,
534            fdls: Vec::new(),
535            fdvs: Vec::new(),
536            vstore: 0,
537            maxstack: CS2_DEFSTACK as u32,
538        }
539    }
540
541    /// a CFF2 table (its outline() takes normalized coordinates)
542    pub fn is_v2(&self) -> bool {
543        self.v2
544    }
545
546    /// a CFF2 table with a variation store: its outlines vary
547    pub fn varies(&self) -> bool {
548        self.v2 && self.vstore != 0
549    }
550
551    // FontMatrix operands (a b c d e f) for upem: kept unless the identity (within 1e-6)
552    fn set_matrix(&mut self, m: &[f64], upem: u32) {
553        let u = upem as i32 as f64;
554        for (i, &v) in m.iter().take(6).enumerate() {
555            self.mat[i] = (v * u) as f32;
556        }
557        let abs = |v: f32| f32::from_bits(v.to_bits() & 0x7FFF_FFFF);
558        if abs(self.mat[0] - 1.0) > F_EPS || abs(self.mat[3] - 1.0) > F_EPS {
559            self.hasmat = true;
560        } else {
561            for i in [1, 2, 4, 5] {
562                if abs(self.mat[i]) > F_EPS {
563                    self.hasmat = true;
564                    break;
565                }
566            }
567        }
568    }
569
570    /// cff_open: ERR_HDR (malformed) / ERR_UNSUPPORTED (not CFF 1 / not Type 2 charstrings)
571    pub fn open(t: Tb, upem: u32) -> Result<Cff, i32> {
572        let mut ops = [0f64; OPS_MAX];
573        let mut c = Cff::empty(false);
574        if c8(t, 0) != 1 {
575            return Err(ERR_UNSUPPORTED);
576        }
577        let hs = c8(t, 2);
578        if hs < 0 {
579            return Err(ERR_HDR);
580        }
581        // Name INDEX, Top DICT INDEX, String INDEX, Global Subr INDEX
582        let mut top = Index::default();
583        let mut strs = Index::default();
584        let p = ix_parse(t, hs as u32, &mut top); // name (parsed into the top slot, reused)
585        if p < 0 {
586            return Err(ERR_HDR);
587        }
588        let p = ix_parse(t, p as u32, &mut top);
589        if p < 0 {
590            return Err(ERR_HDR);
591        }
592        let p = ix_parse(t, p as u32, &mut strs);
593        if p < 0 {
594            return Err(ERR_HDR);
595        }
596        if ix_parse(t, p as u32, &mut c.gs) < 0 {
597            return Err(ERR_HDR);
598        }
599        let Some((ds, dl)) = ix_get(t, &top, 0) else { return Err(ERR_HDR) };
600        let de = ds.wrapping_add(dl);
601        // CharstringType must be 2
602        let n = dict_find(t, ds, de, 1206, &mut ops);
603        if n >= 1 && rt::fx::cvttd(ops[0]) != 2 {
604            return Err(ERR_UNSUPPORTED);
605        }
606        // CharStrings
607        let n = dict_find(t, ds, de, 17, &mut ops);
608        if n < 1 {
609            return Err(ERR_HDR);
610        }
611        let o = rt::fx::cvttd(ops[n as usize - 1]);
612        if o <= 0 || ix_parse(t, o as u32, &mut c.cs) < 0 || c.cs.count == 0 {
613            return Err(ERR_HDR);
614        }
615        // FontMatrix (default 0.001 0 0 0.001 0 0 for upem 1000)
616        let n = dict_find(t, ds, de, 1207, &mut ops);
617        if n >= 6 {
618            let m = ops;
619            c.set_matrix(&m[..6], upem);
620        }
621        // CID-keyed?
622        if dict_find(t, ds, de, 1230, &mut ops) >= 0 {
623            c.cid = true;
624            let n = dict_find(t, ds, de, 1236, &mut ops); // FDArray
625            if n < 1 {
626                return Err(ERR_HDR);
627            }
628            let o = rt::fx::cvttd(ops[n as usize - 1]);
629            if o <= 0 {
630                return Err(ERR_HDR);
631            }
632            let mut fda = Index::default();
633            if ix_parse(t, o as u32, &mut fda) < 0 {
634                return Err(ERR_HDR);
635            }
636            c.nfd = fda.count.min(FD_MAX);
637            let n = dict_find(t, ds, de, 1237, &mut ops); // FDSelect
638            if n < 1 {
639                return Err(ERR_HDR);
640            }
641            let o = rt::fx::cvttd(ops[n as usize - 1]);
642            if o <= 0 || o as u32 >= t.len() {
643                return Err(ERR_HDR);
644            }
645            c.fdsel = o as u32;
646            // every font DICT: its Private DICT and local subrs
647            c.fdls = vec![Index::default(); c.nfd as usize];
648            for fd in 0..c.nfd {
649                if let Some((s, l)) = ix_get(t, &fda, fd) {
650                    cf_private(t, s, s.wrapping_add(l), &mut c.fdls[fd as usize], &mut ops);
651                }
652            }
653        } else {
654            cf_private(t, ds, de, &mut c.ls, &mut ops);
655        }
656        Ok(c)
657    }
658
659    /// cff2_open: ERR_HDR (malformed) / ERR_UNSUPPORTED (not CFF2)
660    pub fn open2(t: Tb, upem: u32) -> Result<Cff, i32> {
661        let mut ops = [0f64; OPS_MAX];
662        let mut c = Cff::empty(true);
663        if c8(t, 0) != 2 {
664            return Err(ERR_UNSUPPORTED);
665        }
666        // header {u8 major 2, minor, headerSize, u16 topDictLength}, the Top DICT after it
667        let hs = c8(t, 2);
668        let tdl = c16(t, 3);
669        if hs < 5 || tdl < 0 {
670            return Err(ERR_HDR);
671        }
672        let ds = hs as u32;
673        let de = ds + tdl as u32;
674        if de > t.len() {
675            return Err(ERR_HDR);
676        }
677        // CharStrings, FDArray, FDSelect, vstore, maxstack, FontMatrix (the first of each)
678        let (mut cso, mut fda, mut fds, mut vst, mut maxst) = (None, None, None, None, None);
679        let mut mat: Option<[f64; 6]> = None;
680        dict2_walk(t, ds, de, &|_| None, &mut |op, a| {
681            let last = a.last().map(|&v| cvttd(v) as i64);
682            let slot = match op {
683                17 => &mut cso,
684                1236 => &mut fda,
685                1237 => &mut fds,
686                24 => &mut vst,
687                25 => &mut maxst,
688                1207 => {
689                    if mat.is_none() && a.len() >= 6 {
690                        mat = Some([a[0], a[1], a[2], a[3], a[4], a[5]]);
691                    }
692                    return;
693                }
694                _ => return,
695            };
696            if slot.is_none() {
697                *slot = last;
698            }
699        });
700        let len = t.len() as i64;
701        let pos = |o: Option<i64>| o.filter(|&o| o > 0 && o < len).map(|o| o as u32);
702        // global subrs right after the Top DICT; CharStrings
703        if ix2_parse(t, de, &mut c.gs) < 0 {
704            return Err(ERR_HDR);
705        }
706        match pos(cso) {
707            Some(o) if ix2_parse(t, o, &mut c.cs) >= 0 && c.cs.count != 0 => {}
708            _ => return Err(ERR_HDR),
709        }
710        if let Some(m) = mat {
711            c.set_matrix(&m, upem);
712        }
713        c.maxstack = maxst.unwrap_or(CS2_DEFSTACK).clamp(CS2_DEFSTACK, CS2_STACK as i64) as u32;
714        // VariationStore {u16 length, item variation store}
715        if let Some(o) = pos(vst)
716            && o as u64 + 2 < t.len() as u64
717        {
718            c.vstore = o + 2;
719        }
720        // FDArray (every CFF2 font has one), FDSelect (none: every glyph uses font DICT 0)
721        let mut fdix = Index::default();
722        match pos(fda) {
723            Some(o) if ix2_parse(t, o, &mut fdix) >= 0 && fdix.count != 0 => {}
724            _ => return Err(ERR_HDR),
725        }
726        c.cid = true;
727        c.nfd = fdix.count.min(FD_MAX);
728        c.fdsel = pos(fds).unwrap_or(0);
729        c.fdls = vec![Index::default(); c.nfd as usize];
730        c.fdvs = vec![0; c.nfd as usize];
731        for fd in 0..c.nfd as usize {
732            if let Some((s, l)) = ix_get(t, &fdix, fd as u32) {
733                c.fdvs[fd] = cf2_private(t, s, s.wrapping_add(l), c.vstore, &mut c.fdls[fd], &mut ops);
734            }
735        }
736        Ok(c)
737    }
738
739    // font DICT index of glyph (-1 on error)  [cf_fdselect]
740    fn fdselect(&self, t: Tb, glyph: u32) -> i32 {
741        let fs = self.fdsel;
742        if fs == 0 {
743            return 0; // CFF2 without FDSelect (a CID-keyed CFF always has one)
744        }
745        match c8(t, fs) {
746            0 => c8(t, fs.wrapping_add(glyph).wrapping_add(1)),
747            3 => {
748                let n = c16(t, fs.wrapping_add(1));
749                if n <= 0 {
750                    return -1;
751                }
752                // ranges {u16 first, u8 fd}, sentinel u16
753                // binary search: the last range whose first glyph is <= glyph
754                let (mut lo, mut hi) = (0u32, n as u32);
755                while lo < hi {
756                    let mid = lo + (hi - lo) / 2;
757                    let first = c16(t, mid.wrapping_mul(3).wrapping_add(fs).wrapping_add(3));
758                    if first < 0 {
759                        return -1;
760                    }
761                    if first as u32 > glyph {
762                        hi = mid;
763                    } else {
764                        lo = mid + 1;
765                    }
766                }
767                if lo == 0 {
768                    return -1;
769                }
770                c8(t, (lo - 1).wrapping_mul(3).wrapping_add(fs).wrapping_add(5))
771            }
772            4 if self.v2 => {
773                // CFF2: {u32 nRanges, {u32 first, u16 fd}[nRanges], u32 sentinel}
774                let n = c_off(t, fs.wrapping_add(1), 4);
775                if n <= 0 {
776                    return -1;
777                }
778                let first = |k: u64| -> i64 {
779                    let at = fs as u64 + 5 + k * 6;
780                    if at + 6 > t.len() as u64 { -1 } else { c_off(t, at as u32, 4) }
781                };
782                let (mut lo, mut hi) = (0u64, n as u64);
783                while lo < hi {
784                    let mid = lo + (hi - lo) / 2;
785                    let f = first(mid);
786                    if f < 0 {
787                        return -1;
788                    }
789                    if f as u64 > glyph as u64 {
790                        hi = mid;
791                    } else {
792                        lo = mid + 1;
793                    }
794                }
795                if lo == 0 {
796                    return -1;
797                }
798                c16(t, (fs as u64 + 5 + (lo - 1) * 6 + 4) as u32)
799            }
800            _ => -1,
801        }
802    }
803
804    /// run the charstring of glyph into the outline buffer: false = malformed  [cff_outline]
805    pub fn outline(&self, t: Tb, glyph: u32, ol: &mut Outline) -> bool {
806        self.outline_var(t, glyph, &[], ol)
807    }
808
809    /// outline at a CFF2 instance's normalized coordinates (empty: the default instance; CFF
810    /// ignores them)
811    pub fn outline_var(&self, t: Tb, glyph: u32, coords: &[i16], ol: &mut Outline) -> bool {
812        if glyph >= self.cs.count {
813            return true;
814        }
815        let (ls, vsi) = if self.cid {
816            let fd = self.fdselect(t, glyph);
817            if fd as u32 >= self.nfd {
818                return false;
819            }
820            (self.fdls[fd as usize], self.fdvs.get(fd as usize).copied().unwrap_or(0))
821        } else {
822            (self.ls, 0)
823        };
824        let Some((s, l)) = ix_get(t, &self.cs, glyph) else { return false };
825        let mut cs = Cs {
826            t,
827            gs: self.gs,
828            ls,
829            st: [0.0; CS2_STACK],
830            sp: 0,
831            maxst: if self.v2 { self.maxstack as usize } else { CS_STACK },
832            x: 0.0,
833            y: 0.0,
834            px: 0.0,
835            py: 0.0,
836            d: [0.0; 6],
837            nstems: 0,
838            wd: self.v2, // CFF2 charstrings carry no width
839            open: false,
840            budget: CS_BUDGET,
841            trans: [0.0; 32],
842            rand: 0,
843            v2: self.v2,
844            vstore: self.vstore,
845            vsi: vsi as u32,
846            coords,
847            sc: Vec::new(),
848            nreg: None,
849            blended: false,
850            ol,
851        };
852        if cs.run(s, s.wrapping_add(l), 0) < 0 {
853            return false;
854        }
855        let ol = cs.ol;
856        ol.close();
857        if self.hasmat {
858            // FontMatrix: (x, y) -> (a x + c y + e, b x + d y + f)
859            let m = self.mat;
860            ol.transform(0, m[0], m[1], m[2], m[3]);
861            ol.translate(0, m[4], m[5]);
862        }
863        true
864    }
865}
866
867// subr bias for an INDEX with `count` entries
868fn cs_bias(count: u32) -> i32 {
869    if count < 1240 {
870        107
871    } else if count < 33900 {
872        1131
873    } else {
874        32768
875    }
876}
877
878fn fabs(v: f32) -> f32 {
879    f32::from_bits(v.to_bits() & 0x7FFF_FFFF)
880}
881
882// ucomiss + je: equal or unordered
883fn zf(a: f32, b: f32) -> bool {
884    !(a < b || a > b)
885}
886
887// the Type 2 interpreter state (g_cs_*); CFF2: the store, the item variation data (vsindex),
888// the coordinates, the scalars of its regions (computed at the first blend), blend seen
889struct Cs<'a, 'o> {
890    t: Tb<'a>,
891    gs: Index,
892    ls: Index,
893    st: [f32; CS2_STACK],
894    sp: usize,
895    maxst: usize, // the argument stack's size (CFF: 48)
896    x: f32,
897    y: f32,
898    px: f32,
899    py: f32,
900    d: [f32; 6],
901    nstems: u32,
902    wd: bool,
903    open: bool,
904    budget: i32,
905    trans: [f32; 32], // transient array and random state live per glyph
906    rand: u32,
907    v2: bool,
908    vstore: u32,
909    vsi: u32,
910    coords: &'a [i16],
911    sc: Vec<f32>,
912    nreg: Option<usize>,
913    blended: bool,
914    ol: &'o mut Outline,
915}
916
917enum Flow {
918    Clear,
919    Loop,
920    Err,
921    Ret1,
922}
923
924impl Cs<'_, '_> {
925    // stack entry i (0.0 when outside the stack)
926    fn arg(&self, i: usize) -> f32 {
927        if i < self.sp { self.st[i] } else { 0.0 }
928    }
929
930    // append (x, y) with tag; opens a contour when needed  [cs_point]
931    fn point(&mut self, tag: u8) {
932        if !self.open {
933            // a drawing operator without moveto: start at the current point
934            self.open = true;
935            self.ol.point(self.px, self.py, OLT_ON);
936        }
937        self.ol.point(self.x, self.y, tag);
938    }
939
940    fn line(&mut self, dx: f32, dy: f32) {
941        self.px = self.x;
942        self.py = self.y;
943        self.x = dx + self.x;
944        self.y = dy + self.y;
945        self.point(OLT_ON);
946    }
947
948    // curve by the 6 deltas in d (dxa dya dxb dyb dxc dyc)
949    fn curve(&mut self) {
950        self.px = self.x;
951        self.py = self.y;
952        for i in 0..3 {
953            self.x = self.d[i * 2] + self.x;
954            self.y = self.d[i * 2 + 1] + self.y;
955            self.point(if i == 2 { OLT_ON } else { OLT_CUBIC });
956        }
957    }
958
959    fn d6(&mut self, i: usize) {
960        for k in 0..6 {
961            self.d[k] = self.arg(i + k);
962        }
963    }
964
965    fn curve6(&mut self, i: usize) {
966        self.d6(i);
967        self.curve();
968    }
969
970    // 1 when the first stack-clearing operator carries an extra (width) argument
971    fn width(&mut self, expected: usize) -> usize {
972        if self.wd {
973            return 0;
974        }
975        self.wd = true;
976        (self.sp > expected) as usize
977    }
978
979    // 1 when the argument count is odd (stems: pairs + width)
980    fn width_even(&mut self) -> u32 {
981        if self.wd {
982            return 0;
983        }
984        self.wd = true;
985        (self.sp & 1) as u32
986    }
987
988    fn moveto_begin(&mut self) {
989        if self.open {
990            self.ol.close();
991            self.open = false;
992        }
993    }
994
995    fn moveto_end(&mut self) {
996        self.open = true;
997        self.ol.point(self.x, self.y, OLT_ON);
998    }
999
1000    fn push(&mut self, v: f32) -> Flow {
1001        if self.sp >= self.maxst {
1002            return Flow::Err;
1003        }
1004        self.st[self.sp] = v;
1005        self.sp += 1;
1006        Flow::Loop
1007    }
1008
1009    fn top(&self) -> Option<f32> {
1010        if self.sp == 0 { None } else { Some(self.st[self.sp - 1]) }
1011    }
1012
1013    fn settop(&mut self, v: f32) -> Flow {
1014        self.st[self.sp - 1] = v;
1015        Flow::Loop
1016    }
1017
1018    // (second, top), both removed
1019    fn pop2(&mut self) -> Option<(f32, f32)> {
1020        if self.sp < 2 {
1021            return None;
1022        }
1023        self.sp -= 2;
1024        Some((self.st[self.sp], self.st[self.sp + 1]))
1025    }
1026
1027    // rotate the top num stack entries by j (positive: towards the top)  [cs_roll]
1028    fn roll(&mut self, num: i32, j: i32) {
1029        let mut r = j % num;
1030        if r < 0 {
1031            r += num;
1032        }
1033        let first = self.sp - num as usize;
1034        for _ in 0..r {
1035            let top = self.st[self.sp - 1];
1036            let mut k = self.sp - 1;
1037            while k > first {
1038                self.st[k] = self.st[k - 1];
1039                k -= 1;
1040            }
1041            self.st[first] = top;
1042        }
1043    }
1044
1045    // CFF2 blend: n defaults, n x k deltas, n -> the n values at the instance (k: the regions of
1046    // item variation data vsi, their scalars at coords, computed once per glyph and vsindex);
1047    // false: malformed (no store, too few operands)
1048    fn blend(&mut self) -> bool {
1049        let Some(nv) = self.top() else { return false };
1050        self.sp -= 1;
1051        let n = cvtt(nv);
1052        if n < 0 {
1053            return false;
1054        }
1055        let k = match self.nreg {
1056            Some(k) => k,
1057            None => {
1058                if var::ivs_scalars(self.t, self.vstore, self.vsi, self.coords, CS2_REGIONS, &mut self.sc).is_none() {
1059                    return false;
1060                }
1061                self.nreg = Some(self.sc.len());
1062                self.sc.len()
1063            }
1064        };
1065        let n = n as usize;
1066        let Some(need) = n.checked_mul(k + 1) else { return false };
1067        if need > self.sp {
1068            return false;
1069        }
1070        let base = self.sp - need;
1071        for i in 0..n {
1072            let mut v = self.st[base + i];
1073            let d = base + n + i * k;
1074            for (r, &s) in self.sc[..k].iter().enumerate() {
1075                if s != 0.0 {
1076                    v = v + self.st[d + r] * s;
1077                }
1078            }
1079            self.st[base + i] = v;
1080        }
1081        self.sp = base + n;
1082        self.blended = true;
1083        true
1084    }
1085
1086    /// cs_run(start, end, depth) -> 0 (return / end of data), 1 (endchar), -1 error
1087    fn run(&mut self, start: u32, end: u32, depth: u32) -> i32 {
1088        if depth > CS_DEPTH {
1089            return -1;
1090        }
1091        let t = self.t;
1092        let mut p = start;
1093        loop {
1094            if p >= end {
1095                return 0;
1096            }
1097            self.budget -= 1;
1098            if self.budget < 0 {
1099                return -1;
1100            }
1101            let b = t.0[p as usize] as u32;
1102            p += 1;
1103            let flow = if b >= 32 || b == 28 {
1104                // ---- numbers
1105                let v: f32;
1106                if b == 28 {
1107                    if p + 2 > end {
1108                        return -1;
1109                    }
1110                    v = (u16::from_be_bytes([t.0[p as usize], t.0[p as usize + 1]]) as i16) as f32;
1111                    p += 2;
1112                } else if b <= 246 {
1113                    v = (b as i32 - 139) as f32;
1114                } else if b == 255 {
1115                    if p + 4 > end {
1116                        return -1;
1117                    }
1118                    let w = u32::from_be_bytes([
1119                        t.0[p as usize],
1120                        t.0[p as usize + 1],
1121                        t.0[p as usize + 2],
1122                        t.0[p as usize + 3],
1123                    ]) as i32;
1124                    p += 4;
1125                    v = w as f32 * F_INV65536;
1126                } else {
1127                    if p >= end {
1128                        return -1;
1129                    }
1130                    let b2 = t.0[p as usize] as i32;
1131                    p += 1;
1132                    v = if b <= 250 {
1133                        (((b as i32 - 247) << 8) + b2 + 108) as f32
1134                    } else {
1135                        (-(((b as i32 - 251) << 8) + b2 + 108)) as f32
1136                    };
1137                }
1138                self.push(v)
1139            } else {
1140                match b {
1141                    1 | 3 | 18 | 23 => {
1142                        // hstem vstem hstemhm vstemhm
1143                        let n = self.width_even();
1144                        self.nstems = self.nstems.wrapping_add((self.sp as u32 - n) >> 1);
1145                        Flow::Clear
1146                    }
1147                    19 | 20 => {
1148                        // hintmask cntrmask
1149                        if self.sp != 0 {
1150                            let n = self.width_even(); // implicit vstem
1151                            self.nstems = self.nstems.wrapping_add((self.sp as u32 - n) >> 1);
1152                        }
1153                        self.wd = true;
1154                        p = p.wrapping_add(self.nstems.wrapping_add(7) >> 3);
1155                        Flow::Clear
1156                    }
1157                    21 => {
1158                        // rmoveto
1159                        let i = self.width(2);
1160                        self.moveto_begin();
1161                        self.x = self.arg(i) + self.x;
1162                        self.y = self.arg(i + 1) + self.y;
1163                        self.moveto_end();
1164                        Flow::Clear
1165                    }
1166                    22 => {
1167                        // hmoveto
1168                        let i = self.width(1);
1169                        self.moveto_begin();
1170                        self.x = self.arg(i) + self.x;
1171                        self.moveto_end();
1172                        Flow::Clear
1173                    }
1174                    4 => {
1175                        // vmoveto
1176                        let i = self.width(1);
1177                        self.moveto_begin();
1178                        self.y = self.arg(i) + self.y;
1179                        self.moveto_end();
1180                        Flow::Clear
1181                    }
1182                    5 => {
1183                        // rlineto
1184                        let mut i = 0;
1185                        while i + 2 <= self.sp {
1186                            let (dx, dy) = (self.arg(i), self.arg(i + 1));
1187                            self.line(dx, dy);
1188                            i += 2;
1189                        }
1190                        Flow::Clear
1191                    }
1192                    6 | 7 => {
1193                        // hlineto / vlineto: alternating
1194                        let mut vert = b == 7;
1195                        let mut i = 0;
1196                        while i < self.sp {
1197                            let v = self.arg(i);
1198                            if vert { self.line(0.0, v) } else { self.line(v, 0.0) }
1199                            vert = !vert;
1200                            i += 1;
1201                        }
1202                        Flow::Clear
1203                    }
1204                    8 => {
1205                        // rrcurveto
1206                        let mut i = 0;
1207                        while i + 6 <= self.sp {
1208                            self.curve6(i);
1209                            i += 6;
1210                        }
1211                        Flow::Clear
1212                    }
1213                    24 => {
1214                        // rcurveline
1215                        let mut i = 0;
1216                        while i + 8 <= self.sp {
1217                            self.curve6(i);
1218                            i += 6;
1219                        }
1220                        if i + 2 <= self.sp {
1221                            let (dx, dy) = (self.arg(i), self.arg(i + 1));
1222                            self.line(dx, dy);
1223                        }
1224                        Flow::Clear
1225                    }
1226                    25 => {
1227                        // rlinecurve
1228                        let mut i = 0;
1229                        while i + 8 <= self.sp {
1230                            let (dx, dy) = (self.arg(i), self.arg(i + 1));
1231                            self.line(dx, dy);
1232                            i += 2;
1233                        }
1234                        if i + 6 <= self.sp {
1235                            self.curve6(i);
1236                        }
1237                        Flow::Clear
1238                    }
1239                    27 => {
1240                        // hhcurveto: dy1? {dxa dxb dyb dxc}+
1241                        let mut i = 0;
1242                        self.d[1] = 0.0;
1243                        if self.sp & 1 != 0 {
1244                            self.d[1] = self.arg(0);
1245                            i = 1;
1246                        }
1247                        while i + 4 <= self.sp {
1248                            self.d[0] = self.arg(i);
1249                            self.d[2] = self.arg(i + 1);
1250                            self.d[3] = self.arg(i + 2);
1251                            self.d[4] = self.arg(i + 3);
1252                            self.d[5] = 0.0;
1253                            self.curve();
1254                            self.d[1] = 0.0;
1255                            i += 4;
1256                        }
1257                        Flow::Clear
1258                    }
1259                    26 => {
1260                        // vvcurveto: dx1? {dya dxb dyb dyc}+
1261                        let mut i = 0;
1262                        self.d[0] = 0.0;
1263                        if self.sp & 1 != 0 {
1264                            self.d[0] = self.arg(0);
1265                            i = 1;
1266                        }
1267                        while i + 4 <= self.sp {
1268                            self.d[1] = self.arg(i);
1269                            self.d[2] = self.arg(i + 1);
1270                            self.d[3] = self.arg(i + 2);
1271                            self.d[4] = 0.0;
1272                            self.d[5] = self.arg(i + 3);
1273                            self.curve();
1274                            self.d[0] = 0.0;
1275                            i += 4;
1276                        }
1277                        Flow::Clear
1278                    }
1279                    30 | 31 => {
1280                        // vhcurveto / hvcurveto: alternating
1281                        let mut h = b == 31;
1282                        let mut i = 0;
1283                        while i + 4 <= self.sp {
1284                            // the last curve takes a fifth argument when exactly 5 remain
1285                            let last = (self.sp - i == 5) as usize;
1286                            if h {
1287                                self.d[0] = self.arg(i);
1288                                self.d[1] = 0.0;
1289                                self.d[2] = self.arg(i + 1);
1290                                self.d[3] = self.arg(i + 2);
1291                                self.d[5] = self.arg(i + 3);
1292                                self.d[4] = 0.0;
1293                                if last != 0 {
1294                                    self.d[4] = self.arg(i + 4);
1295                                }
1296                            } else {
1297                                self.d[0] = 0.0;
1298                                self.d[1] = self.arg(i);
1299                                self.d[2] = self.arg(i + 1);
1300                                self.d[3] = self.arg(i + 2);
1301                                self.d[4] = self.arg(i + 3);
1302                                self.d[5] = 0.0;
1303                                if last != 0 {
1304                                    self.d[5] = self.arg(i + 4);
1305                                }
1306                            }
1307                            self.curve();
1308                            i += 4 + last;
1309                            h = !h;
1310                        }
1311                        Flow::Clear
1312                    }
1313                    10 | 29 => {
1314                        // callsubr / callgsubr
1315                        let ix = if b == 10 { self.ls } else { self.gs };
1316                        if self.sp == 0 {
1317                            return -1;
1318                        }
1319                        self.sp -= 1;
1320                        let idx = cvtt(self.st[self.sp]);
1321                        let i = cs_bias(ix.count).wrapping_add(idx);
1322                        if i < 0 {
1323                            return -1;
1324                        }
1325                        let Some((s, l)) = ix_get(t, &ix, i as u32) else { return -1 };
1326                        let r = self.run(s, s.wrapping_add(l), depth + 1);
1327                        if r < 0 {
1328                            return -1;
1329                        }
1330                        if r != 0 {
1331                            return 1; // endchar inside the subroutine
1332                        }
1333                        Flow::Loop
1334                    }
1335                    11 => return 0, // return
1336                    15 if self.v2 => {
1337                        // vsindex: the item variation data of the following blends
1338                        let Some(v) = self.top() else { return -1 };
1339                        let i = cvtt(v);
1340                        if self.blended || i < 0 {
1341                            return -1;
1342                        }
1343                        self.vsi = i as u32;
1344                        self.nreg = None;
1345                        Flow::Clear
1346                    }
1347                    16 if self.v2 => {
1348                        if !self.blend() {
1349                            return -1;
1350                        }
1351                        Flow::Loop // the values stay on the stack
1352                    }
1353                    14 => {
1354                        // endchar (a seac accent is ignored)
1355                        self.wd = true;
1356                        self.moveto_begin();
1357                        Flow::Ret1
1358                    }
1359                    12 => {
1360                        if p >= end {
1361                            return -1;
1362                        }
1363                        let e = t.0[p as usize] as u32;
1364                        p += 1;
1365                        self.escape(e)
1366                    }
1367                    _ => Flow::Clear, // reserved
1368                }
1369            };
1370            match flow {
1371                Flow::Clear => self.sp = 0,
1372                Flow::Loop => {}
1373                Flow::Err => return -1,
1374                Flow::Ret1 => return 1,
1375            }
1376        }
1377    }
1378
1379    // escaped operators 12 x
1380    fn escape(&mut self, e: u32) -> Flow {
1381        match e {
1382            35 => {
1383                // flex: 12 deltas + fd: two curves
1384                if self.sp < 12 {
1385                    return Flow::Clear;
1386                }
1387                self.d6(0);
1388                self.curve();
1389                self.d6(6);
1390                self.curve();
1391                Flow::Clear
1392            }
1393            34 => {
1394                // hflex: dx1 dx2 dy2 dx3 dx4 dx5 dx6
1395                if self.sp < 7 {
1396                    return Flow::Clear;
1397                }
1398                let y0 = self.y;
1399                self.d = [self.arg(0), 0.0, self.arg(1), self.arg(2), self.arg(3), 0.0];
1400                self.curve();
1401                self.d[0] = self.arg(4);
1402                self.d[1] = 0.0;
1403                self.d[2] = self.arg(5);
1404                self.d[3] = y0 - self.y; // dy5 brings y back to the start
1405                self.d[4] = self.arg(6);
1406                self.d[5] = 0.0;
1407                self.curve();
1408                Flow::Clear
1409            }
1410            36 => {
1411                // hflex1: dx1 dy1 dx2 dy2 dx3 dx4 dx5 dy5 dx6
1412                if self.sp < 9 {
1413                    return Flow::Clear;
1414                }
1415                let y0 = self.y;
1416                self.d = [self.arg(0), self.arg(1), self.arg(2), self.arg(3), self.arg(4), 0.0];
1417                self.curve();
1418                self.d[0] = self.arg(5);
1419                self.d[1] = 0.0;
1420                self.d[2] = self.arg(6);
1421                self.d[3] = self.arg(7);
1422                self.d[4] = self.arg(8);
1423                self.d[5] = (y0 - self.y) - self.d[3]; // back to the start y
1424                self.curve();
1425                Flow::Clear
1426            }
1427            37 => {
1428                // flex1: dx1 dy1 .. dx5 dy5 d6
1429                if self.sp < 11 {
1430                    return Flow::Clear;
1431                }
1432                let (x0, y0) = (self.x, self.y);
1433                let (mut sx, mut sy) = (0.0f32, 0.0f32);
1434                for k in 0..5 {
1435                    sx = sx + self.st[k * 2];
1436                    sy = sy + self.st[k * 2 + 1];
1437                }
1438                let horiz = fabs(sx) > fabs(sy); // the last delta is horizontal
1439                self.d6(0);
1440                self.curve();
1441                self.d[0] = self.arg(6);
1442                self.d[1] = self.arg(7);
1443                self.d[2] = self.arg(8);
1444                self.d[3] = self.arg(9);
1445                // the end point: d6 along the main direction, back to the start in the other
1446                let xx = (self.x + self.d[0]) + self.d[2];
1447                let yy = (self.y + self.d[1]) + self.d[3];
1448                let d6 = self.arg(10);
1449                if horiz {
1450                    self.d[4] = d6;
1451                    self.d[5] = y0 - yy;
1452                } else {
1453                    self.d[5] = d6;
1454                    self.d[4] = x0 - xx;
1455                }
1456                self.curve();
1457                Flow::Clear
1458            }
1459            9 => match self.top() {
1460                Some(v) => self.settop(fabs(v)),
1461                None => Flow::Err,
1462            },
1463            14 => match self.top() {
1464                Some(v) => self.settop(f32::from_bits(v.to_bits() ^ 0x8000_0000)),
1465                None => Flow::Err,
1466            },
1467            26 => match self.top() {
1468                Some(v) => self.settop(sqrt(fabs(v))),
1469                None => Flow::Err,
1470            },
1471            5 => match self.top() {
1472                Some(v) => self.settop(if zf(v, 0.0) { 1.0 } else { 0.0 }),
1473                None => Flow::Err,
1474            },
1475            10 | 11 | 24 | 12 | 3 | 4 | 15 => {
1476                let Some((a, b)) = self.pop2() else { return Flow::Err };
1477                let r = match e {
1478                    10 => a + b,
1479                    11 => a - b,
1480                    24 => a * b,
1481                    12 => {
1482                        if zf(b, 0.0) {
1483                            0.0
1484                        } else {
1485                            a / b
1486                        }
1487                    }
1488                    3 => {
1489                        if zf(a, 0.0) || zf(b, 0.0) {
1490                            0.0
1491                        } else {
1492                            1.0
1493                        }
1494                    }
1495                    4 => {
1496                        if (a < 0.0 || a > 0.0) || (b < 0.0 || b > 0.0) {
1497                            1.0
1498                        } else {
1499                            0.0
1500                        }
1501                    }
1502                    _ => {
1503                        if zf(a, b) {
1504                            1.0
1505                        } else {
1506                            0.0
1507                        }
1508                    }
1509                };
1510                self.push(r)
1511            }
1512            18 => {
1513                // drop
1514                if self.sp == 0 {
1515                    return Flow::Err;
1516                }
1517                self.sp -= 1;
1518                Flow::Loop
1519            }
1520            27 => match self.top() {
1521                Some(v) => self.push(v),
1522                None => Flow::Err,
1523            },
1524            28 => {
1525                // exch
1526                let Some((a, b)) = self.pop2() else { return Flow::Err };
1527                self.st[self.sp] = b;
1528                self.st[self.sp + 1] = a;
1529                self.sp += 2;
1530                Flow::Loop
1531            }
1532            29 => {
1533                // index
1534                let Some(v) = self.top() else { return Flow::Err };
1535                let mut i = cvtt(v);
1536                if i < 0 {
1537                    i = 0;
1538                }
1539                let k = (self.sp as i32).wrapping_sub(2).wrapping_sub(i);
1540                if k < 0 {
1541                    return Flow::Err;
1542                }
1543                let v = self.arg(k as usize);
1544                self.settop(v)
1545            }
1546            30 => {
1547                // roll: num j roll
1548                let Some((a, b)) = self.pop2() else { return Flow::Err };
1549                let num = cvtt(a);
1550                let j = cvtt(b);
1551                if num <= 0 {
1552                    return Flow::Loop;
1553                }
1554                if num as usize > self.sp {
1555                    return Flow::Err;
1556                }
1557                self.roll(num, j);
1558                Flow::Loop
1559            }
1560            20 => {
1561                // put
1562                let Some((a, b)) = self.pop2() else { return Flow::Err };
1563                let i = cvtt(b) as u32;
1564                if i < 32 {
1565                    self.trans[i as usize] = a;
1566                }
1567                Flow::Loop
1568            }
1569            21 => {
1570                // get
1571                let Some(v) = self.top() else { return Flow::Err };
1572                let i = cvtt(v) as u32;
1573                if i >= 32 {
1574                    return Flow::Err;
1575                }
1576                self.settop(self.trans[i as usize])
1577            }
1578            22 => {
1579                // s1 s2 v1 v2 ifelse -> v1 <= v2 ? s1 : s2
1580                if self.sp < 4 {
1581                    return Flow::Err;
1582                }
1583                self.sp -= 4;
1584                let s = self.sp;
1585                let v1 = self.st[s + 2];
1586                let v2 = self.st[s + 3];
1587                let r = if !(v1 > v2) { self.st[s] } else { self.st[s + 1] };
1588                self.push(r)
1589            }
1590            23 => {
1591                // random
1592                let r = self.rand.wrapping_mul(1_103_515_245).wrapping_add(12345);
1593                self.rand = r;
1594                let v = ((r >> 9) | 1) as i32 as f32 * F_INV8M; // never 0
1595                self.push(v)
1596            }
1597            _ => Flow::Clear, // reserved / deprecated
1598        }
1599    }
1600}