1
//! CFF2 font handling.
2
//!
3
//! Refer to [OpenType CFF2 spec](https://learn.microsoft.com/en-us/typography/opentype/spec/cff2)
4
//! for more information.
5

            
6
use rustc_hash::{FxHashMap, FxHashSet};
7
use std::fmt::Debug;
8

            
9
use super::{
10
    owned, read_local_subr_index, CFFError, CFFFont, CFFVariant, CIDData, Charset, CustomCharset,
11
    Dict, DictDefault, DictDelta, Encoding, FDSelect, Index, IndexU32, MaybeOwnedIndex, Operand,
12
    default_blue_scale, default_expansion_factor, default_font_matrix, Operator, SubsetCFF,
13
    Type1Data, CFF, DEFAULT_BLUE_FUZZ, DEFAULT_BLUE_SHIFT, ISO_ADOBE_LAST_SID, OFFSET_ZERO,
14
    OPERAND_ZERO,
15
    STANDARD_STRINGS,
16
};
17
use crate::binary::read::{ReadArrayCow, ReadBinary, ReadCtxt, ReadScope};
18
use crate::binary::write::{WriteBinary, WriteBinaryDep, WriteBuffer, WriteContext};
19
use crate::binary::{I16Be, U16Be, U32Be, U8};
20
use crate::cff::charstring::{
21
    operator, ArgumentsStack, CharStringConversionError, CharStringVisitor,
22
    CharStringVisitorContext, VariableCharStringVisitorContext, VisitOp, TWO_BYTE_OPERATOR_MARK,
23
};
24
use crate::cff::subset::{
25
    rebuild_global_subr_index, rebuild_local_subr_indices, rebuild_type_1_local_subr_index,
26
};
27
use crate::error::{ParseError, WriteError};
28
use crate::font::find_good_cmap_subtable;
29
use crate::glyph_info::GlyphNames;
30
use crate::post::PostTable;
31
use crate::subset::SubsetError;
32
use crate::tables::cmap::{Cmap, CmapSubtable};
33
use crate::tables::os2::{FsSelectionFlag, Os2};
34
use crate::tables::variable_fonts::{ItemVariationStore, OwnedTuple};
35
use crate::tables::{
36
    Fixed, FontTableProvider, HeadTable, HheaTable, HmtxTable, MaxpTable, NameTable,
37
};
38
use crate::variations::VariationError;
39
use crate::{cff, tag, SafeFrom, TryNumFrom};
40

            
41
/// Maximum number of operands in Top DICT, Font DICTs, Private DICTs and CharStrings.
42
///
43
/// > Operators in Top DICT, Font DICTs, Private DICTs and CharStrings may be preceded by up to a
44
/// > maximum of 513 operands.
45
pub const MAX_OPERANDS: usize = 513;
46

            
47
/// Top level representation of a CFF2 font file, typically read from a CFF2 OpenType table.
48
///
49
/// [OpenType CFF2 spec](https://learn.microsoft.com/en-us/typography/opentype/spec/cff2)
50
#[derive(Clone)]
51
pub struct CFF2<'a> {
52
    /// CFF2 Header.
53
    pub header: Header,
54
    /// Top DICT with top-level properties of the font.
55
    pub top_dict: TopDict,
56
    /// INDEX of global subroutines.
57
    pub global_subr_index: MaybeOwnedIndex<'a>,
58
    /// INDEX of char strings (glyphs).
59
    pub char_strings_index: MaybeOwnedIndex<'a>,
60
    /// Item variation store. Required/present for variable fonts.
61
    pub vstore: Option<ItemVariationStore<'a>>,
62
    /// Font dict select. Maps glyph ids to Font DICTs.
63
    pub fd_select: Option<FDSelect<'a>>,
64
    /// Sub-fonts of this CFF2 font.
65
    ///
66
    /// Contains Font DICT, Private DICT, and optional local subroutine INDEX.
67
    pub fonts: Vec<Font<'a>>,
68
}
69

            
70
/// CFF2 Font Header
71
///
72
/// <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#6-header>
73
#[derive(Copy, Clone, Debug, PartialEq)]
74
pub struct Header {
75
    /// Major version (2).
76
    pub major: u8,
77
    /// Minor version.
78
    pub minor: u8,
79
    /// Size of the header in the font (maybe larger than this structure).
80
    pub header_size: u8,
81
    /// Length of the Top DICT
82
    pub top_dict_length: u16,
83
}
84

            
85
#[derive(Debug, PartialEq, Clone)]
86
pub struct TopDictDefault;
87

            
88
#[derive(Debug, PartialEq, Clone)]
89
pub struct FontDictDefault;
90

            
91
#[derive(Debug, PartialEq, Clone)]
92
pub struct PrivateDictDefault;
93

            
94
pub type TopDict = Dict<TopDictDefault>;
95

            
96
pub type FontDict = Dict<FontDictDefault>;
97

            
98
pub type PrivateDict = Dict<PrivateDictDefault>;
99

            
100
#[derive(Clone)]
101
pub struct Font<'a> {
102
    pub font_dict: FontDict,
103
    pub private_dict: PrivateDict,
104
    pub local_subr_index: Option<MaybeOwnedIndex<'a>>,
105
}
106

            
107
struct CharStringInstancer<'a> {
108
    new_char_string: &'a mut WriteBuffer,
109
}
110

            
111
struct StringTable<'a> {
112
    /// Maps strings to string ids
113
    strings: FxHashMap<&'a str, u16>,
114
    next_sid: u16,
115
}
116

            
117
// A type for holding CharString operands in their original form (int/fixed point).
118
#[derive(Debug, Copy, Clone)]
119
pub(crate) enum StackValue {
120
    Int(i16),
121
    Fixed(Fixed),
122
}
123

            
124
// The CFF format to output when subsetting CFF2 to CFF.
125
#[derive(Debug, Copy, Clone)]
126
pub enum OutputFormat {
127
    Type1OrCid,
128
    CidOnly,
129
}
130

            
131
impl<'a> CFF2<'a> {
132
    /// Create a non-variable instance of a variable CFF2 font according to `instance`.
133
    pub fn instance_char_strings(&mut self, instance: &OwnedTuple) -> Result<(), VariationError> {
134
        let mut new_char_strings = Vec::with_capacity(self.char_strings_index.len());
135

            
136
        let mut stack = ArgumentsStack {
137
            data: &mut [StackValue::Int(0); MAX_OPERANDS],
138
            len: 0,
139
            max_len: MAX_OPERANDS,
140
        };
141

            
142
        let vstore = self
143
            .vstore
144
            .as_ref()
145
            .ok_or(CFFError::MissingVariationStore)?;
146

            
147
        // For each glyph in the font, apply variations
148
        let mut new_char_string = WriteBuffer::new();
149
        for glyph_id in 0..self.char_strings_index.len() as u16 {
150
            let font_index = match &self.fd_select {
151
                Some(fd_select) => fd_select
152
                    .font_dict_index(glyph_id)
153
                    .ok_or(CFFError::InvalidFontIndex)?,
154
                None => 0,
155
            };
156
            let font = self
157
                .fonts
158
                .get(usize::from(font_index))
159
                .ok_or(CFFError::InvalidFontIndex)?;
160

            
161
            let mut instancer = CharStringInstancer {
162
                new_char_string: &mut new_char_string,
163
            };
164
            let variable = VariableCharStringVisitorContext { vstore, instance };
165
            let mut ctx = CharStringVisitorContext::new(
166
                glyph_id,
167
                &self.char_strings_index,
168
                font.local_subr_index.as_ref(),
169
                &self.global_subr_index,
170
                Some(variable),
171
            );
172

            
173
            stack.clear();
174
            ctx.visit(CFFFont::CFF2(font), &mut stack, &mut instancer)?;
175
            new_char_strings.push(new_char_string.bytes().to_vec());
176
            new_char_string.clear();
177
        }
178

            
179
        // All local subroutines should have been inlined, so they can be dropped now
180
        for font in self.fonts.iter_mut() {
181
            font.local_subr_index = None;
182
            font.private_dict.remove(Operator::Subrs);
183
            // The Private DICT has to be instanced since it can also contain a blend operator
184
            font.private_dict = font.private_dict.instance(instance, vstore)?;
185
        }
186
        // Global subr INDEX is required so make it empty
187
        self.global_subr_index = MaybeOwnedIndex::Owned(owned::Index { data: Vec::new() });
188
        self.char_strings_index = MaybeOwnedIndex::Owned(owned::Index {
189
            data: new_char_strings,
190
        });
191

            
192
        Ok(())
193
    }
194

            
195
    /// Create a subset of this CFF2 font, converting it to CFF.
196
    ///
197
    /// `glyph_ids` contains the ids of the glyphs to retain. It must begin with 0 (`.notdef`).
198
    pub fn subset_to_cff(
199
        &'a self,
200
        glyph_ids: &[u16],
201
        table_provider: &impl FontTableProvider,
202
        include_fstype: bool,
203
        output_format: OutputFormat,
204
    ) -> Result<SubsetCFF<'a>, SubsetError> {
205
        let num_glyphs = u16::try_from(glyph_ids.len()).map_err(|_| SubsetError::TooManyGlyphs)?;
206
        if glyph_ids.first().copied() != Some(0) {
207
            // .notdef must be first
208
            return Err(SubsetError::NotDef);
209
        }
210

            
211
        let mut fd_select = Vec::with_capacity(glyph_ids.len());
212
        let mut new_to_old_id = Vec::with_capacity(glyph_ids.len());
213
        let mut old_to_new_id =
214
            FxHashMap::with_capacity_and_hasher(glyph_ids.len(), Default::default());
215
        let mut glyph_data = Vec::with_capacity(glyph_ids.len());
216
        let mut used_local_subrs = FxHashMap::default();
217
        let mut used_global_subrs = FxHashSet::default();
218

            
219
        // > If generating CFF 1-compatible font instance from a CFF2 variable font that has more
220
        // > than one Font DICT in the Font DICT INDEX, the CFF 1 font must be written as a
221
        // > CID-keyed font.
222
        let type_1 = match output_format {
223
            OutputFormat::Type1OrCid => glyph_ids.len() < 256 && self.fonts.len() == 1,
224
            OutputFormat::CidOnly => false,
225
        };
226

            
227
        // Read tables needed for the conversion
228
        let cmap_data = table_provider.read_table_data(tag::CMAP)?;
229
        let cmap = ReadScope::new(&cmap_data).read::<Cmap<'_>>()?;
230
        let (cmap_subtable_encoding, cmap_subtable_offset) = find_good_cmap_subtable(&cmap)
231
            .map(|(encoding, encoding_record)| (encoding, encoding_record.offset))
232
            .ok_or(ParseError::UnsuitableCmap)?;
233
        let cmap_subtable = ReadScope::new(&cmap_data[usize::safe_from(cmap_subtable_offset)..])
234
            .read::<CmapSubtable<'_>>()
235
            .ok()
236
            .map(|table| (cmap_subtable_encoding, table));
237
        let maxp =
238
            ReadScope::new(&table_provider.read_table_data(tag::MAXP)?).read::<MaxpTable>()?;
239
        let hhea =
240
            ReadScope::new(&table_provider.read_table_data(tag::HHEA)?).read::<HheaTable>()?;
241
        let hmtx_data = table_provider.read_table_data(tag::HMTX)?;
242
        let hmtx = ReadScope::new(&hmtx_data).read_dep::<HmtxTable<'_>>((
243
            usize::from(maxp.num_glyphs),
244
            usize::from(hhea.num_h_metrics),
245
        ))?;
246
        let post_data = table_provider
247
            .table_data(tag::POST)
248
            .unwrap()
249
            .map(|data| data.into_owned().into_boxed_slice());
250
        let post = post_data
251
            .as_ref()
252
            .map(|data| ReadScope::new(data).read::<PostTable<'_>>())
253
            .transpose()?;
254
        let head_data = table_provider.read_table_data(tag::HEAD)?;
255
        let head = ReadScope::new(&head_data).read::<HeadTable>()?;
256
        let os2_data = table_provider.read_table_data(tag::OS_2)?;
257
        let os2 = ReadScope::new(&os2_data).read_dep::<Os2>(os2_data.len())?;
258

            
259
        // Calculate the width of each glyph. These are used to update the CharStrings when
260
        // converting from CFF2 to CFF CharStrings.
261
        let widths = glyph_ids
262
            .iter()
263
            .copied()
264
            .map(|glyph_id| hmtx.horizontal_advance(glyph_id))
265
            .collect::<Result<Vec<_>, _>>()?;
266
        let default_width_x = mode(&widths).unwrap();
267
        let nominal_width_x = default_width_x;
268

            
269
        // Process each glyph (CharString)
270
        for (&glyph_id, &width) in glyph_ids.iter().zip(widths.iter()) {
271
            let font_index = match &self.fd_select {
272
                Some(fd_select) => fd_select
273
                    .font_dict_index(glyph_id)
274
                    .ok_or(CFFError::InvalidFontIndex)?,
275
                None => 0,
276
            };
277
            let font = self
278
                .fonts
279
                .get(usize::from(font_index))
280
                .ok_or(CFFError::InvalidFontIndex)?;
281

            
282
            // Determine which subrs are used
283
            let subrs = super::charstring::char_string_used_subrs(
284
                CFFFont::CFF2(font),
285
                &self.char_strings_index,
286
                &self.global_subr_index,
287
                glyph_id,
288
            )?;
289
            used_global_subrs.extend(subrs.global_subr_used);
290
            if !subrs.local_subr_used.is_empty() {
291
                used_local_subrs.insert(glyph_id, subrs.local_subr_used);
292
            }
293

            
294
            // Convert CFF2 CharString to CFF CharString
295
            let new_char_string = super::charstring::convert_cff2_to_cff(
296
                CFFFont::CFF2(font),
297
                &self.char_strings_index,
298
                &self.global_subr_index,
299
                glyph_id,
300
                width,
301
                default_width_x,
302
                nominal_width_x,
303
            )
304
            .map_err(|err| match err {
305
                CharStringConversionError::Write(err) => SubsetError::Write(err),
306
                CharStringConversionError::Cff(err) => SubsetError::CFF(err),
307
            })?;
308
            glyph_data.push(new_char_string);
309

            
310
            // Cast is safe as we checked that there is less than u16::MAX glyphs at the start
311
            old_to_new_id.insert(glyph_id, new_to_old_id.len() as u16);
312
            new_to_old_id.push(glyph_id);
313

            
314
            fd_select.push(font_index);
315
        }
316

            
317
        // Subset the Global Subr INDEX
318
        let global_subr_index =
319
            rebuild_global_subr_index(&self.global_subr_index, used_global_subrs)?;
320
        let char_strings_index = MaybeOwnedIndex::Owned(owned::Index { data: glyph_data });
321

            
322
        // Build a new Name INDEX
323
        // FontName/CIDFontName
324
        let name_data = table_provider.read_table_data(tag::NAME)?;
325
        let name_table = ReadScope::new(&name_data).read::<NameTable<'_>>()?;
326
        let font_name = name_table
327
            .string_for_id(NameTable::POSTSCRIPT_NAME)
328
            .unwrap_or_else(|| String::from("Untitled"));
329
        let name_index = owned::Index {
330
            data: vec![font_name.into_bytes()],
331
        };
332

            
333
        let mut string_table = StringTable::new();
334
        let glyph_names;
335

            
336
        // Create the charset
337
        let charset = if type_1 {
338
            // Determine the Charset string ids
339
            // Skip the first glyph_id as it is zero/.notdef which is implied in the charset
340
            let glyph_namer = GlyphNames::new(&cmap_subtable, post_data.clone());
341
            glyph_names = glyph_namer.unique_glyph_names(&glyph_ids[1..]);
342
            let charset_sids = glyph_names
343
                .iter()
344
                .map(|name| string_table.get_or_insert(name))
345
                .collect::<Vec<_>>();
346

            
347
            let iso_adobe = 1..=ISO_ADOBE_LAST_SID;
348
            if charset_sids
349
                .iter()
350
                .zip(iso_adobe)
351
                .all(|(sid, iso_adobe_sid)| *sid == iso_adobe_sid)
352
            {
353
                Charset::ISOAdobe
354
            } else {
355
                Charset::Custom(CustomCharset::Format0 {
356
                    glyphs: ReadArrayCow::Owned(charset_sids),
357
                })
358
            }
359
        } else {
360
            // Because we are using identity encoding the glyph ids map to the CIDs
361
            // in a CID-keyed CFF.
362
            Charset::Custom(CustomCharset::Format0 {
363
                glyphs: ReadArrayCow::Owned(glyph_ids[1..].to_vec()),
364
            })
365
        };
366

            
367
        // Top DICT
368
        let mut top_dict = cff::TopDict::new();
369

            
370
        if !type_1 {
371
            // The Top DICT of CID fonts start with ROS to identify it as a CID font
372
            //
373
            // > If generating CFF 1-compatible font instance from a CFF2 variable font that has
374
            // > more than one Font DICT in the Font DICT INDEX, the CFF 1 font must be written as a
375
            // > CID-keyed font. The ROS used should be Adobe-Identity-0. This maps all glyph IDs to
376
            // > a CID of the same value and carries no semantic content.
377
            let registry = Operand::Integer(string_table.get_or_insert("Adobe").into());
378
            let ordering = Operand::Integer(string_table.get_or_insert("Identity").into());
379
            let supplement = Operand::Integer(0);
380
            top_dict
381
                .inner_mut()
382
                .push((Operator::ROS, vec![registry, ordering, supplement]));
383
        }
384

            
385
        // If the FontMatrix operator is present then copy it across
386
        if let Some(matrix) = self.top_dict.get(Operator::FontMatrix) {
387
            top_dict
388
                .inner_mut()
389
                .push((Operator::FontMatrix, matrix.to_vec()));
390
        }
391

            
392
        // Version
393
        //
394
        // > Equivalent to the fontRevision field in the 'head' table. A CFF 1 version operand can
395
        // > be derived from the fontRevision field, which is a 16.16 Fixed value, and formatting it
396
        // > as a decimal number with three decimal places of precision.
397
        let version = format!("{:.3}", f32::from(head.font_revision));
398
        let sid = string_table.get_or_insert(&version);
399
        top_dict
400
            .inner_mut()
401
            .push((Operator::Version, vec![Operand::Integer(sid.into())]));
402

            
403
        // Notice
404
        //
405
        // > Equivalent to the concatenation of strings from the 'name' table: the Copyright string
406
        // > (name ID 0), a space, followed by the Trademark string (name ID 7).
407
        let copyright = name_table
408
            .string_for_id(NameTable::COPYRIGHT_NOTICE)
409
            .unwrap_or_else(|| String::from("Unspecified"));
410
        let notice = if let Some(trademark) = name_table.string_for_id(NameTable::TRADEMARK) {
411
            format!("{copyright} {trademark}")
412
        } else {
413
            copyright.clone()
414
        };
415
        // Add notice to the string table and push its SID as the operand
416
        let sid = string_table.get_or_insert(&notice);
417
        top_dict
418
            .inner_mut()
419
            .push((Operator::Notice, vec![Operand::Integer(sid.into())]));
420

            
421
        // Copyright
422
        let sid = string_table.get_or_insert(&copyright);
423
        top_dict
424
            .inner_mut()
425
            .push((Operator::Copyright, vec![Operand::Integer(sid.into())]));
426

            
427
        // Full Name
428
        //
429
        // > Full font name that reflects all family and relevant subfamily descriptors.
430
        // > The full font name is generally a combination of name IDs 1 and 2,
431
        // > or of name IDs 16 and 17, or a similar human-readable variant.
432
        let full_name = name_table
433
            .string_for_id(NameTable::FULL_FONT_NAME)
434
            .or_else(|| {
435
                match (
436
                    name_table.string_for_id(NameTable::FONT_FAMILY_NAME),
437
                    name_table.string_for_id(NameTable::FONT_SUBFAMILY_NAME),
438
                ) {
439
                    (Some(family), Some(subfamily)) => Some(format!("{family} {subfamily}")),
440
                    _ => None,
441
                }
442
            })
443
            .or_else(|| {
444
                match (
445
                    name_table.string_for_id(NameTable::TYPOGRAPHIC_FAMILY_NAME),
446
                    name_table.string_for_id(NameTable::TYPOGRAPHIC_SUBFAMILY_NAME),
447
                ) {
448
                    (Some(family), Some(subfamily)) => Some(format!("{family} {subfamily}")),
449
                    _ => None,
450
                }
451
            })
452
            .unwrap_or_else(|| {
453
                let bold = os2.fs_selection.contains(FsSelectionFlag::BOLD);
454
                let italic = os2.fs_selection.contains(FsSelectionFlag::ITALIC);
455
                match (bold, italic) {
456
                    (true, true) => String::from("Unknown Bold Italic"),
457
                    (true, false) => String::from("Unknown Bold"),
458
                    (false, true) => String::from("Unknown Italic"),
459
                    (false, false) => String::from("Unknown Regular"),
460
                }
461
            });
462
        let sid = string_table.get_or_insert(&full_name);
463
        top_dict
464
            .inner_mut()
465
            .push((Operator::FullName, vec![Operand::Integer(sid.into())]));
466

            
467
        // Family Name
468
        let family_name = name_table
469
            .string_for_id(NameTable::TYPOGRAPHIC_FAMILY_NAME)
470
            .or_else(|| name_table.string_for_id(NameTable::FONT_FAMILY_NAME))
471
            .unwrap_or_else(|| String::from("Unknown"));
472
        let sid = string_table.get_or_insert(&family_name);
473
        top_dict
474
            .inner_mut()
475
            .push((Operator::FamilyName, vec![Operand::Integer(sid.into())]));
476

            
477
        // Weight
478
        // In the Top DICT the weight is stored as a string, map the numeric us_weight_class to
479
        // a weight name, favouring names in the CFF standard strings
480
        let weight = match os2.us_weight_class {
481
            0..=149 => "Thin",
482
            150..=249 => "Extra-light",
483
            250..=349 => "Light",
484
            350..=449 => "Regular",
485
            450..=549 => "Medium",
486
            550..=649 => "Semibold",
487
            650..=749 => "Bold",
488
            750..=849 => "Extra-bold",
489
            850.. => "Black",
490
        };
491
        let sid = string_table.get_or_insert(weight);
492
        top_dict
493
            .inner_mut()
494
            .push((Operator::Weight, vec![Operand::Integer(sid.into())]));
495

            
496
        // FontBBox
497
        // Default is 0 0 0 0, so only add if any value is non-zero
498
        if [head.x_min, head.y_min, head.x_max, head.y_max]
499
            .iter()
500
            .any(|val| *val != 0)
501
        {
502
            let bbox = vec![
503
                Operand::Integer(head.x_min.into()),
504
                Operand::Integer(head.y_min.into()),
505
                Operand::Integer(head.x_max.into()),
506
                Operand::Integer(head.y_max.into()),
507
            ];
508
            top_dict.inner_mut().push((Operator::FontBBox, bbox));
509
        }
510

            
511
        // All these operators have defaults so if the `post` table is absent the defaults will be
512
        // used.
513
        if let Some(post) = post {
514
            let is_fixed_pitch = post.header.is_fixed_pitch;
515
            if is_fixed_pitch != 0 {
516
                top_dict
517
                    .inner_mut()
518
                    .push((Operator::IsFixedPitch, vec![Operand::Integer(1)]));
519
            }
520

            
521
            let italic_angle = post.header.italic_angle;
522
            if italic_angle != 0 {
523
                top_dict
524
                    .inner_mut()
525
                    .push((Operator::ItalicAngle, vec![Operand::Integer(italic_angle)]));
526
            }
527

            
528
            let underline_position = post.header.underline_position;
529
            if underline_position != -100 {
530
                top_dict.inner_mut().push((
531
                    Operator::UnderlinePosition,
532
                    vec![Operand::Integer(underline_position.into())],
533
                ));
534
            }
535

            
536
            let underline_thickness = post.header.underline_thickness;
537
            if underline_thickness != 50 {
538
                top_dict.inner_mut().push((
539
                    Operator::UnderlineThickness,
540
                    vec![Operand::Integer(underline_thickness.into())],
541
                ));
542
            }
543
        }
544

            
545
        // PaintType: There is no equivalent in a CFF2 font. If deriving CFF 1-compatible data,
546
        // use the CFF 1 default value of zero.
547
        // StrokeWidth: Use default
548

            
549
        // PostScript
550
        // > There is no equivalent in a CFF2 font. CFF 1 allowed for embedded PostScript code, but
551
        // > this was only ever used in CFF 1 OpenType fonts to provide an FSType key in the Top
552
        // > DICT, to carry the font embedding permissions from the fsType field of the OS/2 table.
553
        // > If deriving CFF 1-compatible data, the value can be copied from the OS/2 table.
554
        //
555
        // — https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-19-cff-1-top-dict-operators-not-used-in-cff2
556
        //
557
        // > When OpenType fonts are converted into CFF for embedding in
558
        // > a document, the font embedding information specified by the
559
        // > FSType bits, and the type of the original font, should be included
560
        // > in the resulting file. (See Technical Note #5147: “Font Embed-
561
        // > ding Guidelines for Adobe Third-party Developers,” for more
562
        // > information.)
563
        // >
564
        // > The embedding information is added to the Top DICT using the
565
        // > PostScript operator (12 21) with a SID operand. The SID points to
566
        // > a string containing the PostScript commands and arguments in
567
        // > the String INDEX.
568
        //
569
        // — https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf pp56
570

            
571
        // [tx](http://adobe-type-tools.github.io/afdko/AFDKO-Overview.html#tx) emits a warning
572
        // when this is present in addition to the fsType in the OS/2 table. Only include it
573
        // when requested by caller.
574
        let post_script;
575
        if include_fstype {
576
            post_script = format!("/FSType {} def /OrigFontType /TrueType def", os2.fs_type);
577
            let sid = string_table.get_or_insert(&post_script);
578
            top_dict
579
                .inner_mut()
580
                .push((Operator::PostScript, vec![Operand::Integer(sid.into())]));
581
        }
582

            
583
        // CIDFontVersion: default
584
        // CIFFontRevision: default
585
        // CIDFontType: default
586

            
587
        // CIDCount
588
        if !type_1 {
589
            top_dict.inner_mut().push((
590
                Operator::CIDCount,
591
                vec![Operand::Integer(num_glyphs.into())],
592
            ));
593
        }
594

            
595
        // Charset - will be updated when writing
596
        //
597
        // The choice of 1 for the placeholder offset is load bearing. The Charset and Encoding
598
        // operators take an offset as their operand but unlike other operators that do this they
599
        // also have a default of 0 since there are some reserved values that refer to predefined
600
        // data. If 0 is used as the placeholder here when calculating the size of the TopDict
601
        // during the WriteBinary impl CFF the operator is omitted since it matches the default.
602
        // However, when the Top DICT is written the offset is always written since it's updated
603
        // the actual offset when it does not refer to predefined data. Using 1 here ensures that
604
        // it is a non-default value and is not omitted when calculating the size of the written
605
        // Top DICT.
606
        top_dict
607
            .inner_mut()
608
            .push((Operator::Charset, vec![Operand::Offset(1)]));
609

            
610
        // Encoding - omitted as we use the default, Standard Encoding
611

            
612
        // CharStrings INDEX offset, will be updated when writing Top DICT
613
        top_dict
614
            .inner_mut()
615
            .push((Operator::CharStrings, vec![Operand::Offset(0)]));
616

            
617
        // The font names need to be stored out here because string_table borrows them. So they
618
        // have to live as long as that.
619
        let mut font_names = if type_1 {
620
            Vec::new()
621
        } else {
622
            Vec::with_capacity(self.fonts.len())
623
        };
624

            
625
        // Private DICT and CID/Type 1 specific structures
626
        let variant = if type_1 {
627
            let font = &self.fonts[0]; // There can only be one font if type_1 is true
628

            
629
            // Build new local_subr_index
630
            let local_subr_index = rebuild_type_1_local_subr_index(
631
                self.fonts[0].local_subr_index.as_ref(),
632
                used_local_subrs,
633
            )?;
634

            
635
            // Build new Private DICT
636
            let mut private_dict = cff::PrivateDict::new();
637
            for (op, operands) in font.private_dict.iter() {
638
                // Filter out Subr ops in the Private DICT if the local subr INDEX is None.
639
                if *op == Operator::Subrs && local_subr_index.is_none() {
640
                    continue;
641
                }
642

            
643
                private_dict.inner_mut().push((*op, operands.clone()));
644
            }
645
            private_dict.inner_mut().push((
646
                Operator::DefaultWidthX,
647
                vec![Operand::Integer(default_width_x.into())],
648
            ));
649
            private_dict.inner_mut().push((
650
                Operator::NominalWidthX,
651
                vec![Operand::Integer(nominal_width_x.into())],
652
            ));
653

            
654
            // Ensure Private operator is in Top DICT
655
            // Size and offset operands will be updated when written out. Both are set to offsets
656
            // so their output size is predictable
657
            top_dict.inner_mut().push((
658
                Operator::Private,
659
                vec![Operand::Offset(0), Operand::Offset(0)],
660
            ));
661

            
662
            let type1 = Type1Data {
663
                encoding: Encoding::Standard,
664
                private_dict,
665
                local_subr_index,
666
            };
667

            
668
            CFFVariant::Type1(type1)
669
        } else {
670
            // Populate font names
671
            (0..self.fonts.len()).for_each(|i| {
672
                let name = format!("{family_name}-{weight}-Part{}", i + 1);
673
                font_names.push(name);
674
            });
675

            
676
            // Build Font DICT and Private DICT for each font
677
            let mut private_dicts = Vec::with_capacity(self.fonts.len());
678
            let mut font_dicts = Vec::with_capacity(self.fonts.len());
679
            for (font, name) in self.fonts.iter().zip(font_names.iter()) {
680
                // Font DICT
681
                let mut font_dict = cff::FontDict::new();
682
                for (op, operands) in font.font_dict.iter() {
683
                    font_dict.inner_mut().push((*op, operands.clone()));
684
                }
685

            
686
                let sid = string_table.get_or_insert(name);
687
                font_dict.replace(Operator::FontName, vec![Operand::Integer(sid.into())]);
688

            
689
                let mut buf = WriteBuffer::new();
690
                cff::FontDict::write_dep(&mut buf, &font_dict, DictDelta::new())?;
691
                font_dicts.push(buf.into_inner());
692

            
693
                // Private DICT
694
                let mut private_dict = cff::PrivateDict::new();
695
                for (op, operands) in font.private_dict.iter() {
696
                    // Filter out Subr ops in the Private DICT if the local subr INDEX is None
697
                    // for this DICT.
698
                    if *op == Operator::Subrs && font.local_subr_index.is_none() {
699
                        continue;
700
                    }
701
                    private_dict.inner_mut().push((*op, operands.clone()));
702
                }
703
                private_dict.inner_mut().push((
704
                    Operator::DefaultWidthX,
705
                    vec![Operand::Integer(default_width_x.into())],
706
                ));
707
                private_dict.inner_mut().push((
708
                    Operator::NominalWidthX,
709
                    vec![Operand::Integer(nominal_width_x.into())],
710
                ));
711
                private_dicts.push(private_dict);
712
            }
713
            let font_dict_index = MaybeOwnedIndex::Owned(owned::Index { data: font_dicts });
714

            
715
            // Add placeholders for FDArray and FDSelect
716
            top_dict
717
                .inner_mut()
718
                .push((Operator::FDArray, vec![Operand::Offset(0)]));
719
            top_dict
720
                .inner_mut()
721
                .push((Operator::FDSelect, vec![Operand::Offset(0)]));
722

            
723
            // Ideally we'd combine this with rebuilding the subr indices below
724
            let (rebuild, local_subr_indices) = if used_local_subrs.is_empty() {
725
                // If there is no used local subrs then all the indices will be None
726
                (false, vec![None; self.fonts.len()])
727
            } else {
728
                (
729
                    true,
730
                    self.fonts
731
                        .iter()
732
                        .map(|font| font.local_subr_index.clone())
733
                        .collect(),
734
                )
735
            };
736

            
737
            let mut cid_data = CIDData {
738
                font_dict_index,
739
                private_dicts,
740
                local_subr_indices,
741
                fd_select: FDSelect::Format0 {
742
                    glyph_font_dict_indices: ReadArrayCow::Owned(fd_select),
743
                },
744
            };
745

            
746
            // Build new local_subr_indices, but only if any local subrs are actually present
747
            if rebuild {
748
                cid_data.local_subr_indices =
749
                    rebuild_local_subr_indices(&cid_data, used_local_subrs)?;
750
            }
751

            
752
            CFFVariant::CID(cid_data)
753
        };
754

            
755
        let font = cff::Font {
756
            top_dict,
757
            char_strings_index,
758
            charset,
759
            data: variant,
760
        };
761

            
762
        // Build new String INDEX
763
        let string_index = string_table.into_string_index();
764

            
765
        let cff = CFF {
766
            header: super::Header {
767
                major: 1,
768
                minor: 0,
769
                hdr_size: 4, // Ignored by WriteBinary
770
                off_size: 4, // We always use 32-bit offsets
771
            },
772
            name_index: MaybeOwnedIndex::Owned(name_index),
773
            string_index: MaybeOwnedIndex::Owned(string_index),
774
            global_subr_index,
775
            fonts: vec![font],
776
        };
777

            
778
        Ok(SubsetCFF::new(cff, new_to_old_id, old_to_new_id))
779
    }
780
}
781

            
782
impl<'a> StringTable<'a> {
783
    fn new() -> Self {
784
        // Load the standard strings into the lookup table
785
        // NOTE(cast): Safe as STANDARD_STRINGS has statically known valid length
786
        let strings = STANDARD_STRINGS
787
            .iter()
788
            .enumerate()
789
            .map(|(sid, &string)| (string, sid as u16))
790
            .collect();
791
        StringTable {
792
            strings,
793
            next_sid: STANDARD_STRINGS.len() as u16,
794
        }
795
    }
796

            
797
    /// find the name in the standard strings, or the string index, or insert into the string index
798
    fn get_or_insert(&mut self, s: &'a str) -> u16 {
799
        // Do a little dance to avoid borrowck errors mutating self.next_sid inside or_insert_with.
800
        let mut next_sid = self.next_sid;
801

            
802
        let sid = *self.strings.entry(s).or_insert_with(|| {
803
            let sid = next_sid;
804
            next_sid += 1;
805
            sid
806
        });
807

            
808
        if next_sid != self.next_sid {
809
            self.next_sid = next_sid;
810
        }
811
        sid
812
    }
813

            
814
    pub fn into_string_index(self) -> owned::Index {
815
        let mut data = Vec::with_capacity(self.strings.len());
816
        let mut strings = self.strings.into_iter().collect::<Vec<_>>();
817
        strings.sort_unstable_by_key(|(_string, sid)| *sid);
818
        let non_standard_strings = strings.into_iter().filter_map(|(string, sid)| {
819
            (usize::from(sid) >= STANDARD_STRINGS.len()).then_some(string)
820
        });
821

            
822
        for string in non_standard_strings {
823
            data.push(string.as_bytes().to_vec());
824
        }
825
        owned::Index { data }
826
    }
827
}
828

            
829
impl CharStringVisitor<StackValue, VariationError> for CharStringInstancer<'_> {
830
    fn visit(
831
        &mut self,
832
        op: VisitOp,
833
        stack: &ArgumentsStack<'_, StackValue>,
834
    ) -> Result<(), VariationError> {
835
        match op {
836
            VisitOp::HorizontalStem
837
            | VisitOp::VerticalStem
838
            | VisitOp::HorizontalStemHintMask
839
            | VisitOp::VerticalStemHintMask
840
            | VisitOp::VerticalMoveTo
841
            | VisitOp::LineTo
842
            | VisitOp::HorizontalLineTo
843
            | VisitOp::VerticalLineTo
844
            | VisitOp::CurveTo
845
            | VisitOp::HintMask
846
            | VisitOp::CounterMask
847
            | VisitOp::MoveTo
848
            | VisitOp::HorizontalMoveTo
849
            | VisitOp::CurveLine
850
            | VisitOp::LineCurve
851
            | VisitOp::VvCurveTo
852
            | VisitOp::HhCurveTo
853
            | VisitOp::VhCurveTo
854
            | VisitOp::HvCurveTo => {
855
                write_stack(self.new_char_string, stack)?;
856
                Ok(U8::write(self.new_char_string, op)?)
857
            }
858
            VisitOp::Return | VisitOp::Endchar => {
859
                // Removed in CFF2
860
                Err(CFFError::InvalidOperator.into())
861
            }
862
            VisitOp::Hflex | VisitOp::Flex | VisitOp::Hflex1 | VisitOp::Flex1 => {
863
                write_stack(self.new_char_string, stack)?;
864
                U8::write(self.new_char_string, TWO_BYTE_OPERATOR_MARK)?;
865
                Ok(U8::write(self.new_char_string, op)?)
866
            }
867
            VisitOp::VsIndex | VisitOp::Blend => Ok(()),
868
        }
869
    }
870

            
871
    fn hint_data(&mut self, _op: VisitOp, hints: &[u8]) -> Result<(), VariationError> {
872
        Ok(self.new_char_string.write_bytes(hints)?)
873
    }
874
}
875

            
876
impl ReadBinary for CFF2<'_> {
877
    type HostType<'a> = CFF2<'a>;
878

            
879
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
880
        // Get a scope that starts at the beginning of the CFF data. This is needed for reading
881
        // data that is specified as an offset from the start of the data later.
882
        let scope = ctxt.scope();
883

            
884
        let header = ctxt.read::<Header>()?;
885
        let top_dict_data = ctxt.read_slice(usize::from(header.top_dict_length))?;
886
        let top_dict = ReadScope::new(top_dict_data)
887
            .ctxt()
888
            .read_dep::<TopDict>(MAX_OPERANDS)?;
889
        let global_subr_index = ctxt.read::<IndexU32>().map(MaybeOwnedIndex::Borrowed)?;
890

            
891
        // CharStrings index
892
        let char_strings_offset = top_dict
893
            .get_i32(Operator::CharStrings)
894
            .unwrap_or(Err(ParseError::MissingValue))?;
895
        let char_strings_index = scope
896
            .offset(usize::try_from(char_strings_offset)?)
897
            .read::<IndexU32>()
898
            .map(MaybeOwnedIndex::Borrowed)?;
899
        let n_glyphs = char_strings_index.len();
900

            
901
        // Font DICT Index
902
        let fd_array_offset = top_dict
903
            .get_i32(Operator::FDArray)
904
            .unwrap_or(Err(ParseError::MissingValue))?;
905
        let fd_array = scope
906
            .offset(usize::try_from(fd_array_offset)?)
907
            .read::<IndexU32>()?;
908

            
909
        // FDSelect if more than one font is present
910
        let fd_select = if fd_array.count > 1 {
911
            let fs_select_offset = top_dict
912
                .get_i32(Operator::FDSelect)
913
                .unwrap_or(Err(ParseError::MissingValue))?;
914
            scope
915
                .offset(usize::try_from(fs_select_offset)?)
916
                .read_dep::<FDSelect<'a>>(n_glyphs)
917
                .map(Some)?
918
        } else {
919
            None
920
        };
921

            
922
        // VariationStore for variable fonts (required for variable fonts, absent otherwise)
923
        let vstore = top_dict
924
            .get_i32(Operator::VStore)
925
            .transpose()?
926
            .map(|offset| {
927
                let mut ctxt = scope.offset(usize::try_from(offset)?).ctxt();
928
                // "The VariationStore data is comprised of two parts: a uint16 field that specifies
929
                // a length, followed by an Item Variation Store structure of the specified length."
930
                let _length = ctxt.read_u16be()?;
931
                ctxt.read::<ItemVariationStore<'_>>()
932
            })
933
            .transpose()?;
934

            
935
        // Font/glyph data
936
        let mut fonts = Vec::with_capacity(fd_array.count);
937
        for font_index in 0..fd_array.count {
938
            let font_dict = fd_array.read::<FontDict>(font_index, MAX_OPERANDS)?;
939
            let (private_dict, private_dict_offset) =
940
                font_dict.read_private_dict::<PrivateDict>(&scope, MAX_OPERANDS)?;
941
            let local_subr_index =
942
                read_local_subr_index::<_, IndexU32>(&scope, &private_dict, private_dict_offset)?
943
                    .map(MaybeOwnedIndex::Borrowed);
944

            
945
            fonts.push(Font {
946
                font_dict,
947
                private_dict,
948
                local_subr_index,
949
            });
950
        }
951

            
952
        Ok(CFF2 {
953
            header,
954
            top_dict,
955
            global_subr_index,
956
            char_strings_index,
957
            vstore,
958
            fd_select,
959
            fonts,
960
        })
961
    }
962
}
963

            
964
impl WriteBinary for CFF2<'_> {
965
    type Output = ();
966

            
967
    fn write<C: WriteContext>(ctxt: &mut C, cff2: Self) -> Result<Self::Output, WriteError> {
968
        // Build new Top DICT
969
        let mut top_dict = TopDict::new();
970
        if let Some(font_matrix) = cff2.top_dict.get(Operator::FontMatrix) {
971
            top_dict
972
                .inner_mut()
973
                .push((Operator::FontMatrix, font_matrix.to_vec()))
974
        }
975
        // Add CharStrings INDEX, FDSelect, and FDArray offsets.
976
        // Actual offsets will be filled in when writing
977
        top_dict
978
            .inner_mut()
979
            .push((Operator::CharStrings, OFFSET_ZERO.to_vec()));
980
        top_dict
981
            .inner_mut()
982
            .push((Operator::FDArray, OFFSET_ZERO.to_vec()));
983
        if cff2.fonts.len() > 1 {
984
            // The FDSelect operator and the structure it points to are required if the Font DICT INDEX
985
            // contains more than one Font DICT, else it must be omitted.
986
            top_dict
987
                .inner_mut()
988
                .push((Operator::FDSelect, OFFSET_ZERO.to_vec()));
989
        }
990
        if cff2.vstore.is_some() {
991
            top_dict
992
                .inner_mut()
993
                .push((Operator::VStore, OFFSET_ZERO.to_vec()));
994
        }
995

            
996
        // Calculate size of TopDict
997
        let mut write_buffer = WriteBuffer::new();
998
        TopDict::write_dep(&mut write_buffer, &top_dict, DictDelta::new())?;
999
        let top_dict_length = u16::try_from(write_buffer.bytes_written())?;
        // Now that the size of the Top DICT is known we can write out the header
        let header = Header {
            top_dict_length,
            ..cff2.header
        };
        Header::write(ctxt, header)?;
        // Reserve space for the Top DICT to be filled in later when the offsets it holds are resolved.
        let top_dict_placeholder = ctxt.reserve::<TopDict, _>(usize::from(top_dict_length))?;
        // Write global Subr INDEX
        MaybeOwnedIndex::write32(ctxt, &cff2.global_subr_index)?;
        // CharStrings INDEX
        top_dict.replace(
            Operator::CharStrings,
            vec![Operand::Offset(i32::try_from(ctxt.bytes_written())?)],
        );
        MaybeOwnedIndex::write32(ctxt, &cff2.char_strings_index)?;
        // FDSelect
        match &cff2.fd_select {
            Some(fd_select) if cff2.fonts.len() > 1 => {
                top_dict.replace(
                    Operator::FDSelect,
                    vec![Operand::Offset(i32::try_from(ctxt.bytes_written())?)],
                );
                FDSelect::write(ctxt, fd_select)?;
            }
            // If there is more than one font then FDSelect is required
            None if cff2.fonts.len() > 1 => return Err(WriteError::BadValue),
            Some(_) | None => {}
        }
        // Write out Private DICTs and Local Subr INDEXes so the offsets can be updated
        let mut font_dicts = Vec::with_capacity(cff2.fonts.len());
        for font in &cff2.fonts {
            // Write Local Subr INDEX
            let local_subr_offset = if let Some(local_subr_index) = &font.local_subr_index {
                let local_subr_offset = i32::try_from(ctxt.bytes_written())?;
                MaybeOwnedIndex::write32(ctxt, local_subr_index)?;
                Some(local_subr_offset)
            } else {
                None
            };
            // Write Private DICT
            // NOTE: The offset to local subrs INDEX is from the start of the Private DICT.
            let private_dict_offset = i32::try_from(ctxt.bytes_written())?;
            let mut private_dict_deltas = DictDelta::new();
            if let Some(local_subr_offset) = local_subr_offset {
                private_dict_deltas
                    .push_offset(Operator::Subrs, local_subr_offset - private_dict_offset);
            }
            PrivateDict::write_dep(ctxt, &font.private_dict, private_dict_deltas)?;
            let private_dict_len = i32::try_from(ctxt.bytes_written())? - private_dict_offset;
            // Build and write out new Font DICT
            let mut font_dict = FontDict::new();
            font_dict.inner_mut().push((
                Operator::Private,
                vec![
                    Operand::Offset(private_dict_len),
                    Operand::Offset(private_dict_offset),
                ],
            ));
            let mut font_dict_buffer = WriteBuffer::new();
            FontDict::write_dep(&mut font_dict_buffer, &font_dict, DictDelta::new())?;
            font_dicts.push(font_dict_buffer.into_inner());
        }
        // Font DICT INDEX
        top_dict.replace(
            Operator::FDArray,
            vec![Operand::Offset(i32::try_from(ctxt.bytes_written())?)],
        );
        let font_dict_index = owned::Index { data: font_dicts };
        owned::IndexU32::write(ctxt, &font_dict_index)?;
        // Variation store, if present
        if let Some(variation_store) = &cff2.vstore {
            top_dict.replace(
                Operator::VStore,
                vec![Operand::Offset(i32::try_from(ctxt.bytes_written())?)],
            );
            ItemVariationStore::write(ctxt, variation_store)?;
        }
        // Now that the offsets are known, write out the Top DICT
        ctxt.write_placeholder_dep(top_dict_placeholder, &top_dict, DictDelta::new())?;
        Ok(())
    }
}
pub(crate) fn write_stack(
    new_char_string: &mut WriteBuffer,
    stack: &ArgumentsStack<'_, StackValue>,
) -> Result<(), WriteError> {
    stack
        .all()
        .iter()
        .try_for_each(|value| write_stack_value(*value, new_char_string))
}
pub(crate) fn write_stack_value(
    value: StackValue,
    new_char_string: &mut WriteBuffer,
) -> Result<(), WriteError> {
    StackValue::write(new_char_string, value)
}
impl BlendOperand for StackValue {
    fn try_as_i32(self) -> Option<i32> {
        match self {
            StackValue::Int(int) => Some(i32::from(int)),
            StackValue::Fixed(fixed) => i32::try_num_from(f32::from(fixed)),
        }
    }
    fn try_as_u16(self) -> Option<u16> {
        match self {
            StackValue::Int(int) => u16::try_from(int).ok(),
            StackValue::Fixed(fixed) => u16::try_num_from(f32::from(fixed)),
        }
    }
    fn try_as_u8(self) -> Option<u8> {
        match self {
            StackValue::Int(int) => u8::try_from(int).ok(),
            StackValue::Fixed(fixed) => u8::try_num_from(f32::from(fixed)),
        }
    }
}
impl WriteBinary for StackValue {
    type Output = ();
    fn write<C: WriteContext>(ctxt: &mut C, val: Self) -> Result<Self::Output, WriteError> {
        match val {
            // Refer to Table 3 Operand Encoding in section 4 of Technical Note #5176 for details on the
            // integer encoding scheme.
            StackValue::Int(int) => {
                match int {
                    // NOTE: Casts are safe due to patterns limiting range
                    -107..=107 => U8::write(ctxt, (int + 139) as u8),
                    108..=1131 => {
                        let int = int - 108;
                        U8::write(ctxt, ((int >> 8) + 247) as u8)?;
                        U8::write(ctxt, int as u8)
                    }
                    -1131..=-108 => {
                        let int = -int - 108;
                        U8::write(ctxt, ((int >> 8) + 251) as u8)?;
                        U8::write(ctxt, int as u8)
                    }
                    -32768..=32767 => {
                        U8::write(ctxt, operator::SHORT_INT)?;
                        I16Be::write(ctxt, int)
                    }
                }
            }
            StackValue::Fixed(fixed) => {
                U8::write(ctxt, operator::FIXED_16_16)?;
                Fixed::write(ctxt, fixed)
            }
        }
    }
}
impl From<StackValue> for f32 {
    fn from(value: StackValue) -> Self {
        match value {
            StackValue::Int(int) => f32::from(int),
            StackValue::Fixed(fixed) => f32::from(fixed),
        }
    }
}
impl From<f32> for StackValue {
    fn from(value: f32) -> Self {
        if value.fract() == 0.0 {
            StackValue::Int(value as i16)
        } else {
            StackValue::Fixed(Fixed::from(value))
        }
    }
}
impl From<i16> for StackValue {
    fn from(value: i16) -> Self {
        StackValue::Int(value)
    }
}
impl From<Fixed> for StackValue {
    fn from(value: Fixed) -> Self {
        StackValue::Fixed(value)
    }
}
/// Trait for values that can be used to implement the `blend` operator.
pub trait BlendOperand: Debug + Copy + Into<f32> + From<f32> + From<i16> + From<Fixed> {
    /// Try to convert `self` into an `i32`.
    fn try_as_i32(self) -> Option<i32>;
    /// Try to convert `self` into a `u16`.
    fn try_as_u16(self) -> Option<u16>;
    /// Try to convert `self` into a `u8`.
    fn try_as_u8(self) -> Option<u8>;
}
pub(super) fn scalars(
    vs_index: u16,
    vstore: &ItemVariationStore<'_>,
    instance: &OwnedTuple,
) -> Result<Vec<Option<f32>>, ParseError> {
    // Each region can now produce its scalar for the particular variation tuple
    vstore
        .regions(vs_index)?
        .map(|region| {
            let region = region?;
            Ok(region.scalar(instance.iter().copied()))
        })
        .collect::<Result<Vec<_>, ParseError>>()
}
pub(super) fn blend<T: BlendOperand>(
    scalars: &[Option<f32>],
    stack: &mut ArgumentsStack<'_, T>,
) -> Result<(), CFFError> {
    // > For k regions, produces n interpolated result value(s) from n*(k + 1) operands.
    //
    // > The last operand on the stack, n, specifies the number of operands that will be left on the
    // > stack for the next operator.
    //
    // > For example, if the blend operator is used in conjunction with the hflex operator, which
    // > requires 6 operands, then n would be set to 6. This operand also informs the handler for
    // > the blend operator that the operator is preceded by n+1 sets of operands. Clear all but n
    // > values from the stack, leaving the values for the subsequent operator corresponding to the
    // > default instance
    let k = scalars.len();
    if stack.len < 1 {
        return Err(CFFError::InvalidArgumentsStackLength);
    }
    let n = stack
        .pop()
        .try_as_u16()
        .map(usize::from)
        .ok_or(CFFError::InvalidOperand)?;
    let num_operands = n * (k + 1);
    if stack.len() < num_operands {
        return Err(CFFError::InvalidArgumentsStackLength);
    }
    // Process n*k operands applying the scalars
    let mut blended = [0.0; MAX_OPERANDS]; // 513 * 32-bit = 2KiB
    let blended = blended.get_mut(..n).ok_or(CFFError::InvalidOperand)?;
    let operands = stack.pop_n(num_operands);
    // for each set of deltas apply the scalar and calculate a new delta to
    // apply to the default values
    let (defaults, rest) = operands.split_at(n);
    for (adjustment, deltas) in blended.iter_mut().zip(rest.chunks(k)) {
        for (delta, scalar) in deltas.iter().copied().zip(scalars.iter()) {
            if let Some(scalar) = scalar {
                *adjustment += scalar * delta.into();
            }
        }
    }
    // apply the deltas to the default values
    defaults
        .iter()
        .copied()
        .zip(blended.iter_mut())
        .for_each(|(default, delta)| *delta += default.into());
    // push the blended values back onto the stack
    blended
        .iter_mut()
        .try_for_each(|value| stack.push(T::from(*value)))
}
impl Header {
    // Sum of size of the four fields in the header
    const SIZE: u8 = 1 + 1 + 1 + 2;
}
impl ReadBinary for Header {
    type HostType<'b> = Self;
    fn read(ctxt: &mut ReadCtxt<'_>) -> Result<Self, ParseError> {
        let major = ctxt.read_u8()?;
        ctxt.check(major == 2)?;
        let minor = ctxt.read_u8()?;
        let header_size = ctxt.read_u8()?;
        let top_dict_length = ctxt.read_u16be()?;
        if header_size < Header::SIZE {
            return Err(ParseError::BadValue);
        }
        // Skip any unknown data
        let _unknown = ctxt.read_slice((header_size - Header::SIZE) as usize)?;
        Ok(Header {
            major,
            minor,
            header_size,
            top_dict_length,
        })
    }
}
impl WriteBinary for Header {
    type Output = ();
    fn write<C: WriteContext>(ctxt: &mut C, header: Self) -> Result<Self::Output, WriteError> {
        U8::write(ctxt, header.major)?;
        U8::write(ctxt, header.minor)?;
        U8::write(ctxt, Header::SIZE)?;
        U16Be::write(ctxt, header.top_dict_length)?;
        Ok(())
    }
}
impl ReadBinary for IndexU32 {
    type HostType<'a> = Index<'a>;
    fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
        let count = usize::safe_from(ctxt.read_u32be()?);
        super::read_index(ctxt, count)
    }
}
impl<'a> WriteBinary<&Index<'a>> for IndexU32 {
    type Output = ();
    fn write<C: WriteContext>(ctxt: &mut C, index: &Index<'a>) -> Result<(), WriteError> {
        U32Be::write(ctxt, u16::try_from(index.count)?)?;
        super::write_index_body(ctxt, index)
    }
}
impl DictDefault for TopDictDefault {
    fn default(op: Operator) -> Option<&'static [Operand]> {
        match op {
            Operator::FontMatrix => Some(default_font_matrix().as_ref()),
            _ => None,
        }
    }
}
impl DictDefault for FontDictDefault {
    fn default(_op: Operator) -> Option<&'static [Operand]> {
        None
    }
}
impl DictDefault for PrivateDictDefault {
    fn default(op: Operator) -> Option<&'static [Operand]> {
        match op {
            Operator::BlueScale => Some(default_blue_scale().as_ref()),
            Operator::BlueShift => Some(&DEFAULT_BLUE_SHIFT),
            Operator::BlueFuzz => Some(&DEFAULT_BLUE_FUZZ),
            Operator::LanguageGroup => Some(&OPERAND_ZERO),
            Operator::ExpansionFactor => Some(default_expansion_factor().as_ref()),
            Operator::VSIndex => Some(&OPERAND_ZERO),
            _ => None,
        }
    }
}
/// Calculate the mode (most common value)
fn mode(widths: &[u16]) -> Option<u16> {
    if widths.is_empty() {
        return None;
    }
    let mut sorted_widths = widths.to_vec();
    sorted_widths.sort_unstable();
    let mut mode = (sorted_widths[0], 1);
    let last = sorted_widths
        .iter()
        .copied()
        .fold((sorted_widths[0], 0), |(prev, count), width| {
            if width == prev {
                (prev, count + 1)
            } else {
                if count > mode.1 {
                    mode = (prev, count)
                }
                (width, 1)
            }
        });
    if last.1 > mode.1 {
        mode = last
    }
    Some(mode.0)
}
#[cfg(test)]
mod tests {
    use super::*;
    use crate::tables::variable_fonts::avar::AvarTable;
    use crate::tables::variable_fonts::fvar::FvarTable;
    use crate::tables::{Fixed, FontTableProvider, OpenTypeData, OpenTypeFont};
    use crate::tag;
    use crate::tests::read_fixture;
    #[test]
    fn read_cff2() {
        let buffer = read_fixture("tests/fonts/opentype/cff2/SourceSansVariable-Roman.abc.otf");
        let otf = ReadScope::new(&buffer).read::<OpenTypeFont<'_>>().unwrap();
        let offset_table = match otf.data {
            OpenTypeData::Single(ttf) => ttf,
            OpenTypeData::Collection(_) => unreachable!(),
        };
        let cff_table_data = offset_table
            .read_table(&otf.scope, tag::CFF2)
            .unwrap()
            .unwrap();
        let cff = cff_table_data
            .read::<CFF2<'_>>()
            .expect("error parsing CFF2 table");
        assert_eq!(cff.header.major, 2);
        let vstore = cff.vstore.as_ref().unwrap();
        assert_eq!(vstore.variation_region_list.variation_regions.len(), 3);
        assert_eq!(vstore.item_variation_data.len(), 2);
        for i in 0..vstore.item_variation_data.len() as u16 {
            let regions = vstore.regions(i).unwrap();
            for region in regions {
                assert!(region.is_ok());
            }
        }
    }
    #[test]
    fn instance_char_strings() {
        let buffer = read_fixture("tests/fonts/opentype/cff2/SourceSansVariable-Roman.abc.otf");
        let otf = ReadScope::new(&buffer).read::<OpenTypeFont<'_>>().unwrap();
        let offset_table = match otf.data {
            OpenTypeData::Single(ttf) => ttf,
            OpenTypeData::Collection(_) => unreachable!(),
        };
        let cff2_table_data = offset_table
            .read_table(&otf.scope, tag::CFF2)
            .unwrap()
            .unwrap();
        let mut cff2 = cff2_table_data
            .read::<CFF2<'_>>()
            .expect("error parsing CFF2 table");
        let fvar_data = offset_table
            .read_table(&otf.scope, tag::FVAR)
            .unwrap()
            .unwrap();
        let fvar = fvar_data
            .read::<FvarTable<'_>>()
            .expect("unable to parse fvar");
        let avar_data = offset_table.read_table(&otf.scope, tag::FVAR).unwrap();
        let avar = avar_data
            .map(|avar_data| avar_data.read::<AvarTable<'_>>())
            .transpose()
            .expect("unable to parse avar table");
        let user_tuple = [Fixed::from(654.0)];
        let normalised_tuple = fvar
            .normalize(user_tuple.iter().copied(), avar.as_ref())
            .expect("unable to normalise user tuple");
        assert!(cff2.instance_char_strings(&normalised_tuple).is_ok());
    }
    #[test]
    fn subset_cff2_table() {
        let buffer = read_fixture("tests/fonts/opentype/cff2/SourceSans3.abc.otf");
        let otf = ReadScope::new(&buffer).read::<OpenTypeFont<'_>>().unwrap();
        let provider = otf.table_provider(0).expect("error reading font file");
        let cff2_data = provider
            .read_table_data(tag::CFF2)
            .expect("unable to read CFF2 data");
        let cff2 = ReadScope::new(&cff2_data)
            .read::<CFF2<'_>>()
            .expect("error parsing CFF2 table");
        let subset = cff2
            .subset_to_cff(&[0, 1], &provider, true, OutputFormat::Type1OrCid)
            .expect("unable to subset CFF2");
        // Write it out
        let mut buf = WriteBuffer::new();
        CFF::write(&mut buf, &subset.into()).unwrap();
        let subset_data = buf.into_inner();
        // Read it back
        let _subset_cff = ReadScope::new(&subset_data)
            .read::<CFF<'_>>()
            .expect("error parsing CFF2 table");
    }
    #[test]
    fn test_mode() {
        assert_eq!(mode(&[]), None);
        assert_eq!(mode(&[5]), Some(5));
        assert_eq!(mode(&[4, 1, 9, 1, 9, 4, 5, 6, 8, 3, 4]), Some(4));
        assert_eq!(mode(&[4, 1, 9, 1, 9, 4, 5, 6, 8, 3, 7]), Some(1));
        assert_eq!(mode(&[4, 1, 9, 1, 9, 4, 5, 6, 8, 3, 9]), Some(9));
    }
}