1#![cfg_attr(not(feature = "std"), no_std)]
13#![forbid(unsafe_code)]
14#![deny(missing_docs)]
15
16extern crate alloc;
17
18#[cfg(feature = "embed-cmaps")]
19mod bcmap;
20mod parse;
21
22#[cfg(feature = "embed-cmaps")]
23pub use bcmap::load_embedded;
24
25#[cfg(not(feature = "embed-cmaps"))]
29pub fn load_embedded(_name: CMapName<'_>) -> Option<&'static [u8]> {
30 None
31}
32
33use alloc::boxed::Box;
34use alloc::string::String;
35use alloc::vec::Vec;
36
37pub type Cid = u32;
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum CMapName<'a> {
43 N83pvRksjH,
46 N90msRksjH,
48 N90msRksjV,
50 N90mspRksjH,
52 N90mspRksjV,
54 N90pvRksjH,
56 AddRksjH,
58 AddRksjV,
60
61 AdobeCns1Ucs2,
66 AdobeGb1Ucs2,
68 AdobeJapan1Ucs2,
70 AdobeKorea1Ucs2,
72
73 B5pcH,
76 B5pcV,
78 CnsEucH,
80 CnsEucV,
82 ETenB5H,
84 ETenB5V,
86 ETenmsB5H,
88 ETenmsB5V,
90
91 EucH,
94 EucV,
96 ExtRksjH,
98 ExtRksjV,
100
101 GbEucH,
104 GbEucV,
106 GbkEucH,
108 GbkEucV,
110 Gbk2kH,
112 Gbk2kV,
114 GbkpEucH,
116 GbkpEucV,
118 GbpcEucH,
120 GbpcEucV,
122
123 H,
126
127 HKscsB5H,
130 HKscsB5V,
132
133 IdentityH,
137 IdentityV,
140
141 KscEucH,
144 KscEucV,
146 KscmsUhcH,
148 KscmsUhcHwH,
150 KscmsUhcHwV,
152 KscmsUhcV,
154 KscpcEucH,
156
157 UniCnsUcs2H,
160 UniCnsUcs2V,
162 UniCnsUtf16H,
164 UniCnsUtf16V,
166
167 UniGbUcs2H,
170 UniGbUcs2V,
172 UniGbUtf16H,
174 UniGbUtf16V,
176
177 UniJisUcs2H,
180 UniJisUcs2HwH,
182 UniJisUcs2HwV,
184 UniJisUcs2V,
186 UniJisUtf16H,
188 UniJisUtf16V,
190
191 UniKsUcs2H,
194 UniKsUcs2V,
196 UniKsUtf16H,
198 UniKsUtf16V,
200
201 V,
204
205 Custom(&'a [u8]),
207}
208
209impl<'a> CMapName<'a> {
210 pub fn from_bytes(name: &'a [u8]) -> Self {
212 match name {
213 b"83pv-RKSJ-H" => Self::N83pvRksjH,
214 b"90ms-RKSJ-H" => Self::N90msRksjH,
215 b"90ms-RKSJ-V" => Self::N90msRksjV,
216 b"90msp-RKSJ-H" => Self::N90mspRksjH,
217 b"90msp-RKSJ-V" => Self::N90mspRksjV,
218 b"90pv-RKSJ-H" => Self::N90pvRksjH,
219 b"Add-RKSJ-H" => Self::AddRksjH,
220 b"Add-RKSJ-V" => Self::AddRksjV,
221 b"Adobe-CNS1-UCS2" => Self::AdobeCns1Ucs2,
222 b"Adobe-GB1-UCS2" => Self::AdobeGb1Ucs2,
223 b"Adobe-Japan1-UCS2" => Self::AdobeJapan1Ucs2,
224 b"Adobe-Korea1-UCS2" => Self::AdobeKorea1Ucs2,
225 b"B5pc-H" => Self::B5pcH,
226 b"B5pc-V" => Self::B5pcV,
227 b"CNS-EUC-H" => Self::CnsEucH,
228 b"CNS-EUC-V" => Self::CnsEucV,
229 b"ETen-B5-H" => Self::ETenB5H,
230 b"ETen-B5-V" => Self::ETenB5V,
231 b"ETenms-B5-H" => Self::ETenmsB5H,
232 b"ETenms-B5-V" => Self::ETenmsB5V,
233 b"EUC-H" => Self::EucH,
234 b"EUC-V" => Self::EucV,
235 b"Ext-RKSJ-H" => Self::ExtRksjH,
236 b"Ext-RKSJ-V" => Self::ExtRksjV,
237 b"GB-EUC-H" => Self::GbEucH,
238 b"GB-EUC-V" => Self::GbEucV,
239 b"GBK-EUC-H" => Self::GbkEucH,
240 b"GBK-EUC-V" => Self::GbkEucV,
241 b"GBK2K-H" => Self::Gbk2kH,
242 b"GBK2K-V" => Self::Gbk2kV,
243 b"GBKp-EUC-H" => Self::GbkpEucH,
244 b"GBKp-EUC-V" => Self::GbkpEucV,
245 b"GBpc-EUC-H" => Self::GbpcEucH,
246 b"GBpc-EUC-V" => Self::GbpcEucV,
247 b"H" => Self::H,
248 b"HKscs-B5-H" => Self::HKscsB5H,
249 b"HKscs-B5-V" => Self::HKscsB5V,
250 b"Identity-H" => Self::IdentityH,
251 b"Identity-V" => Self::IdentityV,
252 b"KSC-EUC-H" => Self::KscEucH,
253 b"KSC-EUC-V" => Self::KscEucV,
254 b"KSCms-UHC-H" => Self::KscmsUhcH,
255 b"KSCms-UHC-HW-H" => Self::KscmsUhcHwH,
256 b"KSCms-UHC-HW-V" => Self::KscmsUhcHwV,
257 b"KSCms-UHC-V" => Self::KscmsUhcV,
258 b"KSCpc-EUC-H" => Self::KscpcEucH,
259 b"UniCNS-UCS2-H" => Self::UniCnsUcs2H,
260 b"UniCNS-UCS2-V" => Self::UniCnsUcs2V,
261 b"UniCNS-UTF16-H" => Self::UniCnsUtf16H,
262 b"UniCNS-UTF16-V" => Self::UniCnsUtf16V,
263 b"UniGB-UCS2-H" => Self::UniGbUcs2H,
264 b"UniGB-UCS2-V" => Self::UniGbUcs2V,
265 b"UniGB-UTF16-H" => Self::UniGbUtf16H,
266 b"UniGB-UTF16-V" => Self::UniGbUtf16V,
267 b"UniJIS-UCS2-H" => Self::UniJisUcs2H,
268 b"UniJIS-UCS2-HW-H" => Self::UniJisUcs2HwH,
269 b"UniJIS-UCS2-HW-V" => Self::UniJisUcs2HwV,
270 b"UniJIS-UCS2-V" => Self::UniJisUcs2V,
271 b"UniJIS-UTF16-H" => Self::UniJisUtf16H,
272 b"UniJIS-UTF16-V" => Self::UniJisUtf16V,
273 b"UniKS-UCS2-H" => Self::UniKsUcs2H,
274 b"UniKS-UCS2-V" => Self::UniKsUcs2V,
275 b"UniKS-UTF16-H" => Self::UniKsUtf16H,
276 b"UniKS-UTF16-V" => Self::UniKsUtf16V,
277 b"V" => Self::V,
278 _ => Self::Custom(name),
279 }
280 }
281
282 pub fn to_bytes(&self) -> &[u8] {
284 match self {
285 Self::N83pvRksjH => b"83pv-RKSJ-H",
286 Self::N90msRksjH => b"90ms-RKSJ-H",
287 Self::N90msRksjV => b"90ms-RKSJ-V",
288 Self::N90mspRksjH => b"90msp-RKSJ-H",
289 Self::N90mspRksjV => b"90msp-RKSJ-V",
290 Self::N90pvRksjH => b"90pv-RKSJ-H",
291 Self::AddRksjH => b"Add-RKSJ-H",
292 Self::AddRksjV => b"Add-RKSJ-V",
293 Self::AdobeCns1Ucs2 => b"Adobe-CNS1-UCS2",
294 Self::AdobeGb1Ucs2 => b"Adobe-GB1-UCS2",
295 Self::AdobeJapan1Ucs2 => b"Adobe-Japan1-UCS2",
296 Self::AdobeKorea1Ucs2 => b"Adobe-Korea1-UCS2",
297 Self::B5pcH => b"B5pc-H",
298 Self::B5pcV => b"B5pc-V",
299 Self::CnsEucH => b"CNS-EUC-H",
300 Self::CnsEucV => b"CNS-EUC-V",
301 Self::ETenB5H => b"ETen-B5-H",
302 Self::ETenB5V => b"ETen-B5-V",
303 Self::ETenmsB5H => b"ETenms-B5-H",
304 Self::ETenmsB5V => b"ETenms-B5-V",
305 Self::EucH => b"EUC-H",
306 Self::EucV => b"EUC-V",
307 Self::ExtRksjH => b"Ext-RKSJ-H",
308 Self::ExtRksjV => b"Ext-RKSJ-V",
309 Self::GbEucH => b"GB-EUC-H",
310 Self::GbEucV => b"GB-EUC-V",
311 Self::GbkEucH => b"GBK-EUC-H",
312 Self::GbkEucV => b"GBK-EUC-V",
313 Self::Gbk2kH => b"GBK2K-H",
314 Self::Gbk2kV => b"GBK2K-V",
315 Self::GbkpEucH => b"GBKp-EUC-H",
316 Self::GbkpEucV => b"GBKp-EUC-V",
317 Self::GbpcEucH => b"GBpc-EUC-H",
318 Self::GbpcEucV => b"GBpc-EUC-V",
319 Self::H => b"H",
320 Self::HKscsB5H => b"HKscs-B5-H",
321 Self::HKscsB5V => b"HKscs-B5-V",
322 Self::IdentityH => b"Identity-H",
323 Self::IdentityV => b"Identity-V",
324 Self::KscEucH => b"KSC-EUC-H",
325 Self::KscEucV => b"KSC-EUC-V",
326 Self::KscmsUhcH => b"KSCms-UHC-H",
327 Self::KscmsUhcHwH => b"KSCms-UHC-HW-H",
328 Self::KscmsUhcHwV => b"KSCms-UHC-HW-V",
329 Self::KscmsUhcV => b"KSCms-UHC-V",
330 Self::KscpcEucH => b"KSCpc-EUC-H",
331 Self::UniCnsUcs2H => b"UniCNS-UCS2-H",
332 Self::UniCnsUcs2V => b"UniCNS-UCS2-V",
333 Self::UniCnsUtf16H => b"UniCNS-UTF16-H",
334 Self::UniCnsUtf16V => b"UniCNS-UTF16-V",
335 Self::UniGbUcs2H => b"UniGB-UCS2-H",
336 Self::UniGbUcs2V => b"UniGB-UCS2-V",
337 Self::UniGbUtf16H => b"UniGB-UTF16-H",
338 Self::UniGbUtf16V => b"UniGB-UTF16-V",
339 Self::UniJisUcs2H => b"UniJIS-UCS2-H",
340 Self::UniJisUcs2HwH => b"UniJIS-UCS2-HW-H",
341 Self::UniJisUcs2HwV => b"UniJIS-UCS2-HW-V",
342 Self::UniJisUcs2V => b"UniJIS-UCS2-V",
343 Self::UniJisUtf16H => b"UniJIS-UTF16-H",
344 Self::UniJisUtf16V => b"UniJIS-UTF16-V",
345 Self::UniKsUcs2H => b"UniKS-UCS2-H",
346 Self::UniKsUcs2V => b"UniKS-UCS2-V",
347 Self::UniKsUtf16H => b"UniKS-UTF16-H",
348 Self::UniKsUtf16V => b"UniKS-UTF16-V",
349 Self::V => b"V",
350 Self::Custom(name) => name,
351 }
352 }
353}
354
355const MAX_NESTING_DEPTH: u32 = 16;
357
358#[derive(Debug, Clone)]
360pub struct CMap {
361 metadata: Metadata,
362 _codespace_ranges: Vec<CodespaceRange>,
365 cid_ranges: PartitionedRanges,
366 notdef_ranges: PartitionedRanges,
367 bf_entries: Vec<BfRange>,
368 base: Option<Box<Self>>,
369}
370
371impl CMap {
372 pub fn parse<'a>(
377 data: &[u8],
378 get_cmap: impl Fn(CMapName<'_>) -> Option<&'a [u8]> + Clone + 'a,
379 ) -> Option<Self> {
380 parse::parse_inner(data, get_cmap, 0)
381 }
382
383 pub fn identity_h() -> Self {
385 Self::identity(WritingMode::Horizontal, b"Identity-H")
386 }
387
388 pub fn identity_v() -> Self {
390 Self::identity(WritingMode::Vertical, b"Identity-V")
391 }
392
393 fn identity(writing_mode: WritingMode, name: &[u8]) -> Self {
394 Self {
395 metadata: Metadata {
396 character_collection: Some(CharacterCollection {
397 family: CidFamily::AdobeIdentity,
398 supplement: 0,
399 }),
400 name: Some(Vec::from(name)),
401 writing_mode: Some(writing_mode),
402 },
403 _codespace_ranges: Vec::new(),
404 cid_ranges: {
405 let mut r = PartitionedRanges::new();
406 r.push(
407 2,
408 CidRange {
409 range: Range {
410 start: 0,
411 end: 0xFFFF,
412 },
413 cid_start: 0,
414 },
415 );
416 r
417 },
418 notdef_ranges: PartitionedRanges::new(),
419 bf_entries: Vec::new(),
420 base: None,
421 }
422 }
423
424 pub fn metadata(&self) -> &Metadata {
426 &self.metadata
427 }
428
429 pub fn lookup_cid_code(&self, code: u32, byte_len: u8) -> Option<Cid> {
433 let cid_ranges = self.cid_ranges.get(byte_len)?;
438 let notdef_ranges = self.notdef_ranges.get(byte_len)?;
439
440 if let Some(entry) = find_in_ranges(cid_ranges, code) {
441 let offset = code.checked_sub(entry.range.start)?;
442 return entry.cid_start.checked_add(offset);
443 } else if let Some(entry) = find_in_ranges(notdef_ranges, code) {
444 return Some(entry.cid_start);
447 }
448
449 if let Some(BfString::Char(lookup)) = self.lookup_bf_string_inner(code, false) {
453 return Some(lookup as u32);
454 }
455
456 self.base
458 .as_ref()
459 .and_then(|b| b.lookup_cid_code(code, byte_len))
460 }
461
462 pub fn lookup_bf_string(&self, code: u32) -> Option<BfString> {
468 self.lookup_bf_string_inner(code, true)
469 }
470
471 fn lookup_bf_string_inner(&self, code: u32, recurse: bool) -> Option<BfString> {
472 if let Some(entry) = find_in_ranges(&self.bf_entries, code) {
473 let offset = u16::try_from(code - entry.range.start).ok()?;
474
475 fn decode_utf16(units: &[u16]) -> Option<BfString> {
476 let mut iter = core::char::decode_utf16(units.iter().copied());
477 let first = iter.next()?.ok()?;
478
479 if iter.next().is_none() {
480 Some(BfString::Char(first))
481 } else {
482 let s = String::from_utf16(units).ok()?;
483 Some(BfString::String(s))
484 }
485 }
486
487 return if offset == 0 {
488 Some(decode_utf16(&entry.dst_base)?)
489 } else {
490 let mut units = entry.dst_base.clone();
491 *units.last_mut()? = units.last()?.checked_add(offset)?;
492 Some(decode_utf16(&units)?)
493 };
494 }
495
496 if recurse {
497 self.base.as_ref()?.lookup_bf_string(code)
498 } else {
499 None
500 }
501 }
502}
503
504trait HasRange {
505 fn range(&self) -> &Range;
506}
507
508fn find_in_ranges<T: HasRange>(entries: &[T], code: u32) -> Option<&T> {
509 let idx = entries
510 .binary_search_by(|entry| {
511 let r = entry.range();
512 if code < r.start {
513 core::cmp::Ordering::Greater
514 } else if code > r.end {
515 core::cmp::Ordering::Less
516 } else {
517 core::cmp::Ordering::Equal
518 }
519 })
520 .ok()?;
521
522 Some(&entries[idx])
523}
524
525#[derive(Debug, Clone)]
527pub(crate) struct Range {
528 pub(crate) start: u32,
529 pub(crate) end: u32,
530}
531
532#[derive(Debug, Clone)]
534pub struct CidRange {
535 pub(crate) range: Range,
536 pub(crate) cid_start: Cid,
537}
538
539impl HasRange for CidRange {
540 fn range(&self) -> &Range {
541 &self.range
542 }
543}
544
545#[derive(Debug, Clone)]
546pub(crate) struct PartitionedRanges {
547 buckets: [Vec<CidRange>; 4],
548}
549
550impl PartitionedRanges {
551 pub(crate) fn new() -> Self {
552 Self {
553 buckets: [Vec::new(), Vec::new(), Vec::new(), Vec::new()],
554 }
555 }
556
557 pub(crate) fn push(&mut self, byte_len: usize, range: CidRange) {
558 if let Some(bucket) = byte_len
559 .checked_sub(1)
560 .and_then(|i| self.buckets.get_mut(i))
561 {
562 bucket.push(range);
563 }
564 }
565
566 pub(crate) fn get(&self, byte_len: u8) -> Option<&[CidRange]> {
567 let idx = (byte_len as usize).checked_sub(1)?;
568 self.buckets.get(idx).map(|v| v.as_slice())
569 }
570
571 pub(crate) fn sort(&mut self) {
572 for bucket in &mut self.buckets {
573 bucket.sort_by(|a, b| a.range.start.cmp(&b.range.start));
574 }
575 }
576}
577
578#[derive(Debug, Clone)]
579pub(crate) struct BfRange {
580 pub(crate) range: Range,
581 pub(crate) dst_base: Vec<u16>,
582}
583
584impl HasRange for BfRange {
585 fn range(&self) -> &Range {
586 &self.range
587 }
588}
589
590#[derive(Debug, Clone)]
592#[allow(dead_code)]
593pub(crate) struct CodespaceRange {
594 pub(crate) number_bytes: u8,
595 pub(crate) low: u32,
596 pub(crate) high: u32,
597}
598
599#[derive(Debug, Clone, PartialEq, Eq)]
601pub enum BfString {
602 Char(char),
604 String(String),
606}
607
608#[derive(Debug, Clone)]
610pub struct Metadata {
611 pub character_collection: Option<CharacterCollection>,
613 pub name: Option<Vec<u8>>,
615 pub writing_mode: Option<WritingMode>,
617}
618
619#[derive(Debug, Clone, PartialEq, Eq)]
621pub enum CidFamily {
622 AdobeJapan1,
624 AdobeGB1,
626 AdobeCNS1,
628 AdobeKorea1,
630 AdobeIdentity,
632 Custom {
634 registry: Vec<u8>,
636 ordering: Vec<u8>,
638 },
639}
640
641impl CidFamily {
642 pub fn from_registry_ordering(registry: &[u8], ordering: &[u8]) -> Self {
644 if registry == b"Adobe" {
645 match ordering {
646 b"Japan1" => Self::AdobeJapan1,
647 b"GB1" => Self::AdobeGB1,
648 b"CNS1" => Self::AdobeCNS1,
649 b"Korea1" => Self::AdobeKorea1,
650 b"Identity" => Self::AdobeIdentity,
651 _ => Self::Custom {
652 registry: registry.to_vec(),
653 ordering: ordering.to_vec(),
654 },
655 }
656 } else {
657 Self::Custom {
658 registry: registry.to_vec(),
659 ordering: ordering.to_vec(),
660 }
661 }
662 }
663
664 pub fn unicode_cmap(&self) -> Option<CMapName<'static>> {
667 match self {
668 Self::AdobeJapan1 => Some(CMapName::UniJisUtf16H),
669 Self::AdobeGB1 => Some(CMapName::UniGbUtf16H),
670 Self::AdobeCNS1 => Some(CMapName::UniCnsUtf16H),
671 Self::AdobeKorea1 => Some(CMapName::UniKsUtf16H),
672 Self::AdobeIdentity | Self::Custom { .. } => None,
673 }
674 }
675
676 pub fn ucs2_cmap(&self) -> Option<CMapName<'static>> {
678 match self {
679 Self::AdobeJapan1 => Some(CMapName::AdobeJapan1Ucs2),
680 Self::AdobeGB1 => Some(CMapName::AdobeGb1Ucs2),
681 Self::AdobeCNS1 => Some(CMapName::AdobeCns1Ucs2),
682 Self::AdobeKorea1 => Some(CMapName::AdobeKorea1Ucs2),
683 Self::AdobeIdentity | Self::Custom { .. } => None,
684 }
685 }
686}
687
688#[derive(Debug, Clone, PartialEq, Eq)]
690pub struct CharacterCollection {
691 pub family: CidFamily,
693 pub supplement: i32,
695}
696
697#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
699pub enum WritingMode {
700 #[default]
702 Horizontal,
703 Vertical,
705}
706
707#[cfg(test)]
708mod tests {
709 use super::*;
710
711 const PREAMBLE: &[u8] = br#"/CIDSystemInfo 3 dict dup begin
716 /Registry (Adobe) def
717 /Ordering (Japan1) def
718 /Supplement 0 def
719end def
720/CMapName /Test def
721/WMode 0 def
7222 begincodespacerange
723<00> <FF>
724<0000> <FFFF>
725endcodespacerange
726"#;
727
728 fn parse_with_preamble(body: &[u8]) -> CMap {
729 let mut data = Vec::new();
730 data.extend_from_slice(PREAMBLE);
731 data.extend_from_slice(body);
732 CMap::parse(&data, |_| None).unwrap()
733 }
734
735 #[test]
736 fn metadata_parsing() {
737 let data = br#"
738/CIDInit /ProcSet findresource begin
73912 dict begin
740begincmap
741/CIDSystemInfo 3 dict dup begin
742 /Registry (Adobe) def
743 /Ordering (Japan1) def
744 /Supplement 6 def
745end def
746/CMapName /Adobe-Japan1-H def
747/CMapType 1 def
748/WMode 0 def
749endcmap"#;
750
751 let cmap = CMap::parse(data, |_| None).unwrap();
752 let cc = cmap.metadata().character_collection.as_ref().unwrap();
753 assert_eq!(cc.family, CidFamily::AdobeJapan1);
754 assert_eq!(cc.supplement, 6);
755 assert_eq!(
756 cmap.metadata().name.as_deref(),
757 Some(b"Adobe-Japan1-H".as_slice())
758 );
759 assert_eq!(cmap.metadata().writing_mode, Some(WritingMode::Horizontal));
760 }
761
762 #[test]
763 fn vertical_writing_mode() {
764 let data = br#"
765/CIDSystemInfo 3 dict dup begin
766 /Registry (Adobe) def
767 /Ordering (Japan1) def
768 /Supplement 6 def
769end def
770/CMapName /Adobe-Japan1-V def
771/WMode 1 def
772"#;
773
774 let cmap = CMap::parse(data, |_| None).unwrap();
775 assert_eq!(cmap.metadata().writing_mode, Some(WritingMode::Vertical));
776 assert_eq!(
777 cmap.metadata().name.as_deref(),
778 Some(b"Adobe-Japan1-V".as_slice())
779 );
780 }
781
782 #[test]
783 fn cid_range_lookup() {
784 let cmap = parse_with_preamble(
785 br#"
7863 begincidrange
787<0000> <00FF> 0
788<0100> <01FF> 256
789<8140> <817E> 633
790endcidrange
791"#,
792 );
793
794 assert_eq!(cmap.lookup_cid_code(0x0000, 2), Some(0));
795 assert_eq!(cmap.lookup_cid_code(0x0042, 2), Some(0x42));
796 assert_eq!(cmap.lookup_cid_code(0x00FF, 2), Some(0xFF));
797
798 assert_eq!(cmap.lookup_cid_code(0x0100, 2), Some(256));
799 assert_eq!(cmap.lookup_cid_code(0x01FF, 2), Some(511));
800
801 assert_eq!(cmap.lookup_cid_code(0x8140, 2), Some(633));
802 assert_eq!(cmap.lookup_cid_code(0x817E, 2), Some(633 + 62));
803 }
804
805 #[test]
806 fn cid_char_lookup() {
807 let cmap = parse_with_preamble(
808 br#"
8093 begincidchar
810<03> 1
811<04> 2
812<20> 50
813endcidchar
814"#,
815 );
816
817 assert_eq!(cmap.lookup_cid_code(0x03, 1), Some(1));
818 assert_eq!(cmap.lookup_cid_code(0x04, 1), Some(2));
819 assert_eq!(cmap.lookup_cid_code(0x20, 1), Some(50));
820 }
821
822 #[test]
823 fn lookup_miss() {
824 let cmap = parse_with_preamble(
825 br#"
8261 begincidrange
827<0100> <01FF> 0
828endcidrange
829"#,
830 );
831
832 assert_eq!(cmap.lookup_cid_code(0x00FF, 2), None);
833 assert_eq!(cmap.lookup_cid_code(0x0200, 2), None);
834 assert_eq!(cmap.lookup_cid_code(0xFFFF, 2), None);
835 }
836
837 #[test]
838 fn multiple_sections() {
839 let cmap = parse_with_preamble(
840 br#"
8412 begincidrange
842<0000> <00FF> 0
843<0100> <01FF> 256
844endcidrange
8451 begincidchar
846<0200> 600
847endcidchar
8481 begincidrange
849<8140> <817E> 633
850endcidrange
851"#,
852 );
853
854 assert_eq!(cmap.lookup_cid_code(0x0000, 2), Some(0));
855 assert_eq!(cmap.lookup_cid_code(0x0100, 2), Some(256));
856 assert_eq!(cmap.lookup_cid_code(0x0200, 2), Some(600));
857 assert_eq!(cmap.lookup_cid_code(0x8140, 2), Some(633));
858 }
859
860 #[test]
861 fn single_byte_codes() {
862 let cmap = parse_with_preamble(
863 br#"
8642 begincidrange
865<00> <7F> 0
866<80> <FF> 200
867endcidrange
868"#,
869 );
870
871 assert_eq!(cmap.lookup_cid_code(0x00, 1), Some(0));
872 assert_eq!(cmap.lookup_cid_code(0x41, 1), Some(0x41));
873 assert_eq!(cmap.lookup_cid_code(0x80, 1), Some(200));
874 assert_eq!(cmap.lookup_cid_code(0xFF, 1), Some(200 + 127));
875 }
876
877 #[test]
878 fn dict_style_cidsysteminfo() {
879 let data = br#"
880/CIDInit /ProcSet findresource begin
88110 dict begin
882begincmap
883/CIDSystemInfo
884<< /Registry (Adobe)
885/Ordering (UCS)
886/Supplement 0
887>> def
888/CMapName /Adobe-Identity-UCS def
889/CMapType 2 def
8901 begincodespacerange
891<0000> <FFFF>
892endcodespacerange
8932 beginbfchar
894<001F> <F049>
895<002A> <F055>
896endbfchar
897endcmap
898CMapName currentdict /CMap defineresource pop
899end
900end
901"#;
902 let cmap = CMap::parse(data, |_| None).unwrap();
903 let cc = cmap.metadata().character_collection.as_ref().unwrap();
904 assert_eq!(
905 cc.family,
906 CidFamily::Custom {
907 registry: b"Adobe".to_vec(),
908 ordering: b"UCS".to_vec(),
909 }
910 );
911 assert_eq!(cc.supplement, 0);
912 assert_eq!(
913 cmap.metadata().name.as_deref(),
914 Some(b"Adobe-Identity-UCS".as_slice())
915 );
916 assert_eq!(
917 cmap.lookup_bf_string(0x001F),
918 Some(BfString::Char('\u{F049}'))
919 );
920 assert_eq!(
921 cmap.lookup_bf_string(0x002A),
922 Some(BfString::Char('\u{F055}'))
923 );
924 }
925
926 #[test]
927 fn usecmap_chaining() {
928 let base_data = br#"
929/CIDSystemInfo 3 dict dup begin
930 /Registry (Adobe) def
931 /Ordering (Japan1) def
932 /Supplement 0 def
933end def
934/CMapName /Base def
935/WMode 0 def
9361 begincodespacerange
937<0000> <FFFF>
938endcodespacerange
9391 begincidrange
940<0000> <00FF> 0
941endcidrange
942"#;
943
944 let child_data = br#"
945/Base usecmap
946/CIDSystemInfo 3 dict dup begin
947 /Registry (Adobe) def
948 /Ordering (Japan1) def
949 /Supplement 0 def
950end def
951/CMapName /Child def
952/WMode 0 def
9531 begincodespacerange
954<0000> <FFFF>
955endcodespacerange
9561 begincidrange
957<0100> <01FF> 256
958endcidrange
959"#;
960
961 let cmap = CMap::parse(child_data, |name| {
962 if name.to_bytes() == b"Base" {
963 Some(base_data.as_slice())
964 } else {
965 None
966 }
967 })
968 .unwrap();
969
970 assert_eq!(cmap.lookup_cid_code(0x0100, 2), Some(256));
971 assert_eq!(cmap.lookup_cid_code(0x01FF, 2), Some(511));
972 assert_eq!(cmap.lookup_cid_code(0x0000, 2), Some(0));
973 assert_eq!(cmap.lookup_cid_code(0x00FF, 2), Some(0xFF));
974
975 assert_eq!(cmap.lookup_cid_code(0x0200, 2), None);
976 }
977
978 #[test]
979 fn usecmap_partial_override() {
980 let base_data = br#"
981/CIDSystemInfo 3 dict dup begin
982 /Registry (Adobe) def
983 /Ordering (Japan1) def
984 /Supplement 0 def
985end def
986/CMapName /Base def
987/WMode 0 def
9881 begincodespacerange
989<0000> <FFFF>
990endcodespacerange
9911 begincidrange
992<0000> <00FF> 0
993endcidrange
994"#;
995
996 let child_data = br#"
997/Base usecmap
998/CIDSystemInfo 3 dict dup begin
999 /Registry (Adobe) def
1000 /Ordering (Japan1) def
1001 /Supplement 0 def
1002end def
1003/CMapName /Child def
1004/WMode 0 def
10051 begincodespacerange
1006<0000> <FFFF>
1007endcodespacerange
10081 begincidrange
1009<0040> <007F> 500
1010endcidrange
1011"#;
1012
1013 let cmap = CMap::parse(child_data, |name| {
1014 if name.to_bytes() == b"Base" {
1015 Some(base_data.as_slice())
1016 } else {
1017 None
1018 }
1019 })
1020 .unwrap();
1021
1022 assert_eq!(cmap.lookup_cid_code(0x0000, 2), Some(0));
1023 assert_eq!(cmap.lookup_cid_code(0x003F, 2), Some(0x3F));
1024 assert_eq!(cmap.lookup_cid_code(0x0040, 2), Some(500));
1025 assert_eq!(cmap.lookup_cid_code(0x007F, 2), Some(563));
1026 assert_eq!(cmap.lookup_cid_code(0x0080, 2), Some(0x80));
1027 assert_eq!(cmap.lookup_cid_code(0x00FF, 2), Some(0xFF));
1028 }
1029
1030 #[test]
1031 fn notdef_char_lookup() {
1032 let cmap = parse_with_preamble(
1033 br#"
10342 beginnotdefchar
1035<03> 10
1036<20> 20
1037endnotdefchar
1038"#,
1039 );
1040
1041 assert_eq!(cmap.lookup_cid_code(0x03, 1), Some(10));
1042 assert_eq!(cmap.lookup_cid_code(0x20, 1), Some(20));
1043 assert_eq!(cmap.lookup_cid_code(0x04, 1), None);
1044 }
1045
1046 #[test]
1047 fn notdef_range_lookup() {
1048 let cmap = parse_with_preamble(
1049 br#"
10501 beginnotdefrange
1051<0000> <001F> 100
1052endnotdefrange
1053"#,
1054 );
1055
1056 assert_eq!(cmap.lookup_cid_code(0x0000, 2), Some(100));
1057 assert_eq!(cmap.lookup_cid_code(0x0001, 2), Some(100));
1058 assert_eq!(cmap.lookup_cid_code(0x001F, 2), Some(100));
1059 assert_eq!(cmap.lookup_cid_code(0x0020, 2), None);
1060 }
1061
1062 #[test]
1063 fn bfchar_lookup() {
1064 let cmap = parse_with_preamble(
1065 br#"
10662 beginbfchar
1067<0041> <0048>
1068<0042> <0065>
1069endbfchar
1070"#,
1071 );
1072
1073 assert_eq!(cmap.lookup_bf_string(0x0041), Some(BfString::Char('H')));
1074 assert_eq!(cmap.lookup_bf_string(0x0042), Some(BfString::Char('e')));
1075 assert_eq!(cmap.lookup_bf_string(0x0043), None);
1076 }
1077
1078 #[test]
1079 fn bfchar_ligature() {
1080 let cmap = parse_with_preamble(
1081 br#"
10821 beginbfchar
1083<005F> <00660066>
1084endbfchar
1085"#,
1086 );
1087
1088 assert_eq!(
1089 cmap.lookup_bf_string(0x005F),
1090 Some(BfString::String(String::from("ff")))
1091 );
1092 }
1093
1094 #[test]
1095 fn bfchar_surrogate_pair() {
1096 let cmap = parse_with_preamble(
1097 br#"
10981 beginbfchar
1099<3A51> <D840DC3E>
1100endbfchar
1101"#,
1102 );
1103
1104 assert_eq!(
1105 cmap.lookup_bf_string(0x3A51),
1106 Some(BfString::Char('\u{2003E}'))
1107 );
1108 }
1109
1110 #[test]
1111 fn bfrange_incrementing() {
1112 let cmap = parse_with_preamble(
1113 br#"
11141 beginbfrange
1115<0000> <0004> <0041>
1116endbfrange
1117"#,
1118 );
1119
1120 assert_eq!(cmap.lookup_bf_string(0x0000), Some(BfString::Char('A')));
1121 assert_eq!(cmap.lookup_bf_string(0x0001), Some(BfString::Char('B')));
1122 assert_eq!(cmap.lookup_bf_string(0x0004), Some(BfString::Char('E')));
1123 assert_eq!(cmap.lookup_bf_string(0x0005), None);
1124 }
1125
1126 #[test]
1127 fn bfrange_array() {
1128 let cmap = parse_with_preamble(
1129 br#"
11301 beginbfrange
1131<005F> <0061> [<00660066> <00660069> <0066006C>]
1132endbfrange
1133"#,
1134 );
1135
1136 assert_eq!(
1138 cmap.lookup_bf_string(0x005F),
1139 Some(BfString::String(String::from("ff")))
1140 );
1141 assert_eq!(
1142 cmap.lookup_bf_string(0x0060),
1143 Some(BfString::String(String::from("fi")))
1144 );
1145 assert_eq!(
1146 cmap.lookup_bf_string(0x0061),
1147 Some(BfString::String(String::from("fl")))
1148 );
1149 }
1150
1151 #[test]
1152 fn unicode_lookup_miss() {
1153 let cmap = parse_with_preamble(
1154 br#"
11551 beginbfchar
1156<0041> <0048>
1157endbfchar
1158"#,
1159 );
1160
1161 assert_eq!(cmap.lookup_bf_string(0x0000), None);
1162 assert_eq!(cmap.lookup_bf_string(0x0042), None);
1163 }
1164
1165 #[test]
1166 fn identity_h() {
1167 let cmap = CMap::identity_h();
1168 assert_eq!(
1169 cmap.metadata().name.as_deref(),
1170 Some(b"Identity-H".as_slice())
1171 );
1172 assert_eq!(cmap.metadata().writing_mode, Some(WritingMode::Horizontal));
1173
1174 assert_eq!(cmap.lookup_cid_code(0x0041, 2), Some(0x0041));
1175 assert_eq!(cmap.lookup_cid_code(0x1234, 2), Some(0x1234));
1176 assert_eq!(cmap.lookup_cid_code(0xFFFF, 2), Some(0xFFFF));
1177
1178 assert_eq!(cmap.lookup_cid_code(0x0041, 1), None);
1179 assert_eq!(cmap.lookup_cid_code(0x0041, 3), None);
1180 }
1181
1182 #[test]
1183 fn identity_v() {
1184 let cmap = CMap::identity_v();
1185 assert_eq!(
1186 cmap.metadata().name.as_deref(),
1187 Some(b"Identity-V".as_slice())
1188 );
1189 assert_eq!(cmap.metadata().writing_mode, Some(WritingMode::Vertical));
1190
1191 assert_eq!(cmap.lookup_cid_code(0x0041, 2), Some(0x0041));
1192 assert_eq!(cmap.lookup_cid_code(0xFFFF, 2), Some(0xFFFF));
1193 }
1194
1195 #[test]
1196 fn minimal_cmap_no_name_no_wmode() {
1197 let data = br#"
1199/CIDInit /ProcSet findresource begin
120012 dict begin
1201begincmap
1202/CMapType 2 def
12031 begincodespacerange
1204<00><ff>
1205endcodespacerange
12061 beginbfrange
1207<01><01><0020>
1208endbfrange
1209endcmap
1210"#;
1211 let cmap = CMap::parse(data, |_| None).unwrap();
1212 assert_eq!(cmap.metadata().name, None);
1213 assert_eq!(cmap.metadata().character_collection, None);
1214 assert_eq!(cmap.metadata().writing_mode, None);
1215 assert_eq!(cmap.lookup_bf_string(0x01), Some(BfString::Char(' ')));
1216 }
1217
1218 #[test]
1219 fn registry_as_name() {
1220 let data = br#"
1223/CIDSystemInfo
1224<< /Registry /ABCDEF+SimSun
1225/Ordering (pdfbeaninc)
1226/Supplement 0
1227>> def
1228/CMapName /ABCDEF+SimSun def
1229/CMapType 2 def
12301 begincodespacerange
1231<0000> <FFFF>
1232endcodespacerange
12331 beginbfrange
1234<0000> <0000> <6881>
1235endbfrange
1236"#;
1237 let cmap = CMap::parse(data, |_| None).unwrap();
1238 let cc = cmap.metadata().character_collection.as_ref().unwrap();
1239 assert_eq!(
1240 cc.family,
1241 CidFamily::Custom {
1242 registry: b"ABCDEF+SimSun".to_vec(),
1243 ordering: b"pdfbeaninc".to_vec(),
1244 }
1245 );
1246 assert_eq!(cc.supplement, 0);
1247 assert_eq!(
1248 cmap.lookup_bf_string(0x0000),
1249 Some(BfString::Char('\u{6881}'))
1250 );
1251 }
1252
1253 #[test]
1254 fn single_byte_bfrange_destination() {
1255 let cmap = parse_with_preamble(
1257 br#"
12581 beginbfrange
1259<00> <7f> <00>
1260endbfrange
1261"#,
1262 );
1263
1264 assert_eq!(
1265 cmap.lookup_bf_string(0x00),
1266 Some(BfString::Char('\u{0000}'))
1267 );
1268 assert_eq!(cmap.lookup_bf_string(0x41), Some(BfString::Char('A')));
1269 assert_eq!(
1270 cmap.lookup_bf_string(0x7f),
1271 Some(BfString::Char('\u{007F}'))
1272 );
1273 }
1274}
1275
1276#[cfg(all(test, feature = "embed-cmaps"))]
1277mod bcmap_tests {
1278 use super::*;
1279
1280 fn get_embedded_cmap(name: CMapName<'_>) -> Option<&'static [u8]> {
1281 load_embedded(name)
1282 }
1283
1284 #[test]
1285 fn embedded_h_cmap() {
1286 let data = load_embedded(CMapName::H).expect("embedded H cmap not found");
1287 let cmap = CMap::parse(data, get_embedded_cmap).expect("failed to parse H cmap");
1288 assert_eq!(cmap.metadata().writing_mode, None);
1289 assert_eq!(
1290 cmap.metadata().character_collection,
1291 Some(CharacterCollection {
1292 family: CidFamily::AdobeJapan1,
1293 supplement: 1,
1294 })
1295 );
1296 assert_eq!(cmap.lookup_cid_code(0x2121, 2), Some(633));
1297 assert_eq!(cmap.lookup_cid_code(0x2221, 2), Some(727));
1298 }
1299
1300 #[test]
1301 fn embedded_90ms_rksj_h() {
1302 let data =
1303 load_embedded(CMapName::N90msRksjH).expect("embedded 90ms-RKSJ-H cmap not found");
1304 let cmap = CMap::parse(data, get_embedded_cmap).expect("failed to parse 90ms-RKSJ-H cmap");
1305 assert_eq!(
1306 cmap.metadata().character_collection,
1307 Some(CharacterCollection {
1308 family: CidFamily::AdobeJapan1,
1309 supplement: 2,
1310 })
1311 );
1312 assert_eq!(cmap.lookup_cid_code(0x20, 1), Some(231));
1313 assert_eq!(cmap.lookup_cid_code(0x8140, 2), Some(633));
1314 }
1315
1316 #[test]
1317 fn embedded_v_cmap() {
1318 let data = load_embedded(CMapName::V).expect("embedded V cmap not found");
1319 let cmap = CMap::parse(data, get_embedded_cmap).expect("failed to parse V cmap");
1320 assert_eq!(cmap.metadata().writing_mode, Some(WritingMode::Vertical));
1321 assert_eq!(
1322 cmap.metadata().character_collection,
1323 Some(CharacterCollection {
1324 family: CidFamily::AdobeJapan1,
1325 supplement: 1,
1326 })
1327 );
1328 assert_eq!(cmap.lookup_cid_code(0x2121, 2), Some(633));
1330 }
1331
1332 #[test]
1333 fn embedded_gbk_euc_h() {
1334 let data = load_embedded(CMapName::GbkEucH).expect("embedded GBK-EUC-H not found");
1335 let cmap = CMap::parse(data, get_embedded_cmap).expect("failed to parse GBK-EUC-H");
1336 assert_eq!(
1337 cmap.metadata().character_collection,
1338 Some(CharacterCollection {
1339 family: CidFamily::AdobeGB1,
1340 supplement: 2,
1341 })
1342 );
1343 assert_eq!(cmap.lookup_cid_code(0x20, 1), Some(7716));
1344 assert_eq!(cmap.lookup_cid_code(0x21, 1), Some(814));
1345 assert_eq!(cmap.lookup_cid_code(0x7E, 1), Some(814 + 0x7E - 0x21));
1346 assert_eq!(cmap.lookup_cid_code(0xFE80, 2), Some(22094));
1347 assert_eq!(cmap.lookup_cid_code(0xFEA0, 2), Some(22094 + 0xA0 - 0x80));
1348 }
1349
1350 #[test]
1351 fn embedded_ksc_euc_h() {
1352 let data = load_embedded(CMapName::KscEucH).unwrap();
1353 let cmap = CMap::parse(data, get_embedded_cmap).unwrap();
1354 assert_eq!(
1355 cmap.metadata().character_collection,
1356 Some(CharacterCollection {
1357 family: CidFamily::AdobeKorea1,
1358 supplement: 0,
1359 })
1360 );
1361 assert_eq!(cmap.lookup_cid_code(0x20, 1), Some(8094));
1362 assert_eq!(cmap.lookup_cid_code(0x41, 1), Some(8094 + 0x41 - 0x20));
1363 assert_eq!(cmap.lookup_cid_code(0xA1A1, 2), Some(101));
1364 assert_eq!(cmap.lookup_cid_code(0xA1FE, 2), Some(101 + 0xFE - 0xA1));
1365 assert_eq!(cmap.lookup_cid_code(0xFDA1, 2), Some(7962));
1366 }
1367
1368 #[test]
1369 fn embedded_b5pc_h() {
1370 let data = load_embedded(CMapName::B5pcH).unwrap();
1371 let cmap = CMap::parse(data, get_embedded_cmap).unwrap();
1372 assert_eq!(
1373 cmap.metadata().character_collection,
1374 Some(CharacterCollection {
1375 family: CidFamily::AdobeCNS1,
1376 supplement: 0,
1377 })
1378 );
1379 assert_eq!(cmap.lookup_cid_code(0x20, 1), Some(1));
1380 assert_eq!(cmap.lookup_cid_code(0x41, 1), Some(1 + 0x41 - 0x20));
1381 assert_eq!(cmap.lookup_cid_code(0x80, 1), Some(61));
1382 assert_eq!(cmap.lookup_cid_code(0xD140, 2), Some(7251));
1383 assert_eq!(cmap.lookup_cid_code(0xD17E, 2), Some(7251 + 0x7E - 0x40));
1384 assert_eq!(cmap.lookup_cid_code(0xF9D5, 2), Some(13642 + 3));
1385 }
1386
1387 #[test]
1388 fn embedded_unijis_utf16_h() {
1389 let data = load_embedded(CMapName::UniJisUtf16H).unwrap();
1390 let cmap = CMap::parse(data, get_embedded_cmap).unwrap();
1391 assert_eq!(
1392 cmap.metadata().character_collection,
1393 Some(CharacterCollection {
1394 family: CidFamily::AdobeJapan1,
1395 supplement: 7,
1396 })
1397 );
1398 assert_eq!(cmap.lookup_cid_code(0x20, 2), Some(1));
1399 assert_eq!(cmap.lookup_cid_code(0x41, 2), Some(1 + 0x41 - 0x20));
1400 assert_eq!(cmap.lookup_cid_code(0x5C, 2), Some(97));
1401 assert_eq!(cmap.lookup_cid_code(0x5BCC, 2), Some(3531));
1402 assert_eq!(cmap.lookup_cid_code(0xD884DF50, 4), Some(19130));
1403 }
1404
1405 #[test]
1406 fn embedded_identity_h() {
1407 let data = load_embedded(CMapName::IdentityH).expect("embedded Identity-H not found");
1408 let cmap = CMap::parse(data, get_embedded_cmap).expect("failed to parse Identity-H cmap");
1409 assert_eq!(cmap.metadata().writing_mode, None);
1410 assert_eq!(
1411 cmap.metadata().character_collection,
1412 Some(CharacterCollection {
1413 family: CidFamily::AdobeIdentity,
1414 supplement: 0,
1415 })
1416 );
1417 assert_eq!(cmap.lookup_cid_code(0x0000, 2), Some(0));
1418 assert_eq!(cmap.lookup_cid_code(0x0041, 2), Some(0x41));
1419 assert_eq!(cmap.lookup_cid_code(0xFFFF, 2), Some(0xFFFF));
1420 }
1421
1422 #[test]
1423 fn embedded_identity_v() {
1424 let data = load_embedded(CMapName::IdentityV).expect("embedded Identity-V not found");
1425 let cmap = CMap::parse(data, get_embedded_cmap).expect("failed to parse Identity-V cmap");
1426 assert_eq!(cmap.metadata().writing_mode, Some(WritingMode::Vertical));
1427 assert_eq!(
1428 cmap.metadata().character_collection,
1429 Some(CharacterCollection {
1430 family: CidFamily::AdobeIdentity,
1431 supplement: 0,
1432 })
1433 );
1434 assert_eq!(cmap.lookup_cid_code(0x0000, 2), Some(0));
1435 assert_eq!(cmap.lookup_cid_code(0x0041, 2), Some(0x41));
1436 assert_eq!(cmap.lookup_cid_code(0xFFFF, 2), Some(0xFFFF));
1437 }
1438
1439 #[test]
1442 fn embedded_adobe_gb1_ucs2() {
1443 let data = load_embedded(CMapName::AdobeGb1Ucs2).unwrap();
1444 let cmap = CMap::parse(data, get_embedded_cmap).unwrap();
1445 assert_eq!(
1446 cmap.metadata().character_collection,
1447 Some(CharacterCollection {
1448 family: CidFamily::Custom {
1449 registry: b"Adobe".to_vec(),
1450 ordering: b"Adobe_GB1_UCS2".to_vec(),
1451 },
1452 supplement: 5,
1453 })
1454 );
1455
1456 assert_eq!(
1457 cmap.lookup_bf_string(0x0063),
1458 Some(BfString::Char('\u{00B7}'))
1459 );
1460 assert_eq!(cmap.lookup_bf_string(0x0001), Some(BfString::Char(' ')));
1461 assert_eq!(cmap.lookup_bf_string(0x0022), Some(BfString::Char('A')));
1462 assert_eq!(cmap.lookup_bf_string(0x005F), Some(BfString::Char('~')));
1463 assert_eq!(
1464 cmap.lookup_bf_string(0x560F),
1465 Some(BfString::String(String::from("\u{90CE}\u{FE00}")))
1466 );
1467 }
1468
1469 #[test]
1470 fn embedded_adobe_japan1_ucs2() {
1471 let data = load_embedded(CMapName::AdobeJapan1Ucs2).unwrap();
1472 let cmap = CMap::parse(data, get_embedded_cmap).unwrap();
1473 assert_eq!(
1474 cmap.metadata().character_collection,
1475 Some(CharacterCollection {
1476 family: CidFamily::Custom {
1477 registry: b"Adobe".to_vec(),
1478 ordering: b"Adobe_Japan1_UCS2".to_vec(),
1479 },
1480 supplement: 7,
1481 })
1482 );
1483
1484 assert_eq!(
1485 cmap.lookup_bf_string(0x003D),
1486 Some(BfString::Char('\u{00A5}'))
1487 );
1488 assert_eq!(cmap.lookup_bf_string(0x0001), Some(BfString::Char(' ')));
1489 assert_eq!(cmap.lookup_bf_string(0x003C), Some(BfString::Char('[')));
1490 assert_eq!(
1491 cmap.lookup_bf_string(0x0476),
1492 Some(BfString::String(String::from("\u{82A6}\u{E0100}")))
1493 );
1494 assert_eq!(
1495 cmap.lookup_bf_string(0x265B),
1496 Some(BfString::String(String::from("10/11")))
1497 );
1498 assert_eq!(
1499 cmap.lookup_bf_string(0x2E6B),
1500 Some(BfString::String(String::from("オングストローム")))
1501 );
1502 }
1503
1504 #[test]
1505 fn embedded_adobe_korea1_ucs2() {
1506 let data = load_embedded(CMapName::AdobeKorea1Ucs2).unwrap();
1507 let cmap = CMap::parse(data, get_embedded_cmap).unwrap();
1508 assert_eq!(
1509 cmap.metadata().character_collection,
1510 Some(CharacterCollection {
1511 family: CidFamily::Custom {
1512 registry: b"Adobe".to_vec(),
1513 ordering: b"Adobe_Korea1_UCS2".to_vec(),
1514 },
1515 supplement: 2,
1516 })
1517 );
1518
1519 assert_eq!(
1520 cmap.lookup_bf_string(0x0060),
1521 Some(BfString::Char('\u{20A9}'))
1522 );
1523 assert_eq!(
1524 cmap.lookup_bf_string(0x2072),
1525 Some(BfString::String(String::from("[10]")))
1526 );
1527 assert_eq!(
1528 cmap.lookup_bf_string(0x2073),
1529 Some(BfString::String(String::from("[11]")))
1530 );
1531 }
1532
1533 #[test]
1534 fn embedded_adobe_cns1_ucs2() {
1535 let data = load_embedded(CMapName::AdobeCns1Ucs2).unwrap();
1536 let cmap = CMap::parse(data, get_embedded_cmap).unwrap();
1537 assert_eq!(
1538 cmap.metadata().character_collection,
1539 Some(CharacterCollection {
1540 family: CidFamily::Custom {
1541 registry: b"Adobe".to_vec(),
1542 ordering: b"Adobe_CNS1_UCS2".to_vec(),
1543 },
1544 supplement: 7,
1545 })
1546 );
1547
1548 assert_eq!(
1549 cmap.lookup_bf_string(0x0060),
1550 Some(BfString::Char('\u{00A9}'))
1551 );
1552 assert_eq!(cmap.lookup_bf_string(0x0001), Some(BfString::Char(' ')));
1553 assert_eq!(cmap.lookup_bf_string(0x005F), Some(BfString::Char('~')));
1554 assert_eq!(
1555 cmap.lookup_bf_string(0x36B0),
1556 Some(BfString::Char('\u{200CC}'))
1557 );
1558 }
1559
1560 #[test]
1561 fn embedded_eten_b5_h() {
1562 let data = load_embedded(CMapName::ETenB5H).unwrap();
1563 let cmap = CMap::parse(data, get_embedded_cmap).unwrap();
1564 assert_eq!(
1565 cmap.metadata().character_collection,
1566 Some(CharacterCollection {
1567 family: CidFamily::AdobeCNS1,
1568 supplement: 0,
1569 })
1570 );
1571 assert_eq!(cmap.lookup_cid_code(0x20, 1), Some(13648));
1572 assert_eq!(cmap.lookup_cid_code(0x7E, 1), Some(13648 + 0x7E - 0x20));
1573 assert_eq!(cmap.lookup_cid_code(0xA140, 2), Some(99));
1574 assert_eq!(cmap.lookup_cid_code(0xA158, 2), Some(99 + 0x58 - 0x40));
1575 assert_eq!(cmap.lookup_cid_code(0xD040, 2), Some(7094));
1576 assert_eq!(cmap.lookup_cid_code(0xF9FE, 2), Some(14056 + 0xFE - 0xD6));
1577 }
1578}