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