1
#![deny(missing_docs)]
2

            
3
//! `COLR` table parsing.
4
//!
5
//! <https://learn.microsoft.com/en-us/typography/opentype/spec/colr>
6

            
7
use std::cmp::Ordering;
8
use std::fmt;
9
use std::fmt::Write;
10
use std::str::FromStr;
11

            
12
use pathfinder_geometry::line_segment::LineSegment2F;
13
use pathfinder_geometry::rect::RectF;
14
use pathfinder_geometry::transform2d::Transform2F;
15
use pathfinder_geometry::vector::{vec2f, Vector2F};
16
use rustc_hash::FxHashSet;
17

            
18
use super::{F2Dot14, Fixed};
19
use crate::binary::{U24Be, U32Be};
20
use crate::outline::{OutlineBuilder, OutlineSink};
21
use crate::tables::cpal::{ColorRecord, Palette};
22
use crate::tables::variable_fonts::{DeltaSetIndexMap, ItemVariationStore};
23
use crate::SafeFrom;
24
use crate::{
25
    binary::{
26
        read::{ReadArray, ReadBinary, ReadBinaryDep, ReadCtxt, ReadFrom, ReadScope},
27
        U16Be,
28
    },
29
    error::ParseError,
30
};
31

            
32
/// `COLR` — Color Table.
33
pub struct ColrTable<'a> {
34
    /// COLR table version, 0 or 1 currently
35
    pub version: u16,
36
    // May be empty in COLRv1
37
    base_glyph_records: ReadArray<'a, BaseGlyph>,
38
    // May be empty in COLRv1
39
    layer_records: ReadArray<'a, Layer>,
40
    base_glyph_list: Option<BaseGlyphList<'a>>,
41
    layer_list: Option<LayerList<'a>>,
42
    clip_list: Option<ClipList<'a>>,
43
    /// Index map for variable COLR fonts
44
    pub var_index_map: Option<DeltaSetIndexMap<'a>>,
45
    /// Variation data for variable COLR fonts
46
    pub item_variation_store: Option<ItemVariationStore<'a>>,
47
}
48

            
49
/// A `COLR` table.
50
impl<'a, 'data> ColrTable<'data> {
51
    /// Lookup a color glyph in this table.
52
    pub fn lookup(&'a self, glyph_id: u16) -> Result<Option<ColrGlyph<'a, 'data>>, ParseError> {
53
        if self.version == 0 {
54
            self.v0_lookup(glyph_id)
55
        } else if self.version == 1 {
56
            // For applications that support COLR version 1, the application should search for a base
57
            // glyph ID first in the BaseGlyphList. Then, if not found, search in the baseGlyphRecords
58
            // array, if present.
59
            if let Some(list) = self.base_glyph_list.as_ref() {
60
                let Some(paint) = list.record(glyph_id)? else {
61
                    return self.v0_lookup(glyph_id);
62
                };
63
                return Ok(Some(ColrGlyph {
64
                    index: glyph_id,
65
                    table: self,
66
                    paint,
67
                }));
68
            } else {
69
                self.v0_lookup(glyph_id)
70
            }
71
        } else {
72
            return Err(ParseError::BadVersion);
73
        }
74
    }
75

            
76
    fn v0_lookup(&'a self, glyph_id: u16) -> Result<Option<ColrGlyph<'a, 'data>>, ParseError> {
77
        // NOTE(unwrap): Safe as search found an entry with the binary search
78
        let Some(base_glyph) = self
79
            .base_glyph_records
80
            .binary_search_by(|base| base.glyph_id.cmp(&glyph_id))
81
            .map(|index| self.base_glyph_records.get_item(index).unwrap())
82
            .ok()
83
        else {
84
            return Ok(None);
85
        };
86

            
87
        let table = PaintTable::Layers(PaintLayers {
88
            first_layer_index: base_glyph.first_layer_index,
89
            num_layers: base_glyph.num_layers,
90
        });
91
        let paint = Paint {
92
            addr: usize::from(glyph_id),
93
            table,
94
        };
95

            
96
        Ok(Some(ColrGlyph {
97
            index: glyph_id,
98
            table: self,
99
            paint,
100
        }))
101
    }
102

            
103
    /// Retrieve a layer from the layer list.
104
    pub fn layer(&self, index: u32) -> Result<Paint<'data>, ParseError> {
105
        let list = self.layer_list.as_ref().ok_or(ParseError::MissingValue)?;
106
        list.layer(index)
107
    }
108

            
109
    /// Retrieve a layer from the layer records.
110
    pub fn layer_record(&self, index: u16) -> Result<Layer, ParseError> {
111
        self.layer_records
112
            .get_item(usize::from(index))
113
            .ok_or(ParseError::BadIndex)
114
    }
115

            
116
    /// Retrieve a clip box from the clip list.
117
    pub fn clip_box(&self, index: u16) -> Result<Option<RectF>, ParseError> {
118
        let clip_box = self
119
            .clip_list
120
            .as_ref()
121
            .and_then(|list| list.clip_box(index).transpose())
122
            .transpose()?;
123

            
124
        if let Some(ClipBox {
125
            x_min,
126
            y_min,
127
            x_max,
128
            y_max,
129
            var_index_base: _,
130
        }) = clip_box
131
        {
132
            Ok(Some(RectF::from_points(
133
                vec2f(x_min.into(), y_min.into()),
134
                vec2f(x_max.into(), y_max.into()),
135
            )))
136
        } else {
137
            Ok(None)
138
        }
139
    }
140
}
141

            
142
/// A glyph from a `COLR` table.
143
pub struct ColrGlyph<'a, 'data> {
144
    /// The base glyph index of this COLR glyph.
145
    index: u16,
146
    table: &'a ColrTable<'data>,
147
    paint: Paint<'data>,
148
}
149

            
150
/// Trait used to traverse the paint tree of a `COLR` glyph.
151
///
152
/// A [Painter] implementation is passed to [ColrGlyph::visit] or
153
/// [Font::visit_colr_glyph][crate::Font::visit_colr_glyph] to traverse the paint tree
154
/// of a glyph.
155
pub trait Painter: OutlineSink {
156
    /// Type used to represent layers in the graphics context.
157
    type Layer;
158
    /// Error type returned from Painter methods.
159
    type Error;
160

            
161
    /// Fill the current path with the supplied color.
162
    fn fill(&mut self, color: Color) -> Result<(), Self::Error>;
163

            
164
    /// Fill the current path with a linear gradient.
165
    fn linear_gradient(
166
        &mut self,
167
        gradient: LinearGradient<'_>,
168
        palette: Palette<'_, '_>,
169
    ) -> Result<(), Self::Error>;
170

            
171
    /// Fill the current path with a radial gradient.
172
    fn radial_gradient(
173
        &mut self,
174
        gradient: RadialGradient<'_>,
175
        palette: Palette<'_, '_>,
176
    ) -> Result<(), Self::Error>;
177

            
178
    /// Fill the current path with a conic gradient.
179
    ///
180
    /// Corresponds to the PaintSweep `COLR` operator.
181
    fn conic_gradient(
182
        &mut self,
183
        gradient: ConicGradient<'_>,
184
        palette: Palette<'_, '_>,
185
    ) -> Result<(), Self::Error>;
186

            
187
    /// Establish a new clip region by intersecting the current clip region with the current path.
188
    fn clip(&mut self) -> Result<(), Self::Error>;
189

            
190
    /// Start a new path.
191
    fn new_path(&mut self) -> Result<(), Self::Error>;
192

            
193
    /// Start a new rendering layer.
194
    fn begin_layer(&mut self) -> Result<(), Self::Error>;
195

            
196
    /// End the current layer, returning it.
197
    fn end_layer(&mut self) -> Result<Self::Layer, Self::Error>;
198

            
199
    /// Compose two layers using the supplied mode.
200
    fn compose_layers(
201
        &mut self,
202
        backdrop: Self::Layer,
203
        source: Self::Layer,
204
        mode: CompositeMode,
205
    ) -> Result<(), Self::Error>;
206

            
207
    /// Save graphics context state.
208
    fn push_state(&mut self) -> Result<(), Self::Error>;
209

            
210
    /// Restore graphics context state previously saved with [push_state][Self::push_state].
211
    fn pop_state(&mut self) -> Result<(), Self::Error>;
212

            
213
    /// Apply the supplied affine transform to the graphics state.
214
    fn transform(&mut self, transform: Transform2F) -> Result<(), Self::Error>;
215

            
216
    /// Apply the supplied translation to the graphics state.
217
    fn translate(&mut self, dx: i16, dy: i16) -> Result<(), Self::Error>;
218

            
219
    /// Scale the graphics state by the supplied X-scale, and Y-scale values.
220
    ///
221
    /// If `center` is `Some` the scaling is performed around the supplied center point.
222
    fn scale(&mut self, sx: f32, sy: f32, center: Option<(i16, i16)>) -> Result<(), Self::Error>;
223

            
224
    /// Apply a rotating transformation to the graphics state.
225
    ///
226
    /// `angle` is in degrees. If `center` is `Some` the rotation is performed around the
227
    /// supplied center point.
228
    fn rotate(&mut self, angle: f32, center: Option<(i16, i16)>) -> Result<(), Self::Error>;
229

            
230
    /// Apply a skew transformation to the graphics state.
231
    ///
232
    /// `angle_x` and `angle_y` are in degrees. If `center` is `Some` the rotation is performed
233
    /// around the supplied center point.
234
    fn skew(
235
        &mut self,
236
        angle_x: f32,
237
        angle_y: f32,
238
        center: Option<(i16, i16)>,
239
    ) -> Result<(), Self::Error>;
240
}
241

            
242
struct PaintStack {
243
    stack: FxHashSet<usize>,
244
}
245

            
246
impl PaintStack {
247
    fn new() -> PaintStack {
248
        PaintStack {
249
            stack: FxHashSet::default(),
250
        }
251
    }
252
    fn push(&mut self, paint: &Paint<'_>) -> Result<(), ParseError> {
253
        if !self.stack.insert(paint.addr) {
254
            return Err(ParseError::LimitExceeded);
255
        }
256
        Ok(())
257
    }
258

            
259
    fn pop(&mut self, paint: &Paint<'_>) {
260
        self.stack.remove(&paint.addr);
261
    }
262
}
263

            
264
impl<'a, 'data> ColrGlyph<'a, 'data> {
265
    /// Read the clip box of this glyph from the clip list.
266
    ///
267
    /// If the `COLR` table does not supply a clip list or there is no clip box for this
268
    /// glyph, then `Ok(None)` is returned.
269
    pub fn clip_box(&self) -> Result<Option<RectF>, ParseError> {
270
        self.table.clip_box(self.index)
271
    }
272

            
273
    /// Traverse the paint tree of this glyph using the supplied `Painter`.
274
    ///
275
    /// Colors are supplied by `palette`, which can be obtained via
276
    /// [CpalTable::palette][super::cpal::CpalTable::palette].
277
    pub fn visit<P, G>(
278
        &self,
279
        painter: &mut P,
280
        glyphs: &mut G,
281
        palette: Palette<'a, 'data>,
282
    ) -> Result<(), P::Error>
283
    where
284
        P: Painter,
285
        P::Error: From<ParseError> + From<G::Error>,
286
        G: OutlineBuilder,
287
    {
288
        self.paint
289
            .visit(painter, glyphs, palette, self.table, &mut PaintStack::new())
290
    }
291
}
292

            
293
impl<'data, 'a> Paint<'data> {
294
    fn visit<P, G>(
295
        &self,
296
        painter: &mut P,
297
        glyphs: &mut G,
298
        palette: Palette<'a, 'data>,
299
        colr: &'a ColrTable<'data>,
300
        stack: &mut PaintStack,
301
    ) -> Result<(), P::Error>
302
    where
303
        P: Painter + OutlineSink,
304
        P::Error: From<ParseError> + From<G::Error>,
305
        G: OutlineBuilder,
306
    {
307
        stack.push(self)?;
308
        match &self.table {
309
            PaintTable::Layers(PaintLayers {
310
                num_layers,
311
                first_layer_index,
312
            }) => {
313
                let range = *first_layer_index
314
                    ..first_layer_index
315
                        .checked_add(*num_layers)
316
                        .ok_or(ParseError::LimitExceeded)?;
317
                for index in range {
318
                    let layer = colr.layer_record(index)?;
319
                    painter.push_state()?;
320

            
321
                    // Apply the outline of the referenced glyph to the clip region
322
                    glyphs.visit(layer.glyph_id, None, painter)?;
323

            
324
                    // Take the intersection of clip regions
325
                    painter.clip()?;
326

            
327
                    // Draw the layer
328
                    let color = palette
329
                        .color(layer.palette_index)
330
                        .map(Color::from)
331
                        .unwrap_or_else(|| Color(0.0, 0.0, 0.0, 0.0));
332
                    painter.fill(color)?;
333

            
334
                    // Restore the previous clip region
335
                    painter.pop_state()?;
336
                }
337
            }
338
            PaintTable::ColrLayers(PaintColrLayers {
339
                num_layers,
340
                first_layer_index,
341
            }) => {
342
                let range = *first_layer_index
343
                    ..first_layer_index
344
                        .checked_add(u32::from(*num_layers))
345
                        .ok_or(ParseError::LimitExceeded)?;
346
                for index in range {
347
                    let layer = colr.layer(index)?;
348
                    layer.visit(painter, glyphs, palette, colr, stack)?;
349
                }
350
            }
351
            PaintTable::Solid(paint_solid) => {
352
                // Fall back on transparent black if color reference is invalid
353
                let color = paint_solid
354
                    .color(palette)
355
                    .unwrap_or(Color(0.0, 0.0, 0.0, 0.0));
356
                painter.fill(color)?;
357
            }
358
            PaintTable::LinearGradient(paint_linear_gradient) => {
359
                let color_line = paint_linear_gradient.color_line()?;
360
                let gradient = LinearGradient {
361
                    color_line,
362
                    start_point: (paint_linear_gradient.x0, paint_linear_gradient.y0),
363
                    end_point: (paint_linear_gradient.x1, paint_linear_gradient.y1),
364
                    rotation_point: (paint_linear_gradient.x2, paint_linear_gradient.y2),
365
                };
366
                painter.linear_gradient(gradient, palette)?;
367
            }
368
            PaintTable::RadialGradient(paint_radial_gradient) => {
369
                let color_line = paint_radial_gradient.color_line()?;
370
                let gradient = RadialGradient {
371
                    color_line,
372
                    start_circle: Circle {
373
                        x: paint_radial_gradient.x0,
374
                        y: paint_radial_gradient.y0,
375
                        radius: paint_radial_gradient.radius0,
376
                    },
377
                    end_circle: Circle {
378
                        x: paint_radial_gradient.x1,
379
                        y: paint_radial_gradient.y1,
380
                        radius: paint_radial_gradient.radius1,
381
                    },
382
                };
383
                painter.radial_gradient(gradient, palette)?;
384
            }
385
            PaintTable::SweepGradient(paint_sweep_gradient) => {
386
                let color_line = paint_sweep_gradient.color_line()?;
387
                let gradient = ConicGradient {
388
                    color_line,
389
                    center: (paint_sweep_gradient.center_x, paint_sweep_gradient.center_y),
390
                    start_angle: paint_sweep_gradient.start_angle.into(),
391
                    end_angle: paint_sweep_gradient.end_angle.into(),
392
                };
393
                painter.conic_gradient(gradient, palette)?;
394
            }
395
            PaintTable::Glyph(paint_glyph) => {
396
                let paint = paint_glyph.subpaint()?;
397
                painter.push_state()?;
398

            
399
                // Apply the outline of the referenced glyph to the clip region
400
                painter.new_path()?;
401
                glyphs.visit(paint_glyph.glyph_id, None, painter)?;
402

            
403
                // Take the intersection of clip regions
404
                painter.clip()?;
405

            
406
                // Visit the paint sub-table
407
                paint.visit(painter, glyphs, palette, colr, stack)?;
408

            
409
                // Restore the previous clip region
410
                painter.pop_state()?;
411
            }
412
            PaintTable::ColrGlyph(paint_colr_glyph) => {
413
                // TODO: This is essentially a sub-routine
414
                // Ideally it would be possible for painters to cache the rendering of a glyph by id
415
                // so it can be reused
416
                if let Some(glyph) = colr.lookup(paint_colr_glyph.glyph_id)? {
417
                    glyph.paint.visit(painter, glyphs, palette, colr, stack)?;
418
                }
419
            }
420
            PaintTable::Transform(paint_transform) => {
421
                let paint = paint_transform.subpaint()?;
422
                let t = &paint_transform.transform;
423
                let transform = Transform2F::row_major(
424
                    t.xx.into(),
425
                    t.yx.into(),
426
                    t.xy.into(),
427
                    t.yy.into(),
428
                    t.dx.into(),
429
                    t.dy.into(),
430
                );
431
                self.visit_transform(&paint, painter, glyphs, palette, colr, stack, |painter| {
432
                    painter.transform(transform)
433
                })?;
434
            }
435
            PaintTable::Translate(paint_translate) => {
436
                let paint = paint_translate.subpaint()?;
437
                self.visit_transform(&paint, painter, glyphs, palette, colr, stack, |painter| {
438
                    painter.translate(paint_translate.dx, paint_translate.dy)
439
                })?;
440
            }
441
            PaintTable::Scale(paint_scale) => {
442
                let PaintScale {
443
                    scale: (sx, sy),
444
                    center,
445
                    ..
446
                } = paint_scale;
447
                let paint = paint_scale.subpaint()?;
448
                self.visit_transform(&paint, painter, glyphs, palette, colr, stack, |painter| {
449
                    painter.scale(f32::from(*sx), f32::from(*sy), *center)
450
                })?;
451
            }
452
            PaintTable::Rotate(paint_rotate) => {
453
                let paint = paint_rotate.subpaint()?;
454
                self.visit_transform(&paint, painter, glyphs, palette, colr, stack, |painter| {
455
                    painter.rotate(raw_to_degrees(paint_rotate.angle), paint_rotate.center)
456
                })?;
457
            }
458
            PaintTable::Skew(paint_skew) => {
459
                let PaintSkew {
460
                    skew_angle: (sx, sy),
461
                    center,
462
                    ..
463
                } = paint_skew;
464
                let paint = paint_skew.subpaint()?;
465
                self.visit_transform(&paint, painter, glyphs, palette, colr, stack, |painter| {
466
                    painter.skew(raw_to_degrees(*sx), raw_to_degrees(*sy), *center)
467
                })?;
468
            }
469
            PaintTable::Composite(paint_composite) => {
470
                let paint_backdrop = paint_composite.backdrop()?;
471
                let paint_source = paint_composite.source()?;
472

            
473
                painter.begin_layer()?;
474
                paint_backdrop.visit(painter, glyphs, palette, colr, stack)?;
475
                let backdrop = painter.end_layer()?;
476
                painter.begin_layer()?;
477
                paint_source.visit(painter, glyphs, palette, colr, stack)?;
478
                let source = painter.end_layer()?;
479
                painter.compose_layers(backdrop, source, paint_composite.composite_mode)?;
480
            }
481
        }
482
        stack.pop(self);
483

            
484
        Ok(())
485
    }
486

            
487
    fn visit_transform<F, P, G>(
488
        &self,
489
        paint: &Paint<'data>,
490
        painter: &mut P,
491
        glyphs: &mut G,
492
        palette: Palette<'a, 'data>,
493
        colr: &'a ColrTable<'data>,
494
        stack: &mut PaintStack,
495
        f: F,
496
    ) -> Result<(), P::Error>
497
    where
498
        P: Painter + OutlineSink,
499
        P::Error: From<ParseError> + From<G::Error>,
500
        F: FnOnce(&mut P) -> Result<(), P::Error>,
501
        G: OutlineBuilder,
502
    {
503
        painter.push_state()?;
504
        f(painter)?;
505
        paint.visit(painter, glyphs, palette, colr, stack)?;
506
        painter.pop_state()?;
507
        Ok(())
508
    }
509
}
510

            
511
/// A [Painter] implementation that prints paint operators.
512
///
513
/// ### Example
514
///
515
/// ```
516
/// use allsorts::binary::read::ReadScope;
517
/// use allsorts::tables::colr::DebugVisitor;
518
/// use allsorts::tables::OpenTypeFont;
519
/// use allsorts::Font;
520
/// #
521
/// # pub fn read_fixture<P: AsRef<std::path::Path>>(path: P) -> Vec<u8> {
522
/// #     std::fs::read(&std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join(path))
523
/// #         .expect("error reading file contents")
524
/// # }
525
///
526
/// let buffer = read_fixture(
527
///     "tests/fonts/colr/SixtyfourConvergence-Regular-VariableFont_BLED,SCAN,XELA,YELA.ttf",
528
/// );
529
/// let otf = ReadScope::new(&buffer).read::<OpenTypeFont<'_>>().unwrap();
530
/// let table_provider = otf.table_provider(0).expect("error reading font file");
531
/// let mut font = Font::new(table_provider).unwrap();
532
/// let mut painter = DebugVisitor;
533
/// let glyph_id = 47; // 'S'
534
/// match font.visit_colr_glyph(glyph_id, 0, &mut painter) {
535
///     Ok(()) => {}
536
///     Err(err) => panic!("error visiting COLR glyph: {}", err),
537
/// }
538
/// ```
539
pub struct DebugVisitor;
540

            
541
impl Painter for DebugVisitor {
542
    type Layer = ();
543
    type Error = Box<dyn std::error::Error>;
544

            
545
    fn fill(&mut self, color: Color) -> Result<(), Self::Error> {
546
        println!("fill {:?}", color);
547
        Ok(())
548
    }
549

            
550
    fn linear_gradient(
551
        &mut self,
552
        gradient: LinearGradient<'_>,
553
        _palette: Palette<'_, '_>,
554
    ) -> Result<(), Self::Error> {
555
        println!("linear_gradient {:?}", gradient);
556
        Ok(())
557
    }
558

            
559
    fn radial_gradient(
560
        &mut self,
561
        gradient: RadialGradient<'_>,
562
        _palette: Palette<'_, '_>,
563
    ) -> Result<(), Self::Error> {
564
        println!("radial_gradient {:?}", gradient);
565
        Ok(())
566
    }
567

            
568
    fn conic_gradient(
569
        &mut self,
570
        gradient: ConicGradient<'_>,
571
        _palette: Palette<'_, '_>,
572
    ) -> Result<(), Self::Error> {
573
        println!("conic_gradient {:?}", gradient);
574
        Ok(())
575
    }
576

            
577
    fn clip(&mut self) -> Result<(), Self::Error> {
578
        println!("clip");
579
        Ok(())
580
    }
581

            
582
    fn new_path(&mut self) -> Result<(), Self::Error> {
583
        println!("new_path");
584
        Ok(())
585
    }
586

            
587
    fn begin_layer(&mut self) -> Result<(), Self::Error> {
588
        println!("begin_layer");
589
        Ok(())
590
    }
591

            
592
    fn end_layer(&mut self) -> Result<Self::Layer, Self::Error> {
593
        println!("end_layer");
594
        Ok(())
595
    }
596

            
597
    fn compose_layers(
598
        &mut self,
599
        _backdrop: Self::Layer,
600
        _source: Self::Layer,
601
        mode: CompositeMode,
602
    ) -> Result<(), Self::Error> {
603
        println!("compose_layers {:?}", mode);
604
        Ok(())
605
    }
606

            
607
    fn push_state(&mut self) -> Result<(), Self::Error> {
608
        println!("push_state");
609
        Ok(())
610
    }
611

            
612
    fn pop_state(&mut self) -> Result<(), Self::Error> {
613
        println!("pop_state");
614
        Ok(())
615
    }
616

            
617
    fn transform(&mut self, t: Transform2F) -> Result<(), Self::Error> {
618
        println!("transform {:?}", t);
619
        Ok(())
620
    }
621

            
622
    fn translate(&mut self, dx: i16, dy: i16) -> Result<(), Self::Error> {
623
        println!("translate {}, {}", dx, dy);
624
        Ok(())
625
    }
626

            
627
    fn scale(&mut self, sx: f32, sy: f32, center: Option<(i16, i16)>) -> Result<(), Self::Error> {
628
        println!("scale, {}, {} @ {:?}", sx, sy, center);
629
        Ok(())
630
    }
631

            
632
    fn rotate(&mut self, angle: f32, center: Option<(i16, i16)>) -> Result<(), Self::Error> {
633
        println!("rotate, angle {} @ {:?}", angle, center);
634
        Ok(())
635
    }
636

            
637
    fn skew(
638
        &mut self,
639
        angle_x: f32,
640
        angle_y: f32,
641
        center: Option<(i16, i16)>,
642
    ) -> Result<(), Self::Error> {
643
        println!(
644
            "skew, angle_x {}, angle_y {} @ {:?}",
645
            angle_x, angle_y, center
646
        );
647
        Ok(())
648
    }
649
}
650

            
651
impl OutlineSink for DebugVisitor {
652
    fn move_to(&mut self, to: Vector2F) {
653
        println!("move_to {:?}", to);
654
    }
655

            
656
    fn line_to(&mut self, to: Vector2F) {
657
        println!("line_to {:?}", to);
658
    }
659

            
660
    fn quadratic_curve_to(&mut self, ctrl: Vector2F, to: Vector2F) {
661
        println!("quadratic_curve_to {:?}, {:?}", ctrl, to);
662
    }
663

            
664
    fn cubic_curve_to(&mut self, ctrl: LineSegment2F, to: Vector2F) {
665
        println!("cubic_curve_to {:?}, {:?}", ctrl, to);
666
    }
667

            
668
    fn close(&mut self) {
669
        println!("close");
670
    }
671
}
672

            
673
impl ReadBinary for ColrTable<'_> {
674
    type HostType<'a> = ColrTable<'a>;
675

            
676
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
677
        let colr_scope = ctxt.scope();
678
        let version = ctxt.read_u16be()?;
679
        // Number of BaseGlyph records; may be 0 in a version 1 table.
680
        let num_base_glyph_records = ctxt.read_u16be()?;
681
        // Offset to baseGlyphRecords array, from beginning of COLR table (may be NULL).
682
        let base_glyph_records_offset = ctxt.read_u32be()?;
683
        // Offset to layerRecords array, from beginning of COLR table (may be NULL).
684
        let layer_records_offset = ctxt.read_u32be()?;
685
        // Number of Layer records; may be 0 in a version 1 table.
686
        let num_layer_records = ctxt.read_u16be()?;
687

            
688
        let base_glyph_records = colr_scope
689
            .read_optional_array(base_glyph_records_offset, num_base_glyph_records)?
690
            .unwrap_or_else(ReadArray::empty);
691
        let layer_records = colr_scope
692
            .read_optional_array(layer_records_offset, num_layer_records)?
693
            .unwrap_or_else(ReadArray::empty);
694

            
695
        if version == 0 {
696
            Ok(ColrTable {
697
                version,
698
                base_glyph_records,
699
                layer_records,
700
                base_glyph_list: None,
701
                layer_list: None,
702
                clip_list: None,
703
                var_index_map: None,
704
                item_variation_store: None,
705
            })
706
        } else if version == 1 {
707
            // Offset to BaseGlyphList table, from beginning of COLR table.
708
            let base_glyph_list_offset = ctxt.read_u32be()?;
709
            // Offset to LayerList table, from beginning of COLR table (may be NULL).
710
            let layer_list_offset = ctxt.read_u32be()?;
711
            // Offset to ClipList table, from beginning of COLR table (may be NULL).
712
            let clip_list_offset = ctxt.read_u32be()?;
713
            // Offset to DeltaSetIndexMap table, from beginning of COLR table (may be NULL).
714
            let var_index_map_offset = ctxt.read_u32be()?;
715
            // Offset to ItemVariationStore, from beginning of COLR table (may be NULL).
716
            let item_variation_store_offset = ctxt.read_u32be()?;
717

            
718
            let base_glyph_list = (base_glyph_list_offset != 0)
719
                .then(|| {
720
                    colr_scope
721
                        .offset(usize::safe_from(base_glyph_list_offset))
722
                        .ctxt()
723
                        .read::<BaseGlyphList<'_>>()
724
                })
725
                .transpose()?;
726

            
727
            let layer_list = (layer_list_offset != 0)
728
                .then(|| {
729
                    colr_scope
730
                        .offset(usize::safe_from(layer_list_offset))
731
                        .ctxt()
732
                        .read::<LayerList<'_>>()
733
                })
734
                .transpose()?;
735

            
736
            let clip_list = (clip_list_offset != 0)
737
                .then(|| {
738
                    colr_scope
739
                        .offset(usize::safe_from(clip_list_offset))
740
                        .ctxt()
741
                        .read::<ClipList<'_>>()
742
                })
743
                .transpose()?;
744

            
745
            let var_index_map = (var_index_map_offset != 0)
746
                .then(|| {
747
                    colr_scope
748
                        .offset(usize::safe_from(var_index_map_offset))
749
                        .ctxt()
750
                        .read::<DeltaSetIndexMap<'_>>()
751
                })
752
                .transpose()?;
753

            
754
            let item_variation_store = (item_variation_store_offset != 0)
755
                .then(|| {
756
                    colr_scope
757
                        .offset(usize::safe_from(item_variation_store_offset))
758
                        .ctxt()
759
                        .read::<ItemVariationStore<'_>>()
760
                })
761
                .transpose()?;
762

            
763
            Ok(ColrTable {
764
                version,
765
                base_glyph_records,
766
                layer_records,
767
                base_glyph_list,
768
                layer_list,
769
                clip_list,
770
                var_index_map,
771
                item_variation_store,
772
            })
773
        } else {
774
            Err(ParseError::BadVersion)
775
        }
776
    }
777
}
778

            
779
/// BaseGlyph record.
780
#[derive(Debug, Copy, Clone)]
781
struct BaseGlyph {
782
    /// Glyph ID of the base glyph.
783
    glyph_id: u16,
784
    /// Index (base 0) into the layerRecords array.
785
    first_layer_index: u16,
786
    /// Number of color layers associated with this glyph.
787
    num_layers: u16,
788
}
789

            
790
/// Layer record.
791
#[derive(Debug, Clone, Copy)]
792
pub struct Layer {
793
    /// Glyph ID of the glyph used for a given layer.
794
    ///
795
    /// The glyphID in a Layer record must be less than the numGlyphs value in the `maxp` table.
796
    pub glyph_id: u16,
797
    /// Index (base 0) for a palette entry in the `CPAL` table.
798
    ///
799
    /// The paletteIndex value must be less than the numPaletteEntries value in the `CPAL` table. A
800
    /// paletteIndex value of 0xFFFF is a special case, indicating that the text foreground color
801
    /// (as determined by the application) is to be used.
802
    pub palette_index: u16,
803
}
804

            
805
impl ReadFrom for BaseGlyph {
806
    type ReadType = (U16Be, U16Be, U16Be);
807

            
808
    fn read_from((glyph_id, first_layer_index, num_layers): (u16, u16, u16)) -> Self {
809
        BaseGlyph {
810
            glyph_id,
811
            first_layer_index,
812
            num_layers,
813
        }
814
    }
815
}
816

            
817
impl ReadFrom for Layer {
818
    type ReadType = (U16Be, U16Be);
819

            
820
    fn read_from((glyph_id, palette_index): (u16, u16)) -> Self {
821
        Layer {
822
            glyph_id,
823
            palette_index,
824
        }
825
    }
826
}
827

            
828
#[derive(Debug)]
829
struct BaseGlyphList<'a> {
830
    scope: ReadScope<'a>,
831
    records: ReadArray<'a, BaseGlyphPaintRecord>,
832
}
833

            
834
impl<'a> BaseGlyphList<'a> {
835
    pub fn record(&self, glyph_id: u16) -> Result<Option<Paint<'a>>, ParseError> {
836
        let Some(record_index) = self
837
            .records
838
            .binary_search_by(|record| record.glyph_id.cmp(&glyph_id))
839
            .ok()
840
        else {
841
            return Ok(None);
842
        };
843
        // NOTE(unwrap): Safe as binary search found item at record_index
844
        let record = self.records.get_item(record_index).unwrap();
845
        self.scope
846
            .offset(usize::safe_from(record.paint_offset))
847
            .read::<Paint<'_>>()
848
            .map(Some)
849
    }
850
}
851

            
852
impl ReadBinary for BaseGlyphList<'_> {
853
    type HostType<'a> = BaseGlyphList<'a>;
854

            
855
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
856
        let scope = ctxt.scope();
857
        let num_base_glyph_paint_records = ctxt.read_u32be()?;
858
        let base_glyph_paint_records =
859
            ctxt.read_array(usize::safe_from(num_base_glyph_paint_records))?;
860

            
861
        Ok(BaseGlyphList {
862
            scope,
863
            records: base_glyph_paint_records,
864
        })
865
    }
866
}
867

            
868
#[derive(Debug, Copy, Clone)]
869
struct BaseGlyphPaintRecord {
870
    /// Glyph ID of the base glyph.
871
    glyph_id: u16,
872
    /// Offset to a Paint table, from beginning of BaseGlyphList table.
873
    paint_offset: u32,
874
}
875

            
876
impl ReadFrom for BaseGlyphPaintRecord {
877
    type ReadType = (U16Be, U32Be);
878

            
879
    fn read_from((glyph_id, paint_offset): (u16, u32)) -> Self {
880
        BaseGlyphPaintRecord {
881
            glyph_id,
882
            paint_offset,
883
        }
884
    }
885
}
886

            
887
#[derive(Debug)]
888
struct LayerList<'a> {
889
    scope: ReadScope<'a>,
890
    paint_offsets: ReadArray<'a, U32Be>,
891
}
892

            
893
impl<'a> LayerList<'a> {
894
    pub fn layer(&self, index: u32) -> Result<Paint<'a>, ParseError> {
895
        let offset = self
896
            .paint_offsets
897
            .get_item(usize::safe_from(index))
898
            .ok_or(ParseError::BadIndex)?;
899
        self.scope
900
            .offset(usize::safe_from(offset))
901
            .read::<Paint<'a>>()
902
    }
903
}
904

            
905
impl ReadBinary for LayerList<'_> {
906
    type HostType<'a> = LayerList<'a>;
907

            
908
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
909
        let scope = ctxt.scope();
910
        let num_layers = ctxt.read_u32be()?;
911
        let paint_offsets = ctxt.read_array(usize::safe_from(num_layers))?;
912

            
913
        Ok(LayerList {
914
            scope,
915
            paint_offsets,
916
        })
917
    }
918
}
919

            
920
#[derive(Debug)]
921
struct ClipList<'a> {
922
    scope: ReadScope<'a>,
923
    /// Clip records. Sorted by startGlyphID.
924
    clips: ReadArray<'a, Clip>,
925
}
926

            
927
impl ClipList<'_> {
928
    fn clip_box(&self, glyph_id: u16) -> Result<Option<ClipBox>, ParseError> {
929
        let clip_index = self
930
            .clips
931
            .binary_search_by(|clip| {
932
                if clip.contains(glyph_id) {
933
                    Ordering::Equal
934
                } else if glyph_id < clip.start_glyph_id {
935
                    Ordering::Greater
936
                } else {
937
                    Ordering::Less
938
                }
939
            })
940
            .ok();
941

            
942
        let clip = match clip_index {
943
            // NOTE(unwrap): Safe as binary search found item at index
944
            Some(index) => self.clips.get_item(index).unwrap(),
945
            None => return Ok(None),
946
        };
947

            
948
        self.scope
949
            .offset(usize::safe_from(clip.clip_box_offset))
950
            .read::<ClipBox>()
951
            .map(Some)
952
    }
953
}
954

            
955
impl ReadBinary for ClipList<'_> {
956
    type HostType<'a> = ClipList<'a>;
957

            
958
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
959
        let scope = ctxt.scope();
960
        let format = ctxt.read_u8()?;
961
        ctxt.check_version(format == 1)?;
962
        let num_clips = ctxt.read_u32be()?;
963
        let clips = ctxt.read_array(usize::safe_from(num_clips))?;
964

            
965
        Ok(ClipList { scope, clips })
966
    }
967
}
968

            
969
/// Clip record.
970
#[derive(Debug, Clone, Copy)]
971
struct Clip {
972
    /// First glyph ID in the range.
973
    start_glyph_id: u16,
974
    /// Last glyph ID in the range.
975
    end_glyph_id: u16,
976
    /// Offset to a ClipBox table, from beginning of ClipList table.
977
    clip_box_offset: u32, // This is read from a 24-bit value
978
}
979

            
980
impl Clip {
981
    fn contains(&self, glyph_id: u16) -> bool {
982
        (self.start_glyph_id..=self.end_glyph_id).contains(&glyph_id)
983
    }
984
}
985

            
986
impl ReadFrom for Clip {
987
    type ReadType = (U16Be, U16Be, U24Be);
988

            
989
    fn read_from((start_glyph_id, end_glyph_id, clip_box_offset): (u16, u16, u32)) -> Self {
990
        Clip {
991
            start_glyph_id,
992
            end_glyph_id,
993
            clip_box_offset,
994
        }
995
    }
996
}
997

            
998
/// Clip box for COLR glyph.
999
#[derive(Debug, Clone, Copy)]
pub struct ClipBox {
    /// Minimum x of clip box.
    ///
    /// For variation, use varIndexBase + 0.
    pub x_min: i16,
    /// Minimum y of clip box.
    ///
    /// For variation, use varIndexBase + 1.
    pub y_min: i16,
    /// Maximum x of clip box.
    ///
    /// For variation, use varIndexBase + 2.
    pub x_max: i16,
    /// Maximum y of clip box.
    ///
    /// For variation, use varIndexBase + 3.
    pub y_max: i16,
    /// Base index into DeltaSetIndexMap.
    pub var_index_base: Option<u32>,
}
impl ClipBox {
    /// Obtain the width of this clip box.
    pub fn width(&self) -> i16 {
        self.x_max - self.x_min
    }
    /// Obtain the height of this clip box.
    pub fn height(&self) -> i16 {
        self.y_max - self.y_min
    }
}
impl ReadBinary for ClipBox {
    type HostType<'a> = ClipBox;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        let format = ctxt.read_u8()?;
        ctxt.check_version(format == 1 || format == 2)?;
        let x_min = ctxt.read_i16be()?;
        let y_min = ctxt.read_i16be()?;
        let x_max = ctxt.read_i16be()?;
        let y_max = ctxt.read_i16be()?;
        let var_index_base = (format == 2).then(|| ctxt.read_u32be()).transpose()?;
        Ok(ClipBox {
            x_min,
            y_min,
            x_max,
            y_max,
            var_index_base,
        })
    }
}
/// A possibly variable color stop in a gradient.
pub struct ColorStop {
    /// Position on a color line.
    ///
    /// For variation, use varIndexBase + 0.
    pub stop_offset: F2Dot14,
    /// Index for a `CPAL` palette entry.
    pub palette_index: u16,
    /// Alpha value.
    ///
    /// For variation, use varIndexBase + 1.
    pub alpha: F2Dot14,
    /// Base index into DeltaSetIndexMap if the color line is variable.
    pub var_index_base: Option<u32>,
}
impl ColorStop {
    /// The offset of this color stop along the color line.
    pub fn offset(&self) -> f32 {
        f32::from(self.stop_offset)
    }
    /// The color of this color-stop, according to the supplied palette.
    ///
    /// Obtain a [Palette] using the [palette method on CpalTable][super::cpal::CpalTable::palette].
    pub fn color(&self, palette: Palette<'_, '_>) -> Option<Color> {
        // The alpha value in the COLR structure is multiplied into the alpha value given in the
        // CPAL color entry. If the palette entry index is 0xFFFF, the alpha value in the COLR
        // structure is multiplied into the alpha value of the text foreground color.
        let color = palette.color(self.palette_index)?;
        let color = Color::new_with_alpha(color, self.alpha);
        Some(color)
    }
}
impl From<StaticColorStop> for ColorStop {
    fn from(
        StaticColorStop {
            stop_offset,
            palette_index,
            alpha,
        }: StaticColorStop,
    ) -> Self {
        ColorStop {
            stop_offset,
            palette_index,
            alpha,
            var_index_base: None,
        }
    }
}
impl From<VarColorStop> for ColorStop {
    fn from(
        VarColorStop {
            stop_offset,
            palette_index,
            alpha,
            var_index_base,
        }: VarColorStop,
    ) -> Self {
        ColorStop {
            stop_offset,
            palette_index,
            alpha,
            var_index_base: Some(var_index_base),
        }
    }
}
/// A non-variable gradient color stop.
#[derive(Debug, Clone, Copy)]
pub struct StaticColorStop {
    /// Position on a color line.
    pub stop_offset: F2Dot14,
    /// Index for a `CPAL` palette entry.
    pub palette_index: u16,
    /// Alpha value.
    pub alpha: F2Dot14,
}
impl ReadFrom for StaticColorStop {
    type ReadType = (F2Dot14, U16Be, F2Dot14);
    fn read_from((stop_offset, palette_index, alpha): (F2Dot14, u16, F2Dot14)) -> Self {
        StaticColorStop {
            stop_offset,
            palette_index,
            alpha,
        }
    }
}
/// A variable gradient color stop.
#[derive(Debug, Clone, Copy)]
pub struct VarColorStop {
    /// Position on a color line.
    ///
    /// For variation, use varIndexBase + 0.
    pub stop_offset: F2Dot14,
    /// Index for a `CPAL` palette entry.
    pub palette_index: u16,
    /// Alpha value.
    ///
    /// For variation, use varIndexBase + 1.
    pub alpha: F2Dot14,
    /// Base index into DeltaSetIndexMap.
    pub var_index_base: u32,
}
impl ReadFrom for VarColorStop {
    type ReadType = (F2Dot14, U16Be, F2Dot14, U32Be);
    fn read_from(
        (stop_offset, palette_index, alpha, var_index_base): (F2Dot14, u16, F2Dot14, u32),
    ) -> Self {
        VarColorStop {
            stop_offset,
            palette_index,
            alpha,
            var_index_base,
        }
    }
}
/// A gradient color line.
#[derive(Debug)]
pub enum ColorLine<'a> {
    /// A non-variable color line.
    Static(StaticColorLine<'a>),
    /// A variable color line.
    Variable(VarColorLine<'a>),
}
impl<'a> ColorLine<'a> {
    /// The extend mode of this color line.
    pub fn extend(&self) -> Extend {
        match self {
            ColorLine::Static(line) => line.extend,
            ColorLine::Variable(line) => line.extend,
        }
    }
    /// Iterator over the stops of this color line.
    pub fn color_stops<'b>(&'b self) -> ColorStopIter<'b, 'a> {
        ColorStopIter {
            line: self,
            index: 0,
        }
    }
}
impl<'a> From<StaticColorLine<'a>> for ColorLine<'a> {
    fn from(line: StaticColorLine<'a>) -> Self {
        ColorLine::Static(line)
    }
}
impl<'a> From<VarColorLine<'a>> for ColorLine<'a> {
    fn from(line: VarColorLine<'a>) -> Self {
        ColorLine::Variable(line)
    }
}
/// Color line iterator.
///
/// Returned from [ColorLine::color_stops]
#[derive(Copy, Clone)]
pub struct ColorStopIter<'a, 'data> {
    line: &'a ColorLine<'data>,
    index: usize,
}
impl ColorStopIter<'_, '_> {
    /// Retrieve a specific color stop on the color line.
    ///
    /// None if `index` is >= color stops length.
    pub fn get_item(&self, index: usize) -> Option<ColorStop> {
        match self.line {
            ColorLine::Static(line) => line.color_stops.get_item(index).map(ColorStop::from),
            ColorLine::Variable(line) => line.color_stops.get_item(index).map(ColorStop::from),
        }
    }
}
impl Iterator for ColorStopIter<'_, '_> {
    type Item = ColorStop;
    fn next(&mut self) -> Option<Self::Item> {
        let stop = self.get_item(self.index);
        self.index = self.index.saturating_add(1);
        stop
    }
    fn size_hint(&self) -> (usize, Option<usize>) {
        let remaining = match self.line {
            ColorLine::Static(line) => line.color_stops().len() - self.index,
            ColorLine::Variable(line) => line.color_stops().len() - self.index,
        };
        (remaining, Some(remaining))
    }
}
impl ExactSizeIterator for ColorStopIter<'_, '_> {}
/// A non-variable gradient color line.
#[derive(Debug, Clone)]
pub struct StaticColorLine<'a> {
    /// An Extend enum value.
    extend: Extend,
    /// ColorStop records.
    color_stops: ReadArray<'a, StaticColorStop>,
}
impl<'a> StaticColorLine<'a> {
    /// The extend mode of this color line.
    pub fn extend(&self) -> Extend {
        self.extend
    }
    /// The color stops of this color line.
    pub fn color_stops(&self) -> &ReadArray<'a, StaticColorStop> {
        &self.color_stops
    }
}
impl ReadBinary for StaticColorLine<'_> {
    type HostType<'a> = StaticColorLine<'a>;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        let extend = ctxt.read_u8()?;
        // If a ColorLine in a font has an unrecognized extend value,
        // applications should use EXTEND_PAD by default.
        let extend = Extend::try_from(extend).unwrap_or(Extend::Pad);
        let num_stops = ctxt.read_u16be()?;
        let color_stops = ctxt.read_array(usize::from(num_stops))?;
        Ok(StaticColorLine {
            extend,
            color_stops,
        })
    }
}
/// A variable gradient color line.
#[derive(Debug, Clone)]
pub struct VarColorLine<'a> {
    /// An Extend enum value.
    extend: Extend,
    /// Allows for variations.
    color_stops: ReadArray<'a, VarColorStop>,
}
impl<'a> VarColorLine<'a> {
    /// The extend mode of this color line.
    pub fn extend(&self) -> Extend {
        self.extend
    }
    /// The color stops of this color line.
    pub fn color_stops(&self) -> &ReadArray<'a, VarColorStop> {
        &self.color_stops
    }
}
impl ReadBinary for VarColorLine<'_> {
    type HostType<'a> = VarColorLine<'a>;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        let extend = ctxt.read_u8()?;
        // If a ColorLine in a font has an unrecognized extend value,
        // applications should use EXTEND_PAD by default.
        let extend = Extend::try_from(extend).unwrap_or(Extend::Pad);
        let num_stops = ctxt.read_u16be()?;
        let color_stops = ctxt.read_array(usize::from(num_stops))?;
        Ok(VarColorLine {
            extend,
            color_stops,
        })
    }
}
/// Gradient extend mode.
///
/// This defines how a gradient is extended if its color stops do not
/// cover the full 0 to 1.0 range.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Extend {
    /// Use nearest color stop.
    Pad,
    /// Repeat from farthest color stop.
    Repeat,
    /// Mirror color line from nearest end.
    Reflect,
}
impl TryFrom<u8> for Extend {
    type Error = ParseError;
    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Extend::Pad),
            1 => Ok(Extend::Repeat),
            2 => Ok(Extend::Reflect),
            _ => Err(ParseError::BadValue),
        }
    }
}
/// A COLR Paint table.
///
/// This describes a particular graphical operation.
#[derive(Debug)]
pub struct Paint<'a> {
    addr: usize,
    table: PaintTable<'a>,
}
#[derive(Debug)]
enum PaintTable<'a> {
    /// V0 layers
    Layers(PaintLayers),
    ColrLayers(PaintColrLayers),
    Solid(PaintSolid),
    LinearGradient(PaintLinearGradient<'a>),
    RadialGradient(PaintRadialGradient<'a>),
    SweepGradient(PaintSweepGradient<'a>),
    Glyph(PaintGlyph<'a>),
    ColrGlyph(PaintColrGlyph),
    Transform(PaintTransform<'a>),
    Translate(PaintTranslate<'a>),
    Scale(PaintScale<'a>),
    Rotate(PaintRotate<'a>),
    Skew(PaintSkew<'a>),
    Composite(PaintComposite<'a>),
}
macro_rules! subpaint {
    ($t:ty) => {
        impl<'data> $t {
            fn subpaint(&self) -> Result<Paint<'data>, ParseError> {
                self.scope
                    .offset(usize::safe_from(self.paint_offset))
                    .ctxt()
                    .read::<Paint<'_>>()
            }
        }
    };
}
subpaint!(PaintGlyph<'data>);
subpaint!(PaintTransform<'data>);
subpaint!(PaintTranslate<'data>);
subpaint!(PaintScale<'data>);
subpaint!(PaintRotate<'data>);
subpaint!(PaintSkew<'data>);
#[derive(Debug)]
struct PaintLayers {
    /// Index (base 0) into the layerRecords array.
    first_layer_index: u16,
    /// Number of color layers associated with this glyph.
    num_layers: u16,
}
#[derive(Debug)]
struct PaintColrLayers {
    /// Number of offsets to paint tables to read from LayerList.
    num_layers: u8,
    /// Index (base 0) into the LayerList.
    first_layer_index: u32,
}
#[derive(Debug)]
struct PaintSolid {
    /// Index for a CPAL palette entry.
    palette_index: u16,
    /// Alpha value.
    alpha: F2Dot14,
    /// Base index into DeltaSetIndexMap.
    _var_index_base: Option<u32>,
}
trait Gradient {
    fn scope(&self) -> ReadScope<'_>;
    fn color_line_offset(&self) -> u32;
    fn var_index_base(&self) -> Option<u32>;
    fn color_line(&self) -> Result<ColorLine<'_>, ParseError> {
        let mut ctxt = self
            .scope()
            .offset(usize::safe_from(self.color_line_offset()))
            .ctxt();
        if self.var_index_base().is_some() {
            ctxt.read::<VarColorLine<'_>>().map(ColorLine::from)
        } else {
            ctxt.read::<StaticColorLine<'_>>().map(ColorLine::from)
        }
    }
}
#[derive(Debug)]
struct PaintLinearGradient<'a> {
    scope: ReadScope<'a>,
    /// Offset to ColorLine table, from beginning of PaintLinearGradient table.
    color_line_offset: u32, // Offset24,
    /// Start point (p₀) x coordinate.
    x0: i16,
    /// Start point (p₀) y coordinate.
    y0: i16,
    /// End point (p₁) x coordinate.
    x1: i16,
    /// End point (p₁) y coordinate.
    y1: i16,
    /// Rotation point (p₂) x coordinate.
    x2: i16,
    /// Rotation point (p₂) y coordinate.
    y2: i16,
    /// Base index into DeltaSetIndexMap.
    var_index_base: Option<u32>,
}
/// A linear gradient, possibly rotated about a point.
#[derive(Debug)]
pub struct LinearGradient<'a> {
    /// The color line of the gradient
    pub color_line: ColorLine<'a>,
    /// Start point (p₀)
    pub start_point: (i16, i16),
    /// End point (p₁)
    pub end_point: (i16, i16),
    /// Rotation point (p₂)
    pub rotation_point: (i16, i16),
}
#[derive(Debug)]
struct PaintRadialGradient<'a> {
    scope: ReadScope<'a>,
    /// Offset to VarColorLine table, from beginning of PaintVarRadialGradient table.
    color_line_offset: u32, // Offset24,
    /// Start circle center x coordinate.
    ///
    /// For variation, use varIndexBase + 0.
    x0: i16,
    /// Start circle center y coordinate.
    ///
    /// For variation, use varIndexBase + 1.
    y0: i16,
    /// Start circle radius.
    ///
    /// For variation, use varIndexBase + 2.
    radius0: u16,
    /// End circle center x coordinate.
    ///
    /// For variation, use varIndexBase + 3.
    x1: i16,
    /// End circle center y coordinate.
    ///
    /// For variation, use varIndexBase + 4.
    y1: i16,
    /// End circle radius.
    ///
    /// For variation, use varIndexBase + 5.
    radius1: u16,
    /// Base index into DeltaSetIndexMap.
    var_index_base: Option<u32>,
}
/// A gradient of colors along a cylinder defined by two circles.
#[derive(Debug)]
pub struct RadialGradient<'a> {
    /// The color line of the gradient
    pub color_line: ColorLine<'a>,
    /// Start circle
    pub start_circle: Circle,
    /// End circle
    pub end_circle: Circle,
}
/// A constituent circle of a radial gradient.
#[derive(Debug, Copy, Clone)]
pub struct Circle {
    /// The center X coordinate of the circle
    pub x: i16,
    /// The center Y coordinate of the circle
    pub y: i16,
    /// The radius of the circle
    pub radius: u16,
}
#[derive(Debug)]
struct PaintSweepGradient<'a> {
    scope: ReadScope<'a>,
    /// Offset to VarColorLine table, from beginning of PaintVarSweepGradient table.
    color_line_offset: u32, // Offset24,
    /// Center x coordinate.
    ///
    /// For variation, use varIndexBase + 0.
    center_x: i16,
    /// Center y coordinate.
    ///
    /// For variation, use varIndexBase + 1.
    center_y: i16,
    /// Start of the angular range of the gradient: add 1.0 and multiply by 180° to retrieve counter-clockwise degrees.
    ///
    /// For variation, use varIndexBase + 2.
    start_angle: F2Dot14,
    /// End of the angular range of the gradient: add 1.0 and multiply by 180° to retrieve counter-clockwise degrees.
    ///
    /// For variation, use varIndexBase + 3.
    end_angle: F2Dot14,
    /// Base index into DeltaSetIndexMap.
    var_index_base: Option<u32>,
}
/// A conic or sweep gradient that provides a gradation of colors that sweep around a center point.
#[derive(Debug)]
pub struct ConicGradient<'a> {
    /// The color line of the gradient
    pub color_line: ColorLine<'a>,
    /// The center point of the sweep
    pub center: (i16, i16),
    /// The starting angle of the sweep (raw value)
    ///
    /// **Note:** This is the raw value from the font. Add 1.0 and multiply by 180 to get
    /// the angle in degrees.
    pub start_angle: f32,
    /// The ending angle of the sweep (raw value)
    ///
    /// **Note:** This is the raw value from the font. Add 1.0 and multiply by 180 to get
    /// the angle in degrees.
    pub end_angle: f32,
}
#[derive(Debug)]
struct PaintGlyph<'a> {
    scope: ReadScope<'a>,
    /// Offset to a Paint table, from beginning of PaintGlyph table.
    paint_offset: u32, // Offset24,
    /// Glyph ID for the source outline.
    glyph_id: u16,
}
#[derive(Debug)]
struct PaintColrGlyph {
    /// Glyph ID for a BaseGlyphList base glyph.
    glyph_id: u16,
}
#[derive(Debug)]
struct PaintTransform<'a> {
    scope: ReadScope<'a>,
    /// Offset to a Paint table, from beginning of PaintGlyph table.
    paint_offset: u32, // Offset24,
    /// Offset to an Affine2x3 table, from beginning of PaintTransform table.
    transform: Affine2x3,
}
#[derive(Debug)]
struct Affine2x3 {
    /// x-component of transformed x-basis vector.
    ///
    /// For variation, use varIndexBase + 0.
    xx: Fixed,
    /// y-component of transformed x-basis vector.
    ///
    /// For variation, use varIndexBase + 1.
    yx: Fixed,
    /// x-component of transformed y-basis vector.
    ///
    /// For variation, use varIndexBase + 2.
    xy: Fixed,
    /// y-component of transformed y-basis vector.
    ///
    /// For variation, use varIndexBase + 3.
    yy: Fixed,
    /// Translation in x direction.
    ///
    /// For variation, use varIndexBase + 4.
    dx: Fixed,
    /// Translation in y direction.
    ///
    /// For variation, use varIndexBase + 5.
    dy: Fixed,
    /// Base index into DeltaSetIndexMap.
    _var_index_base: Option<u32>,
}
#[derive(Debug)]
struct PaintTranslate<'a> {
    scope: ReadScope<'a>,
    /// Offset to a Paint subtable, from beginning of PaintVarTranslate table.
    paint_offset: u32, // Offset24,
    /// Translation in x direction.
    ///
    /// For variation, use varIndexBase + 0.
    dx: i16,
    /// Translation in y direction.
    ///
    /// For variation, use varIndexBase + 1.
    dy: i16,
    /// Base index into DeltaSetIndexMap.
    _var_index_base: Option<u32>,
}
#[derive(Debug)]
struct PaintScale<'a> {
    scope: ReadScope<'a>,
    /// Offset to a Paint subtable, from beginning of PaintVarScale table.
    paint_offset: u32, // Offset24,
    /// Scale factor in (x, y) directions.
    ///
    /// For variation, use varIndexBase + 0 for x, varIndexBase + 1 for y.
    scale: (F2Dot14, F2Dot14),
    /// Coordinates for the center of scaling (x, y).
    ///
    /// For variation, use varIndexBase + 2 for x, varIndexBase + 3 for y.
    center: Option<(i16, i16)>,
    /// Base index into DeltaSetIndexMap.
    _var_index_base: Option<u32>,
}
#[derive(Debug)]
struct PaintRotate<'a> {
    scope: ReadScope<'a>,
    /// Offset to a Paint subtable, from beginning of PaintVarRotate table.
    paint_offset: u32, // Offset24,
    /// Rotation angle, 180° in counter-clockwise degrees per 1.0 of value.
    ///
    /// For variation, use varIndexBase + 0.
    angle: F2Dot14,
    /// Coordinates for the center of rotation (x, y).
    ///
    /// For variation, use varIndexBase + 1 for x and varIndexBase + 2 for y.
    center: Option<(i16, i16)>,
    /// Base index into DeltaSetIndexMap.
    _var_index_base: Option<u32>,
}
#[derive(Debug)]
struct PaintSkew<'a> {
    scope: ReadScope<'a>,
    /// Offset to a Paint subtable, from beginning of PaintVarSkew table.
    paint_offset: u32, // Offset24,
    /// Angle of skew (x, y)
    ///
    /// 180° in counter-clockwise degrees per 1.0 of value.
    ///
    /// For variation, use varIndexBase + 0 for x-axis and varIndexBase + 1 for y-axis.
    skew_angle: (F2Dot14, F2Dot14),
    /// Coordinates for the center of rotation (x, y).
    ///
    /// For variation, use varIndexBase + 2 for x and varIndexBase + 3 for y.
    center: Option<(i16, i16)>,
    /// Base index into DeltaSetIndexMap.
    _var_index_base: Option<u32>,
}
#[derive(Debug)]
struct PaintComposite<'a> {
    scope: ReadScope<'a>,
    /// Offset to a source Paint table, from beginning of PaintComposite table.
    source_paint_offset: u32, // Offset24,
    /// A CompositeMode enumeration value.
    composite_mode: CompositeMode,
    /// Offset to a backdrop Paint table, from beginning of PaintComposite table.
    backdrop_paint_offset: u32, // Offset24,
}
/// Mode to use for compositing paint format.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum CompositeMode {
    // Porter-Duff modes
    /// Clear
    Clear,
    /// Source (“Copy” in Composition & Blending Level 1)
    Src,
    /// Destination
    Dest,
    /// Source Over
    SrcOver,
    /// Destination Over
    DestOver,
    /// Source In
    SrcIn,
    /// Destination In
    DestIn,
    /// Source Out
    SrcOut,
    /// Destination Out
    DestOut,
    /// Source Atop
    SrcAtop,
    /// Destination Atop
    DestAtop,
    /// XOR
    Xor,
    /// Plus (“Lighter” in Composition & Blending Level 1)
    Plus,
    // Separable color blend modes:
    /// screen
    Screen,
    /// overlay
    Overlay,
    /// darken
    Darken,
    /// lighten
    Lighten,
    /// color-dodge
    ColorDodge,
    /// color-burn
    ColorBurn,
    /// hard-light
    HardLight,
    /// soft-light
    SoftLight,
    /// difference
    Difference,
    /// exclusion
    Exclusion,
    /// multiply
    Multiply,
    // Non-separable color blend modes:
    /// hue
    HslHue,
    /// saturation
    HslSaturation,
    /// color
    HslColor,
    /// luminosity
    HslLuminosity,
}
impl TryFrom<u8> for CompositeMode {
    type Error = ParseError;
    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(CompositeMode::Clear),
            1 => Ok(CompositeMode::Src),
            2 => Ok(CompositeMode::Dest),
            3 => Ok(CompositeMode::SrcOver),
            4 => Ok(CompositeMode::DestOver),
            5 => Ok(CompositeMode::SrcIn),
            6 => Ok(CompositeMode::DestIn),
            7 => Ok(CompositeMode::SrcOut),
            8 => Ok(CompositeMode::DestOut),
            9 => Ok(CompositeMode::SrcAtop),
            10 => Ok(CompositeMode::DestAtop),
            11 => Ok(CompositeMode::Xor),
            12 => Ok(CompositeMode::Plus),
            13 => Ok(CompositeMode::Screen),
            14 => Ok(CompositeMode::Overlay),
            15 => Ok(CompositeMode::Darken),
            16 => Ok(CompositeMode::Lighten),
            17 => Ok(CompositeMode::ColorDodge),
            18 => Ok(CompositeMode::ColorBurn),
            19 => Ok(CompositeMode::HardLight),
            20 => Ok(CompositeMode::SoftLight),
            21 => Ok(CompositeMode::Difference),
            22 => Ok(CompositeMode::Exclusion),
            23 => Ok(CompositeMode::Multiply),
            24 => Ok(CompositeMode::HslHue),
            25 => Ok(CompositeMode::HslSaturation),
            26 => Ok(CompositeMode::HslColor),
            27 => Ok(CompositeMode::HslLuminosity),
            _ => Err(ParseError::BadValue),
        }
    }
}
impl ReadBinary for Paint<'_> {
    type HostType<'a> = Paint<'a>;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        // Paint tables can introduce cycles because they can refer to each other in order to
        // reduce duplication. When rendering a paint table if any of its ancestors are encountered
        // again then a cycle is introduced. We need to detect this to prevent an infinite loop.
        //
        // Identifying a paint table as being the "same" poses some challenges. It's not equality
        // that matters since it would be ok for two equivalent paint tables to be encountered that
        // contain the same data _if_ they were stored separately. Additionally, the offsets used
        // to refer to paint tables are relative to different base positions so these can't be used
        // either.
        //
        // With the assumption that the paint tables are all being read from the same slice of data
        // in memory the offsets will end up resolving to addresses in that slice. An offset that
        // resolves to the same address as an existing paint table in the stack indicates that it's
        // pointing to the same paint table and thus a cycle.
        //
        // So, we track the address that each paint table originated from.
        let addr = ctxt.scope().data().as_ptr() as usize;
        // Peek the format to determine paint type to read
        let format = ctxt.scope().ctxt().read_u8()?;
        let table = match format {
            1 => PaintTable::ColrLayers(ctxt.read::<PaintColrLayers>()?),
            2 | 3 => PaintTable::Solid(ctxt.read::<PaintSolid>()?),
            4 | 5 => PaintTable::LinearGradient(ctxt.read::<PaintLinearGradient<'_>>()?),
            6 | 7 => PaintTable::RadialGradient(ctxt.read::<PaintRadialGradient<'_>>()?),
            8 | 9 => PaintTable::SweepGradient(ctxt.read::<PaintSweepGradient<'_>>()?),
            10 => PaintTable::Glyph(ctxt.read::<PaintGlyph<'_>>()?),
            11 => PaintTable::ColrGlyph(ctxt.read::<PaintColrGlyph>()?),
            12 | 13 => PaintTable::Transform(ctxt.read::<PaintTransform<'_>>()?),
            14 | 15 => PaintTable::Translate(ctxt.read::<PaintTranslate<'_>>()?),
            16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 => {
                PaintTable::Scale(ctxt.read::<PaintScale<'_>>()?)
            }
            24 | 25 | 26 | 27 => PaintTable::Rotate(ctxt.read::<PaintRotate<'_>>()?),
            28 | 29 | 30 | 31 => PaintTable::Skew(ctxt.read::<PaintSkew<'_>>()?),
            32 => PaintTable::Composite(ctxt.read::<PaintComposite<'_>>()?),
            _ => return Err(ParseError::BadValue),
        };
        Ok(Paint { addr, table })
    }
}
impl ReadBinary for PaintColrLayers {
    type HostType<'a> = PaintColrLayers;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        let format = ctxt.read_u8()?;
        ctxt.check_version(format == 1)?;
        let num_layers = ctxt.read_u8()?;
        let first_layer_index = ctxt.read_u32be()?;
        Ok(PaintColrLayers {
            num_layers,
            first_layer_index,
        })
    }
}
impl PaintSolid {
    fn color(&self, palette: Palette<'_, '_>) -> Option<Color> {
        // The alpha value in the COLR structure is multiplied into the alpha value given in the
        // CPAL color entry. If the palette entry index is 0xFFFF, the alpha value in the COLR
        // structure is multiplied into the alpha value of the text foreground color.
        let color = palette.color(self.palette_index)?;
        Some(Color::new_with_alpha(color, self.alpha))
    }
}
impl ReadBinary for PaintSolid {
    type HostType<'a> = PaintSolid;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        let format = ctxt.read_u8()?;
        let palette_index = ctxt.read_u16be()?;
        let alpha = ctxt.read::<F2Dot14>()?;
        let var_index_base = match format {
            2 => None,
            3 => ctxt.read_u32be().map(Some)?,
            _ => return Err(ParseError::BadValue),
        };
        Ok(PaintSolid {
            palette_index,
            alpha,
            _var_index_base: var_index_base,
        })
    }
}
macro_rules! gradient {
    ($t:ty) => {
        impl Gradient for $t {
            fn scope(&self) -> ReadScope<'_> {
                self.scope
            }
            fn color_line_offset(&self) -> u32 {
                self.color_line_offset
            }
            fn var_index_base(&self) -> Option<u32> {
                self.var_index_base
            }
        }
    };
}
gradient!(PaintLinearGradient<'_>);
gradient!(PaintRadialGradient<'_>);
gradient!(PaintSweepGradient<'_>);
impl ReadBinary for PaintLinearGradient<'_> {
    type HostType<'a> = PaintLinearGradient<'a>;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        let scope = ctxt.scope();
        let format = ctxt.read_u8()?;
        let color_line_offset = ctxt.read::<U24Be>()?;
        let x0 = ctxt.read_i16be()?;
        let y0 = ctxt.read_i16be()?;
        let x1 = ctxt.read_i16be()?;
        let y1 = ctxt.read_i16be()?;
        let x2 = ctxt.read_i16be()?;
        let y2 = ctxt.read_i16be()?;
        let var_index_base = match format {
            4 => None,
            5 => ctxt.read_u32be().map(Some)?,
            _ => return Err(ParseError::BadValue),
        };
        Ok(PaintLinearGradient {
            scope,
            color_line_offset,
            x0,
            y0,
            x1,
            y1,
            x2,
            y2,
            var_index_base,
        })
    }
}
impl ReadBinary for PaintRadialGradient<'_> {
    type HostType<'a> = PaintRadialGradient<'a>;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        let scope = ctxt.scope();
        let format = ctxt.read_u8()?;
        let color_line_offset = ctxt.read::<U24Be>()?;
        let x0 = ctxt.read_i16be()?;
        let y0 = ctxt.read_i16be()?;
        let radius0 = ctxt.read_u16be()?;
        let x1 = ctxt.read_i16be()?;
        let y1 = ctxt.read_i16be()?;
        let radius1 = ctxt.read_u16be()?;
        let var_index_base = match format {
            6 => None,
            7 => ctxt.read_u32be().map(Some)?,
            _ => return Err(ParseError::BadValue),
        };
        Ok(PaintRadialGradient {
            scope,
            color_line_offset,
            x0,
            y0,
            radius0,
            x1,
            y1,
            radius1,
            var_index_base,
        })
    }
}
impl ReadBinary for PaintSweepGradient<'_> {
    type HostType<'a> = PaintSweepGradient<'a>;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        let scope = ctxt.scope();
        let format = ctxt.read_u8()?;
        let color_line_offset = ctxt.read::<U24Be>()?;
        let center_x = ctxt.read_i16be()?;
        let center_y = ctxt.read_i16be()?;
        let start_angle = ctxt.read::<F2Dot14>()?;
        let end_angle = ctxt.read::<F2Dot14>()?;
        let var_index_base = match format {
            8 => None,
            9 => ctxt.read_u32be().map(Some)?,
            _ => return Err(ParseError::BadValue),
        };
        Ok(PaintSweepGradient {
            scope,
            color_line_offset,
            center_x,
            center_y,
            start_angle,
            end_angle,
            var_index_base,
        })
    }
}
impl ReadBinary for PaintGlyph<'_> {
    type HostType<'a> = PaintGlyph<'a>;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        let scope = ctxt.scope();
        let format = ctxt.read_u8()?;
        ctxt.check_version(format == 10)?;
        let paint_offset = ctxt.read::<U24Be>()?;
        let glyph_id = ctxt.read_u16be()?;
        Ok(PaintGlyph {
            scope,
            paint_offset,
            glyph_id,
        })
    }
}
impl ReadBinary for PaintColrGlyph {
    type HostType<'a> = PaintColrGlyph;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        let format = ctxt.read_u8()?;
        ctxt.check_version(format == 11)?;
        let glyph_id = ctxt.read_u16be()?;
        Ok(PaintColrGlyph { glyph_id })
    }
}
impl ReadBinary for PaintTransform<'_> {
    type HostType<'a> = PaintTransform<'a>;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        let scope = ctxt.scope();
        let format = ctxt.read_u8()?;
        let paint_offset = ctxt.read::<U24Be>()?;
        let transform_offset = ctxt.read::<U24Be>().map(SafeFrom::safe_from)?;
        let variable = match format {
            12 => false,
            13 => true,
            _ => return Err(ParseError::BadValue),
        };
        let transform = scope
            .offset(transform_offset)
            .ctxt()
            .read_dep::<Affine2x3>(variable)?;
        Ok(PaintTransform {
            scope,
            paint_offset,
            transform,
        })
    }
}
impl ReadBinaryDep for Affine2x3 {
    type Args<'a> = bool;
    type HostType<'a> = Affine2x3;
    fn read_dep<'a>(
        ctxt: &mut ReadCtxt<'a>,
        variable: bool,
    ) -> Result<Self::HostType<'a>, ParseError> {
        let xx = ctxt.read::<Fixed>()?;
        let yx = ctxt.read::<Fixed>()?;
        let xy = ctxt.read::<Fixed>()?;
        let yy = ctxt.read::<Fixed>()?;
        let dx = ctxt.read::<Fixed>()?;
        let dy = ctxt.read::<Fixed>()?;
        let var_index_base = variable.then(|| ctxt.read_u32be()).transpose()?;
        Ok(Affine2x3 {
            xx,
            yx,
            xy,
            yy,
            dx,
            dy,
            _var_index_base: var_index_base,
        })
    }
}
impl ReadBinary for PaintTranslate<'_> {
    type HostType<'a> = PaintTranslate<'a>;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        let scope = ctxt.scope();
        let format = ctxt.read_u8()?;
        let paint_offset = ctxt.read::<U24Be>()?;
        let dx = ctxt.read_i16be()?;
        let dy = ctxt.read_i16be()?;
        let var_index_base = match format {
            14 => None,
            15 => ctxt.read_u32be().map(Some)?,
            _ => return Err(ParseError::BadValue),
        };
        Ok(PaintTranslate {
            scope,
            paint_offset,
            dx,
            dy,
            _var_index_base: var_index_base,
        })
    }
}
impl ReadBinary for PaintScale<'_> {
    type HostType<'a> = PaintScale<'a>;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        let scope = ctxt.scope();
        let format = ctxt.read_u8()?;
        let paint_offset = ctxt.read::<U24Be>()?;
        let (scale, center, var_index_base) = match format {
            // PaintScale and PaintVarScale
            16 | 17 => {
                let scale_x = ctxt.read::<F2Dot14>()?;
                let scale_y = ctxt.read::<F2Dot14>()?;
                let var_index_base = (format == 17).then(|| ctxt.read_u32be()).transpose()?;
                ((scale_x, scale_y), None, var_index_base)
            }
            // PaintScaleAroundCenter and PaintVarScaleAroundCenter
            18 | 19 => {
                let scale_x = ctxt.read::<F2Dot14>()?;
                let scale_y = ctxt.read::<F2Dot14>()?;
                let center_x = ctxt.read_i16be()?;
                let center_y = ctxt.read_i16be()?;
                let var_index_base = (format == 19).then(|| ctxt.read_u32be()).transpose()?;
                (
                    (scale_x, scale_y),
                    Some((center_x, center_y)),
                    var_index_base,
                )
            }
            // PaintScaleUniform and PaintVarScaleUniform
            20 | 21 => {
                let scale = ctxt.read::<F2Dot14>()?;
                let var_index_base = (format == 21).then(|| ctxt.read_u32be()).transpose()?;
                ((scale, scale), None, var_index_base)
            }
            // PaintScaleUniformAroundCenter and PaintVarScaleUniformAroundCenter
            22 | 23 => {
                let scale = ctxt.read::<F2Dot14>()?;
                let center_x = ctxt.read_i16be()?;
                let center_y = ctxt.read_i16be()?;
                let var_index_base = (format == 23).then(|| ctxt.read_u32be()).transpose()?;
                ((scale, scale), Some((center_x, center_y)), var_index_base)
            }
            _ => return Err(ParseError::BadValue),
        };
        Ok(PaintScale {
            scope,
            paint_offset,
            scale,
            center,
            _var_index_base: var_index_base,
        })
    }
}
impl ReadBinary for PaintRotate<'_> {
    type HostType<'a> = PaintRotate<'a>;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        let scope = ctxt.scope();
        let format = ctxt.read_u8()?;
        let paint_offset = ctxt.read::<U24Be>()?;
        let angle = ctxt.read::<F2Dot14>()?;
        let (center, var_index_base) = match format {
            // PaintRotate
            24 => (None, None),
            // PaintVarRotate
            25 => {
                let var_index_base = ctxt.read_u32be()?;
                (None, Some(var_index_base))
            }
            // PaintRotateAroundCenter and PaintVarRotateAroundCenter
            26 | 27 => {
                let center_x = ctxt.read_i16be()?;
                let center_y = ctxt.read_i16be()?;
                let var_index_base = (format == 27).then(|| ctxt.read_u32be()).transpose()?;
                (Some((center_x, center_y)), var_index_base)
            }
            _ => return Err(ParseError::BadValue),
        };
        Ok(PaintRotate {
            scope,
            paint_offset,
            angle,
            center,
            _var_index_base: var_index_base,
        })
    }
}
impl ReadBinary for PaintSkew<'_> {
    type HostType<'a> = PaintSkew<'a>;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        let scope = ctxt.scope();
        let format = ctxt.read_u8()?;
        let paint_offset = ctxt.read::<U24Be>()?;
        let x_skew_angle = ctxt.read::<F2Dot14>()?;
        let y_skew_angle = ctxt.read::<F2Dot14>()?;
        let (center, var_index_base) = match format {
            // PaintSkew
            28 => (None, None),
            // PaintVarSkew
            29 => {
                let var_index_base = ctxt.read_u32be()?;
                (None, Some(var_index_base))
            }
            // PaintSkewAroundCenter and PaintVarSkewAroundCenter
            30 | 31 => {
                let center_x = ctxt.read_i16be()?;
                let center_y = ctxt.read_i16be()?;
                let var_index_base = (format == 31).then(|| ctxt.read_u32be()).transpose()?;
                (Some((center_x, center_y)), var_index_base)
            }
            _ => return Err(ParseError::BadValue),
        };
        Ok(PaintSkew {
            scope,
            paint_offset,
            skew_angle: (x_skew_angle, y_skew_angle),
            center,
            _var_index_base: var_index_base,
        })
    }
}
impl<'data> PaintComposite<'data> {
    fn backdrop(&self) -> Result<Paint<'data>, ParseError> {
        self.scope
            .offset(usize::safe_from(self.backdrop_paint_offset))
            .ctxt()
            .read::<Paint<'data>>()
    }
    fn source(&self) -> Result<Paint<'data>, ParseError> {
        self.scope
            .offset(usize::safe_from(self.source_paint_offset))
            .ctxt()
            .read::<Paint<'data>>()
    }
}
impl ReadBinary for PaintComposite<'_> {
    type HostType<'a> = PaintComposite<'a>;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        let scope = ctxt.scope();
        let format = ctxt.read_u8()?;
        ctxt.check_version(format == 32)?;
        let source_paint_offset = ctxt.read::<U24Be>()?;
        let composite_mode = ctxt.read::<CompositeMode>()?;
        let backdrop_paint_offset = ctxt.read::<U24Be>()?;
        Ok(PaintComposite {
            scope,
            source_paint_offset,
            composite_mode,
            backdrop_paint_offset,
        })
    }
}
impl ReadBinary for CompositeMode {
    type HostType<'a> = CompositeMode;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        ctxt.read_u8()
            .map_err(ParseError::from)
            .and_then(TryFrom::try_from)
    }
}
/// An RGBA color
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Color(pub f32, pub f32, pub f32, pub f32);
impl Color {
    /// Create a new Color from the supplied [ColorRecord] and alpha value
    pub fn new_with_alpha(color: ColorRecord, alpha: F2Dot14) -> Self {
        // "The alpha indicated in this record is multiplied with the alpha component of the CPAL
        // entry (converted to float—divide by 255)."
        //
        // "Values for alpha outside the range [0., 1.] (inclusive) are reserved; values outside
        // this range must be clamped."
        let alpha = ((f32::from(color.alpha) / 255.0) * f32::from(alpha)).clamp(0.0, 1.0);
        Color(
            f32::from(color.red) / 255.0,
            f32::from(color.green) / 255.0,
            f32::from(color.blue) / 255.0,
            alpha,
        )
    }
}
impl fmt::Display for Color {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let r = (255.0 * self.0).round() as u8;
        let g = (255.0 * self.1).round() as u8;
        let b = (255.0 * self.2).round() as u8;
        let a = (255.0 * self.3).round() as u8;
        f.write_char('#')?;
        write!(f, "{:02x}", r)?;
        write!(f, "{:02x}", g)?;
        write!(f, "{:02x}", b)?;
        write!(f, "{:02x}", a)
    }
}
impl From<ColorRecord> for Color {
    fn from(color: ColorRecord) -> Self {
        Color(
            f32::from(color.red) / 255.0,
            f32::from(color.green) / 255.0,
            f32::from(color.blue) / 255.0,
            f32::from(color.alpha) / 255.0,
        )
    }
}
impl FromStr for Color {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if !s.starts_with('#') || s.len() != 9 || s.chars().skip(1).any(|c| !c.is_ascii_hexdigit())
        {
            return Err(ParseError::BadValue);
        }
        // NOTE(unwrap): Safe as we have verified all chars are ASCII hex digits
        let r = u8::from_str_radix(&s[1..3], 16).unwrap();
        let g = u8::from_str_radix(&s[3..5], 16).unwrap();
        let b = u8::from_str_radix(&s[5..7], 16).unwrap();
        let a = u8::from_str_radix(&s[7..9], 16).unwrap();
        Ok(Color(
            f32::from(r) / 255.0,
            f32::from(g) / 255.0,
            f32::from(b) / 255.0,
            f32::from(a) / 255.0,
        ))
    }
}
impl fmt::Debug for ColrTable<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let ColrTable {
            version,
            base_glyph_records,
            layer_records,
            base_glyph_list,
            layer_list,
            clip_list,
            var_index_map,
            item_variation_store,
        } = self;
        f.debug_struct("ColrTable")
            .field("version", version)
            .field("base_glyph_records", base_glyph_records)
            .field("layer_records", layer_records)
            .field("base_glyph_list", base_glyph_list)
            .field("layer_list", layer_list)
            .field("clip_list", clip_list)
            .field("var_index_map", var_index_map)
            .field(
                "item_variation_store",
                if item_variation_store.is_some() {
                    &"Some(_)"
                } else {
                    &"None"
                },
            )
            .finish()
    }
}
/// Convert the raw angle value in a paint format to degrees.
fn raw_to_degrees(angle: F2Dot14) -> f32 {
    f32::from(angle) * 180.
}
#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        tables::{FontTableProvider, OpenTypeFont},
        tag,
        tests::read_fixture,
        Font,
    };
    #[test]
    fn test_read_colr_v1_variable() {
        let buffer = read_fixture(
            "tests/fonts/colr/SixtyfourConvergence-Regular-VariableFont_BLED,SCAN,XELA,YELA.ttf",
        );
        let otf = ReadScope::new(&buffer).read::<OpenTypeFont<'_>>().unwrap();
        let table_provider = otf.table_provider(0).expect("error reading font file");
        let colr_data = table_provider
            .read_table_data(tag::COLR)
            .expect("unable to read COLR data");
        let colr = ReadScope::new(&colr_data)
            .read::<ColrTable<'_>>()
            .expect("unable to parse COLR table");
        assert!(colr.base_glyph_records.is_empty());
        assert!(colr.layer_records.is_empty());
        assert!(colr.layer_list.is_none());
        assert!(colr.clip_list.is_none());
        assert_eq!(colr.var_index_map.as_ref().map(|map| map.len()), Some(8));
        assert_eq!(
            colr.base_glyph_list.as_ref().map(|list| list.records.len()),
            Some(481)
        );
        assert!(colr.lookup(1).unwrap().is_some());
    }
    #[test]
    #[cfg(feature = "prince")]
    fn test_visit_colr_nabla_glyph() {
        let buffer =
            read_fixture("../../../tests/data/fonts/colr/Nabla-Regular-VariableFont_EDPT,EHLT.ttf");
        let otf = ReadScope::new(&buffer).read::<OpenTypeFont<'_>>().unwrap();
        let table_provider = otf.table_provider(0).expect("error reading font file");
        let mut font = Font::new(table_provider).unwrap();
        let mut painter = DebugVisitor;
        let glyph_id = 62; // 'N'
        match font.visit_colr_glyph(glyph_id, 0, &mut painter) {
            Ok(()) => {}
            Err(err) => panic!("error visiting COLR glyph: {}", err),
        }
    }
}