1use crate::error::{PdfError, Result};
7use crate::objects::{Dictionary, Object};
8use crate::text::fonts::truetype::TrueTypeFont;
9use std::collections::{HashMap, HashSet};
10use std::fs;
11use std::path::Path;
12
13#[derive(Debug, Clone, Copy, PartialEq)]
15pub enum FontType {
16 Type1,
18 TrueType,
20 Type3,
22 Type0,
24}
25
26#[derive(Debug, Clone, PartialEq)]
28pub enum FontEncoding {
29 StandardEncoding,
31 MacRomanEncoding,
33 WinAnsiEncoding,
35 Custom(Vec<EncodingDifference>),
37 Identity,
39}
40
41#[derive(Debug, Clone, PartialEq)]
43pub struct EncodingDifference {
44 pub code: u8,
46 pub names: Vec<String>,
48}
49
50#[derive(Debug, Clone, Copy, Default)]
52pub struct FontFlags {
53 pub fixed_pitch: bool,
55 pub serif: bool,
57 pub symbolic: bool,
59 pub script: bool,
61 pub non_symbolic: bool,
63 pub italic: bool,
65 pub all_cap: bool,
67 pub small_cap: bool,
69 pub force_bold: bool,
71}
72
73impl FontFlags {
74 pub fn to_flags(&self) -> u32 {
76 let mut flags = 0u32;
77
78 if self.fixed_pitch {
79 flags |= 1 << 0;
80 }
81 if self.serif {
82 flags |= 1 << 1;
83 }
84 if self.symbolic {
85 flags |= 1 << 2;
86 }
87 if self.script {
88 flags |= 1 << 3;
89 }
90 if self.non_symbolic {
91 flags |= 1 << 5;
92 }
93 if self.italic {
94 flags |= 1 << 6;
95 }
96 if self.all_cap {
97 flags |= 1 << 16;
98 }
99 if self.small_cap {
100 flags |= 1 << 17;
101 }
102 if self.force_bold {
103 flags |= 1 << 18;
104 }
105
106 flags
107 }
108}
109
110#[derive(Debug, Clone)]
112pub struct FontDescriptor {
113 pub font_name: String,
115 pub font_family: Option<String>,
117 pub font_stretch: Option<String>,
119 pub font_weight: Option<i32>,
121 pub flags: FontFlags,
123 pub font_bbox: [f64; 4],
125 pub italic_angle: f64,
127 pub ascent: f64,
129 pub descent: f64,
131 pub leading: Option<f64>,
133 pub cap_height: f64,
135 pub x_height: Option<f64>,
137 pub stem_v: f64,
139 pub stem_h: Option<f64>,
141 pub avg_width: Option<f64>,
143 pub max_width: Option<f64>,
145 pub missing_width: Option<f64>,
147}
148
149impl FontDescriptor {
150 #[allow(clippy::too_many_arguments)]
152 pub fn new(
153 font_name: String,
154 flags: FontFlags,
155 font_bbox: [f64; 4],
156 italic_angle: f64,
157 ascent: f64,
158 descent: f64,
159 cap_height: f64,
160 stem_v: f64,
161 ) -> Self {
162 Self {
163 font_name,
164 font_family: None,
165 font_stretch: None,
166 font_weight: None,
167 flags,
168 font_bbox,
169 italic_angle,
170 ascent,
171 descent,
172 leading: None,
173 cap_height,
174 x_height: None,
175 stem_v,
176 stem_h: None,
177 avg_width: None,
178 max_width: None,
179 missing_width: None,
180 }
181 }
182
183 pub fn to_pdf_dict(&self) -> Dictionary {
185 let mut dict = Dictionary::new();
186
187 dict.set("Type", Object::Name("FontDescriptor".to_string()));
188 dict.set("FontName", Object::Name(self.font_name.clone()));
189
190 if let Some(ref family) = self.font_family {
191 dict.set("FontFamily", Object::String(family.clone()));
192 }
193
194 if let Some(ref stretch) = self.font_stretch {
195 dict.set("FontStretch", Object::Name(stretch.clone()));
196 }
197
198 if let Some(weight) = self.font_weight {
199 dict.set("FontWeight", Object::Integer(weight as i64));
200 }
201
202 dict.set("Flags", Object::Integer(self.flags.to_flags() as i64));
203
204 let bbox = vec![
205 Object::Real(self.font_bbox[0]),
206 Object::Real(self.font_bbox[1]),
207 Object::Real(self.font_bbox[2]),
208 Object::Real(self.font_bbox[3]),
209 ];
210 dict.set("FontBBox", Object::Array(bbox));
211
212 dict.set("ItalicAngle", Object::Real(self.italic_angle));
213 dict.set("Ascent", Object::Real(self.ascent));
214 dict.set("Descent", Object::Real(self.descent));
215
216 if let Some(leading) = self.leading {
217 dict.set("Leading", Object::Real(leading));
218 }
219
220 dict.set("CapHeight", Object::Real(self.cap_height));
221
222 if let Some(x_height) = self.x_height {
223 dict.set("XHeight", Object::Real(x_height));
224 }
225
226 dict.set("StemV", Object::Real(self.stem_v));
227
228 if let Some(stem_h) = self.stem_h {
229 dict.set("StemH", Object::Real(stem_h));
230 }
231
232 if let Some(avg_width) = self.avg_width {
233 dict.set("AvgWidth", Object::Real(avg_width));
234 }
235
236 if let Some(max_width) = self.max_width {
237 dict.set("MaxWidth", Object::Real(max_width));
238 }
239
240 if let Some(missing_width) = self.missing_width {
241 dict.set("MissingWidth", Object::Real(missing_width));
242 }
243
244 dict
245 }
246}
247
248#[derive(Debug, Clone)]
250pub struct FontMetrics {
251 pub first_char: u8,
253 pub last_char: u8,
255 pub widths: Vec<f64>,
257 pub missing_width: f64,
259}
260
261impl FontMetrics {
262 pub fn new(first_char: u8, last_char: u8, widths: Vec<f64>, missing_width: f64) -> Self {
264 Self {
265 first_char,
266 last_char,
267 widths,
268 missing_width,
269 }
270 }
271
272 pub fn get_width(&self, char_code: u8) -> f64 {
274 if char_code < self.first_char || char_code > self.last_char {
275 self.missing_width
276 } else {
277 let index = (char_code - self.first_char) as usize;
278 self.widths
279 .get(index)
280 .copied()
281 .unwrap_or(self.missing_width)
282 }
283 }
284}
285
286#[derive(Debug, Clone)]
288pub struct CustomFont {
289 pub name: String,
291 pub font_type: FontType,
293 pub encoding: FontEncoding,
295 pub descriptor: FontDescriptor,
297 pub metrics: FontMetrics,
299 pub font_data: Option<Vec<u8>>,
301 pub font_file_type: Option<FontFileType>,
303 pub truetype_font: Option<TrueTypeFont>,
305 pub used_glyphs: HashSet<u16>,
307}
308
309#[derive(Debug, Clone, Copy, PartialEq)]
311pub enum FontFileType {
312 Type1,
314 TrueType,
316 OpenTypeCFF,
318}
319
320impl CustomFont {
321 pub fn from_bytes(name: &str, data: Vec<u8>) -> Result<Self> {
323 let ttf = TrueTypeFont::parse(data.clone()).map_err(|e| {
325 PdfError::InvalidStructure(format!("Failed to parse TrueType font: {}", e))
326 })?;
327
328 let font_name = ttf.get_font_name().unwrap_or_else(|_| name.to_string());
330
331 let flags = FontFlags {
333 fixed_pitch: false,
334 symbolic: false,
335 non_symbolic: true,
336 ..Default::default()
337 };
338
339 let descriptor = FontDescriptor::new(
340 font_name.clone(),
341 flags,
342 [-500.0, -300.0, 1500.0, 1000.0], 0.0, 750.0, -250.0, 700.0, 100.0, );
349
350 let mut widths = Vec::new();
352 if let Ok(cmap_tables) = ttf.parse_cmap() {
353 if let Some(cmap) = cmap_tables
354 .iter()
355 .find(|t| t.platform_id == 3 && t.encoding_id == 1)
356 .or_else(|| cmap_tables.first())
357 {
358 for char_code in 32u8..=255 {
359 if let Some(&glyph_id) = cmap.mappings.get(&(char_code as u32)) {
360 if let Ok((advance_width, _)) = ttf.get_glyph_metrics(glyph_id) {
361 let width = (advance_width as f64 * 1000.0) / ttf.units_per_em as f64;
362 widths.push(width);
363 } else {
364 widths.push(250.0);
365 }
366 } else {
367 widths.push(250.0);
368 }
369 }
370 }
371 }
372
373 let metrics = FontMetrics {
374 first_char: 32,
375 last_char: 255,
376 widths,
377 missing_width: 250.0,
378 };
379
380 let font = Self {
381 name: name.to_string(),
382 font_type: FontType::Type0, encoding: FontEncoding::Identity, descriptor,
385 metrics,
386 font_data: Some(data),
387 font_file_type: Some(FontFileType::TrueType),
388 truetype_font: Some(ttf),
389 used_glyphs: HashSet::new(),
390 };
391
392 Ok(font)
393 }
394
395 pub fn new_type1(
397 name: String,
398 encoding: FontEncoding,
399 descriptor: FontDescriptor,
400 metrics: FontMetrics,
401 ) -> Self {
402 Self {
403 name,
404 font_type: FontType::Type1,
405 encoding,
406 descriptor,
407 metrics,
408 font_data: None,
409 font_file_type: None,
410 truetype_font: None,
411 used_glyphs: HashSet::new(),
412 }
413 }
414
415 pub fn new_truetype(
417 name: String,
418 encoding: FontEncoding,
419 descriptor: FontDescriptor,
420 metrics: FontMetrics,
421 ) -> Self {
422 Self {
423 name,
424 font_type: FontType::TrueType,
425 encoding,
426 descriptor,
427 metrics,
428 font_data: None,
429 font_file_type: None,
430 truetype_font: None,
431 used_glyphs: HashSet::new(),
432 }
433 }
434
435 pub fn optimize_for_text(&mut self, text: &str) {
437 let needs_unicode = text.chars().any(|c| c as u32 > 255);
439
440 if needs_unicode && self.font_type != FontType::Type0 {
441 self.convert_to_type0();
443 }
444
445 self.mark_characters_used(text);
447 }
448
449 fn convert_to_type0(&mut self) {
451 if self.font_type == FontType::TrueType {
453 self.font_type = FontType::Type0;
454 self.encoding = FontEncoding::Identity;
455
456 self.used_glyphs.clear();
458 }
459 }
460
461 pub fn get_glyph_mapping(&self) -> Option<HashMap<u32, u16>> {
463 if let Some(ref ttf) = self.truetype_font {
464 if let Ok(cmap_tables) = ttf.parse_cmap() {
466 let cmap = cmap_tables
469 .iter()
470 .find(|t| t.platform_id == 3 && t.encoding_id == 1)
471 .or_else(|| cmap_tables.iter().find(|t| t.platform_id == 0));
472
473 if let Some(cmap) = cmap {
474 return Some(cmap.mappings.clone());
475 }
476 }
477 }
478 None
479 }
480
481 pub fn load_font_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
483 let data = fs::read(path.as_ref())?;
484
485 let file_type = Self::detect_font_file_type(&data)?;
487 self.font_file_type = Some(file_type);
488
489 if matches!(file_type, FontFileType::TrueType) {
491 match TrueTypeFont::parse(data.clone()) {
492 Ok(ttf) => {
493 if let Ok(font_name) = ttf.get_font_name() {
495 self.name = font_name.clone();
496 self.descriptor.font_name = font_name;
497 }
498
499 if let Ok(cmap_tables) = ttf.parse_cmap() {
501 if let Some(cmap) = cmap_tables
503 .iter()
504 .find(|t| t.platform_id == 3 && t.encoding_id == 1)
505 .or_else(|| cmap_tables.first())
506 {
507 let mut widths = Vec::new();
509 for char_code in self.metrics.first_char..=self.metrics.last_char {
510 if let Some(&glyph_id) = cmap.mappings.get(&(char_code as u32)) {
511 if let Ok((advance_width, _)) = ttf.get_glyph_metrics(glyph_id)
512 {
513 let width = (advance_width as f64 * 1000.0)
515 / ttf.units_per_em as f64;
516 widths.push(width);
517 } else {
518 widths.push(self.metrics.missing_width);
519 }
520 } else {
521 widths.push(self.metrics.missing_width);
522 }
523 }
524 self.metrics.widths = widths;
525 }
526 }
527
528 self.truetype_font = Some(ttf);
529 }
530 Err(_) => {
531 }
533 }
534 }
535
536 self.font_data = Some(data);
537 Ok(())
538 }
539
540 pub fn load_truetype_font<P: AsRef<Path>>(path: P) -> Result<Self> {
542 let data = fs::read(path.as_ref())?;
543
544 let ttf = TrueTypeFont::parse(data.clone()).map_err(|e| {
546 PdfError::InvalidStructure(format!("Failed to parse TrueType font: {}", e))
547 })?;
548
549 let font_name = ttf
551 .get_font_name()
552 .unwrap_or_else(|_| "Unknown".to_string());
553
554 let fixed_pitch = ttf.is_fixed_pitch().unwrap_or(false);
556 let flags = FontFlags {
557 fixed_pitch,
558 symbolic: false,
559 non_symbolic: true,
560 ..Default::default()
561 };
562
563 let font_bbox = ttf
564 .get_font_bbox()
565 .unwrap_or([-500.0, -300.0, 1500.0, 1000.0]);
566 let font_bbox_f64 = [
567 font_bbox[0] as f64,
568 font_bbox[1] as f64,
569 font_bbox[2] as f64,
570 font_bbox[3] as f64,
571 ];
572 let italic_angle = ttf.get_italic_angle().unwrap_or(0.0) as f64;
573 let ascent = ttf.get_ascent().unwrap_or(750) as f64;
574 let descent = ttf.get_descent().unwrap_or(-250) as f64;
575 let cap_height = ttf.get_cap_height().unwrap_or(700.0) as f64;
576 let stem_width = ttf.get_stem_width().unwrap_or(100.0) as f64;
577
578 let descriptor = FontDescriptor::new(
579 font_name.clone(),
580 flags,
581 font_bbox_f64,
582 italic_angle,
583 ascent,
584 descent,
585 cap_height,
586 stem_width,
587 );
588
589 let mut widths = Vec::new();
591 if let Ok(cmap_tables) = ttf.parse_cmap() {
592 if let Some(cmap) = cmap_tables
593 .iter()
594 .find(|t| t.platform_id == 3 && t.encoding_id == 1)
595 .or_else(|| cmap_tables.first())
596 {
597 for char_code in 32u8..=255 {
598 if let Some(&glyph_id) = cmap.mappings.get(&(char_code as u32)) {
599 if let Ok((advance_width, _)) = ttf.get_glyph_metrics(glyph_id) {
600 let width = (advance_width as f64 * 1000.0) / ttf.units_per_em as f64;
601 widths.push(width);
602 } else {
603 widths.push(250.0);
604 }
605 } else {
606 widths.push(250.0);
607 }
608 }
609 }
610 }
611
612 if widths.is_empty() {
613 widths = vec![250.0; 224]; }
615
616 let metrics = FontMetrics::new(32, 255, widths, 250.0);
617
618 let mut font = Self::new_truetype(
619 font_name,
620 FontEncoding::WinAnsiEncoding,
621 descriptor,
622 metrics,
623 );
624
625 font.font_data = Some(data);
626 font.font_file_type = Some(FontFileType::TrueType);
627 font.truetype_font = Some(ttf);
628
629 Ok(font)
630 }
631
632 pub fn mark_characters_used(&mut self, text: &str) {
634 if let Some(ref ttf) = self.truetype_font {
635 if let Ok(cmap_tables) = ttf.parse_cmap() {
636 if let Some(cmap) = cmap_tables
637 .iter()
638 .find(|t| t.platform_id == 3 && t.encoding_id == 1)
639 .or_else(|| cmap_tables.first())
640 {
641 for ch in text.chars() {
642 if let Some(&glyph_id) = cmap.mappings.get(&(ch as u32)) {
643 self.used_glyphs.insert(glyph_id);
644 }
645 }
646 }
647 }
648 }
649 }
650
651 pub fn get_subset_font_data(&self) -> Result<Option<Vec<u8>>> {
653 if self.font_type != FontType::TrueType {
654 return Ok(self.font_data.clone());
655 }
656
657 if let Some(ref ttf) = self.truetype_font {
658 if self.used_glyphs.is_empty() {
659 return Ok(self.font_data.clone());
661 }
662
663 let subset_data = ttf.create_subset(&self.used_glyphs).map_err(|e| {
665 PdfError::InvalidStructure(format!("Failed to create font subset: {}", e))
666 })?;
667
668 Ok(Some(subset_data))
669 } else {
670 Ok(self.font_data.clone())
671 }
672 }
673
674 fn detect_font_file_type(data: &[u8]) -> Result<FontFileType> {
676 if data.len() < 4 {
677 return Err(PdfError::InvalidStructure(
678 "Font file too small".to_string(),
679 ));
680 }
681
682 let signature = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
684 match signature {
685 0x00010000 | 0x74727565 => Ok(FontFileType::TrueType), 0x4F54544F => Ok(FontFileType::OpenTypeCFF), _ => {
688 if data.starts_with(b"%!PS") || data.starts_with(b"%!FontType1") {
690 Ok(FontFileType::Type1)
691 } else {
692 Err(PdfError::InvalidStructure(
693 "Unknown font file format".to_string(),
694 ))
695 }
696 }
697 }
698 }
699
700 pub fn to_pdf_dict(&self) -> Dictionary {
702 let mut dict = Dictionary::new();
703
704 dict.set("Type", Object::Name("Font".to_string()));
706 dict.set(
707 "Subtype",
708 Object::Name(
709 match self.font_type {
710 FontType::Type1 => "Type1",
711 FontType::TrueType => "TrueType",
712 FontType::Type3 => "Type3",
713 FontType::Type0 => "Type0",
714 }
715 .to_string(),
716 ),
717 );
718
719 dict.set("BaseFont", Object::Name(self.name.clone()));
721
722 match &self.encoding {
724 FontEncoding::StandardEncoding => {
725 dict.set("Encoding", Object::Name("StandardEncoding".to_string()));
726 }
727 FontEncoding::MacRomanEncoding => {
728 dict.set("Encoding", Object::Name("MacRomanEncoding".to_string()));
729 }
730 FontEncoding::WinAnsiEncoding => {
731 dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
732 }
733 FontEncoding::Custom(differences) => {
734 let mut enc_dict = Dictionary::new();
735 enc_dict.set("Type", Object::Name("Encoding".to_string()));
736
737 let mut diff_array = Vec::new();
739 for diff in differences {
740 diff_array.push(Object::Integer(diff.code as i64));
741 for name in &diff.names {
742 diff_array.push(Object::Name(name.clone()));
743 }
744 }
745 enc_dict.set("Differences", Object::Array(diff_array));
746
747 dict.set("Encoding", Object::Dictionary(enc_dict));
748 }
749 FontEncoding::Identity => {
750 dict.set("Encoding", Object::Name("Identity-H".to_string()));
751 }
752 }
753
754 dict.set("FirstChar", Object::Integer(self.metrics.first_char as i64));
756 dict.set("LastChar", Object::Integer(self.metrics.last_char as i64));
757
758 let widths: Vec<Object> = self
759 .metrics
760 .widths
761 .iter()
762 .map(|&w| Object::Real(w))
763 .collect();
764 dict.set("Widths", Object::Array(widths));
765
766 dict
769 }
770}
771
772#[derive(Debug, Clone)]
774pub struct FontManager {
775 fonts: HashMap<String, CustomFont>,
777 next_font_id: usize,
779}
780
781impl Default for FontManager {
782 fn default() -> Self {
783 Self::new()
784 }
785}
786
787impl FontManager {
788 pub fn new() -> Self {
790 Self {
791 fonts: HashMap::new(),
792 next_font_id: 1,
793 }
794 }
795
796 pub fn register_font(&mut self, font: CustomFont) -> Result<String> {
798 let font_name = format!("F{}", self.next_font_id);
799 self.fonts.insert(font_name.clone(), font);
800 self.next_font_id += 1;
801 Ok(font_name)
802 }
803
804 pub fn get_font(&self, name: &str) -> Option<&CustomFont> {
806 self.fonts.get(name)
807 }
808
809 pub fn get_font_glyph_mapping(&self, name: &str) -> Option<HashMap<u32, u16>> {
811 if let Some(font) = self.fonts.get(name) {
812 font.get_glyph_mapping()
813 } else {
814 None
815 }
816 }
817
818 pub fn fonts(&self) -> &HashMap<String, CustomFont> {
820 &self.fonts
821 }
822
823 pub fn to_resource_dictionary(&self) -> Result<Dictionary> {
825 let mut font_dict = Dictionary::new();
826
827 for (name, font) in &self.fonts {
828 font_dict.set(name, Object::Dictionary(font.to_pdf_dict()));
829 }
830
831 Ok(font_dict)
832 }
833
834 pub fn create_standard_type1(name: &str) -> Result<CustomFont> {
836 let (encoding, descriptor, metrics) = match name {
837 "Helvetica" => (
838 FontEncoding::WinAnsiEncoding,
839 FontDescriptor::new(
840 "Helvetica".to_string(),
841 FontFlags {
842 non_symbolic: true,
843 ..Default::default()
844 },
845 [-166.0, -225.0, 1000.0, 931.0],
846 0.0,
847 718.0,
848 -207.0,
849 718.0,
850 88.0,
851 ),
852 FontMetrics::new(32, 255, Self::helvetica_widths(), 278.0),
853 ),
854 "Times-Roman" => (
855 FontEncoding::WinAnsiEncoding,
856 FontDescriptor::new(
857 "Times-Roman".to_string(),
858 FontFlags {
859 serif: true,
860 non_symbolic: true,
861 ..Default::default()
862 },
863 [-168.0, -218.0, 1000.0, 898.0],
864 0.0,
865 683.0,
866 -217.0,
867 662.0,
868 84.0,
869 ),
870 FontMetrics::new(32, 255, Self::times_widths(), 250.0),
871 ),
872 _ => {
873 return Err(PdfError::InvalidStructure(
874 "Unknown standard font".to_string(),
875 ))
876 }
877 };
878
879 Ok(CustomFont::new_type1(
880 name.to_string(),
881 encoding,
882 descriptor,
883 metrics,
884 ))
885 }
886
887 fn helvetica_widths() -> Vec<f64> {
889 vec![278.0; 224] }
893
894 fn times_widths() -> Vec<f64> {
896 vec![250.0; 224] }
900}
901
902#[cfg(test)]
903mod tests {
904 use super::*;
905
906 #[test]
907 fn test_font_type() {
908 assert_eq!(FontType::Type1, FontType::Type1);
909 assert_ne!(FontType::Type1, FontType::TrueType);
910 }
911
912 #[test]
913 fn test_font_flags() {
914 let mut flags = FontFlags::default();
915 assert_eq!(flags.to_flags(), 0);
916
917 flags.fixed_pitch = true;
918 flags.serif = true;
919 flags.italic = true;
920 let value = flags.to_flags();
921 assert!(value & (1 << 0) != 0); assert!(value & (1 << 1) != 0); assert!(value & (1 << 6) != 0); }
925
926 #[test]
927 fn test_font_descriptor() {
928 let flags = FontFlags {
929 serif: true,
930 non_symbolic: true,
931 ..Default::default()
932 };
933 let descriptor = FontDescriptor::new(
934 "TestFont".to_string(),
935 flags,
936 [-100.0, -200.0, 1000.0, 900.0],
937 0.0,
938 700.0,
939 -200.0,
940 700.0,
941 80.0,
942 );
943
944 let dict = descriptor.to_pdf_dict();
945 assert_eq!(
946 dict.get("Type"),
947 Some(&Object::Name("FontDescriptor".to_string()))
948 );
949 assert_eq!(
950 dict.get("FontName"),
951 Some(&Object::Name("TestFont".to_string()))
952 );
953 }
954
955 #[test]
956 fn test_font_metrics() {
957 let widths = vec![100.0, 200.0, 300.0];
958 let metrics = FontMetrics::new(65, 67, widths, 250.0);
959
960 assert_eq!(metrics.get_width(65), 100.0);
961 assert_eq!(metrics.get_width(66), 200.0);
962 assert_eq!(metrics.get_width(67), 300.0);
963 assert_eq!(metrics.get_width(64), 250.0); assert_eq!(metrics.get_width(68), 250.0); }
966
967 #[test]
968 fn test_encoding_difference() {
969 let diff = EncodingDifference {
970 code: 128,
971 names: vec!["Euro".to_string(), "bullet".to_string()],
972 };
973 assert_eq!(diff.code, 128);
974 assert_eq!(diff.names.len(), 2);
975 }
976
977 #[test]
978 fn test_custom_font_type1() {
979 let flags = FontFlags::default();
980 let descriptor = FontDescriptor::new(
981 "CustomType1".to_string(),
982 flags,
983 [0.0, 0.0, 1000.0, 1000.0],
984 0.0,
985 750.0,
986 -250.0,
987 750.0,
988 100.0,
989 );
990 let metrics = FontMetrics::new(32, 126, vec![250.0; 95], 250.0);
991
992 let font = CustomFont::new_type1(
993 "CustomType1".to_string(),
994 FontEncoding::StandardEncoding,
995 descriptor,
996 metrics,
997 );
998
999 assert_eq!(font.font_type, FontType::Type1);
1000 assert_eq!(font.name, "CustomType1");
1001 }
1002
1003 #[test]
1004 fn test_custom_font_truetype() {
1005 let flags = FontFlags::default();
1006 let descriptor = FontDescriptor::new(
1007 "CustomTrueType".to_string(),
1008 flags,
1009 [0.0, 0.0, 1000.0, 1000.0],
1010 0.0,
1011 750.0,
1012 -250.0,
1013 750.0,
1014 100.0,
1015 );
1016 let metrics = FontMetrics::new(32, 126, vec![250.0; 95], 250.0);
1017
1018 let font = CustomFont::new_truetype(
1019 "CustomTrueType".to_string(),
1020 FontEncoding::WinAnsiEncoding,
1021 descriptor,
1022 metrics,
1023 );
1024
1025 assert_eq!(font.font_type, FontType::TrueType);
1026 assert_eq!(font.name, "CustomTrueType");
1027 }
1028
1029 #[test]
1030 fn test_font_manager() {
1031 let mut manager = FontManager::new();
1032
1033 let font = FontManager::create_standard_type1("Helvetica").unwrap();
1034 let font_name = manager.register_font(font).unwrap();
1035
1036 assert!(font_name.starts_with('F'));
1037 assert!(manager.get_font(&font_name).is_some());
1038
1039 let registered_font = manager.get_font(&font_name).unwrap();
1040 assert_eq!(registered_font.name, "Helvetica");
1041 }
1042
1043 #[test]
1044 fn test_detect_font_file_type() {
1045 let ttf_data = vec![0x00, 0x01, 0x00, 0x00];
1047 let font_type = CustomFont::detect_font_file_type(&ttf_data).unwrap();
1048 assert_eq!(font_type, FontFileType::TrueType);
1049
1050 let type1_data = b"%!PS-AdobeFont-1.0";
1052 let font_type = CustomFont::detect_font_file_type(type1_data).unwrap();
1053 assert_eq!(font_type, FontFileType::Type1);
1054
1055 let invalid_data = vec![0xFF, 0xFF];
1057 assert!(CustomFont::detect_font_file_type(&invalid_data).is_err());
1058 }
1059
1060 #[test]
1061 fn test_font_encoding() {
1062 let encoding = FontEncoding::StandardEncoding;
1063 assert!(matches!(encoding, FontEncoding::StandardEncoding));
1064
1065 let custom = FontEncoding::Custom(vec![EncodingDifference {
1066 code: 128,
1067 names: vec!["Euro".to_string()],
1068 }]);
1069 assert!(matches!(custom, FontEncoding::Custom(_)));
1070 }
1071
1072 #[test]
1073 fn test_font_descriptor_optional_fields() {
1074 let mut descriptor = FontDescriptor::new(
1075 "TestFont".to_string(),
1076 FontFlags::default(),
1077 [0.0, 0.0, 1000.0, 1000.0],
1078 0.0,
1079 750.0,
1080 -250.0,
1081 750.0,
1082 100.0,
1083 );
1084
1085 descriptor.font_family = Some("TestFamily".to_string());
1086 descriptor.font_weight = Some(700);
1087 descriptor.x_height = Some(500.0);
1088
1089 let dict = descriptor.to_pdf_dict();
1090 assert!(dict.get("FontFamily").is_some());
1091 assert!(dict.get("FontWeight").is_some());
1092 assert!(dict.get("XHeight").is_some());
1093 }
1094
1095 #[test]
1096 fn test_font_pdf_dict_generation() {
1097 let flags = FontFlags::default();
1098 let descriptor = FontDescriptor::new(
1099 "TestFont".to_string(),
1100 flags,
1101 [0.0, 0.0, 1000.0, 1000.0],
1102 0.0,
1103 750.0,
1104 -250.0,
1105 750.0,
1106 100.0,
1107 );
1108 let metrics = FontMetrics::new(32, 126, vec![250.0; 95], 250.0);
1109
1110 let font = CustomFont::new_type1(
1111 "TestFont".to_string(),
1112 FontEncoding::WinAnsiEncoding,
1113 descriptor,
1114 metrics,
1115 );
1116
1117 let dict = font.to_pdf_dict();
1118 assert_eq!(dict.get("Type"), Some(&Object::Name("Font".to_string())));
1119 assert_eq!(
1120 dict.get("Subtype"),
1121 Some(&Object::Name("Type1".to_string()))
1122 );
1123 assert_eq!(
1124 dict.get("BaseFont"),
1125 Some(&Object::Name("TestFont".to_string()))
1126 );
1127 assert_eq!(
1128 dict.get("Encoding"),
1129 Some(&Object::Name("WinAnsiEncoding".to_string()))
1130 );
1131 }
1132}