Coverage Report

Created: 2025-07-10 06:41

/src/ttf-parser/src/tables/cff/cff2.rs
Line
Count
Source (jump to first uncovered line)
1
//! A [Compact Font Format 2 Table](
2
//! https://docs.microsoft.com/en-us/typography/opentype/spec/cff2) implementation.
3
4
// https://docs.microsoft.com/en-us/typography/opentype/spec/cff2charstr
5
6
use core::convert::TryFrom;
7
use core::ops::Range;
8
9
use super::argstack::ArgumentsStack;
10
use super::charstring::CharStringParser;
11
use super::dict::DictionaryParser;
12
use super::index::{parse_index, Index};
13
use super::{calc_subroutine_bias, conv_subroutine_index, Builder, CFFError};
14
use crate::parser::{NumFrom, Stream, TryNumFrom};
15
use crate::var_store::*;
16
use crate::{GlyphId, NormalizedCoordinate, OutlineBuilder, Rect, RectF};
17
18
// https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#7-top-dict-data
19
// 'Operators in DICT may be preceded by up to a maximum of 513 operands.'
20
const MAX_OPERANDS_LEN: usize = 513;
21
22
// https://docs.microsoft.com/en-us/typography/opentype/spec/cff2charstr#appendix-b-cff2-charstring-implementation-limits
23
const STACK_LIMIT: u8 = 10;
24
const MAX_ARGUMENTS_STACK_LEN: usize = 513;
25
26
const TWO_BYTE_OPERATOR_MARK: u8 = 12;
27
28
// https://docs.microsoft.com/en-us/typography/opentype/spec/cff2charstr#4-charstring-operators
29
mod operator {
30
    pub const HORIZONTAL_STEM: u8 = 1;
31
    pub const VERTICAL_STEM: u8 = 3;
32
    pub const VERTICAL_MOVE_TO: u8 = 4;
33
    pub const LINE_TO: u8 = 5;
34
    pub const HORIZONTAL_LINE_TO: u8 = 6;
35
    pub const VERTICAL_LINE_TO: u8 = 7;
36
    pub const CURVE_TO: u8 = 8;
37
    pub const CALL_LOCAL_SUBROUTINE: u8 = 10;
38
    pub const VS_INDEX: u8 = 15;
39
    pub const BLEND: u8 = 16;
40
    pub const HORIZONTAL_STEM_HINT_MASK: u8 = 18;
41
    pub const HINT_MASK: u8 = 19;
42
    pub const COUNTER_MASK: u8 = 20;
43
    pub const MOVE_TO: u8 = 21;
44
    pub const HORIZONTAL_MOVE_TO: u8 = 22;
45
    pub const VERTICAL_STEM_HINT_MASK: u8 = 23;
46
    pub const CURVE_LINE: u8 = 24;
47
    pub const LINE_CURVE: u8 = 25;
48
    pub const VV_CURVE_TO: u8 = 26;
49
    pub const HH_CURVE_TO: u8 = 27;
50
    pub const SHORT_INT: u8 = 28;
51
    pub const CALL_GLOBAL_SUBROUTINE: u8 = 29;
52
    pub const VH_CURVE_TO: u8 = 30;
53
    pub const HV_CURVE_TO: u8 = 31;
54
    pub const HFLEX: u8 = 34;
55
    pub const FLEX: u8 = 35;
56
    pub const HFLEX1: u8 = 36;
57
    pub const FLEX1: u8 = 37;
58
    pub const FIXED_16_16: u8 = 255;
59
}
60
61
// https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#table-9-top-dict-operator-entries
62
mod top_dict_operator {
63
    pub const CHAR_STRINGS_OFFSET: u16 = 17;
64
    pub const VARIATION_STORE_OFFSET: u16 = 24;
65
    pub const FONT_DICT_INDEX_OFFSET: u16 = 1236;
66
}
67
68
// https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#table-10-font-dict-operator-entries
69
mod font_dict_operator {
70
    pub const PRIVATE_DICT_SIZE_AND_OFFSET: u16 = 18;
71
}
72
73
// https://docs.microsoft.com/en-us/typography/opentype/spec/cff2#table-16-private-dict-operators
74
mod private_dict_operator {
75
    pub const LOCAL_SUBROUTINES_OFFSET: u16 = 19;
76
}
77
78
#[derive(Clone, Copy, Default)]
79
struct TopDictData {
80
    char_strings_offset: usize,
81
    font_dict_index_offset: Option<usize>,
82
    variation_store_offset: Option<usize>,
83
}
84
85
2.89k
fn parse_top_dict(data: &[u8]) -> Option<TopDictData> {
86
2.89k
    let mut dict_data = TopDictData::default();
87
2.89k
88
2.89k
    let mut operands_buffer = [0.0; MAX_OPERANDS_LEN];
89
2.89k
    let mut dict_parser = DictionaryParser::new(data, &mut operands_buffer);
90
834k
    while let Some(operator) = dict_parser.parse_next() {
91
831k
        if operator.get() == top_dict_operator::CHAR_STRINGS_OFFSET {
92
5.03k
            dict_data.char_strings_offset = dict_parser.parse_offset()?;
93
826k
        } else if operator.get() == top_dict_operator::FONT_DICT_INDEX_OFFSET {
94
10.7k
            dict_data.font_dict_index_offset = dict_parser.parse_offset();
95
816k
        } else if operator.get() == top_dict_operator::VARIATION_STORE_OFFSET {
96
17.8k
            dict_data.variation_store_offset = dict_parser.parse_offset();
97
798k
        }
98
    }
99
100
    // Must be set, otherwise there are nothing to parse.
101
2.67k
    if dict_data.char_strings_offset == 0 {
102
376
        return None;
103
2.30k
    }
104
2.30k
105
2.30k
    Some(dict_data)
106
2.89k
}
107
108
14.4k
fn parse_font_dict(data: &[u8]) -> Option<Range<usize>> {
109
14.4k
    let mut private_dict_range = None;
110
14.4k
111
14.4k
    let mut operands_buffer = [0.0; MAX_OPERANDS_LEN];
112
14.4k
    let mut dict_parser = DictionaryParser::new(data, &mut operands_buffer);
113
89.2k
    while let Some(operator) = dict_parser.parse_next() {
114
77.6k
        if operator.get() == font_dict_operator::PRIVATE_DICT_SIZE_AND_OFFSET {
115
2.95k
            dict_parser.parse_operands()?;
116
2.74k
            let operands = dict_parser.operands();
117
2.74k
118
2.74k
            if operands.len() == 2 {
119
1.73k
                let len = usize::try_from(operands[0] as i32).ok()?;
120
1.51k
                let start = usize::try_from(operands[1] as i32).ok()?;
121
1.30k
                let end = start.checked_add(len)?;
122
1.30k
                private_dict_range = Some(start..end);
123
1.00k
            }
124
125
2.31k
            break;
126
74.7k
        }
127
    }
128
129
13.8k
    private_dict_range
130
14.4k
}
131
132
1.17k
fn parse_private_dict(data: &[u8]) -> Option<usize> {
133
1.17k
    let mut subroutines_offset = None;
134
1.17k
    let mut operands_buffer = [0.0; MAX_OPERANDS_LEN];
135
1.17k
    let mut dict_parser = DictionaryParser::new(data, &mut operands_buffer);
136
96.0k
    while let Some(operator) = dict_parser.parse_next() {
137
95.7k
        if operator.get() == private_dict_operator::LOCAL_SUBROUTINES_OFFSET {
138
833
            dict_parser.parse_operands()?;
139
779
            let operands = dict_parser.operands();
140
779
141
779
            if operands.len() == 1 {
142
469
                subroutines_offset = usize::try_from(operands[0] as i32).ok();
143
469
            }
144
145
779
            break;
146
94.9k
        }
147
    }
148
149
1.11k
    subroutines_offset
150
1.17k
}
151
152
/// CFF2 allows up to 65535 scalars, but an average font will have 3-5.
153
/// So 64 is more than enough.
154
const SCALARS_MAX: u8 = 64;
155
156
#[derive(Clone, Copy)]
157
pub(crate) struct Scalars {
158
    d: [f32; SCALARS_MAX as usize], // 256B
159
    len: u8,
160
}
161
162
impl Default for Scalars {
163
101k
    fn default() -> Self {
164
101k
        Scalars {
165
101k
            d: [0.0; SCALARS_MAX as usize],
166
101k
            len: 0,
167
101k
        }
168
101k
    }
169
}
170
171
impl Scalars {
172
108k
    pub fn len(&self) -> u8 {
173
108k
        self.len
174
108k
    }
175
176
102k
    pub fn clear(&mut self) {
177
102k
        self.len = 0;
178
102k
    }
179
180
685k
    pub fn at(&self, i: u8) -> f32 {
181
685k
        if i < self.len {
182
685k
            self.d[usize::from(i)]
183
        } else {
184
0
            0.0
185
        }
186
685k
    }
187
188
487k
    pub fn push(&mut self, n: f32) -> Option<()> {
189
487k
        if self.len < SCALARS_MAX {
190
485k
            self.d[usize::from(self.len)] = n;
191
485k
            self.len += 1;
192
485k
            Some(())
193
        } else {
194
2.18k
            None
195
        }
196
487k
    }
197
}
198
199
struct CharStringParserContext<'a> {
200
    metadata: &'a Table<'a>,
201
    coordinates: &'a [NormalizedCoordinate],
202
    scalars: Scalars,
203
    had_vsindex: bool,
204
    had_blend: bool,
205
    stems_len: u32,
206
}
207
208
impl CharStringParserContext<'_> {
209
102k
    fn update_scalars(&mut self, index: u16) -> Result<(), CFFError> {
210
102k
        self.scalars.clear();
211
212
102k
        let indices = self
213
102k
            .metadata
214
102k
            .item_variation_store
215
102k
            .region_indices(index)
216
102k
            .ok_or(CFFError::InvalidItemVariationDataIndex)?;
217
585k
        for index in indices {
218
487k
            let scalar = self
219
487k
                .metadata
220
487k
                .item_variation_store
221
487k
                .regions
222
487k
                .evaluate_region(index, self.coordinates);
223
487k
            self.scalars
224
487k
                .push(scalar)
225
487k
                .ok_or(CFFError::BlendRegionsLimitReached)?;
226
        }
227
228
97.7k
        Ok(())
229
102k
    }
230
}
231
232
101k
fn parse_char_string(
233
101k
    data: &[u8],
234
101k
    metadata: &Table,
235
101k
    coordinates: &[NormalizedCoordinate],
236
101k
    builder: &mut dyn OutlineBuilder,
237
101k
) -> Result<Rect, CFFError> {
238
101k
    let mut ctx = CharStringParserContext {
239
101k
        metadata,
240
101k
        coordinates,
241
101k
        scalars: Scalars::default(),
242
101k
        had_vsindex: false,
243
101k
        had_blend: false,
244
101k
        stems_len: 0,
245
101k
    };
246
101k
247
101k
    // Load scalars at default index.
248
101k
    ctx.update_scalars(0)?;
249
250
97.0k
    let mut inner_builder = Builder {
251
97.0k
        builder,
252
97.0k
        bbox: RectF::new(),
253
97.0k
    };
254
97.0k
255
97.0k
    let stack = ArgumentsStack {
256
97.0k
        data: &mut [0.0; MAX_ARGUMENTS_STACK_LEN], // 2052B
257
97.0k
        len: 0,
258
97.0k
        max_len: MAX_ARGUMENTS_STACK_LEN,
259
97.0k
    };
260
97.0k
    let mut parser = CharStringParser {
261
97.0k
        stack,
262
97.0k
        builder: &mut inner_builder,
263
97.0k
        x: 0.0,
264
97.0k
        y: 0.0,
265
97.0k
        has_move_to: false,
266
97.0k
        is_first_move_to: true,
267
97.0k
        width_only: false,
268
97.0k
    };
269
97.0k
    _parse_char_string(&mut ctx, data, 0, &mut parser)?;
270
    // let _ = _parse_char_string(&mut ctx, data, 0.0, 0.0, &mut stack, 0, &mut inner_builder)?;
271
272
41.0k
    let bbox = parser.builder.bbox;
273
41.0k
274
41.0k
    // Check that bbox was changed.
275
41.0k
    if bbox.is_default() {
276
35.7k
        return Err(CFFError::ZeroBBox);
277
5.24k
    }
278
5.24k
279
5.24k
    bbox.to_rect().ok_or(CFFError::BboxOverflow)
280
101k
}
281
282
195k
fn _parse_char_string(
283
195k
    ctx: &mut CharStringParserContext,
284
195k
    char_string: &[u8],
285
195k
    depth: u8,
286
195k
    p: &mut CharStringParser,
287
195k
) -> Result<(), CFFError> {
288
195k
    let mut s = Stream::new(char_string);
289
2.25M
    while !s.at_end() {
290
2.13M
        let op = s.read::<u8>().ok_or(CFFError::ReadOutOfBounds)?;
291
2.13M
        match op {
292
            0 | 2 | 9 | 11 | 13 | 14 | 17 => {
293
                // Reserved.
294
26.3k
                return Err(CFFError::InvalidOperator);
295
            }
296
            operator::HORIZONTAL_STEM
297
            | operator::VERTICAL_STEM
298
            | operator::HORIZONTAL_STEM_HINT_MASK
299
22.1k
            | operator::VERTICAL_STEM_HINT_MASK => {
300
22.1k
                // y dy {dya dyb}* hstem
301
22.1k
                // x dx {dxa dxb}* vstem
302
22.1k
                // y dy {dya dyb}* hstemhm
303
22.1k
                // x dx {dxa dxb}* vstemhm
304
22.1k
305
22.1k
                ctx.stems_len += p.stack.len() as u32 >> 1;
306
22.1k
307
22.1k
                // We are ignoring the hint operators.
308
22.1k
                p.stack.clear();
309
22.1k
            }
310
            operator::VERTICAL_MOVE_TO => {
311
7.30k
                p.parse_vertical_move_to(0)?;
312
            }
313
            operator::LINE_TO => {
314
27.5k
                p.parse_line_to()?;
315
            }
316
            operator::HORIZONTAL_LINE_TO => {
317
19.8k
                p.parse_horizontal_line_to()?;
318
            }
319
            operator::VERTICAL_LINE_TO => {
320
11.6k
                p.parse_vertical_line_to()?;
321
            }
322
            operator::CURVE_TO => {
323
2.01k
                p.parse_curve_to()?;
324
            }
325
            operator::CALL_LOCAL_SUBROUTINE => {
326
102k
                if p.stack.is_empty() {
327
782
                    return Err(CFFError::InvalidArgumentsStackLength);
328
101k
                }
329
101k
330
101k
                if depth == STACK_LIMIT {
331
7
                    return Err(CFFError::NestingLimitReached);
332
101k
                }
333
101k
334
101k
                let subroutine_bias = calc_subroutine_bias(ctx.metadata.local_subrs.len());
335
101k
                let index = conv_subroutine_index(p.stack.pop(), subroutine_bias)?;
336
101k
                let char_string = ctx
337
101k
                    .metadata
338
101k
                    .local_subrs
339
101k
                    .get(index)
340
101k
                    .ok_or(CFFError::InvalidSubroutineIndex)?;
341
98.5k
                _parse_char_string(ctx, char_string, depth + 1, p)?;
342
            }
343
            TWO_BYTE_OPERATOR_MARK => {
344
                // flex
345
2.80k
                let op2 = s.read::<u8>().ok_or(CFFError::ReadOutOfBounds)?;
346
2.71k
                match op2 {
347
285
                    operator::HFLEX => p.parse_hflex()?,
348
516
                    operator::FLEX => p.parse_flex()?,
349
727
                    operator::HFLEX1 => p.parse_hflex1()?,
350
683
                    operator::FLEX1 => p.parse_flex1()?,
351
504
                    _ => return Err(CFFError::UnsupportedOperator),
352
                }
353
            }
354
            operator::VS_INDEX => {
355
                // |- ivs vsindex (15) |-
356
357
                // `vsindex` must precede the first `blend` operator, and may occur only once.
358
2.30k
                if ctx.had_blend || ctx.had_vsindex {
359
                    // TODO: maybe add a custom error
360
281
                    return Err(CFFError::InvalidOperator);
361
2.02k
                }
362
2.02k
363
2.02k
                if p.stack.len() != 1 {
364
817
                    return Err(CFFError::InvalidArgumentsStackLength);
365
1.21k
                }
366
367
1.21k
                let index = u16::try_num_from(p.stack.pop())
368
1.21k
                    .ok_or(CFFError::InvalidItemVariationDataIndex)?;
369
1.05k
                ctx.update_scalars(index)?;
370
371
710
                ctx.had_vsindex = true;
372
710
373
710
                p.stack.clear();
374
            }
375
            operator::BLEND => {
376
                // num(0)..num(n-1), delta(0,0)..delta(k-1,0),
377
                // delta(0,1)..delta(k-1,1) .. delta(0,n-1)..delta(k-1,n-1)
378
                // n blend (16) val(0)..val(n-1)
379
380
108k
                ctx.had_blend = true;
381
382
108k
                let n = u16::try_num_from(p.stack.pop())
383
108k
                    .ok_or(CFFError::InvalidNumberOfBlendOperands)?;
384
108k
                let k = ctx.scalars.len();
385
108k
386
108k
                let len = usize::from(n) * (usize::from(k) + 1);
387
108k
                if p.stack.len() < len {
388
2.26k
                    return Err(CFFError::InvalidArgumentsStackLength);
389
105k
                }
390
105k
391
105k
                let start = p.stack.len() - len;
392
343k
                for i in (0..n).rev() {
393
685k
                    for j in 0..k {
394
685k
                        let delta = p.stack.pop();
395
685k
                        p.stack.data[start + usize::from(i)] += delta * ctx.scalars.at(k - j - 1);
396
685k
                    }
397
                }
398
            }
399
3.74k
            operator::HINT_MASK | operator::COUNTER_MASK => {
400
3.74k
                ctx.stems_len += p.stack.len() as u32 >> 1;
401
3.74k
                s.advance(usize::num_from((ctx.stems_len + 7) >> 3));
402
3.74k
403
3.74k
                // We are ignoring the hint operators.
404
3.74k
                p.stack.clear();
405
3.74k
            }
406
            operator::MOVE_TO => {
407
15.7k
                p.parse_move_to(0)?;
408
            }
409
            operator::HORIZONTAL_MOVE_TO => {
410
7.14k
                p.parse_horizontal_move_to(0)?;
411
            }
412
            operator::CURVE_LINE => {
413
1.60k
                p.parse_curve_line()?;
414
            }
415
            operator::LINE_CURVE => {
416
1.53k
                p.parse_line_curve()?;
417
            }
418
            operator::VV_CURVE_TO => {
419
6.04k
                p.parse_vv_curve_to()?;
420
            }
421
            operator::HH_CURVE_TO => {
422
6.75k
                p.parse_hh_curve_to()?;
423
            }
424
            operator::SHORT_INT => {
425
5.06k
                let n = s.read::<i16>().ok_or(CFFError::ReadOutOfBounds)?;
426
4.84k
                p.stack.push(f32::from(n))?;
427
            }
428
            operator::CALL_GLOBAL_SUBROUTINE => {
429
1.11k
                if p.stack.is_empty() {
430
453
                    return Err(CFFError::InvalidArgumentsStackLength);
431
657
                }
432
657
433
657
                if depth == STACK_LIMIT {
434
0
                    return Err(CFFError::NestingLimitReached);
435
657
                }
436
657
437
657
                let subroutine_bias = calc_subroutine_bias(ctx.metadata.global_subrs.len());
438
657
                let index = conv_subroutine_index(p.stack.pop(), subroutine_bias)?;
439
487
                let char_string = ctx
440
487
                    .metadata
441
487
                    .global_subrs
442
487
                    .get(index)
443
487
                    .ok_or(CFFError::InvalidSubroutineIndex)?;
444
34
                _parse_char_string(ctx, char_string, depth + 1, p)?;
445
            }
446
            operator::VH_CURVE_TO => {
447
7.56k
                p.parse_vh_curve_to()?;
448
            }
449
            operator::HV_CURVE_TO => {
450
10.6k
                p.parse_hv_curve_to()?;
451
            }
452
1.69M
            32..=246 => {
453
1.51M
                p.parse_int1(op)?;
454
            }
455
176k
            247..=250 => {
456
76.9k
                p.parse_int2(op, &mut s)?;
457
            }
458
99.4k
            251..=254 => {
459
99.4k
                p.parse_int3(op, &mut s)?;
460
            }
461
            operator::FIXED_16_16 => {
462
39.2k
                p.parse_fixed(&mut s)?;
463
            }
464
        }
465
    }
466
467
119k
    Ok(())
468
195k
}
469
470
/// A [Compact Font Format 2 Table](
471
/// https://docs.microsoft.com/en-us/typography/opentype/spec/cff2).
472
#[derive(Clone, Copy, Default)]
473
pub struct Table<'a> {
474
    global_subrs: Index<'a>,
475
    local_subrs: Index<'a>,
476
    char_strings: Index<'a>,
477
    item_variation_store: ItemVariationStore<'a>,
478
}
479
480
impl<'a> Table<'a> {
481
    /// Parses a table from raw data.
482
3.14k
    pub fn parse(data: &'a [u8]) -> Option<Self> {
483
3.14k
        let mut s = Stream::new(data);
484
485
        // Parse Header.
486
3.14k
        let major = s.read::<u8>()?;
487
3.12k
        s.skip::<u8>(); // minor
488
3.12k
        let header_size = s.read::<u8>()?;
489
3.11k
        let top_dict_length = s.read::<u16>()?;
490
491
3.10k
        if major != 2 {
492
139
            return None;
493
2.97k
        }
494
2.97k
495
2.97k
        // Jump to Top DICT. It's not necessarily right after the header.
496
2.97k
        if header_size > 5 {
497
990
            s.advance(usize::from(header_size) - 5);
498
1.98k
        }
499
500
2.97k
        let top_dict_data = s.read_bytes(usize::from(top_dict_length))?;
501
2.89k
        let top_dict = parse_top_dict(top_dict_data)?;
502
503
2.30k
        let mut metadata = Self::default();
504
2.30k
505
2.30k
        // Parse Global Subroutines INDEX.
506
2.30k
        metadata.global_subrs = parse_index::<u32>(&mut s)?;
507
508
        metadata.char_strings = {
509
1.96k
            let mut s = Stream::new_at(data, top_dict.char_strings_offset)?;
510
1.86k
            parse_index::<u32>(&mut s)?
511
        };
512
513
1.81k
        if let Some(offset) = top_dict.variation_store_offset {
514
1.10k
            let mut s = Stream::new_at(data, offset)?;
515
1.04k
            s.skip::<u16>(); // length
516
1.04k
            metadata.item_variation_store = ItemVariationStore::parse(s)?;
517
713
        }
518
519
        // TODO: simplify
520
1.58k
        if let Some(offset) = top_dict.font_dict_index_offset {
521
1.11k
            let mut s = Stream::new_at(data, offset)?;
522
14.4k
            'outer: for font_dict_data in parse_index::<u32>(&mut s)? {
523
14.4k
                if let Some(private_dict_range) = parse_font_dict(font_dict_data) {
524
                    // 'Private DICT size and offset, from start of the CFF2 table.'
525
1.30k
                    let private_dict_data = data.get(private_dict_range.clone())?;
526
1.17k
                    if let Some(subroutines_offset) = parse_private_dict(private_dict_data) {
527
                        // 'The local subroutines offset is relative to the beginning
528
                        // of the Private DICT data.'
529
174
                        if let Some(start) =
530
174
                            private_dict_range.start.checked_add(subroutines_offset)
531
                        {
532
174
                            let data = data.get(start..data.len())?;
533
94
                            let mut s = Stream::new(data);
534
94
                            metadata.local_subrs = parse_index::<u32>(&mut s)?;
535
86
                            break 'outer;
536
0
                        }
537
999
                    }
538
13.1k
                }
539
            }
540
473
        }
541
542
1.30k
        Some(metadata)
543
3.14k
    }
544
545
    /// Outlines a glyph.
546
20.9M
    pub fn outline(
547
20.9M
        &self,
548
20.9M
        coordinates: &[NormalizedCoordinate],
549
20.9M
        glyph_id: GlyphId,
550
20.9M
        builder: &mut dyn OutlineBuilder,
551
20.9M
    ) -> Result<Rect, CFFError> {
552
20.9M
        let data = self
553
20.9M
            .char_strings
554
20.9M
            .get(u32::from(glyph_id.0))
555
20.9M
            .ok_or(CFFError::NoGlyph)?;
556
101k
        parse_char_string(data, self, coordinates, builder)
557
20.9M
    }
558
}
559
560
impl core::fmt::Debug for Table<'_> {
561
0
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
562
0
        write!(f, "Table {{ ... }}")
563
0
    }
564
}