1use byteorder::{BigEndian, WriteBytesExt};
3use std::io::{self, Write};
4
5#[cfg(feature = "trace_encoder")]
6use log::debug;
7
8#[cfg(not(feature = "trace_encoder"))]
9use crate::debug;
10
11pub const JB2_MAGIC: &[u8; 10] = b"\x97JBIG2\r\n\x1A\n";
13
14pub const JB2_VERSION: u8 = 0x02;
16
17#[derive(Debug, Clone)]
19pub struct Jbig2Config {
20 pub generic: GenericRegionConfig,
22
23 pub sd_template: u8, pub sd_at: [(i8, i8); 4], pub text_ds_offset: i8, pub text_refine: bool, pub text_log_strips: u8, pub text_ref_corner: u8, pub text_transposed: bool, pub text_comb_op: u8, pub text_refine_template: u8, pub halftone: HalftoneConfig,
38
39 pub dpi: u32,
41 pub symbol_mode: bool,
42 pub refine: bool,
43 pub refine_template: u8,
44 pub duplicate_line_removal: bool,
45 pub auto_thresh: bool,
46 pub hash: bool,
47 pub want_full_headers: bool,
48 pub is_lossless: bool,
49 pub default_pixel: bool,
50 pub match_tolerance: u32,
55 pub lossy_symbol_mode: LossySymbolMode,
56 pub sym_unify_min_class_size: usize,
57 pub sym_unify_max_err: u32,
58 pub sym_unify_max_dx: i32,
59 pub sym_unify_max_dy: i32,
60 pub sym_unify_class_accept_limit: u32,
61 pub sym_unify_core_close_threshold: u32,
62 pub sym_unify_min_core_ratio_permille: u16,
63 pub sym_unify_min_class_usage: usize,
64 pub sym_unify_min_page_span: usize,
65 pub sym_unify_min_estimated_gain: i32,
66 pub sym_unify_context_mode: SymbolContextMode,
67 pub sym_unify_border_score_slack: u32,
68 pub sym_unify_score_rescue_slack: u32,
69 pub sym_unify_max_border_outside_ink: u32,
70 pub sym_unify_refine_min_subcluster_size: usize,
71 pub sym_unify_refine_min_usage: usize,
72 pub sym_unify_refine_min_page_span: usize,
73 pub sym_unify_refine_max_score: u32,
74 pub sym_unify_refine_min_gain: i32,
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum LossySymbolMode {
79 Off,
80 SymbolUnify,
81}
82
83#[derive(Debug, Clone, Copy)]
85pub struct HalftoneConfig {
86 pub grid_size_m: u32,
88 pub quant_levels_n: u32,
90 pub sharpening_l: f32,
92 pub template: u8,
94 pub lossless: bool,
97 pub gray_mmr: bool,
100 pub dict_mmr: bool,
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub enum SymbolContextMode {
108 WeightedJaccard,
109 Cosine,
110 Hybrid,
111}
112
113impl Default for HalftoneConfig {
114 fn default() -> Self {
115 Self {
116 grid_size_m: 4, quant_levels_n: 16, sharpening_l: 0.5, template: 0,
120 lossless: false, gray_mmr: false,
122 dict_mmr: true,
123 }
124 }
125}
126
127impl Default for Jbig2Config {
128 fn default() -> Self {
129 Self {
130 generic: GenericRegionConfig::default(),
131 sd_template: 0,
132 sd_at: [(0, 0), (0, 0), (0, 0), (0, 0)],
133 text_ds_offset: 0,
134 text_refine: false,
135 text_log_strips: 0,
136 text_ref_corner: 1,
137 text_transposed: false,
138 text_comb_op: 0,
139 text_refine_template: 0,
140 halftone: HalftoneConfig::default(),
141 dpi: 300,
142 symbol_mode: false,
143 refine: false, refine_template: 0,
145 duplicate_line_removal: true,
146 auto_thresh: true,
147 hash: true,
148 want_full_headers: true,
149 is_lossless: false,
150 default_pixel: false,
151 match_tolerance: 7,
152 lossy_symbol_mode: LossySymbolMode::Off,
153 sym_unify_min_class_size: 2,
154 sym_unify_max_err: 12,
155 sym_unify_max_dx: 1,
156 sym_unify_max_dy: 1,
157 sym_unify_class_accept_limit: 16,
158 sym_unify_core_close_threshold: 11,
159 sym_unify_min_core_ratio_permille: 350,
160 sym_unify_min_class_usage: 2,
161 sym_unify_min_page_span: 1,
162 sym_unify_min_estimated_gain: 0,
163 sym_unify_context_mode: SymbolContextMode::Hybrid,
164 sym_unify_border_score_slack: 4,
165 sym_unify_score_rescue_slack: 2,
166 sym_unify_max_border_outside_ink: 1,
167 sym_unify_refine_min_subcluster_size: 2,
168 sym_unify_refine_min_usage: 12,
169 sym_unify_refine_min_page_span: 3,
170 sym_unify_refine_max_score: 8,
171 sym_unify_refine_min_gain: 20,
172 }
173 }
174}
175
176impl Jbig2Config {
177 pub fn new() -> Self {
179 Self::default()
180 }
181
182 pub fn text() -> Self {
184 let mut cfg = Self::default();
185 cfg.symbol_mode = true;
186 cfg.auto_thresh = true;
187 cfg.duplicate_line_removal = true;
188 cfg
192 }
193
194 pub fn text_symbol_unify() -> Self {
195 let mut cfg = Self::text();
196 cfg.lossy_symbol_mode = LossySymbolMode::SymbolUnify;
197 cfg.refine = false;
198 cfg.text_refine = false;
199 cfg.sym_unify_min_class_size = 2;
200 cfg.sym_unify_max_err = 12;
201 cfg.sym_unify_max_dx = 1;
202 cfg.sym_unify_max_dy = 1;
203 cfg.sym_unify_class_accept_limit = 16;
204 cfg.sym_unify_core_close_threshold = 11;
205 cfg.sym_unify_min_core_ratio_permille = 350;
206 cfg.sym_unify_min_class_usage = 2;
207 cfg.sym_unify_min_page_span = 1;
208 cfg.sym_unify_min_estimated_gain = 0;
209 cfg.sym_unify_context_mode = SymbolContextMode::Hybrid;
210 cfg.sym_unify_border_score_slack = 4;
211 cfg.sym_unify_score_rescue_slack = 2;
212 cfg.sym_unify_max_border_outside_ink = 1;
213 cfg.sym_unify_refine_min_subcluster_size = 2;
214 cfg.sym_unify_refine_min_usage = 12;
215 cfg.sym_unify_refine_min_page_span = 3;
216 cfg.sym_unify_refine_max_score = 8;
217 cfg.sym_unify_refine_min_gain = 20;
218 cfg
219 }
220
221 #[inline]
222 pub fn uses_symbol_unify(&self) -> bool {
223 self.lossy_symbol_mode == LossySymbolMode::SymbolUnify
224 }
225
226 #[inline]
227 pub fn uses_lossy_symbol_dictionary(&self) -> bool {
228 self.lossy_symbol_mode != LossySymbolMode::Off
229 }
230
231 pub fn lossless() -> Self {
233 let mut cfg = Self::default();
234 cfg.symbol_mode = false;
235 cfg.refine = false; cfg.is_lossless = true;
237 cfg.duplicate_line_removal = false;
238 cfg
239 }
240}
241
242#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
244pub enum SegmentType {
245 #[default]
246 SymbolDictionary = 0,
247 IntermediateTextRegion = 4,
248 ImmediateTextRegion = 6,
249 ImmediateLosslessTextRegion = 7,
250 PatternDictionary = 16,
251 IntermediateHalftoneRegion = 20,
252 ImmediateHalftoneRegion = 22,
253 ImmediateLosslessHalftoneRegion = 23,
254 IntermediateGenericRegion = 36,
255 ImmediateGenericRegion = 38,
256 ImmediateLosslessGenericRegion = 39,
257 IntermediateGenericRefinementRegion = 40,
258 ImmediateGenericRefinementRegion = 42,
259 ImmediateLosslessGenericRefinementRegion = 43,
260 PageInformation = 48,
261 EndOfPage = 49,
262 EndOfStripe = 50,
263 EndOfFile = 51,
264 Profiles = 52,
265 Tables = 53,
266 ColorPalette = 54,
267 FileHeader = 56,
268 Extension = 62,
269}
270
271impl TryFrom<u8> for SegmentType {
272 type Error = io::Error;
273
274 fn try_from(value: u8) -> Result<Self, Self::Error> {
275 match value {
276 0 => Ok(SegmentType::SymbolDictionary),
277 4 => Ok(SegmentType::IntermediateTextRegion),
278 6 => Ok(SegmentType::ImmediateTextRegion),
279 7 => Ok(SegmentType::ImmediateLosslessTextRegion),
280 16 => Ok(SegmentType::PatternDictionary),
281 20 => Ok(SegmentType::IntermediateHalftoneRegion),
282 22 => Ok(SegmentType::ImmediateHalftoneRegion),
283 23 => Ok(SegmentType::ImmediateLosslessHalftoneRegion),
284 36 => Ok(SegmentType::IntermediateGenericRegion),
285 38 => Ok(SegmentType::ImmediateGenericRegion),
286 39 => Ok(SegmentType::ImmediateLosslessGenericRegion),
287 40 => Ok(SegmentType::IntermediateGenericRefinementRegion),
288 42 => Ok(SegmentType::ImmediateGenericRefinementRegion),
289 43 => Ok(SegmentType::ImmediateLosslessGenericRefinementRegion),
290 48 => Ok(SegmentType::PageInformation),
291 49 => Ok(SegmentType::EndOfPage),
292 50 => Ok(SegmentType::EndOfStripe),
293 51 => Ok(SegmentType::EndOfFile),
294 52 => Ok(SegmentType::Profiles),
295 53 => Ok(SegmentType::Tables),
296 54 => Ok(SegmentType::ColorPalette),
297 56 => Ok(SegmentType::FileHeader),
298 62 => Ok(SegmentType::Extension),
299 _ => Err(io::Error::new(
300 io::ErrorKind::InvalidData,
301 format!("Invalid segment type: {}", value),
302 )),
303 }
304 }
305}
306
307#[derive(Debug)]
313pub struct FileHeader {
314 pub organisation_type: bool, pub unknown_n_pages: bool, pub n_pages: u32, }
318
319impl FileHeader {
320 pub fn to_bytes(&self) -> Vec<u8> {
321 const MAGIC: &[u8] = b"\x97JB2\r\n\x1A\n";
322 let mut buf = Vec::with_capacity(8 + 1 + if self.unknown_n_pages { 0 } else { 4 });
323 buf.extend_from_slice(MAGIC);
324
325 let mut flags = 0u8;
326 if self.organisation_type {
327 flags |= 0x01;
328 }
329 if self.unknown_n_pages {
330 flags |= 0x02;
331 }
332 buf.push(flags);
333
334 if !self.unknown_n_pages {
335 buf.write_u32::<BigEndian>(self.n_pages).unwrap();
336 }
337 buf
338 }
339}
340
341#[derive(Debug, Default)]
347pub struct PageInfo {
348 pub width: u32, pub height: u32, pub xres: u32, pub yres: u32, pub is_lossless: bool, pub contains_refinements: bool, pub default_pixel: bool, pub default_operator: u8, pub aux_buffers: bool, pub operator_override: bool, pub reserved: bool, pub segment_flags: u16, }
361
362impl PageInfo {
363 pub fn to_bytes(&self) -> Vec<u8> {
364 let mut buf = Vec::with_capacity(4 * 4 + 1 + 2);
365 buf.write_u32::<BigEndian>(self.width).unwrap();
366 buf.write_u32::<BigEndian>(self.height).unwrap();
367 buf.write_u32::<BigEndian>(self.xres).unwrap();
368 buf.write_u32::<BigEndian>(self.yres).unwrap();
369
370 let mut b = 0u8;
371 if self.is_lossless {
372 b |= 0x01;
373 }
374 if self.contains_refinements {
375 b |= 0x02;
376 }
377 if self.default_pixel {
378 b |= 0x04;
379 }
380 b |= (self.default_operator & 0x03) << 3;
381 if self.aux_buffers {
382 b |= 0x20;
383 }
384 if self.operator_override {
385 b |= 0x40;
386 }
387 buf.push(b);
389 buf.write_u16::<BigEndian>(self.segment_flags).unwrap();
390 buf
391 }
392}
393
394#[derive(Debug, Clone)]
400pub struct GenericRegionParams {
401 pub width: u32, pub height: u32, pub x: u32, pub y: u32, pub comb_operator: u8, pub mmr: bool, pub template: u8, pub tpgdon: bool, pub at: [(i8, i8); 4], pub at_pixels: Vec<(i8, i8)>, }
412
413impl GenericRegionParams {
414 pub fn new(width: u32, height: u32, dpi: u32) -> Self {
415 let at_pixels = vec![(3, -1), (-3, -1), (2, -2), (-2, -2)];
416 Self {
417 width,
418 height,
419 x: 0,
420 y: 0,
421 comb_operator: 4, mmr: false,
423 template: 0,
424 tpgdon: true,
425 at: [(3, -1), (-3, -1), (2, -2), (-2, -2)],
426 at_pixels,
427 }
428 }
429
430 pub fn validate(&self) -> Result<(), &'static str> {
432 if self.template > 3 {
433 return Err("Template ID must be 0–3");
434 }
435 if self.at_pixels.len() > 4 {
436 return Err("Maximum 4 AT pixels allowed");
437 }
438 if self.comb_operator > 4 {
439 return Err("Invalid combination operator");
440 }
441 Ok(())
442 }
443 pub fn to_bytes(&self) -> Vec<u8> {
444 use byteorder::{BigEndian, WriteBytesExt};
445 let at_count = match self.template {
447 0 => 4, 1 => 1, _ => 0, };
451 let mut buf = Vec::with_capacity(18 + at_count * 2);
452
453 buf.write_u32::<BigEndian>(self.width).unwrap();
454 buf.write_u32::<BigEndian>(self.height).unwrap();
455 buf.write_u32::<BigEndian>(self.x).unwrap();
456 buf.write_u32::<BigEndian>(self.y).unwrap();
457 buf.push(self.comb_operator);
458
459 let mut flags = 0u8;
460 if self.mmr {
461 flags |= 0x01; }
463 flags |= (self.template & 0x03) << 1; if self.tpgdon {
465 flags |= 0x08; }
467 buf.push(flags);
469
470 let at_count = match self.template {
476 0 => 4, 1 => 1, _ => 0, };
480 for i in 0..at_count {
481 buf.push(self.at[i].0 as u8);
482 buf.push(self.at[i].1 as u8);
483 }
484 buf
485 }
486}
487
488#[derive(Clone, Debug)]
490pub struct GenericRegionConfig {
491 pub width: u32,
493 pub height: u32,
494 pub x: u32,
495 pub y: u32,
496 pub comb_operator: u8, pub template: u8, pub tpgdon: bool, pub mmr: bool, pub at_pixels: Vec<(i8, i8)>, pub dpi: u32, }
507
508impl GenericRegionConfig {
509 pub fn new(width: u32, height: u32, dpi: u32) -> Self {
511 Self {
512 width,
513 height,
514 x: 0,
515 y: 0,
516 comb_operator: 0, template: 0, tpgdon: false, at_pixels: vec![(3, -1), (-3, -1), (2, -2), (-2, -2)],
521 mmr: false, dpi,
523 }
524 }
525
526 pub fn validate(&self) -> Result<(), &'static str> {
528 if self.template > 3 {
529 return Err("Template ID must be 0–3");
530 }
531 if self.at_pixels.len() > 4 {
532 return Err("Maximum 4 AT pixels allowed");
533 }
534 if self.comb_operator > 4 {
535 return Err("Invalid combination operator");
536 }
537 Ok(())
538 }
539}
540
541impl Default for GenericRegionConfig {
542 fn default() -> Self {
543 Self::new(0, 0, 300)
544 }
545}
546
547impl From<GenericRegionConfig> for GenericRegionParams {
548 fn from(cfg: GenericRegionConfig) -> Self {
549 let mut at = [(0i8, 0i8); 4];
550 for (i, &(dx, dy)) in cfg.at_pixels.iter().enumerate().take(4) {
551 at[i] = (dx, dy);
552 }
553 GenericRegionParams {
554 width: cfg.width,
555 height: cfg.height,
556 x: cfg.x,
557 y: cfg.y,
558 comb_operator: cfg.comb_operator,
559 mmr: cfg.mmr, template: cfg.template,
561 tpgdon: cfg.tpgdon,
562 at,
563 at_pixels: cfg.at_pixels.clone(),
564 }
565 }
566}
567
568#[derive(Debug)]
574pub struct SymbolDictParams {
575 pub sd_template: u8, pub at: [(i8, i8); 4], pub refine_aggregate: bool,
578 pub refine_template: u8,
579 pub refine_at: [(i8, i8); 2],
580 pub exsyms: u32, pub newsyms: u32, }
583
584impl SymbolDictParams {
585 pub fn to_bytes(&self) -> Vec<u8> {
586 let mut buf = Vec::with_capacity(
587 2 + 8
588 + if self.refine_aggregate && self.refine_template == 0 {
589 4
590 } else {
591 0
592 }
593 + 4
594 + 4,
595 );
596
597 let mut flags = 0u16;
598 flags |= ((self.sd_template & 0x03) as u16) << 10;
599 if self.refine_aggregate {
600 flags |= 1 << 1; }
602 if self.refine_aggregate && (self.refine_template & 0x01) == 1 {
603 flags |= 1 << 12; }
605
606 buf.write_u16::<BigEndian>(flags).unwrap();
607 for &(x, y) in &self.at {
608 buf.push(x as u8);
609 buf.push(y as u8);
610 }
611 if self.refine_aggregate && self.refine_template == 0 {
612 for &(x, y) in &self.refine_at {
613 buf.push(x as u8);
614 buf.push(y as u8);
615 }
616 }
617 buf.write_u32::<BigEndian>(self.exsyms).unwrap();
618 buf.write_u32::<BigEndian>(self.newsyms).unwrap();
619 buf
620 }
621}
622
623#[derive(Debug)]
629pub struct TextRegionParams {
630 pub width: u32, pub height: u32, pub x: u32, pub y: u32, pub ds_offset: i8, pub refine: bool, pub log_strips: u8, pub ref_corner: u8, pub transposed: bool, pub comb_op: u8, pub refine_template: u8, }
642
643impl TextRegionParams {
644 pub fn to_bytes(&self) -> Vec<u8> {
645 let mut buf = Vec::with_capacity(16 + 1 + 2 + if self.refine { 4 } else { 0 });
646 buf.write_u32::<BigEndian>(self.width).unwrap();
647 buf.write_u32::<BigEndian>(self.height).unwrap();
648 buf.write_u32::<BigEndian>(self.x).unwrap();
649 buf.write_u32::<BigEndian>(self.y).unwrap();
650 buf.write_u8(self.comb_op & 0x07).unwrap();
652
653 let mut flags0 = 0u8;
655 flags0 |= ((self.comb_op >> 1) & 0x01) as u8; flags0 |= ((self.ds_offset as u8) & 0x1F) << 2; if self.refine && self.refine_template == 1 {
658 flags0 |= 1 << 7; }
660 buf.write_u8(flags0).unwrap();
661
662 let mut flags1 = 0u8;
663 if self.refine {
664 flags1 |= 1 << 1; }
666 flags1 |= (self.log_strips & 0x03) << 2; flags1 |= (self.ref_corner & 0x03) << 4; if self.transposed {
669 flags1 |= 1 << 6; }
671 flags1 |= (self.comb_op & 0x01) << 7; buf.write_u8(flags1).unwrap();
673
674 if self.refine && self.refine_template == 0 {
677 buf.extend_from_slice(&[0xFF, 0xFF, 0xFF, 0xFF]); }
679 buf
680 }
681}
682
683#[derive(Debug, Clone, Default)]
685pub struct HalftoneParams {
686 pub width: u32,
687 pub height: u32,
688 pub x: u32,
689 pub y: u32,
690 pub region_comb_operator: u8,
691 pub mmr: bool,
692 pub skip_enabled: bool,
693 pub halftone_comb_operator: u8,
694 pub default_pixel: bool,
695 pub grid_width: u32, pub grid_height: u32, pub grid_x: i32, pub grid_y: i32, pub grid_vector_x: u16, pub grid_vector_y: u16, pub pattern_width: u8, pub pattern_height: u8, pub template: u8, }
705
706impl HalftoneParams {
707 pub fn to_bytes(&self) -> Vec<u8> {
708 let mut buf = Vec::with_capacity(31);
709 buf.write_u32::<BigEndian>(self.width).unwrap();
710 buf.write_u32::<BigEndian>(self.height).unwrap();
711 buf.write_u32::<BigEndian>(self.x).unwrap();
712 buf.write_u32::<BigEndian>(self.y).unwrap();
713 buf.write_u8(self.region_comb_operator & 0x07).unwrap();
714
715 let mut flags = 0u8;
716 if self.mmr {
717 flags |= 0x01;
718 }
719 flags |= (self.template & 0x03) << 1;
720 if self.skip_enabled {
721 flags |= 0x08;
722 }
723 flags |= (self.halftone_comb_operator & 0x07) << 4;
724 if self.default_pixel {
725 flags |= 0x80;
726 }
727
728 buf.write_u8(flags).unwrap();
729
730 buf.write_u32::<BigEndian>(self.grid_width).unwrap();
731 buf.write_u32::<BigEndian>(self.grid_height).unwrap();
732 buf.write_i32::<BigEndian>(self.grid_x).unwrap();
733 buf.write_i32::<BigEndian>(self.grid_y).unwrap();
734 buf.write_u16::<BigEndian>(self.grid_vector_x).unwrap();
735 buf.write_u16::<BigEndian>(self.grid_vector_y).unwrap();
736
737 buf
738 }
739}
740
741#[derive(Debug, Clone, Default)]
742pub struct PatternDictionaryParams {
743 pub mmr: bool,
744 pub template: u8,
745 pub pattern_width: u8,
746 pub pattern_height: u8,
747 pub gray_max: u32,
748}
749
750impl PatternDictionaryParams {
751 pub fn to_bytes(&self) -> Vec<u8> {
752 let mut buf = Vec::with_capacity(7);
753 let mut flags = 0u8;
754 if self.mmr {
755 flags |= 0x01;
756 }
757 flags |= (self.template & 0x03) << 1;
758 buf.push(flags);
759 buf.push(self.pattern_width);
760 buf.push(self.pattern_height);
761 buf.write_u32::<BigEndian>(self.gray_max).unwrap();
762 buf
763 }
764}
765
766#[derive(Debug, Clone, Default)]
772pub struct Segment {
773 pub number: u32, pub seg_type: SegmentType, pub deferred_non_retain: bool, pub retain_flags: u8, pub page_association_type: u8, pub referred_to: Vec<u32>, pub page: Option<u32>, pub payload: Vec<u8>, }
782
783fn encode_varint(mut v: u32, buf: &mut Vec<u8>) {
784 while v >= 0x80 {
785 buf.push((v as u8) | 0x80);
786 v >>= 7;
787 }
788 buf.push(v as u8);
789}
790
791impl Segment {
792 pub fn write_into<W: Write>(&self, w: &mut W) -> io::Result<()> {
793 w.write_u32::<BigEndian>(self.number)?;
794
795 let page_num_val = self.page.unwrap_or(0);
797 let page_size_is_4_bytes = page_num_val > 0xFF;
798 let flags1 = (self.seg_type as u8 & 0x3F)
799 | ((page_size_is_4_bytes as u8) << 6)
800 | ((self.deferred_non_retain as u8) << 7);
801 w.write_u8(flags1)?;
802
803 let referred_to_count = self.referred_to.len();
805 if referred_to_count > 7 {
806 return Err(io::Error::new(
807 io::ErrorKind::InvalidInput,
808 "referred_to count > 7 is not supported",
809 ));
810 }
811 let flags2 = (self.retain_flags & 0x1F) | ((referred_to_count as u8) << 5);
812 w.write_u8(flags2)?;
813
814 let ref_size = if self.number <= 256 {
815 1
816 } else if self.number <= 65_536 {
817 2
818 } else {
819 4
820 };
821
822 for &r_num in &self.referred_to {
823 match ref_size {
824 1 => w.write_u8(r_num as u8)?,
825 2 => w.write_u16::<BigEndian>(r_num as u16)?,
826 4 => w.write_u32::<BigEndian>(r_num)?,
827 _ => unreachable!(),
828 }
829 }
830
831 if page_size_is_4_bytes {
833 w.write_u32::<BigEndian>(page_num_val)?;
834 } else {
835 w.write_u8(page_num_val as u8)?;
836 }
837
838 let payload_len = self.payload.len() as u32;
839 debug!("Segment {} payload length: {}", self.number, payload_len);
840 w.write_u32::<BigEndian>(payload_len)?;
841 w.write_all(&self.payload)?;
842
843 debug!(
844 "Segment::write_into: Wrote segment {}: Type={:?}, Page={:?}, PA Type={}, Data Length={}",
845 self.number, self.seg_type, self.page, self.page_association_type, payload_len
846 );
847 Ok(())
848 }
849}