1use crate::error::{PdfError, Result};
10use crate::objects::{Dictionary, Object, ObjectId};
11use crate::text::fonts::truetype::TrueTypeFont;
12use std::collections::{HashMap, HashSet};
13
14#[derive(Debug, Clone, Copy, PartialEq)]
16pub enum FontType {
17 TrueType,
19 Type0,
21}
22
23#[derive(Debug, Clone, PartialEq)]
25pub enum FontEncoding {
26 StandardEncoding,
28 MacRomanEncoding,
30 WinAnsiEncoding,
32 Custom(Vec<EncodingDifference>),
34 Identity,
36}
37
38#[derive(Debug, Clone, PartialEq)]
40pub struct EncodingDifference {
41 pub code: u8,
43 pub names: Vec<String>,
45}
46
47#[derive(Debug, Clone, Copy, Default)]
49pub struct FontFlags {
50 pub fixed_pitch: bool,
52 pub serif: bool,
54 pub symbolic: bool,
56 pub script: bool,
58 pub non_symbolic: bool,
60 pub italic: bool,
62 pub all_cap: bool,
64 pub small_cap: bool,
66 pub force_bold: bool,
68}
69
70impl FontFlags {
71 pub fn to_flags(&self) -> u32 {
73 let mut flags = 0u32;
74 if self.fixed_pitch {
75 flags |= 1 << 0;
76 }
77 if self.serif {
78 flags |= 1 << 1;
79 }
80 if self.symbolic {
81 flags |= 1 << 2;
82 }
83 if self.script {
84 flags |= 1 << 3;
85 }
86 if self.non_symbolic {
87 flags |= 1 << 5;
88 }
89 if self.italic {
90 flags |= 1 << 6;
91 }
92 if self.all_cap {
93 flags |= 1 << 16;
94 }
95 if self.small_cap {
96 flags |= 1 << 17;
97 }
98 if self.force_bold {
99 flags |= 1 << 18;
100 }
101 flags
102 }
103}
104
105#[derive(Debug, Clone)]
107pub struct FontDescriptor {
108 pub font_name: String,
110 pub flags: FontFlags,
112 pub bbox: [i32; 4],
114 pub italic_angle: f64,
116 pub ascent: i32,
118 pub descent: i32,
120 pub cap_height: i32,
122 pub stem_v: i32,
124 pub stem_h: i32,
126 pub avg_width: i32,
128 pub max_width: i32,
130 pub missing_width: i32,
132 pub font_file: Option<ObjectId>,
134}
135
136#[derive(Debug, Clone)]
138pub struct FontMetrics {
139 pub ascent: i32,
141 pub descent: i32,
143 pub cap_height: i32,
145 pub x_height: i32,
147 pub stem_v: i32,
149 pub stem_h: i32,
151 pub avg_width: i32,
153 pub max_width: i32,
155 pub missing_width: i32,
157}
158
159#[derive(Debug)]
161pub struct FontEmbedder {
162 embedded_fonts: HashMap<String, EmbeddedFontData>,
164 next_font_id: u32,
166}
167
168#[derive(Debug, Clone)]
170pub struct EmbeddedFontData {
171 pub pdf_name: String,
173 pub font_type: FontType,
175 pub descriptor: FontDescriptor,
177 pub font_program: Vec<u8>,
179 pub encoding: FontEncoding,
181 pub metrics: FontMetrics,
183 pub subset_glyphs: Option<HashSet<u16>>,
185 pub unicode_mappings: HashMap<u16, String>,
187}
188
189#[derive(Debug, Clone)]
191pub struct EmbeddingOptions {
192 pub subset: bool,
194 pub max_subset_size: Option<usize>,
196 pub compress_font_streams: bool,
198 pub embed_license_info: bool,
200}
201
202impl Default for EmbeddingOptions {
203 fn default() -> Self {
204 Self {
205 subset: true,
206 max_subset_size: Some(256),
207 compress_font_streams: true,
208 embed_license_info: false,
209 }
210 }
211}
212
213impl FontEmbedder {
214 pub fn new() -> Self {
216 Self {
217 embedded_fonts: HashMap::new(),
218 next_font_id: 1,
219 }
220 }
221
222 pub fn embed_truetype_font(
224 &mut self,
225 font_data: &[u8],
226 used_glyphs: &HashSet<u16>,
227 options: &EmbeddingOptions,
228 ) -> Result<String> {
229 let font = TrueTypeFont::from_data(font_data)
231 .map_err(|e| PdfError::FontError(format!("Failed to parse font: {e}")))?;
232
233 let font_name = format!("ABCDEF+Font{next_id}", next_id = self.next_font_id);
235 self.next_font_id += 1;
236
237 let should_subset =
239 options.subset && used_glyphs.len() < options.max_subset_size.unwrap_or(256);
240
241 let font_program = if should_subset {
243 font.create_subset(used_glyphs)
244 .map_err(|e| PdfError::FontError(format!("Failed to create subset: {e}")))?
245 } else {
246 font_data.to_vec()
247 };
248
249 let metrics = self.extract_font_metrics(&font)?;
251
252 let descriptor = self.create_font_descriptor(&font, &font_name)?;
254
255 let encoding = self.create_encoding_for_font(&font, used_glyphs)?;
257
258 let unicode_mappings = self.create_unicode_mappings(&font, used_glyphs)?;
260
261 let embedded_font = EmbeddedFontData {
263 pdf_name: font_name.clone(),
264 font_type: FontType::TrueType,
265 descriptor,
266 font_program,
267 encoding,
268 metrics,
269 subset_glyphs: if should_subset {
270 Some(used_glyphs.clone())
271 } else {
272 None
273 },
274 unicode_mappings,
275 };
276
277 self.embedded_fonts.insert(font_name.clone(), embedded_font);
278 Ok(font_name)
279 }
280
281 pub fn embed_cid_font(
283 &mut self,
284 font_data: &[u8],
285 used_chars: &HashSet<u32>,
286 _cmap_name: &str,
287 options: &EmbeddingOptions,
288 ) -> Result<String> {
289 let font = TrueTypeFont::from_data(font_data)
291 .map_err(|e| PdfError::FontError(format!("Failed to parse font: {e}")))?;
292
293 let font_name = format!("ABCDEF+CIDFont{next_id}", next_id = self.next_font_id);
295 self.next_font_id += 1;
296
297 let used_glyphs = self.chars_to_glyphs(&font, used_chars)?;
299
300 let font_program = if options.subset {
302 font.create_subset(&used_glyphs)
303 .map_err(|e| PdfError::FontError(format!("Failed to create subset: {e}")))?
304 } else {
305 font_data.to_vec()
306 };
307
308 let metrics = self.extract_font_metrics(&font)?;
310
311 let descriptor = self.create_cid_font_descriptor(&font, &font_name)?;
313
314 let encoding = FontEncoding::Identity;
316
317 let unicode_mappings = self.create_cid_unicode_mappings(&font, used_chars)?;
319
320 let embedded_font = EmbeddedFontData {
321 pdf_name: font_name.clone(),
322 font_type: FontType::Type0,
323 descriptor,
324 font_program,
325 encoding,
326 metrics,
327 subset_glyphs: Some(used_glyphs),
328 unicode_mappings,
329 };
330
331 self.embedded_fonts.insert(font_name.clone(), embedded_font);
332 Ok(font_name)
333 }
334
335 pub fn generate_font_dictionary(&self, font_name: &str) -> Result<Dictionary> {
337 let font_data = self
338 .embedded_fonts
339 .get(font_name)
340 .ok_or_else(|| PdfError::FontError(format!("Font {font_name} not found")))?;
341
342 match font_data.font_type {
343 FontType::TrueType => self.generate_truetype_dictionary(font_data),
344 FontType::Type0 => self.generate_type0_dictionary(font_data),
345 }
347 }
348
349 fn generate_truetype_dictionary(&self, font_data: &EmbeddedFontData) -> Result<Dictionary> {
351 let mut font_dict = Dictionary::new();
352
353 font_dict.set("Type", Object::Name("Font".to_string()));
355 font_dict.set("Subtype", Object::Name("TrueType".to_string()));
356 font_dict.set("BaseFont", Object::Name(font_data.pdf_name.clone()));
357
358 font_dict.set("FontDescriptor", Object::Reference(ObjectId::new(0, 0))); match &font_data.encoding {
363 FontEncoding::WinAnsiEncoding => {
364 font_dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
365 }
366 FontEncoding::MacRomanEncoding => {
367 font_dict.set("Encoding", Object::Name("MacRomanEncoding".to_string()));
368 }
369 FontEncoding::StandardEncoding => {
370 font_dict.set("Encoding", Object::Name("StandardEncoding".to_string()));
371 }
372 FontEncoding::Custom(differences) => {
373 let mut encoding_dict = Dictionary::new();
374 encoding_dict.set("Type", Object::Name("Encoding".to_string()));
375 encoding_dict.set("BaseEncoding", Object::Name("WinAnsiEncoding".to_string()));
376
377 let mut diff_array = Vec::new();
379 for diff in differences {
380 diff_array.push(Object::Integer(diff.code as i64));
381 for name in &diff.names {
382 diff_array.push(Object::Name(name.clone()));
383 }
384 }
385 encoding_dict.set("Differences", Object::Array(diff_array));
386 font_dict.set("Encoding", Object::Dictionary(encoding_dict));
387 }
388 _ => {}
389 }
390
391 font_dict.set("FirstChar", Object::Integer(32));
393 font_dict.set("LastChar", Object::Integer(255));
394
395 let widths: Vec<Object> = (32..=255)
397 .map(|_| Object::Integer(500)) .collect();
399 font_dict.set("Widths", Object::Array(widths));
400
401 Ok(font_dict)
402 }
403
404 fn generate_type0_dictionary(&self, font_data: &EmbeddedFontData) -> Result<Dictionary> {
406 let mut font_dict = Dictionary::new();
407
408 font_dict.set("Type", Object::Name("Font".to_string()));
410 font_dict.set("Subtype", Object::Name("Type0".to_string()));
411 font_dict.set("BaseFont", Object::Name(font_data.pdf_name.clone()));
412
413 font_dict.set("Encoding", Object::Name("Identity-H".to_string()));
415
416 font_dict.set(
418 "DescendantFonts",
419 Object::Array(vec![
420 Object::Reference(ObjectId::new(0, 0)), ]),
422 );
423
424 if !font_data.unicode_mappings.is_empty() {
426 font_dict.set("ToUnicode", Object::Reference(ObjectId::new(0, 0))); }
428
429 Ok(font_dict)
430 }
431
432 pub fn generate_font_descriptor(&self, font_name: &str) -> Result<Dictionary> {
434 let font_data = self
435 .embedded_fonts
436 .get(font_name)
437 .ok_or_else(|| PdfError::FontError(format!("Font {font_name} not found")))?;
438
439 let mut desc_dict = Dictionary::new();
440
441 desc_dict.set("Type", Object::Name("FontDescriptor".to_string()));
442 desc_dict.set("FontName", Object::Name(font_data.pdf_name.clone()));
443
444 desc_dict.set(
446 "Flags",
447 Object::Integer(font_data.descriptor.flags.to_flags() as i64),
448 );
449
450 desc_dict.set("Ascent", Object::Integer(font_data.metrics.ascent as i64));
452 desc_dict.set("Descent", Object::Integer(font_data.metrics.descent as i64));
453 desc_dict.set(
454 "CapHeight",
455 Object::Integer(font_data.metrics.cap_height as i64),
456 );
457 desc_dict.set(
458 "ItalicAngle",
459 Object::Real(font_data.descriptor.italic_angle),
460 );
461 desc_dict.set("StemV", Object::Integer(font_data.descriptor.stem_v as i64));
462
463 let bbox = vec![
465 Object::Integer(font_data.descriptor.bbox[0] as i64),
466 Object::Integer(font_data.descriptor.bbox[1] as i64),
467 Object::Integer(font_data.descriptor.bbox[2] as i64),
468 Object::Integer(font_data.descriptor.bbox[3] as i64),
469 ];
470 desc_dict.set("FontBBox", Object::Array(bbox));
471
472 match font_data.font_type {
474 FontType::TrueType => {
475 desc_dict.set("FontFile2", Object::Reference(ObjectId::new(0, 0)));
476 }
478 FontType::Type0 => {
479 desc_dict.set("FontFile2", Object::Reference(ObjectId::new(0, 0)));
480 }
482 }
483
484 Ok(desc_dict)
485 }
486
487 pub fn generate_tounicode_cmap(&self, font_name: &str) -> Result<String> {
489 let font_data = self
490 .embedded_fonts
491 .get(font_name)
492 .ok_or_else(|| PdfError::FontError(format!("Font {font_name} not found")))?;
493
494 if font_data.unicode_mappings.is_empty() {
495 return Err(PdfError::FontError(
496 "No Unicode mappings available".to_string(),
497 ));
498 }
499
500 let mut cmap_content = String::new();
501
502 cmap_content.push_str("/CIDInit /ProcSet findresource begin\n");
504 cmap_content.push_str("12 dict begin\n");
505 cmap_content.push_str("begincmap\n");
506 cmap_content.push_str("/CIDSystemInfo\n");
507 cmap_content.push_str("<<\n");
508 cmap_content.push_str("/Registry (Adobe)\n");
509 cmap_content.push_str("/Ordering (UCS)\n");
510 cmap_content.push_str("/Supplement 0\n");
511 cmap_content.push_str(">> def\n");
512 cmap_content.push_str("/CMapName /Adobe-Identity-UCS def\n");
513 cmap_content.push_str("/CMapType 2 def\n");
514 cmap_content.push_str("1 begincodespacerange\n");
515 cmap_content.push_str("<0000> <FFFF>\n");
516 cmap_content.push_str("endcodespacerange\n");
517
518 cmap_content.push_str(&format!(
520 "{} beginbfchar\n",
521 font_data.unicode_mappings.len()
522 ));
523 for (glyph_id, unicode_string) in &font_data.unicode_mappings {
524 cmap_content.push_str(&format!(
525 "<{:04X}> <{}>\n",
526 glyph_id,
527 unicode_string
528 .chars()
529 .map(|c| format!("{c:04X}", c = c as u32))
530 .collect::<String>()
531 ));
532 }
533 cmap_content.push_str("endbfchar\n");
534
535 cmap_content.push_str("endcmap\n");
537 cmap_content.push_str("CMapName currentdict /CMap defineresource pop\n");
538 cmap_content.push_str("end\n");
539 cmap_content.push_str("end\n");
540
541 Ok(cmap_content)
542 }
543
544 pub fn embedded_fonts(&self) -> &HashMap<String, EmbeddedFontData> {
546 &self.embedded_fonts
547 }
548
549 fn extract_font_metrics(&self, _font: &TrueTypeFont) -> Result<FontMetrics> {
551 Ok(FontMetrics {
554 ascent: 750,
555 descent: -250,
556 cap_height: 700,
557 x_height: 500,
558 stem_v: 100,
559 stem_h: 50,
560 avg_width: 500,
561 max_width: 1000,
562 missing_width: 500,
563 })
564 }
565
566 fn create_font_descriptor(
568 &self,
569 _font: &TrueTypeFont,
570 font_name: &str,
571 ) -> Result<FontDescriptor> {
572 Ok(FontDescriptor {
573 font_name: font_name.to_string(),
574 flags: FontFlags {
575 non_symbolic: true,
576 ..Default::default()
577 },
578 bbox: [-100, -250, 1000, 750], italic_angle: 0.0,
580 ascent: 750,
581 descent: -250,
582 cap_height: 700,
583 stem_v: 100,
584 stem_h: 50,
585 avg_width: 500,
586 max_width: 1000,
587 missing_width: 500,
588 font_file: None,
589 })
590 }
591
592 fn create_cid_font_descriptor(
594 &self,
595 font: &TrueTypeFont,
596 font_name: &str,
597 ) -> Result<FontDescriptor> {
598 self.create_font_descriptor(font, font_name)
600 }
601
602 fn create_encoding_for_font(
604 &self,
605 _font: &TrueTypeFont,
606 _used_glyphs: &HashSet<u16>,
607 ) -> Result<FontEncoding> {
608 Ok(FontEncoding::WinAnsiEncoding)
611 }
612
613 fn create_unicode_mappings(
615 &self,
616 _font: &TrueTypeFont,
617 used_glyphs: &HashSet<u16>,
618 ) -> Result<HashMap<u16, String>> {
619 let mut mappings = HashMap::new();
620
621 for glyph_id in used_glyphs {
623 if *glyph_id < 256 {
624 let unicode_char = char::from(*glyph_id as u8);
625 if unicode_char.is_ascii_graphic() || unicode_char == ' ' {
626 mappings.insert(*glyph_id, unicode_char.to_string());
627 }
628 }
629 }
630
631 Ok(mappings)
632 }
633
634 fn create_cid_unicode_mappings(
636 &self,
637 _font: &TrueTypeFont,
638 used_chars: &HashSet<u32>,
639 ) -> Result<HashMap<u16, String>> {
640 let mut mappings = HashMap::new();
641
642 for &char_code in used_chars {
644 if let Some(unicode_char) = char::from_u32(char_code) {
645 let glyph_id = char_code as u16; mappings.insert(glyph_id, unicode_char.to_string());
648 }
649 }
650
651 Ok(mappings)
652 }
653
654 fn chars_to_glyphs(&self, _font: &TrueTypeFont, chars: &HashSet<u32>) -> Result<HashSet<u16>> {
656 let mut glyphs = HashSet::new();
657
658 glyphs.insert(0);
660
661 for &char_code in chars {
663 let glyph_id = if char_code < 65536 {
665 char_code as u16
666 } else {
667 0 };
669 glyphs.insert(glyph_id);
670 }
671
672 Ok(glyphs)
673 }
674}
675
676impl Default for FontEmbedder {
677 fn default() -> Self {
678 Self::new()
679 }
680}
681
682#[cfg(test)]
683mod tests {
684 use super::*;
685
686 #[test]
687 fn test_font_embedder_creation() {
688 let embedder = FontEmbedder::new();
689 assert_eq!(embedder.embedded_fonts.len(), 0);
690 assert_eq!(embedder.next_font_id, 1);
691 }
692
693 #[test]
694 fn test_embedding_options_default() {
695 let options = EmbeddingOptions::default();
696 assert!(options.subset);
697 assert_eq!(options.max_subset_size, Some(256));
698 assert!(options.compress_font_streams);
699 assert!(!options.embed_license_info);
700 }
701
702 #[test]
703 fn test_generate_tounicode_cmap_empty() {
704 let mut embedder = FontEmbedder::new();
705
706 let font_data = EmbeddedFontData {
708 pdf_name: "TestFont".to_string(),
709 font_type: FontType::TrueType,
710 descriptor: FontDescriptor {
711 font_name: "TestFont".to_string(),
712 flags: FontFlags::default(),
713 bbox: [0, 0, 1000, 1000],
714 italic_angle: 0.0,
715 ascent: 750,
716 descent: -250,
717 cap_height: 700,
718 stem_v: 100,
719 stem_h: 50,
720 avg_width: 500,
721 max_width: 1000,
722 missing_width: 500,
723 font_file: None,
724 },
725 font_program: vec![],
726 encoding: FontEncoding::WinAnsiEncoding,
727 metrics: FontMetrics {
728 ascent: 750,
729 descent: -250,
730 cap_height: 700,
731 x_height: 500,
732 stem_v: 100,
733 stem_h: 50,
734 avg_width: 500,
735 max_width: 1000,
736 missing_width: 500,
737 },
738 subset_glyphs: None,
739 unicode_mappings: HashMap::new(),
740 };
741
742 embedder
743 .embedded_fonts
744 .insert("TestFont".to_string(), font_data);
745
746 let result = embedder.generate_tounicode_cmap("TestFont");
747 assert!(result.is_err());
748 }
749
750 #[test]
751 fn test_generate_truetype_dictionary() {
752 let embedder = FontEmbedder::new();
753
754 let font_data = EmbeddedFontData {
755 pdf_name: "TestFont".to_string(),
756 font_type: FontType::TrueType,
757 descriptor: FontDescriptor {
758 font_name: "TestFont".to_string(),
759 flags: FontFlags::default(),
760 bbox: [0, 0, 1000, 1000],
761 italic_angle: 0.0,
762 ascent: 750,
763 descent: -250,
764 cap_height: 700,
765 stem_v: 100,
766 stem_h: 50,
767 avg_width: 500,
768 max_width: 1000,
769 missing_width: 500,
770 font_file: None,
771 },
772 font_program: vec![],
773 encoding: FontEncoding::WinAnsiEncoding,
774 metrics: FontMetrics {
775 ascent: 750,
776 descent: -250,
777 cap_height: 700,
778 x_height: 500,
779 stem_v: 100,
780 stem_h: 50,
781 avg_width: 500,
782 max_width: 1000,
783 missing_width: 500,
784 },
785 subset_glyphs: None,
786 unicode_mappings: HashMap::new(),
787 };
788
789 let dict = embedder.generate_truetype_dictionary(&font_data).unwrap();
790
791 if let Some(Object::Name(font_type)) = dict.get("Type") {
793 assert_eq!(font_type, "Font");
794 }
795 if let Some(Object::Name(subtype)) = dict.get("Subtype") {
796 assert_eq!(subtype, "TrueType");
797 }
798 if let Some(Object::Name(base_font)) = dict.get("BaseFont") {
799 assert_eq!(base_font, "TestFont");
800 }
801 }
802
803 #[test]
804 fn test_generate_type0_dictionary() {
805 let embedder = FontEmbedder::new();
806
807 let font_data = EmbeddedFontData {
808 pdf_name: "TestCIDFont".to_string(),
809 font_type: FontType::Type0,
810 descriptor: FontDescriptor {
811 font_name: "TestCIDFont".to_string(),
812 flags: FontFlags::default(),
813 bbox: [0, 0, 1000, 1000],
814 italic_angle: 0.0,
815 ascent: 750,
816 descent: -250,
817 cap_height: 700,
818 stem_v: 100,
819 stem_h: 50,
820 avg_width: 500,
821 max_width: 1000,
822 missing_width: 500,
823 font_file: None,
824 },
825 font_program: vec![],
826 encoding: FontEncoding::Identity,
827 metrics: FontMetrics {
828 ascent: 750,
829 descent: -250,
830 cap_height: 700,
831 x_height: 500,
832 stem_v: 100,
833 stem_h: 50,
834 avg_width: 500,
835 max_width: 1000,
836 missing_width: 500,
837 },
838 subset_glyphs: None,
839 unicode_mappings: HashMap::new(),
840 };
841
842 let dict = embedder.generate_type0_dictionary(&font_data).unwrap();
843
844 if let Some(Object::Name(subtype)) = dict.get("Subtype") {
846 assert_eq!(subtype, "Type0");
847 }
848 if let Some(Object::Name(encoding)) = dict.get("Encoding") {
849 assert_eq!(encoding, "Identity-H");
850 }
851 if let Some(Object::Array(descendant_fonts)) = dict.get("DescendantFonts") {
852 assert_eq!(descendant_fonts.len(), 1);
853 }
854 }
855
856 #[test]
857 fn test_chars_to_glyphs_conversion() {
858 let _embedder = FontEmbedder::new();
859 let _font_data = vec![0; 100]; let chars: HashSet<u32> = [65, 66, 67].iter().cloned().collect(); assert!(chars.len() == 3);
868 }
869
870 #[test]
871 fn test_unicode_mappings_creation() {
872 let _embedder = FontEmbedder::new();
873 let glyphs: HashSet<u16> = [65, 66, 67].iter().cloned().collect();
874
875 let _font_data = vec![0; 100];
877
878 assert!(glyphs.len() == 3);
881 }
882
883 #[test]
884 fn test_font_descriptor_generation() {
885 let _embedder = FontEmbedder::new();
886
887 let font_data = EmbeddedFontData {
888 pdf_name: "TestFont".to_string(),
889 font_type: FontType::TrueType,
890 descriptor: FontDescriptor {
891 font_name: "TestFont".to_string(),
892 flags: FontFlags {
893 non_symbolic: true,
894 serif: true,
895 ..Default::default()
896 },
897 bbox: [-100, -250, 1000, 750],
898 italic_angle: 0.0,
899 ascent: 750,
900 descent: -250,
901 cap_height: 700,
902 stem_v: 100,
903 stem_h: 50,
904 avg_width: 500,
905 max_width: 1000,
906 missing_width: 500,
907 font_file: None,
908 },
909 font_program: vec![],
910 encoding: FontEncoding::WinAnsiEncoding,
911 metrics: FontMetrics {
912 ascent: 750,
913 descent: -250,
914 cap_height: 700,
915 x_height: 500,
916 stem_v: 100,
917 stem_h: 50,
918 avg_width: 500,
919 max_width: 1000,
920 missing_width: 500,
921 },
922 subset_glyphs: None,
923 unicode_mappings: HashMap::new(),
924 };
925
926 let mut embedder_with_font = FontEmbedder::new();
927 embedder_with_font
928 .embedded_fonts
929 .insert("TestFont".to_string(), font_data);
930
931 let desc_dict = embedder_with_font
932 .generate_font_descriptor("TestFont")
933 .unwrap();
934
935 if let Some(Object::Name(font_name)) = desc_dict.get("FontName") {
937 assert_eq!(font_name, "TestFont");
938 }
939 if let Some(Object::Integer(flags)) = desc_dict.get("Flags") {
940 assert!(*flags > 0); }
942 if let Some(Object::Array(bbox)) = desc_dict.get("FontBBox") {
943 assert_eq!(bbox.len(), 4);
944 }
945 }
946}