Skip to main content

hayro_jbig2/
lib.rs

1/*!
2A memory-safe, pure-Rust JBIG2 decoder.
3
4`hayro-jbig2` decodes JBIG2 images as specified in ITU-T T.88 (also known as
5ISO/IEC 14492). JBIG2 is a bi-level image compression standard commonly used
6in PDF documents for compressing scanned text documents.
7
8The crate is `no_std` compatible but requires an allocator to be available.
9
10# Safety
11This crate forbids unsafe code via a crate-level attribute.
12*/
13
14#![cfg_attr(not(feature = "std"), no_std)]
15#![forbid(unsafe_code)]
16#![allow(missing_docs)]
17
18extern crate alloc;
19
20use alloc::vec::Vec;
21
22use crate::arithmetic_decoder::ArithmeticDecoderContext;
23
24/// A reusable context for decoding JBIG2 images.
25#[derive(Default)]
26pub struct DecoderContext {
27    pub(crate) page_state: PageState,
28    pub(crate) scratch_buffers: ScratchBuffers,
29    pub(crate) page_bitmap: Bitmap,
30}
31
32#[derive(Default)]
33pub(crate) struct ScratchBuffers {
34    pub(crate) contexts: Vec<ArithmeticDecoderContext>,
35}
36
37/// A decoder for JBIG2 images.
38pub trait Decoder {
39    /// Push a single pixel to the output.
40    fn push_pixel(&mut self, black: bool);
41    /// Push multiple chunks of 8 pixels of the same color.
42    ///
43    /// The `chunk_count` parameter indicates how many 8-pixel chunks to push.
44    /// For example, if this method is called with `white = true` and
45    /// `chunk_count = 10`, 80 white pixels are pushed (10 × 8 = 80).
46    ///
47    /// You can assume that this method is only called if the number of already
48    /// pushed pixels is a multiple of 8 (i.e. byte-aligned).
49    fn push_pixel_chunk(&mut self, black: bool, chunk_count: u32);
50    /// Called when a row has been completed.
51    fn next_line(&mut self);
52}
53
54#[cfg(feature = "image")]
55pub mod integration;
56
57mod arithmetic_decoder;
58mod bitmap;
59mod decode;
60mod error;
61mod file;
62mod gray_scale;
63mod huffman_table;
64mod integer_decoder;
65mod lazy;
66mod page_info;
67mod reader;
68mod segment;
69mod simd;
70mod symbol_id_decoder;
71
72use error::bail;
73pub use error::{
74    DecodeError, FormatError, HuffmanError, OverflowError, ParseError, RegionError, Result,
75    SegmentError, SymbolError, TemplateError,
76};
77
78use crate::file::parse_segments_sequential;
79use bitmap::Bitmap;
80use decode::CombinationOperator;
81use decode::generic;
82use decode::generic_refinement;
83use decode::halftone;
84use decode::pattern;
85use decode::pattern::PatternDictionary;
86use decode::symbol;
87use decode::symbol::SymbolDictionary;
88use decode::text;
89use file::parse_file;
90use huffman_table::{HuffmanTable, StandardHuffmanTables};
91use page_info::{PageInformation, parse_page_information};
92use reader::Reader;
93use segment::SegmentType;
94
95/// A JBIG2 image.
96pub struct Image<'a> {
97    /// The parsed segments.
98    segments: Vec<segment::Segment<'a>>,
99    /// The width of the image in pixels.
100    width: u32,
101    /// The height of the image in pixels.
102    height: u32,
103    /// The height determined from `EndOfStripe` segments, if applicable.
104    height_from_stripes: Option<u32>,
105}
106
107impl<'a> Image<'a> {
108    /// Parse a JBIG2 file from the given data.
109    ///
110    /// The file is expected to use the sequential or random-access organization,
111    /// as defined in Annex D.1 and D.2.
112    pub fn new(data: &'a [u8]) -> Result<Self> {
113        let file = parse_file(data)?;
114        Self::from_segments(file.segments)
115    }
116
117    /// Parse an embedded JBIG2 image with optional global segments.
118    ///
119    /// The file is expected to use the embedded organization defined in
120    /// Annex D.3.
121    pub fn new_embedded(data: &'a [u8], globals: Option<&'a [u8]>) -> Result<Self> {
122        let mut segments = Vec::new();
123        if let Some(globals_data) = globals {
124            let mut reader = Reader::new(globals_data);
125            parse_segments_sequential(&mut reader, &mut segments)?;
126        };
127
128        let mut reader = Reader::new(data);
129        parse_segments_sequential(&mut reader, &mut segments)?;
130
131        segments.sort_by_key(|seg| seg.header.segment_number);
132
133        Self::from_segments(segments)
134    }
135
136    fn from_segments(segments: Vec<segment::Segment<'a>>) -> Result<Self> {
137        // Pre-scan for stripe height from EndOfStripe segments.
138        let height_from_stripes = segments
139            .iter()
140            .filter(|seg| seg.header.segment_type == SegmentType::EndOfStripe)
141            .filter_map(|seg| u32::from_be_bytes(seg.data.try_into().ok()?).checked_add(1))
142            .max();
143
144        // Find and parse page information to extract dimensions.
145        let page_info_seg = segments
146            .iter()
147            .find(|s| s.header.segment_type == SegmentType::PageInformation)
148            .ok_or(FormatError::MissingPageInfo)?;
149
150        let mut reader = Reader::new(page_info_seg.data);
151        let page_info = parse_page_information(&mut reader)?;
152
153        // "A page's bitmap height may be declared in its page information segment
154        // to be unknown (by specifying a height of 0xFFFFFFFF). In this case, the
155        // page must be striped." (7.4.8.2)
156        let height = if page_info.height == 0xFFFF_FFFF {
157            height_from_stripes.ok_or(FormatError::UnknownPageHeight)?
158        } else {
159            page_info.height
160        };
161
162        if page_info.width == 0 || height == 0 {
163            bail!(FormatError::EmptyPage);
164        }
165
166        Ok(Self {
167            segments,
168            width: page_info.width,
169            height,
170            height_from_stripes,
171        })
172    }
173
174    /// The width of the image in pixels.
175    pub fn width(&self) -> u32 {
176        self.width
177    }
178
179    /// The height of the image in pixels.
180    pub fn height(&self) -> u32 {
181        self.height
182    }
183
184    /// Decode the image data through the given [`Decoder`].
185    pub fn decode<D: Decoder>(&self, decoder: &mut D) -> Result<()> {
186        let mut ctx = DecoderContext::default();
187
188        self.decode_with(decoder, &mut ctx)
189    }
190
191    /// Decode the image data through the given [`Decoder`] and [`DecoderContext`].
192    ///
193    /// This is useful in case you want to convert multiple JBIG2 images,
194    /// as it allows `hayro-jbig2` to reuse allocations during decoding.
195    pub fn decode_with<D: Decoder>(&self, decoder: &mut D, ctx: &mut DecoderContext) -> Result<()> {
196        decode_segments(&self.segments, self.height_from_stripes, ctx)?;
197        emit_bitmap(&ctx.page_bitmap, decoder);
198
199        Ok(())
200    }
201}
202
203fn emit_bitmap<D: Decoder>(bitmap: &Bitmap, decoder: &mut D) {
204    let width = bitmap.width;
205    let bytes_per_row = width.div_ceil(8) as usize;
206
207    for row in bitmap.data.chunks_exact(bitmap.stride as usize) {
208        let mut x = 0_u32;
209        let mut chunk_byte: Option<u8> = None;
210        let mut chunk_count = 0_u32;
211
212        let bytes = row.iter().flat_map(|w| w.to_be_bytes()).take(bytes_per_row);
213
214        for byte in bytes {
215            let remaining = width - x;
216
217            if remaining >= 8 && (byte == 0x00 || byte == 0xFF) {
218                // Continue the previous chunk.
219                if chunk_byte == Some(byte) {
220                    chunk_count += 1;
221                    x += 8;
222                    continue;
223                }
224
225                // Flush previous chunk if any, then start new one.
226                if let Some(b) = chunk_byte {
227                    decoder.push_pixel_chunk(b == 0xFF, chunk_count);
228                }
229
230                chunk_byte = Some(byte);
231                chunk_count = 1;
232                x += 8;
233
234                continue;
235            }
236
237            // Can't continue/start chunk, flush any existing chunk first.
238            if let Some(b) = chunk_byte.take() {
239                decoder.push_pixel_chunk(b == 0xFF, chunk_count);
240                chunk_count = 0;
241            }
242
243            // Emit individual pixels.
244            let count = remaining.min(8);
245            for i in 0..count {
246                decoder.push_pixel((byte >> (7 - i)) & 1 != 0);
247            }
248            x += count;
249        }
250
251        // Flush any remaining chunk at end of row.
252        if let Some(b) = chunk_byte {
253            decoder.push_pixel_chunk(b == 0xFF, chunk_count);
254        }
255
256        decoder.next_line();
257    }
258}
259
260fn decode_segments(
261    segments: &[segment::Segment<'_>],
262    height_from_stripes: Option<u32>,
263    decoder_ctx: &mut DecoderContext,
264) -> Result<()> {
265    // Find and parse page information segment first.
266    if let Some(page_info) = segments
267        .iter()
268        .find(|s| s.header.segment_type == SegmentType::PageInformation)
269    {
270        let mut reader = Reader::new(page_info.data);
271        init_page(
272            &mut reader,
273            height_from_stripes,
274            &mut decoder_ctx.page_state,
275            &mut decoder_ctx.page_bitmap,
276        )?;
277    } else {
278        bail!(FormatError::MissingPageInfo);
279    }
280
281    let page_bitmap = &mut decoder_ctx.page_bitmap;
282    let page_state = &mut decoder_ctx.page_state;
283    let scratch_buffers = &mut decoder_ctx.scratch_buffers;
284
285    // Process all segments.
286    for seg in segments {
287        let mut reader = Reader::new(seg.data);
288
289        match seg.header.segment_type {
290            SegmentType::PageInformation => {
291                // Already processed above, skip.
292            }
293            SegmentType::ImmediateGenericRegion | SegmentType::ImmediateLosslessGenericRegion => {
294                let had_unknown_length = seg.header.data_length.is_none();
295                let header = generic::parse(&mut reader, had_unknown_length)?;
296
297                if page_state.can_decode_directly(page_bitmap, &header.region_info, false) {
298                    generic::decode_into(&header, page_bitmap, scratch_buffers)?;
299                } else {
300                    let region = generic::decode(&header, scratch_buffers)?;
301                    page_bitmap.combine(
302                        &region.bitmap,
303                        region.bitmap.x_location as i32,
304                        region.bitmap.y_location as i32,
305                        region.combination_operator,
306                    );
307                }
308                page_state.page_pristine = false;
309            }
310            SegmentType::IntermediateGenericRegion => {
311                // Intermediate segments cannot have unknown length.
312                let header = generic::parse(&mut reader, false)?;
313                let region = generic::decode(&header, scratch_buffers)?;
314                page_state.store_region(seg.header.segment_number, region.bitmap);
315            }
316            SegmentType::PatternDictionary => {
317                let header = pattern::parse(&mut reader)?;
318                let dictionary = pattern::decode(&header, scratch_buffers)?;
319                page_state.store_pattern_dictionary(seg.header.segment_number, dictionary);
320            }
321            SegmentType::SymbolDictionary => {
322                // "1) Concatenate all the input symbol dictionaries to form SDINSYMS."
323                // (6.5.5, step 1)
324                // Collect references to avoid cloning; symbols are only cloned if re-exported.
325                let input_symbols: Vec<&Bitmap> = seg
326                    .header
327                    .referred_to_segments
328                    .iter()
329                    .filter_map(|&num| page_state.get_symbol_dictionary(num))
330                    .flat_map(|dict| dict.exported_symbols.iter())
331                    .collect();
332
333                // Collect Huffman tables from referred table segments.
334                let referred_tables: Vec<HuffmanTable> = seg
335                    .header
336                    .referred_to_segments
337                    .iter()
338                    .filter_map(|&num| page_state.get_huffman_table(num))
339                    .cloned()
340                    .collect();
341
342                // Get retained contexts from the last referred symbol dictionary (7.4.2.2 step 3).
343                let retained_contexts = seg
344                    .header
345                    .referred_to_segments
346                    .last()
347                    .and_then(|&num| page_state.get_symbol_dictionary(num))
348                    .and_then(|dict| dict.retained_contexts.as_ref());
349
350                let header = symbol::parse(&mut reader)?;
351                let dictionary = symbol::decode(
352                    &header,
353                    &input_symbols,
354                    &referred_tables,
355                    &page_state.standard_tables,
356                    retained_contexts,
357                )?;
358                page_state.store_symbol_dictionary(seg.header.segment_number, dictionary);
359            }
360            SegmentType::ImmediateTextRegion | SegmentType::ImmediateLosslessTextRegion => {
361                // Collect symbols from referred symbol dictionaries (SBSYMS).
362                let symbols: Vec<&Bitmap> = seg
363                    .header
364                    .referred_to_segments
365                    .iter()
366                    .filter_map(|&num| page_state.get_symbol_dictionary(num))
367                    .flat_map(|dict| dict.exported_symbols.iter())
368                    .collect();
369
370                // Collect Huffman tables from referred table segments.
371                // "These user-supplied Huffman decoding tables may be supplied either
372                // as a Tables segment..." (7.4.3.1.6)
373                let referred_tables: Vec<HuffmanTable> = seg
374                    .header
375                    .referred_to_segments
376                    .iter()
377                    .filter_map(|&num| page_state.get_huffman_table(num))
378                    .cloned()
379                    .collect();
380
381                let header = text::parse(&mut reader, symbols.len() as u32)?;
382
383                if page_state.can_decode_directly(
384                    page_bitmap,
385                    &header.region_info,
386                    header.flags.default_pixel,
387                ) {
388                    text::decode_into(
389                        &header,
390                        &symbols,
391                        &referred_tables,
392                        &page_state.standard_tables,
393                        page_bitmap,
394                        scratch_buffers,
395                    )?;
396                } else {
397                    let region = text::decode(
398                        &header,
399                        &symbols,
400                        &referred_tables,
401                        &page_state.standard_tables,
402                        scratch_buffers,
403                    )?;
404                    page_bitmap.combine(
405                        &region.bitmap,
406                        region.bitmap.x_location as i32,
407                        region.bitmap.y_location as i32,
408                        region.combination_operator,
409                    );
410                }
411                page_state.page_pristine = false;
412            }
413            SegmentType::IntermediateTextRegion => {
414                // Collect symbols from referred symbol dictionaries (SBSYMS).
415                let symbols: Vec<&Bitmap> = seg
416                    .header
417                    .referred_to_segments
418                    .iter()
419                    .filter_map(|&num| page_state.get_symbol_dictionary(num))
420                    .flat_map(|dict| dict.exported_symbols.iter())
421                    .collect();
422
423                // Collect Huffman tables from referred table segments.
424                let referred_tables: Vec<HuffmanTable> = seg
425                    .header
426                    .referred_to_segments
427                    .iter()
428                    .filter_map(|&num| page_state.get_huffman_table(num))
429                    .cloned()
430                    .collect();
431
432                let header = text::parse(&mut reader, symbols.len() as u32)?;
433                let region = text::decode(
434                    &header,
435                    &symbols,
436                    &referred_tables,
437                    &page_state.standard_tables,
438                    scratch_buffers,
439                )?;
440                page_state.store_region(seg.header.segment_number, region.bitmap);
441            }
442            SegmentType::ImmediateHalftoneRegion | SegmentType::ImmediateLosslessHalftoneRegion => {
443                let pattern_dict = seg
444                    .header
445                    .referred_to_segments
446                    .first()
447                    .and_then(|&num| page_state.get_pattern_dictionary(num))
448                    .ok_or(SegmentError::MissingPatternDictionary)?;
449
450                let header = halftone::parse(&mut reader)?;
451
452                if page_state.can_decode_directly(
453                    page_bitmap,
454                    &header.region_info,
455                    header.flags.initial_pixel_color,
456                ) {
457                    halftone::decode_into(&header, pattern_dict, page_bitmap, scratch_buffers)?;
458                } else {
459                    let region = halftone::decode(&header, pattern_dict, scratch_buffers)?;
460                    page_bitmap.combine(
461                        &region.bitmap,
462                        region.bitmap.x_location as i32,
463                        region.bitmap.y_location as i32,
464                        region.combination_operator,
465                    );
466                }
467                page_state.page_pristine = false;
468            }
469            SegmentType::IntermediateHalftoneRegion => {
470                let pattern_dict = seg
471                    .header
472                    .referred_to_segments
473                    .first()
474                    .and_then(|&num| page_state.get_pattern_dictionary(num))
475                    .ok_or(SegmentError::MissingPatternDictionary)?;
476
477                let header = halftone::parse(&mut reader)?;
478                let region = halftone::decode(&header, pattern_dict, scratch_buffers)?;
479                page_state.store_region(seg.header.segment_number, region.bitmap);
480            }
481            SegmentType::IntermediateGenericRefinementRegion => {
482                // Same logic as immediate refinement, but store result instead of combining.
483                let reference = seg
484                    .header
485                    .referred_to_segments
486                    .first()
487                    .and_then(|&num| page_state.get_referred_segment(num))
488                    .unwrap_or(page_bitmap);
489
490                let header = generic_refinement::parse(&mut reader)?;
491                let region = generic_refinement::decode(&header, reference, scratch_buffers)?;
492                page_state.store_region(seg.header.segment_number, region.bitmap);
493            }
494            SegmentType::ImmediateGenericRefinementRegion
495            | SegmentType::ImmediateLosslessGenericRefinementRegion => {
496                // "3) Determine the buffer associated with the region segment that
497                // this segment refers to." (7.4.7.5)
498                //
499                // "2) If there are no referred-to segments, then use the page
500                // bitmap as the reference buffer." (7.4.7.5)
501                let referred_segment = seg
502                    .header
503                    .referred_to_segments
504                    .first()
505                    .and_then(|&num| page_state.get_referred_segment(num));
506
507                let header = generic_refinement::parse(&mut reader)?;
508
509                if let Some(referred_segment) = referred_segment
510                    && page_state.can_decode_directly(page_bitmap, &header.region_info, false)
511                {
512                    generic_refinement::decode_into(
513                        &header,
514                        referred_segment,
515                        page_bitmap,
516                        scratch_buffers,
517                    )?;
518                } else {
519                    let reference = referred_segment.unwrap_or(page_bitmap);
520                    let region = generic_refinement::decode(&header, reference, scratch_buffers)?;
521                    page_bitmap.combine(
522                        &region.bitmap,
523                        region.bitmap.x_location as i32,
524                        region.bitmap.y_location as i32,
525                        region.combination_operator,
526                    );
527                }
528                page_state.page_pristine = false;
529            }
530            SegmentType::Tables => {
531                // "Tables – see 7.4.13." (type 53)
532                // "This segment contains data which defines one or more user-supplied
533                // Huffman coding tables." (7.4.13)
534                let table = HuffmanTable::read_custom(&mut reader)?;
535                page_state.store_huffman_table(seg.header.segment_number, table);
536            }
537            SegmentType::EndOfPage | SegmentType::EndOfFile => {
538                break;
539            }
540            // Other segment types not yet implemented.
541            _ => {}
542        }
543    }
544
545    Ok(())
546}
547
548/// Page-level decoding state for a JBIG2 page.
549#[derive(Default)]
550pub(crate) struct PageState {
551    /// The parsed page information.
552    pub(crate) page_info: PageInformation,
553    /// Whether the page bitmap is still in its initial state (not yet painted to).
554    pub(crate) page_pristine: bool,
555    /// Decoded intermediate regions, stored as (`segment_number`, region) pairs.
556    pub(crate) referred_segments: Vec<(u32, Bitmap)>,
557    /// Decoded pattern dictionaries, stored as (`segment_number`, dictionary) pairs.
558    pub(crate) pattern_dictionaries: Vec<(u32, PatternDictionary)>,
559    /// Decoded symbol dictionaries, stored as (`segment_number`, dictionary) pairs.
560    pub(crate) symbol_dictionaries: Vec<(u32, SymbolDictionary)>,
561    /// Decoded Huffman tables from table segments, stored as (`segment_number`, table) pairs.
562    /// "Tables – see 7.4.13." (type 53)
563    pub(crate) huffman_tables: Vec<(u32, HuffmanTable)>,
564    /// Standard Huffman tables (`TABLE_A` through `TABLE_O`).
565    pub(crate) standard_tables: StandardHuffmanTables,
566}
567
568impl PageState {
569    fn reset(&mut self, page_info: PageInformation) {
570        self.page_info = page_info;
571        self.page_pristine = true;
572        self.referred_segments.clear();
573        self.pattern_dictionaries.clear();
574        self.symbol_dictionaries.clear();
575        self.huffman_tables.clear();
576        // Standard tables are lazily built and reused across images.
577    }
578
579    /// Check if an immediate region can be decoded directly into the page bitmap.
580    fn can_decode_directly(
581        &self,
582        page_bitmap: &Bitmap,
583        region_info: &decode::RegionSegmentInfo,
584        region_default_pixel: bool,
585    ) -> bool {
586        if !self.page_pristine {
587            return false;
588        }
589
590        let covers_page = region_info.x_location == 0
591            && region_info.y_location == 0
592            && region_info.width == page_bitmap.width
593            && region_info.height == page_bitmap.height;
594
595        if !covers_page {
596            return false;
597        }
598
599        let page_default_is_zero = self.page_info.flags.default_pixel == 0;
600
601        if region_default_pixel == page_default_is_zero {
602            return false;
603        }
604
605        let op = region_info.combination_operator;
606        match op {
607            CombinationOperator::Replace => true,
608            CombinationOperator::Or | CombinationOperator::Xor => page_default_is_zero,
609            CombinationOperator::And | CombinationOperator::Xnor => !page_default_is_zero,
610        }
611    }
612
613    /// Store a decoded region for later reference.
614    fn store_region(&mut self, segment_number: u32, region: Bitmap) {
615        self.referred_segments.push((segment_number, region));
616    }
617
618    /// Look up a referred segment by number.
619    fn get_referred_segment(&self, segment_number: u32) -> Option<&Bitmap> {
620        self.referred_segments
621            .binary_search_by_key(&segment_number, |(num, _)| *num)
622            .ok()
623            .map(|idx| &self.referred_segments[idx].1)
624    }
625
626    /// Store a decoded pattern dictionary for later reference.
627    fn store_pattern_dictionary(&mut self, segment_number: u32, dictionary: PatternDictionary) {
628        self.pattern_dictionaries.push((segment_number, dictionary));
629    }
630
631    /// Look up a pattern dictionary by segment number.
632    fn get_pattern_dictionary(&self, segment_number: u32) -> Option<&PatternDictionary> {
633        self.pattern_dictionaries
634            .binary_search_by_key(&segment_number, |(num, _)| *num)
635            .ok()
636            .map(|idx| &self.pattern_dictionaries[idx].1)
637    }
638
639    /// Store a decoded symbol dictionary for later reference.
640    fn store_symbol_dictionary(&mut self, segment_number: u32, dictionary: SymbolDictionary) {
641        self.symbol_dictionaries.push((segment_number, dictionary));
642    }
643
644    /// Look up a symbol dictionary by segment number.
645    fn get_symbol_dictionary(&self, segment_number: u32) -> Option<&SymbolDictionary> {
646        self.symbol_dictionaries
647            .binary_search_by_key(&segment_number, |(num, _)| *num)
648            .ok()
649            .map(|idx| &self.symbol_dictionaries[idx].1)
650    }
651
652    /// Store a decoded Huffman table for later reference.
653    fn store_huffman_table(&mut self, segment_number: u32, table: HuffmanTable) {
654        self.huffman_tables.push((segment_number, table));
655    }
656
657    /// Look up a Huffman table by segment number.
658    fn get_huffman_table(&self, segment_number: u32) -> Option<&HuffmanTable> {
659        self.huffman_tables
660            .binary_search_by_key(&segment_number, |(num, _)| *num)
661            .ok()
662            .map(|idx| &self.huffman_tables[idx].1)
663    }
664}
665
666/// Parse page information and initialize the page bitmap.
667fn init_page(
668    reader: &mut Reader<'_>,
669    height_from_stripes: Option<u32>,
670    page: &mut PageState,
671    bitmap: &mut Bitmap,
672) -> Result<()> {
673    let page_info = parse_page_information(reader)?;
674
675    // "A page's bitmap height may be declared in its page information segment
676    // to be unknown (by specifying a height of 0xFFFFFFFF). In this case, the
677    // page must be striped." (7.4.8.2)
678    let height = if page_info.height == 0xFFFF_FFFF {
679        height_from_stripes.ok_or(FormatError::UnknownPageHeight)?
680    } else {
681        page_info.height
682    };
683
684    // "Bit 2: Page default pixel value. This bit contains the initial value
685    // for every pixel in the page, before any region segments are decoded
686    // or drawn." (7.4.8.5)
687    bitmap.reinitialize(page_info.width, height, page_info.flags.default_pixel != 0)?;
688
689    page.reset(page_info);
690
691    Ok(())
692}