1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
//! Symbol Dictionary (ITU T.88 6.5, 7.4.2).
//!
//! A symbol dictionary contains an array of glyph bitmaps that can be
//! referenced by text region segments for character placement.
use alloc::vec;
use alloc::vec::Vec;
use crate::image::Jbig2Image;
/// A symbol dictionary — an array of glyph images.
#[derive(Debug, Clone)]
pub struct SymbolDict {
pub glyphs: Vec<Option<Jbig2Image>>,
}
impl SymbolDict {
/// Create a new empty symbol dictionary with `n` slots.
pub fn new(n: u32) -> Self {
SymbolDict {
glyphs: vec![None; n as usize],
}
}
/// Number of symbols in this dictionary.
pub fn n_symbols(&self) -> u32 {
self.glyphs.len() as u32
}
/// Get a glyph by index.
pub fn glyph(&self, id: u32) -> Option<&Jbig2Image> {
self.glyphs.get(id as usize).and_then(|g| g.as_ref())
}
/// Set a glyph at the given index.
pub fn set_glyph(&mut self, id: u32, image: Jbig2Image) {
if (id as usize) < self.glyphs.len() {
self.glyphs[id as usize] = Some(image);
}
}
/// Concatenate multiple symbol dictionaries into one.
pub fn cat(dicts: &[&SymbolDict]) -> Self {
let total: usize = dicts.iter().map(|d| d.glyphs.len()).sum();
let mut result = SymbolDict {
glyphs: Vec::with_capacity(total),
};
for dict in dicts {
for glyph in &dict.glyphs {
result.glyphs.push(glyph.clone());
}
}
result
}
/// Select exported symbols according to the JBIG2 export algorithm.
///
/// Given the input symbols and new symbols, produces the export list
/// by toggling an export flag for runs of symbols.
/// `flags[i]` = true means symbol i is exported.
pub fn export(
input_syms: &SymbolDict,
new_syms: &SymbolDict,
export_flags: &[bool],
) -> Self {
let total = input_syms.n_symbols() + new_syms.n_symbols();
let mut result = Vec::new();
for i in 0..total as usize {
if i < export_flags.len() && export_flags[i] {
let glyph = if (i as u32) < input_syms.n_symbols() {
input_syms.glyph(i as u32).cloned()
} else {
new_syms.glyph(i as u32 - input_syms.n_symbols()).cloned()
};
result.push(glyph);
}
}
SymbolDict { glyphs: result }
}
}
/// Symbol dictionary segment flags (Table 13).
#[derive(Debug, Clone)]
pub struct SymbolDictParams {
pub sdhuff: bool,
pub sdrefagg: bool,
pub sdtemplate: u8,
pub sdrtemplate: u8,
pub sdat: [i8; 8],
pub sdrat: [i8; 4],
pub sdnuminsyms: u32,
pub sdnumnewsyms: u32,
pub sdnumexsyms: u32,
}
impl SymbolDictParams {
/// Parse symbol dictionary flags from segment data.
/// Returns (params, bytes consumed after region header).
pub fn parse(data: &[u8]) -> Option<(Self, usize)> {
// Minimum: 2 bytes flags + 4 bytes SDNUMEXSYMS + 4 bytes SDNUMNEWSYMS = 10
if data.len() < 10 {
return None;
}
let flags = u16::from_be_bytes([data[0], data[1]]);
let sdhuff = flags & 1 != 0;
let sdrefagg = (flags >> 1) & 1 != 0;
let sdtemplate = ((flags >> 10) & 3) as u8;
let sdrtemplate = ((flags >> 12) & 1) as u8;
let mut offset = 2;
// SDAT (adaptive template)
let mut sdat = [0i8; 8];
if !sdhuff {
let n = if sdtemplate == 0 { 8 } else { 2 };
if data.len() < offset + n {
return None;
}
for i in 0..n {
sdat[i] = data[offset + i] as i8;
}
offset += n;
}
// SDRAT (refinement adaptive template)
let mut sdrat = [0i8; 4];
if sdrefagg && sdrtemplate == 0 {
if data.len() < offset + 4 {
return None;
}
for i in 0..4 {
sdrat[i] = data[offset + i] as i8;
}
offset += 4;
}
// SDNUMEXSYMS and SDNUMNEWSYMS
if data.len() < offset + 8 {
return None;
}
let sdnumexsyms = u32::from_be_bytes([
data[offset], data[offset + 1], data[offset + 2], data[offset + 3],
]);
offset += 4;
let sdnumnewsyms = u32::from_be_bytes([
data[offset], data[offset + 1], data[offset + 2], data[offset + 3],
]);
offset += 4;
Some((
SymbolDictParams {
sdhuff,
sdrefagg,
sdtemplate,
sdrtemplate,
sdat,
sdrat,
sdnuminsyms: 0, // set by caller from referred segments
sdnumnewsyms,
sdnumexsyms,
},
offset,
))
}
}