Skip to main content

prescript/
cmap.rs

1//! Cmap to map CharCode to CID, used in Type0/CID font
2
3use crate::{
4    Name, Result,
5    machine::{
6        Key, Machine, MachineError, MachinePlugin, MachineResult, RuntimeDictionary, RuntimeValue,
7        TypeCheckSnafu, UndefinedSnafu, ok,
8    },
9    sname,
10};
11use educe::Educe;
12use either::Either;
13use log::{debug, error};
14use once_cell::unsync::OnceCell;
15use phf::phf_map;
16use snafu::{OptionExt as _, ResultExt as _, ensure_whatever};
17use std::{collections::HashMap, rc::Rc, str::from_utf8};
18use tinyvec::ArrayVec;
19
20/// Convert from CharCode using cmap, use it to select glyph id
21#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
22pub struct CID(pub u16);
23
24/// Input code type, can be one/two/three/four bytes.
25/// TODO: bytes in any length
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27enum CharCode {
28    One([u8; 1]),
29    Two([u8; 2]),
30    Three([u8; 3]),
31    Four([u8; 4]),
32}
33
34impl CharCode {
35    fn from_str_buf(s: &[u8]) -> Self {
36        match s.len() {
37            1 => Self::One([s[0]]),
38            2 => Self::Two([s[0], s[1]]),
39            3 => Self::Three([s[0], s[1], s[2]]),
40            4 => Self::Four([s[0], s[1], s[2], s[3]]),
41            _ => unreachable!("invalid bytes length"),
42        }
43    }
44
45    pub fn n_bytes(self) -> usize {
46        match self {
47            Self::One(_) => 1,
48            Self::Two(_) => 2,
49            Self::Three(_) => 3,
50            Self::Four(_) => 4,
51        }
52    }
53}
54
55fn parse_cid_from_str_buf(s: &[u8]) -> CID {
56    let bytes = match s.len() {
57        1 => [0, s[0]],
58        2 => [s[0], s[1]],
59        _ => unreachable!(),
60    };
61    CID(u16::from_be_bytes(bytes))
62}
63
64impl AsRef<[u8]> for CharCode {
65    fn as_ref(&self) -> &[u8] {
66        match self {
67            Self::One(b) => b,
68            Self::Two(b) => b,
69            Self::Three(b) => b,
70            Self::Four(b) => b,
71        }
72    }
73}
74
75impl From<&[u8]> for CharCode {
76    fn from(bytes: &[u8]) -> Self {
77        match bytes.len() {
78            1 => Self::One([bytes[0]]),
79            2 => Self::Two([bytes[0], bytes[1]]),
80            3 => Self::Three([bytes[0], bytes[1], bytes[2]]),
81            4 => Self::Four([bytes[0], bytes[1], bytes[2], bytes[3]]),
82            _ => unreachable!("invalid bytes length"),
83        }
84    }
85}
86
87/// Common trait that maps CharCode to CID.
88trait CodeMap {
89    fn map(&self, code: CharCode) -> Option<CID>;
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
93struct ByteRange {
94    lower: u8,
95    upper: u8,
96}
97
98impl ByteRange {
99    pub fn new(lower: u8, upper: u8) -> Self {
100        assert!(lower <= upper);
101        Self { lower, upper }
102    }
103
104    fn in_range(self, c: u8) -> bool {
105        self.lower <= c && c <= self.upper
106    }
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq)]
110enum CodeSpaceResult {
111    /// Matched
112    Matched(CharCode),
113    /// Partial match, not enough bytes, or prefix bytes matched.
114    Partial(CharCode),
115    /// No match
116    NotMatched,
117}
118
119/// A range entry in code space, lower and upper must have the same length.
120/// A range matches N bytes, N is the length of inner array, each item
121/// defines a range of bytes, first item for first byte, second item for
122/// second byte, and so on.
123#[derive(Debug, Clone, PartialEq, Eq)]
124struct CodeRange(ArrayVec<[ByteRange; 4]>);
125
126impl CodeRange {
127    fn from_str_buf(lower: &[u8], upper: &[u8]) -> Option<Self> {
128        if lower.len() != upper.len() {
129            return None;
130        }
131        let mut r = ArrayVec::new();
132        for (l, u) in lower.iter().copied().zip(upper.iter().copied()) {
133            r.push(ByteRange::new(l, u));
134        }
135        Some(Self(r))
136    }
137
138    /// If ch not in range, return None,
139    /// else return offset from lower bound.
140    fn offset(&self, ch: CharCode) -> Option<u16> {
141        if ch.n_bytes() != self.n_bytes() {
142            return None;
143        }
144
145        let mut offset = 0u16;
146        for (r, c) in self.0.iter().zip(ch.as_ref().iter().copied()) {
147            if !r.in_range(c) {
148                return None;
149            }
150            offset = offset * (r.upper as u16 - r.lower as u16 + 1) + (c as u16 - r.lower as u16);
151        }
152        Some(offset)
153    }
154
155    /// `ch` in this range if: ch has same length as range, and each byte in nth byte range.
156    fn in_range(&self, ch: CharCode) -> bool {
157        self.offset(ch).is_some()
158    }
159
160    /// Find next code.
161    fn next_code(&self, codes: &[u8]) -> CodeSpaceResult {
162        match self
163            .0
164            .iter()
165            .zip(codes.iter().copied())
166            .take_while(|(r, c)| r.in_range(*c))
167            .count()
168        {
169            0 => CodeSpaceResult::NotMatched,
170            n if n == self.n_bytes() => CodeSpaceResult::Matched(CharCode::from(&codes[..n])),
171            _ => {
172                CodeSpaceResult::Partial(CharCode::from(&codes[..self.n_bytes().min(codes.len())]))
173            }
174        }
175    }
176
177    fn n_bytes(&self) -> usize {
178        self.0.len()
179    }
180}
181
182struct CodeRangeParser;
183
184impl EntryParser<CodeRange> for CodeRangeParser {
185    fn parse_entry<P>(&self, m: &mut Machine<'_, P>) -> Result<CodeRange, MachineError> {
186        let s_upper = m.pop()?.string()?;
187        let s_upper = s_upper.borrow();
188        let s_lower = m.pop()?.string()?;
189        let s_lower = s_lower.borrow();
190        CodeRange::from_str_buf(&s_lower, &s_upper).context(TypeCheckSnafu)
191    }
192}
193
194/// CodeSpace made up by CodeRanges.
195#[derive(Debug, Clone, PartialEq, Eq, Default)]
196struct CodeSpace(Box<[CodeRange]>);
197
198impl CodeSpace {
199    fn new(ranges: Vec<CodeRange>) -> Self {
200        Self(ranges.into_boxed_slice())
201    }
202
203    /// Take next code from input codes, return the rest codes and the next code.
204    /// If next code not in code space, return `Left(next_code)`.
205    /// Returns minimal bytes of current CodeSpace, even in error cases, append zero if not
206    /// enough bytes.
207    fn next_code<'a>(&self, codes: &'a [u8]) -> Result<(&'a [u8], Either<CharCode, CharCode>)> {
208        let next = self
209            .0
210            .iter()
211            .find_map(|r| {
212                let r = r.next_code(codes);
213                match r {
214                    CodeSpaceResult::Matched(code) => Some(Either::Right(Ok(code))),
215                    CodeSpaceResult::Partial(code) => Some(Either::Left(code)),
216                    CodeSpaceResult::NotMatched => None,
217                }
218            })
219            .unwrap_or_else(|| Either::Left(CharCode::One([codes[0]])))
220            .map_left(|code| {
221                let min_bytes = self.min_bytes()?;
222                if code.n_bytes() >= min_bytes {
223                    return Ok(code);
224                }
225
226                let mut bytes = Vec::with_capacity(min_bytes);
227                bytes.extend_from_slice(&codes[..min_bytes.min(codes.len())]);
228                bytes.resize(min_bytes, 0);
229                Ok(CharCode::from(bytes.as_slice()))
230            })
231            .factor_err()?;
232        Ok((&codes[next.into_inner().n_bytes().min(codes.len())..], next))
233    }
234
235    fn min_bytes(&self) -> Result<usize> {
236        self.0
237            .iter()
238            .map(CodeRange::n_bytes)
239            .min()
240            .whatever_context("Should not happen")
241    }
242}
243
244/// Maps a range of codes to CID, first code in range map to `start_cid`,
245/// 2nd code map to `start_cid + 1`, and so on.
246#[derive(Debug, Clone, PartialEq, Eq)]
247struct IncRangeMap {
248    range: CodeRange,
249    start_cid: CID,
250}
251
252impl CodeMap for IncRangeMap {
253    fn map(&self, code: CharCode) -> Option<CID> {
254        self.range
255            .offset(code)
256            .map(|offset| CID(self.start_cid.0 + offset))
257    }
258}
259
260struct IncRangeMapParser;
261
262impl EntryParser<IncRangeMap> for IncRangeMapParser {
263    fn parse_entry<P>(&self, m: &mut Machine<'_, P>) -> Result<IncRangeMap, MachineError> {
264        let cid = m.pop()?.int()?.try_into().whatever_context("Cast to cid")?;
265        let s_upper = m.pop()?.string()?;
266        let s_lower = m.pop()?.string()?;
267        let range = CodeRange::from_str_buf(&s_lower.borrow(), &s_upper.borrow())
268            .context(TypeCheckSnafu)?;
269        Ok(IncRangeMap {
270            range,
271            start_cid: CID(cid),
272        })
273    }
274}
275
276struct BFIncRangeMapParser;
277
278impl EntryParser<IncRangeMap> for BFIncRangeMapParser {
279    fn parse_entry<P>(&self, m: &mut Machine<'_, P>) -> Result<IncRangeMap, MachineError> {
280        let cid = parse_cid_from_str_buf(&m.pop()?.string()?.borrow());
281        let s_upper = m.pop()?.string()?;
282        let s_lower = m.pop()?.string()?;
283        let range = CodeRange::from_str_buf(&s_lower.borrow(), &s_upper.borrow())
284            .context(TypeCheckSnafu)?;
285        Ok(IncRangeMap {
286            range,
287            start_cid: cid,
288        })
289    }
290}
291
292/// Maps a range of codes to CID, all codes in range map to `cid`.
293#[derive(Debug, Clone, PartialEq, Eq)]
294struct RangeMapToOne {
295    range: CodeRange,
296    cid: CID,
297}
298
299impl CodeMap for RangeMapToOne {
300    fn map(&self, code: CharCode) -> Option<CID> {
301        (self.range.in_range(code)).then_some(self.cid)
302    }
303}
304
305struct RangeMapToOneParser;
306
307impl EntryParser<RangeMapToOne> for RangeMapToOneParser {
308    fn parse_entry<P>(&self, m: &mut Machine<'_, P>) -> Result<RangeMapToOne, MachineError> {
309        let cid = m.pop()?.int()?.try_into().whatever_context("cast to cid")?;
310        let s_upper = m.pop()?.string()?;
311        let s_lower = m.pop()?.string()?;
312        let range = CodeRange::from_str_buf(&s_lower.borrow(), &s_upper.borrow())
313            .context(TypeCheckSnafu)?;
314        Ok(RangeMapToOne {
315            range,
316            cid: CID(cid),
317        })
318    }
319}
320
321/// Maps a single code to CID.
322#[derive(Debug, Clone, PartialEq, Eq)]
323struct SingleCodeMap {
324    code: CharCode,
325    cid: CID,
326}
327
328impl SingleCodeMap {
329    fn new(code: CharCode, cid: CID) -> Self {
330        Self { code, cid }
331    }
332}
333
334impl CodeMap for SingleCodeMap {
335    fn map(&self, code: CharCode) -> Option<CID> {
336        (code == self.code).then_some(self.cid)
337    }
338}
339
340struct SingleCodeMapParser;
341
342impl EntryParser<SingleCodeMap> for SingleCodeMapParser {
343    fn parse_entry<P>(&self, m: &mut Machine<'_, P>) -> Result<SingleCodeMap, MachineError> {
344        let cid = m.pop()?.int()?.try_into().whatever_context("cast to cid")?;
345        let s_code = m.pop()?.string()?;
346        let code = CharCode::from_str_buf(&s_code.borrow());
347        Ok(SingleCodeMap::new(code, CID(cid)))
348    }
349}
350
351struct BFSingleCodeMapParser;
352
353impl EntryParser<SingleCodeMap> for BFSingleCodeMapParser {
354    fn parse_entry<P>(&self, m: &mut Machine<'_, P>) -> Result<SingleCodeMap, MachineError> {
355        let cid = parse_cid_from_str_buf(&m.pop()?.string()?.borrow());
356        let s_code = m.pop()?.string()?;
357        let code = CharCode::from_str_buf(&s_code.borrow());
358        Ok(SingleCodeMap::new(code, cid))
359    }
360}
361
362/// Compound mapper that combines range and single code maps.
363/// Single Code maps has higher priority than range maps.
364#[derive(Debug, Clone, PartialEq, Eq, Educe)]
365#[educe(Default)]
366struct Mapper<R> {
367    ranges: Box<[R]>,
368    chars: Box<[SingleCodeMap]>,
369}
370
371impl<R: CodeMap> CodeMap for Mapper<R> {
372    fn map(&self, code: CharCode) -> Option<CID> {
373        let find_in_chars = self.chars.iter().filter_map(|m| m.map(code));
374        let find_in_ranges = self.ranges.iter().filter_map(|m| m.map(code));
375        find_in_chars.chain(find_in_ranges).next()
376    }
377}
378
379#[derive(Debug, Clone, PartialEq, Eq, Default, Hash)]
380pub struct CIDSystemInfo {
381    registry: String,
382    ordering: String,
383    supplement: u16,
384}
385
386impl CIDSystemInfo {
387    fn from_dict<P>(d: &RuntimeDictionary<'_, P>) -> MachineResult<Self> {
388        let registry = from_utf8(&d[&sname("Registry")].string()?.borrow())
389            .whatever_context("Read Registry name from utf8")?
390            .to_owned();
391        let ordering = from_utf8(&d[&sname("Ordering")].string()?.borrow())
392            .whatever_context("Read Ordering name from utf8")?
393            .to_owned();
394        let supplement = d[&sname("Supplement")]
395            .int()?
396            .try_into()
397            .whatever_context("Read Supplement name from utf8")?;
398        Ok(Self {
399            registry,
400            ordering,
401            supplement,
402        })
403    }
404}
405
406#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
407pub enum WriteMode {
408    #[default]
409    Horizontal = 0,
410    Vertical = 1,
411}
412
413impl WriteMode {
414    fn parse(v: i32) -> MachineResult<Self> {
415        match v {
416            0 => Ok(Self::Horizontal),
417            1 => Ok(Self::Vertical),
418            _ => {
419                error!("Invalid WriteMode: {}", v);
420                Err(TypeCheckSnafu.build())
421            }
422        }
423    }
424}
425
426const GB_EUC_H: &[u8] = include_bytes!("../cmap-resources/Adobe-GB1-6/CMap/GB-EUC-H");
427const GB_EUC_V: &[u8] = include_bytes!("../cmap-resources/Adobe-GB1-6/CMap/GB-EUC-V");
428const GBPC_EUC_H: &[u8] = include_bytes!("../cmap-resources/Adobe-GB1-6/CMap/GBpc-EUC-H");
429const GBPC_EUC_V: &[u8] = include_bytes!("../cmap-resources/Adobe-GB1-6/CMap/GBpc-EUC-V");
430const GBK_EUC_V: &[u8] = include_bytes!("../cmap-resources/Adobe-GB1-6/CMap/GBK-EUC-H");
431const GBK_EUC_H: &[u8] = include_bytes!("../cmap-resources/Adobe-GB1-6/CMap/GBK-EUC-V");
432const GBKP_EUC_V: &[u8] = include_bytes!("../cmap-resources/Adobe-GB1-6/CMap/GBKp-EUC-H");
433const GBKP_EUC_H: &[u8] = include_bytes!("../cmap-resources/Adobe-GB1-6/CMap/GBKp-EUC-V");
434const GBK2K_H: &[u8] = include_bytes!("../cmap-resources/Adobe-GB1-6/CMap/GBK2K-H");
435const GBK2K_V: &[u8] = include_bytes!("../cmap-resources/Adobe-GB1-6/CMap/GBK2K-V");
436const UNI_GB_GCSS_H: &[u8] = include_bytes!("../cmap-resources/Adobe-GB1-6/CMap/UniGB-UCS2-H");
437const UNI_GB_GCSS_V: &[u8] = include_bytes!("../cmap-resources/Adobe-GB1-6/CMap/UniGB-UCS2-V");
438const UNI_GB_UTF16_H: &[u8] = include_bytes!("../cmap-resources/Adobe-GB1-6/CMap/UniGB-UTF16-H");
439const UNI_GB_UTF16_V: &[u8] = include_bytes!("../cmap-resources/Adobe-GB1-6/CMap/UniGB-UTF16-V");
440
441const B5PC_H: &[u8] = include_bytes!("../cmap-resources/Adobe-CNS1-7/CMap/B5pc-H");
442const B5PC_V: &[u8] = include_bytes!("../cmap-resources/Adobe-CNS1-7/CMap/B5pc-V");
443const HKSCS_B5_H: &[u8] = include_bytes!("../cmap-resources/Adobe-CNS1-7/CMap/HKscs-B5-H");
444const HKSCS_B5_V: &[u8] = include_bytes!("../cmap-resources/Adobe-CNS1-7/CMap/HKscs-B5-V");
445const ETEN_B5_H: &[u8] = include_bytes!("../cmap-resources/Adobe-CNS1-7/CMap/ETen-B5-H");
446const ETEN_B5_V: &[u8] = include_bytes!("../cmap-resources/Adobe-CNS1-7/CMap/ETen-B5-V");
447const ETENMS_B5_H: &[u8] = include_bytes!("../cmap-resources/Adobe-CNS1-7/CMap/ETenms-B5-H");
448const ETENMS_B5_V: &[u8] = include_bytes!("../cmap-resources/Adobe-CNS1-7/CMap/ETenms-B5-V");
449const CNS_EUC_H: &[u8] = include_bytes!("../cmap-resources/Adobe-CNS1-7/CMap/CNS-EUC-H");
450const CNS_EUC_V: &[u8] = include_bytes!("../cmap-resources/Adobe-CNS1-7/CMap/CNS-EUC-V");
451const UNI_CNS_UCS2_H: &[u8] = include_bytes!("../cmap-resources/Adobe-CNS1-7/CMap/UniCNS-UCS2-H");
452const UNI_CNS_UCS2_V: &[u8] = include_bytes!("../cmap-resources/Adobe-CNS1-7/CMap/UniCNS-UCS2-V");
453const UNI_CNS_UTF16_H: &[u8] = include_bytes!("../cmap-resources/Adobe-CNS1-7/CMap/UniCNS-UTF16-H");
454const UNI_CNS_UTF16_V: &[u8] = include_bytes!("../cmap-resources/Adobe-CNS1-7/CMap/UniCNS-UTF16-V");
455
456const _83PV_RKSJ_H: &[u8] = include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/83pv-RKSJ-H");
457const _90MS_RKSJ_H: &[u8] = include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/90ms-RKSJ-H");
458const _90MS_RKSJ_V: &[u8] = include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/90ms-RKSJ-V");
459const _90MSP_RKSJ_H: &[u8] = include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/90msp-RKSJ-H");
460const _90MSP_RKSJ_V: &[u8] = include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/90msp-RKSJ-V");
461const _90PV_RKSJ_H: &[u8] = include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/90pv-RKSJ-H");
462const ADD_RKSJ_H: &[u8] = include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/Add-RKSJ-H");
463const ADD_RKSJ_V: &[u8] = include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/Add-RKSJ-V");
464const EUC_H: &[u8] = include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/EUC-H");
465const EUC_V: &[u8] = include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/EUC-V");
466const EXT_H: &[u8] = include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/Ext-RKSJ-H");
467const EXT_V: &[u8] = include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/Ext-RKSJ-V");
468const H: &[u8] = include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/H");
469const V: &[u8] = include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/V");
470const UNI_JIS_UCS2_H: &[u8] = include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/UniJIS-UCS2-H");
471const UNI_JIS_UCS2_V: &[u8] = include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/UniJIS-UCS2-V");
472const UNI_JIS_UCS2_HW_H: &[u8] =
473    include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/UniJIS-UCS2-HW-H");
474const UNI_JIS_UCS2_HW_V: &[u8] =
475    include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/UniJIS-UCS2-HW-V");
476const UNI_JIS_UTF16_H: &[u8] =
477    include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/UniJIS-UTF16-H");
478const UNI_JIS_UTF16_V: &[u8] =
479    include_bytes!("../cmap-resources/Adobe-Japan1-7/CMap/UniJIS-UTF16-V");
480
481const KSC_EUC_H: &[u8] = include_bytes!("../cmap-resources/Adobe-Korea1-2/CMap/KSC-EUC-H");
482const KSC_EUC_V: &[u8] = include_bytes!("../cmap-resources/Adobe-Korea1-2/CMap/KSC-EUC-V");
483const KSCMS_UHC_H: &[u8] = include_bytes!("../cmap-resources/Adobe-Korea1-2/CMap/KSCms-UHC-H");
484const KSCMS_UHC_V: &[u8] = include_bytes!("../cmap-resources/Adobe-Korea1-2/CMap/KSCms-UHC-V");
485const KSCMS_UHC_HW_H: &[u8] =
486    include_bytes!("../cmap-resources/Adobe-Korea1-2/CMap/KSCms-UHC-HW-H");
487const KSCMS_UHC_HW_V: &[u8] =
488    include_bytes!("../cmap-resources/Adobe-Korea1-2/CMap/KSCms-UHC-HW-V");
489const KSCPC_EUC_H: &[u8] = include_bytes!("../cmap-resources/Adobe-Korea1-2/CMap/KSCpc-EUC-H");
490const UNI_KS_UCS2_H: &[u8] = include_bytes!("../cmap-resources/Adobe-Korea1-2/CMap/UniKS-UCS2-H");
491const UNI_KS_UCS2_V: &[u8] = include_bytes!("../cmap-resources/Adobe-Korea1-2/CMap/UniKS-UCS2-V");
492const UNI_KS_UTF16_H: &[u8] = include_bytes!("../cmap-resources/Adobe-Korea1-2/CMap/UniKS-UTF16-H");
493const UNI_KS_UTF16_V: &[u8] = include_bytes!("../cmap-resources/Adobe-Korea1-2/CMap/UniKS-UTF16-V");
494
495const IDENTITY_H: &[u8] = include_bytes!("../cmap-resources/Adobe-Identity-0/CMap/Identity-H");
496const IDENTITY_V: &[u8] = include_bytes!("../cmap-resources/Adobe-Identity-0/CMap/Identity-V");
497static PREDEFINED_CMAPS: phf::Map<&'static str, &'static [u8]> = phf_map! {
498    "GB-EUC-H" => GB_EUC_H,
499    "GB-EUC-V" => GB_EUC_V,
500    "GBpc-EUC-H" => GBPC_EUC_H,
501    "GBpc-EUC-V" => GBPC_EUC_V,
502    "GBK-EUC-H" => GBK_EUC_V,
503    "GBK-EUC-V" => GBK_EUC_H,
504    "GBKp-EUC-H" => GBKP_EUC_V,
505    "GBKp-EUC-V" => GBKP_EUC_H,
506    "GBK2K-H" => GBK2K_H,
507    "GBK2K-V" => GBK2K_V,
508    "UniGB-UCS2-H" => UNI_GB_GCSS_H,
509    "UniGB-UCS2-V" => UNI_GB_GCSS_V,
510    "UniGB-UTF16-H" => UNI_GB_UTF16_H,
511    "UniGB-UTF16-V" => UNI_GB_UTF16_V,
512
513    "B5pc-H" => B5PC_H,
514    "B5pc-V" => B5PC_V,
515    "HKscs-B5-H" => HKSCS_B5_H,
516    "HKscs-B5-V" => HKSCS_B5_V,
517    "ETen-B5-H" => ETEN_B5_H,
518    "ETen-B5-V" => ETEN_B5_V,
519    "ETenms-B5-H" => ETENMS_B5_H,
520    "ETenms-B5-V" => ETENMS_B5_V,
521    "CNS-EUC-H" => CNS_EUC_H,
522    "CNS-EUC-V" => CNS_EUC_V,
523    "UniCNS-UCS2-H" => UNI_CNS_UCS2_H,
524    "UniCNS-UCS2-V" => UNI_CNS_UCS2_V,
525    "UniCNS-UTF16-H" => UNI_CNS_UTF16_H,
526    "UniCNS-UTF16-V" => UNI_CNS_UTF16_V,
527
528    "83pv-RKSJ-H" => _83PV_RKSJ_H,
529    "90ms-RKSJ-H" => _90MS_RKSJ_H,
530    "90ms-RKSJ-V" => _90MS_RKSJ_V,
531    "90msp-RKSJ-H" => _90MSP_RKSJ_H,
532    "90msp-RKSJ-V" => _90MSP_RKSJ_V,
533    "90pv-RKSJ-H" => _90PV_RKSJ_H,
534    "Add-RKSJ-H" => ADD_RKSJ_H,
535    "Add-RKSJ-V" => ADD_RKSJ_V,
536    "EUC-H" => EUC_H,
537    "EUC-V" => EUC_V,
538    "Ext-RKSJ-H" => EXT_H,
539    "Ext-RKSJ-V" => EXT_V,
540    "H" => H,
541    "V" => V,
542    "UniJIS-UCS2-H" => UNI_JIS_UCS2_H,
543    "UniJIS-UCS2-V" => UNI_JIS_UCS2_V,
544    "UniJIS-UCS2-HW-H" => UNI_JIS_UCS2_HW_H,
545    "UniJIS-UCS2-HW-V" => UNI_JIS_UCS2_HW_V,
546    "UniJIS-UTF16-H" => UNI_JIS_UTF16_H,
547    "UniJIS-UTF16-V" => UNI_JIS_UTF16_V,
548
549    "KSC-EUC-H" => KSC_EUC_H,
550    "KSC-EUC-V" => KSC_EUC_V,
551    "KSCms-UHC-H" => KSCMS_UHC_H,
552    "KSCms-UHC-V" => KSCMS_UHC_V,
553    "KSCms-UHC-HW-H" => KSCMS_UHC_HW_H,
554    "KSCms-UHC-HW-V" => KSCMS_UHC_HW_V,
555    "KSCpc-EUC-H" => KSCPC_EUC_H,
556    "UniKS-UCS2-H" => UNI_KS_UCS2_H,
557    "UniKS-UCS2-V" => UNI_KS_UCS2_V,
558    "UniKS-UTF16-H" => UNI_KS_UTF16_H,
559    "UniKS-UTF16-V" => UNI_KS_UTF16_V,
560
561    "Identity-H" => IDENTITY_H,
562    "Identity-V" => IDENTITY_V,
563};
564
565/// CMapRegistry contains all CMaps, access by CMap Name.
566#[derive(Debug)]
567pub struct CMapRegistry {
568    predefined: HashMap<&'static str, OnceCell<Rc<CMap>>>,
569    files: HashMap<Name, Rc<CMap>>,
570}
571
572impl Default for CMapRegistry {
573    fn default() -> Self {
574        Self::new()
575    }
576}
577
578impl CMapRegistry {
579    pub fn new() -> Self {
580        Self {
581            predefined: (PREDEFINED_CMAPS
582                .keys()
583                .copied()
584                .map(|k| (k, OnceCell::new())))
585            .collect(),
586            files: HashMap::new(),
587        }
588    }
589
590    pub fn add(&mut self, cmap: CMap) {
591        self.files.insert(cmap.name.clone(), Rc::new(cmap));
592    }
593
594    pub fn get(&self, name: &Name) -> Result<Option<Rc<CMap>>, MachineError> {
595        self.predefined
596            .get(name.as_str())
597            .map(|c| {
598                Ok(Rc::clone(c.get_or_try_init(|| {
599                    let file = PREDEFINED_CMAPS[name.as_str()];
600                    Ok(Rc::new(self.parse_cmap_file(file)?))
601                })?))
602            })
603            .or_else(|| Ok::<_, MachineError>(self.files.get(name).cloned()).transpose())
604            .transpose()
605    }
606
607    fn parse_cmap_file(&self, file: &[u8]) -> Result<CMap, MachineError> {
608        let p = CMapMachinePlugin {
609            registry: self,
610            parsed: None,
611            entries_parsing: None,
612            code_space_entries: Default::default(),
613            cid_range_entries: Default::default(),
614            cid_char_entries: Default::default(),
615            notdef_range_entries: Default::default(),
616            notdef_char_entries: Default::default(),
617            use_cmap: None,
618        };
619        let mut m = Machine::<'_, CMapMachinePlugin<'_>>::with_plugin(file, p);
620        m.execute()?;
621        let mut p = m.take_plugin();
622        p.parsed
623            .take()
624            .whatever_context("CMap not defined in cmap file")
625    }
626
627    /// Add a CMap file, parse it and add to registry.
628    pub fn add_cmap_file(&mut self, file: &[u8]) -> Result<Rc<CMap>> {
629        let parsed = self
630            .parse_cmap_file(file)
631            .whatever_context("parse cmap file")?;
632        let name = parsed.name.clone();
633        debug!("CMap added: {}", name);
634        self.add(parsed);
635        self.get(&name)
636            .with_whatever_context(|_| format!("get cmap: {}", name))?
637            .with_whatever_context(|| format!("CMap not found: {}", name))
638    }
639}
640
641/// CMap maps sequence CharCode to sequence of CIDs.
642#[derive(Debug, PartialEq, Eq)]
643pub struct CMap {
644    pub cid_system_info: CIDSystemInfo,
645    pub w_mode: WriteMode,
646    pub name: Name,
647
648    code_space: CodeSpace,
649    cid_map: Mapper<IncRangeMap>,
650    notdef_map: Mapper<RangeMapToOne>,
651    use_map: Option<Rc<CMap>>,
652}
653
654const DEFAULT_NOTDEF: CID = CID(0);
655
656impl CMap {
657    /// Map(Decode) char codes to CIDs.
658    /// If code out of code space, or not mapped to cid, use notdef_map to map to a designed notdef
659    /// char, if code not in notdef_map, returns 0 (notdef).
660    pub fn map(&self, mut codes: &[u8]) -> Result<Vec<CID>> {
661        let mut r = Vec::with_capacity(codes.len());
662        while !codes.is_empty() {
663            let code;
664            (codes, code) = self.next_cid(codes)?;
665            let cid = code.map_left(|c| self.map_undef(c)).into_inner();
666
667            r.push(cid);
668        }
669        Ok(r)
670    }
671
672    /// Get next cid, update codes buffer, without map notdef.
673    /// If use_map not null, recover codes buffer, call next_cid.
674    fn next_cid<'a>(&self, codes: &'a [u8]) -> Result<(&'a [u8], Either<CharCode, CID>)> {
675        let (new_codes, code) = self.code_space.next_code(codes)?;
676        let cid_or_code = code.right_and_then(|c| self.cid_map.map(c).ok_or(c).into());
677
678        let Some(use_map) = self.use_map.as_ref() else {
679            return Ok((new_codes, cid_or_code));
680        };
681
682        cid_or_code
683            .map_either(
684                |_| use_map.next_cid(codes),
685                |cid| Ok((new_codes, Either::Right(cid))),
686            )
687            .into_inner()
688    }
689
690    /// Map undef cid, if notdef_map failed, call use_map.map_undef() if has use_map
691    fn map_undef(&self, ch: CharCode) -> CID {
692        self.notdef_map.map(ch).unwrap_or_else(|| {
693            self.use_map
694                .as_ref()
695                .map_or(DEFAULT_NOTDEF, |m| m.map_undef(ch))
696        })
697    }
698}
699
700trait EntryParser<T> {
701    fn parse_entry<P>(&self, m: &mut Machine<'_, P>) -> Result<T, MachineError>;
702}
703
704struct EntriesParsing {
705    n: usize,
706}
707
708impl EntriesParsing {
709    fn new<P>(m: &mut Machine<'_, P>) -> Result<Self, MachineError> {
710        Ok(Self {
711            n: m.pop()?.int()? as usize,
712        })
713    }
714
715    fn on_end<P, EP: EntryParser<T>, T>(
716        self,
717        parser: &EP,
718        m: &mut Machine<'_, P>,
719    ) -> Result<Vec<T>, MachineError> {
720        let mut entries = Vec::with_capacity(self.n);
721        for _ in 0..self.n {
722            entries.push(parser.parse_entry(m)?);
723        }
724        entries.reverse();
725        Ok(entries)
726    }
727}
728
729#[derive(Educe)]
730#[educe(Default)]
731struct EntriesParser<T> {
732    entries: Vec<T>,
733}
734
735impl<T> EntriesParser<T> {
736    fn extend(&mut self, entries: Vec<T>) {
737        self.entries.extend(entries);
738    }
739
740    fn take(&mut self) -> Vec<T> {
741        std::mem::take(&mut self.entries)
742    }
743}
744
745/// CMap Machine plugin.
746struct CMapMachinePlugin<'a> {
747    registry: &'a CMapRegistry,
748    parsed: Option<CMap>,
749    use_cmap: Option<Rc<CMap>>,
750    entries_parsing: Option<EntriesParsing>,
751    code_space_entries: EntriesParser<CodeRange>,
752    cid_range_entries: EntriesParser<IncRangeMap>,
753    cid_char_entries: EntriesParser<SingleCodeMap>,
754    notdef_range_entries: EntriesParser<RangeMapToOne>,
755    notdef_char_entries: EntriesParser<SingleCodeMap>,
756}
757
758macro_rules! built_in_ops {
759    ($($k:literal => $v:expr),* $(,)?) => {
760        std::iter::Iterator::collect(std::iter::IntoIterator::into_iter([$((Key::Name(Name::from_static($k)), RuntimeValue::<'_, CMapMachinePlugin<'_>>::BuiltInOp($v)),)*]))
761    };
762}
763
764impl MachinePlugin for CMapMachinePlugin<'_> {
765    fn find_proc_set_resource<'b>(&self, name: &Name) -> Option<RuntimeDictionary<'b, Self>> {
766        (name == "CIDInit").then(|| -> HashMap<Key, RuntimeValue<'_, Self>> {
767            built_in_ops!(
768                "begincmap" => |_| {
769                    ok()
770                },
771                "endcmap" => |_| {
772                    ok()
773                },
774                "CMapName" => |m| {
775                    let d = m.current_dict()?;
776                    m.push(d.borrow().get(&sname("CMapName")).whatever_context("get CMapName")?.clone());
777                    ok()
778                },
779                "begincodespacerange" => |m| {
780                    m.p.entries_parsing = Some(EntriesParsing::new(m)?);
781                    ok()
782                },
783                "endcodespacerange" => |m| {
784                    let entries = m.p.entries_parsing.take().whatever_context("take entries_parsing")?.on_end(&CodeRangeParser, m)?;
785                    m.p.code_space_entries.extend(entries);
786                    ok()
787                },
788                "begincidrange" => |m| {
789                    m.p.entries_parsing = Some(EntriesParsing::new(m)?);
790                    ok()
791                },
792                "endcidrange" => |m| {
793                    let entries = m.p.entries_parsing.take().whatever_context("take entries_parsing")?.on_end(&IncRangeMapParser, m)?;
794                    m.p.cid_range_entries.extend(entries);
795                    ok()
796                },
797                "beginbfrange" => |m| {
798                    m.p.entries_parsing = Some(EntriesParsing::new(m)?);
799                    ok()
800                },
801                "endbfrange" => |m| {
802                    let entries = m.p.entries_parsing.take().whatever_context("take entries_parsing")?.on_end(&BFIncRangeMapParser, m)?;
803                    m.p.cid_range_entries.extend(entries);
804                    ok()
805                },
806                "begincidchar" => |m| {
807                    m.p.entries_parsing = Some(EntriesParsing::new(m)?);
808                    ok()
809                },
810                "endcidchar" => |m| {
811                    let entries = m.p.entries_parsing.take().whatever_context("take entries_parsing")?.on_end(&SingleCodeMapParser, m)?;
812                    m.p.cid_char_entries.extend(entries);
813                    ok()
814                },
815                "beginbfchar" => |m| {
816                    m.p.entries_parsing = Some(EntriesParsing::new(m)?);
817                    ok()
818                },
819                "endbfchar" => |m| {
820                    let entries = m.p.entries_parsing.take().whatever_context("take entries_parsing")?.on_end(&BFSingleCodeMapParser, m)?;
821                    m.p.cid_char_entries.extend(entries);
822                    ok()
823                },
824                "beginnotdefrange" => |m| {
825                    m.p.entries_parsing = Some(EntriesParsing::new(m)?);
826                    ok()
827                },
828                "endnotdefrange" => |m| {
829                    let entries = m.p.entries_parsing.take().whatever_context("take entries_parsing")?.on_end(&RangeMapToOneParser, m)?;
830                    m.p.notdef_range_entries.extend(entries);
831                    ok()
832                },
833                "beginnotdefchar" => |m| {
834                    m.p.entries_parsing = Some(EntriesParsing::new(m)?);
835                    ok()
836                },
837                "endnotdefchar" => |m| {
838                    let entries = m.p.entries_parsing.take().whatever_context("take entries_parsing")?.on_end(&SingleCodeMapParser, m)?;
839                    m.p.notdef_char_entries.extend(entries);
840                    ok()
841                },
842                "defineresource" => |m| {
843                    let res_category = m.pop()?.name()?;
844                    ensure_whatever!(res_category == sname("CMap"), "Not CMap resource");
845                    let d = m.pop()?.dict()?;
846                    let d_ref = d.borrow();
847                    let cmap_name = m.pop()?.name()?;
848                    let cmap = CMap {
849                        cid_system_info: CIDSystemInfo::from_dict(&d_ref[&sname("CIDSystemInfo")].dict()?.borrow())?,
850                        w_mode: WriteMode::parse(
851                            d_ref.get(&sname("WMode")).map_or_else(|| Ok(0), |v| v.int()).whatever_context("get WMode")?
852                        ).whatever_context("parse WMode")?,
853                        name: cmap_name,
854                        code_space: CodeSpace::new(m.p.code_space_entries.take()),
855                        cid_map: Mapper {
856                            ranges: m.p.cid_range_entries.take().into(),
857                            chars: m.p.cid_char_entries.take().into(),
858                        },
859                        notdef_map: Mapper{
860                            ranges: m.p.notdef_range_entries.take().into(),
861                            chars: m.p.notdef_char_entries.take().into(),
862                        },
863                        use_map: m.p.use_cmap.take(),
864                    };
865                    m.p.parsed = Some(cmap);
866                    // should push cmap object to stack, but cmap object not RuntimeValue
867                    // so push a dummy value. This value normally is not used and pop up immediately.
868                    m.push(sname("cmap stub"));
869                    ok()
870                },
871                "usecmap" => |m| {
872                    let name = m.pop()?.name()?;
873                    let cmap = m.p.registry.get(&name).with_whatever_context(|_|format!("get cmap: {}", name))?.context(UndefinedSnafu)?;
874                    m.p.use_cmap = Some(cmap);
875                    ok()
876                },
877            )
878        })
879    }
880}
881
882#[cfg(test)]
883mod tests;