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 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.clone(),
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) = cmap_tables
356 .iter()
357 .find(|t| t.platform_id == 3 && t.encoding_id == 1) .or_else(|| cmap_tables.iter().find(|t| t.platform_id == 0)) .or_else(|| cmap_tables.first())
360 {
361 let ranges = [
363 (0x0020, 0x007F), (0x00A0, 0x00FF), (0x3000, 0x303F), (0x3040, 0x309F), (0x30A0, 0x30FF), (0x4E00, 0x9FFF), ];
370
371 for (start, end) in ranges {
372 for char_code in start..=end {
373 if let Some(&glyph_id) = cmap.mappings.get(&char_code) {
374 if let Ok((advance_width, _)) = ttf.get_glyph_metrics(glyph_id) {
375 let width =
376 (advance_width as f64 * 1000.0) / ttf.units_per_em as f64;
377 if let Some(ch) = char::from_u32(char_code) {
378 char_widths.insert(ch, width);
379 }
380 }
381 }
382 }
383 }
384 }
385 }
386
387 let mut widths = Vec::new();
389 for char_code in 32u8..=255 {
390 let ch = char::from(char_code);
391 let width = char_widths.get(&ch).copied().unwrap_or(500.0); widths.push(width);
393 }
394
395 let metrics = FontMetrics {
396 first_char: 32,
397 last_char: 255,
398 widths,
399 missing_width: 500.0, };
401
402 let font = Self {
403 name: name.to_string(),
404 font_type: FontType::Type0, encoding: FontEncoding::Identity, descriptor,
407 metrics,
408 font_data: Some(data),
409 font_file_type: Some(FontFileType::TrueType),
410 truetype_font: Some(ttf),
411 used_glyphs: HashSet::new(),
412 };
413
414 Ok(font)
415 }
416
417 pub fn new_type1(
419 name: String,
420 encoding: FontEncoding,
421 descriptor: FontDescriptor,
422 metrics: FontMetrics,
423 ) -> Self {
424 Self {
425 name,
426 font_type: FontType::Type1,
427 encoding,
428 descriptor,
429 metrics,
430 font_data: None,
431 font_file_type: None,
432 truetype_font: None,
433 used_glyphs: HashSet::new(),
434 }
435 }
436
437 pub fn new_truetype(
439 name: String,
440 encoding: FontEncoding,
441 descriptor: FontDescriptor,
442 metrics: FontMetrics,
443 ) -> Self {
444 Self {
445 name,
446 font_type: FontType::TrueType,
447 encoding,
448 descriptor,
449 metrics,
450 font_data: None,
451 font_file_type: None,
452 truetype_font: None,
453 used_glyphs: HashSet::new(),
454 }
455 }
456
457 pub fn new_cff(
459 name: String,
460 encoding: FontEncoding,
461 descriptor: FontDescriptor,
462 metrics: FontMetrics,
463 ) -> Self {
464 Self {
465 name,
466 font_type: FontType::CFF,
467 encoding,
468 descriptor,
469 metrics,
470 font_data: None,
471 font_file_type: None,
472 truetype_font: None,
473 used_glyphs: HashSet::new(),
474 }
475 }
476
477 pub fn optimize_for_text(&mut self, text: &str) {
479 let needs_unicode = text.chars().any(|c| c as u32 > 255);
481
482 if needs_unicode && self.font_type != FontType::Type0 && self.font_type != FontType::CFF {
483 self.convert_to_type0();
485 }
486
487 self.mark_characters_used(text);
489 }
490
491 fn convert_to_type0(&mut self) {
493 if self.font_type == FontType::TrueType {
495 self.font_type = FontType::Type0;
496 self.encoding = FontEncoding::Identity;
497
498 self.used_glyphs.clear();
500 }
501 }
502
503 pub fn get_glyph_mapping(&self) -> Option<HashMap<u32, u16>> {
505 if let Some(ref ttf) = self.truetype_font {
506 if let Ok(cmap_tables) = ttf.parse_cmap() {
508 let cmap = cmap_tables
511 .iter()
512 .find(|t| t.platform_id == 3 && t.encoding_id == 1)
513 .or_else(|| cmap_tables.iter().find(|t| t.platform_id == 0));
514
515 if let Some(cmap) = cmap {
516 return Some(cmap.mappings.clone());
517 }
518 }
519 }
520 None
521 }
522
523 pub fn load_font_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
525 let data = fs::read(path.as_ref())?;
526
527 let file_type = Self::detect_font_file_type(&data)?;
529 self.font_file_type = Some(file_type);
530
531 if matches!(file_type, FontFileType::TrueType) {
533 match TrueTypeFont::parse(data.clone()) {
534 Ok(ttf) => {
535 if let Ok(font_name) = ttf.get_font_name() {
537 self.name = font_name.clone();
538 self.descriptor.font_name = font_name;
539 }
540
541 if let Ok(cmap_tables) = ttf.parse_cmap() {
543 if let Some(cmap) = cmap_tables
545 .iter()
546 .find(|t| t.platform_id == 3 && t.encoding_id == 1)
547 .or_else(|| cmap_tables.first())
548 {
549 let mut widths = Vec::new();
551 for char_code in self.metrics.first_char..=self.metrics.last_char {
552 if let Some(&glyph_id) = cmap.mappings.get(&(char_code as u32)) {
553 if let Ok((advance_width, _)) = ttf.get_glyph_metrics(glyph_id)
554 {
555 let width = (advance_width as f64 * 1000.0)
557 / ttf.units_per_em as f64;
558 widths.push(width);
559 } else {
560 widths.push(self.metrics.missing_width);
561 }
562 } else {
563 widths.push(self.metrics.missing_width);
564 }
565 }
566 self.metrics.widths = widths;
567 }
568 }
569
570 self.truetype_font = Some(ttf);
571 }
572 Err(_) => {
573 }
575 }
576 }
577
578 self.font_data = Some(data);
579 Ok(())
580 }
581
582 pub fn load_truetype_font<P: AsRef<Path>>(path: P) -> Result<Self> {
584 let data = fs::read(path.as_ref())?;
585
586 let ttf = TrueTypeFont::parse(data.clone()).map_err(|e| {
588 PdfError::InvalidStructure(format!("Failed to parse TrueType font: {}", e))
589 })?;
590
591 let font_name = ttf
593 .get_font_name()
594 .unwrap_or_else(|_| "Unknown".to_string());
595
596 let fixed_pitch = ttf.is_fixed_pitch().unwrap_or(false);
598 let flags = FontFlags {
599 fixed_pitch,
600 symbolic: false,
601 non_symbolic: true,
602 ..Default::default()
603 };
604
605 let font_bbox = ttf
606 .get_font_bbox()
607 .unwrap_or([-500.0, -300.0, 1500.0, 1000.0]);
608 let font_bbox_f64 = [
609 font_bbox[0] as f64,
610 font_bbox[1] as f64,
611 font_bbox[2] as f64,
612 font_bbox[3] as f64,
613 ];
614 let italic_angle = ttf.get_italic_angle().unwrap_or(0.0) as f64;
615 let ascent = ttf.get_ascent().unwrap_or(750) as f64;
616 let descent = ttf.get_descent().unwrap_or(-250) as f64;
617 let cap_height = ttf.get_cap_height().unwrap_or(700.0) as f64;
618 let stem_width = ttf.get_stem_width().unwrap_or(100.0) as f64;
619
620 let descriptor = FontDescriptor::new(
621 font_name.clone(),
622 flags,
623 font_bbox_f64,
624 italic_angle,
625 ascent,
626 descent,
627 cap_height,
628 stem_width,
629 );
630
631 let mut widths = Vec::new();
633 if let Ok(cmap_tables) = ttf.parse_cmap() {
634 if let Some(cmap) = cmap_tables
635 .iter()
636 .find(|t| t.platform_id == 3 && t.encoding_id == 1)
637 .or_else(|| cmap_tables.first())
638 {
639 for char_code in 32u8..=255 {
640 if let Some(&glyph_id) = cmap.mappings.get(&(char_code as u32)) {
641 if let Ok((advance_width, _)) = ttf.get_glyph_metrics(glyph_id) {
642 let width = (advance_width as f64 * 1000.0) / ttf.units_per_em as f64;
643 widths.push(width);
644 } else {
645 widths.push(250.0);
646 }
647 } else {
648 widths.push(250.0);
649 }
650 }
651 }
652 }
653
654 if widths.is_empty() {
655 widths = vec![250.0; 224]; }
657
658 let metrics = FontMetrics::new(32, 255, widths, 250.0);
659
660 let mut font = if ttf.is_cff {
662 let mut font = Self::new_cff(font_name, FontEncoding::Identity, descriptor, metrics);
664 font.font_file_type = Some(FontFileType::OpenTypeCFF);
665 font
666 } else {
667 let mut font = Self::new_truetype(
669 font_name,
670 FontEncoding::WinAnsiEncoding,
671 descriptor,
672 metrics,
673 );
674 font.font_file_type = Some(FontFileType::TrueType);
675 font
676 };
677
678 font.font_data = Some(data);
679 font.truetype_font = Some(ttf);
680
681 Ok(font)
682 }
683
684 pub fn mark_characters_used(&mut self, text: &str) {
686 if let Some(ref ttf) = self.truetype_font {
687 if let Ok(cmap_tables) = ttf.parse_cmap() {
688 if let Some(cmap) = cmap_tables
689 .iter()
690 .find(|t| t.platform_id == 3 && t.encoding_id == 1)
691 .or_else(|| cmap_tables.first())
692 {
693 for ch in text.chars() {
694 if let Some(&glyph_id) = cmap.mappings.get(&(ch as u32)) {
695 self.used_glyphs.insert(glyph_id);
696 }
697 }
698 }
699 }
700 }
701 }
702
703 pub fn get_subset_font_data(&self) -> Result<Option<Vec<u8>>> {
705 if self.font_type != FontType::TrueType {
706 return Ok(self.font_data.clone());
707 }
708
709 if let Some(ref ttf) = self.truetype_font {
710 if self.used_glyphs.is_empty() {
711 return Ok(self.font_data.clone());
713 }
714
715 let subset_data = ttf.create_subset(&self.used_glyphs).map_err(|e| {
717 PdfError::InvalidStructure(format!("Failed to create font subset: {}", e))
718 })?;
719
720 Ok(Some(subset_data))
721 } else {
722 Ok(self.font_data.clone())
723 }
724 }
725
726 fn detect_font_file_type(data: &[u8]) -> Result<FontFileType> {
728 if data.len() < 4 {
729 return Err(PdfError::InvalidStructure(
730 "Font file too small".to_string(),
731 ));
732 }
733
734 let signature = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
736 match signature {
737 0x00010000 | 0x74727565 => Ok(FontFileType::TrueType), 0x4F54544F => Ok(FontFileType::OpenTypeCFF), _ => {
740 if data.starts_with(b"%!PS") || data.starts_with(b"%!FontType1") {
742 Ok(FontFileType::Type1)
743 } else {
744 Err(PdfError::InvalidStructure(
745 "Unknown font file format".to_string(),
746 ))
747 }
748 }
749 }
750 }
751
752 pub fn to_pdf_dict(&self) -> Dictionary {
754 let mut dict = Dictionary::new();
755
756 dict.set("Type", Object::Name("Font".to_string()));
758 dict.set(
759 "Subtype",
760 Object::Name(
761 match self.font_type {
762 FontType::Type1 => "Type1",
763 FontType::TrueType => "TrueType",
764 FontType::CFF => "Type0", FontType::Type3 => "Type3",
766 FontType::Type0 => "Type0",
767 }
768 .to_string(),
769 ),
770 );
771
772 dict.set("BaseFont", Object::Name(self.name.clone()));
774
775 match &self.encoding {
777 FontEncoding::StandardEncoding => {
778 dict.set("Encoding", Object::Name("StandardEncoding".to_string()));
779 }
780 FontEncoding::MacRomanEncoding => {
781 dict.set("Encoding", Object::Name("MacRomanEncoding".to_string()));
782 }
783 FontEncoding::WinAnsiEncoding => {
784 dict.set("Encoding", Object::Name("WinAnsiEncoding".to_string()));
785 }
786 FontEncoding::Custom(differences) => {
787 let mut enc_dict = Dictionary::new();
788 enc_dict.set("Type", Object::Name("Encoding".to_string()));
789
790 let mut diff_array = Vec::new();
792 for diff in differences {
793 diff_array.push(Object::Integer(diff.code as i64));
794 for name in &diff.names {
795 diff_array.push(Object::Name(name.clone()));
796 }
797 }
798 enc_dict.set("Differences", Object::Array(diff_array));
799
800 dict.set("Encoding", Object::Dictionary(enc_dict));
801 }
802 FontEncoding::Identity => {
803 dict.set("Encoding", Object::Name("Identity-H".to_string()));
804 }
805 }
806
807 dict.set("FirstChar", Object::Integer(self.metrics.first_char as i64));
809 dict.set("LastChar", Object::Integer(self.metrics.last_char as i64));
810
811 let widths: Vec<Object> = self
812 .metrics
813 .widths
814 .iter()
815 .map(|&w| Object::Real(w))
816 .collect();
817 dict.set("Widths", Object::Array(widths));
818
819 dict
822 }
823}
824
825#[derive(Debug, Clone)]
827pub struct FontManager {
828 fonts: HashMap<String, CustomFont>,
830 next_font_id: usize,
832}
833
834impl Default for FontManager {
835 fn default() -> Self {
836 Self::new()
837 }
838}
839
840impl FontManager {
841 pub fn new() -> Self {
843 Self {
844 fonts: HashMap::new(),
845 next_font_id: 1,
846 }
847 }
848
849 pub fn register_font(&mut self, font: CustomFont) -> Result<String> {
851 let font_name = format!("F{}", self.next_font_id);
852 self.fonts.insert(font_name.clone(), font);
853 self.next_font_id += 1;
854 Ok(font_name)
855 }
856
857 pub fn get_font(&self, name: &str) -> Option<&CustomFont> {
859 self.fonts.get(name)
860 }
861
862 pub fn get_font_glyph_mapping(&self, name: &str) -> Option<HashMap<u32, u16>> {
864 if let Some(font) = self.fonts.get(name) {
865 font.get_glyph_mapping()
866 } else {
867 None
868 }
869 }
870
871 pub fn fonts(&self) -> &HashMap<String, CustomFont> {
873 &self.fonts
874 }
875
876 pub fn to_resource_dictionary(&self) -> Result<Dictionary> {
878 let mut font_dict = Dictionary::new();
879
880 for (name, font) in &self.fonts {
881 font_dict.set(name, Object::Dictionary(font.to_pdf_dict()));
882 }
883
884 Ok(font_dict)
885 }
886
887 pub fn create_standard_type1(name: &str) -> Result<CustomFont> {
889 let (encoding, descriptor, metrics) = match name {
890 "Helvetica" => (
891 FontEncoding::WinAnsiEncoding,
892 FontDescriptor::new(
893 "Helvetica".to_string(),
894 FontFlags {
895 non_symbolic: true,
896 ..Default::default()
897 },
898 [-166.0, -225.0, 1000.0, 931.0],
899 0.0,
900 718.0,
901 -207.0,
902 718.0,
903 88.0,
904 ),
905 FontMetrics::new(32, 255, Self::helvetica_widths(), 278.0),
906 ),
907 "Times-Roman" => (
908 FontEncoding::WinAnsiEncoding,
909 FontDescriptor::new(
910 "Times-Roman".to_string(),
911 FontFlags {
912 serif: true,
913 non_symbolic: true,
914 ..Default::default()
915 },
916 [-168.0, -218.0, 1000.0, 898.0],
917 0.0,
918 683.0,
919 -217.0,
920 662.0,
921 84.0,
922 ),
923 FontMetrics::new(32, 255, Self::times_widths(), 250.0),
924 ),
925 _ => {
926 return Err(PdfError::InvalidStructure(
927 "Unknown standard font".to_string(),
928 ))
929 }
930 };
931
932 Ok(CustomFont::new_type1(
933 name.to_string(),
934 encoding,
935 descriptor,
936 metrics,
937 ))
938 }
939
940 fn helvetica_widths() -> Vec<f64> {
942 vec![278.0; 224] }
946
947 fn times_widths() -> Vec<f64> {
949 vec![250.0; 224] }
953}
954
955#[cfg(test)]
956mod tests {
957 use super::*;
958
959 #[test]
960 fn test_font_type() {
961 assert_eq!(FontType::Type1, FontType::Type1);
962 assert_ne!(FontType::Type1, FontType::TrueType);
963 }
964
965 #[test]
966 fn test_font_flags() {
967 let mut flags = FontFlags::default();
968 assert_eq!(flags.to_flags(), 0);
969
970 flags.fixed_pitch = true;
971 flags.serif = true;
972 flags.italic = true;
973 let value = flags.to_flags();
974 assert!(value & (1 << 0) != 0); assert!(value & (1 << 1) != 0); assert!(value & (1 << 6) != 0); }
978
979 #[test]
980 fn test_font_descriptor() {
981 let flags = FontFlags {
982 serif: true,
983 non_symbolic: true,
984 ..Default::default()
985 };
986 let descriptor = FontDescriptor::new(
987 "TestFont".to_string(),
988 flags,
989 [-100.0, -200.0, 1000.0, 900.0],
990 0.0,
991 700.0,
992 -200.0,
993 700.0,
994 80.0,
995 );
996
997 let dict = descriptor.to_pdf_dict();
998 assert_eq!(
999 dict.get("Type"),
1000 Some(&Object::Name("FontDescriptor".to_string()))
1001 );
1002 assert_eq!(
1003 dict.get("FontName"),
1004 Some(&Object::Name("TestFont".to_string()))
1005 );
1006 }
1007
1008 #[test]
1009 fn test_font_metrics() {
1010 let widths = vec![100.0, 200.0, 300.0];
1011 let metrics = FontMetrics::new(65, 67, widths, 250.0);
1012
1013 assert_eq!(metrics.get_width(65), 100.0);
1014 assert_eq!(metrics.get_width(66), 200.0);
1015 assert_eq!(metrics.get_width(67), 300.0);
1016 assert_eq!(metrics.get_width(64), 250.0); assert_eq!(metrics.get_width(68), 250.0); }
1019
1020 #[test]
1021 fn test_encoding_difference() {
1022 let diff = EncodingDifference {
1023 code: 128,
1024 names: vec!["Euro".to_string(), "bullet".to_string()],
1025 };
1026 assert_eq!(diff.code, 128);
1027 assert_eq!(diff.names.len(), 2);
1028 }
1029
1030 #[test]
1031 fn test_custom_font_type1() {
1032 let flags = FontFlags::default();
1033 let descriptor = FontDescriptor::new(
1034 "CustomType1".to_string(),
1035 flags,
1036 [0.0, 0.0, 1000.0, 1000.0],
1037 0.0,
1038 750.0,
1039 -250.0,
1040 750.0,
1041 100.0,
1042 );
1043 let metrics = FontMetrics::new(32, 126, vec![250.0; 95], 250.0);
1044
1045 let font = CustomFont::new_type1(
1046 "CustomType1".to_string(),
1047 FontEncoding::StandardEncoding,
1048 descriptor,
1049 metrics,
1050 );
1051
1052 assert_eq!(font.font_type, FontType::Type1);
1053 assert_eq!(font.name, "CustomType1");
1054 }
1055
1056 #[test]
1057 fn test_custom_font_truetype() {
1058 let flags = FontFlags::default();
1059 let descriptor = FontDescriptor::new(
1060 "CustomTrueType".to_string(),
1061 flags,
1062 [0.0, 0.0, 1000.0, 1000.0],
1063 0.0,
1064 750.0,
1065 -250.0,
1066 750.0,
1067 100.0,
1068 );
1069 let metrics = FontMetrics::new(32, 126, vec![250.0; 95], 250.0);
1070
1071 let font = CustomFont::new_truetype(
1072 "CustomTrueType".to_string(),
1073 FontEncoding::WinAnsiEncoding,
1074 descriptor,
1075 metrics,
1076 );
1077
1078 assert_eq!(font.font_type, FontType::TrueType);
1079 assert_eq!(font.name, "CustomTrueType");
1080 }
1081
1082 #[test]
1083 fn test_font_manager() {
1084 let mut manager = FontManager::new();
1085
1086 let font = FontManager::create_standard_type1("Helvetica").unwrap();
1087 let font_name = manager.register_font(font).unwrap();
1088
1089 assert!(font_name.starts_with('F'));
1090 assert!(manager.get_font(&font_name).is_some());
1091
1092 let registered_font = manager.get_font(&font_name).unwrap();
1093 assert_eq!(registered_font.name, "Helvetica");
1094 }
1095
1096 #[test]
1097 fn test_detect_font_file_type() {
1098 let ttf_data = vec![0x00, 0x01, 0x00, 0x00];
1100 let font_type = CustomFont::detect_font_file_type(&ttf_data).unwrap();
1101 assert_eq!(font_type, FontFileType::TrueType);
1102
1103 let type1_data = b"%!PS-AdobeFont-1.0";
1105 let font_type = CustomFont::detect_font_file_type(type1_data).unwrap();
1106 assert_eq!(font_type, FontFileType::Type1);
1107
1108 let invalid_data = vec![0xFF, 0xFF];
1110 assert!(CustomFont::detect_font_file_type(&invalid_data).is_err());
1111 }
1112
1113 #[test]
1114 fn test_font_encoding() {
1115 let encoding = FontEncoding::StandardEncoding;
1116 assert!(matches!(encoding, FontEncoding::StandardEncoding));
1117
1118 let custom = FontEncoding::Custom(vec![EncodingDifference {
1119 code: 128,
1120 names: vec!["Euro".to_string()],
1121 }]);
1122 assert!(matches!(custom, FontEncoding::Custom(_)));
1123 }
1124
1125 #[test]
1126 fn test_font_descriptor_optional_fields() {
1127 let mut descriptor = FontDescriptor::new(
1128 "TestFont".to_string(),
1129 FontFlags::default(),
1130 [0.0, 0.0, 1000.0, 1000.0],
1131 0.0,
1132 750.0,
1133 -250.0,
1134 750.0,
1135 100.0,
1136 );
1137
1138 descriptor.font_family = Some("TestFamily".to_string());
1139 descriptor.font_weight = Some(700);
1140 descriptor.x_height = Some(500.0);
1141
1142 let dict = descriptor.to_pdf_dict();
1143 assert!(dict.get("FontFamily").is_some());
1144 assert!(dict.get("FontWeight").is_some());
1145 assert!(dict.get("XHeight").is_some());
1146 }
1147
1148 #[test]
1149 fn test_font_pdf_dict_generation() {
1150 let flags = FontFlags::default();
1151 let descriptor = FontDescriptor::new(
1152 "TestFont".to_string(),
1153 flags,
1154 [0.0, 0.0, 1000.0, 1000.0],
1155 0.0,
1156 750.0,
1157 -250.0,
1158 750.0,
1159 100.0,
1160 );
1161 let metrics = FontMetrics::new(32, 126, vec![250.0; 95], 250.0);
1162
1163 let font = CustomFont::new_type1(
1164 "TestFont".to_string(),
1165 FontEncoding::WinAnsiEncoding,
1166 descriptor,
1167 metrics,
1168 );
1169
1170 let dict = font.to_pdf_dict();
1171 assert_eq!(dict.get("Type"), Some(&Object::Name("Font".to_string())));
1172 assert_eq!(
1173 dict.get("Subtype"),
1174 Some(&Object::Name("Type1".to_string()))
1175 );
1176 assert_eq!(
1177 dict.get("BaseFont"),
1178 Some(&Object::Name("TestFont".to_string()))
1179 );
1180 assert_eq!(
1181 dict.get("Encoding"),
1182 Some(&Object::Name("WinAnsiEncoding".to_string()))
1183 );
1184 }
1185}