oxidize_pdf/text/
font_manager.rs

1//! Font Manager for Type 1 and TrueType font support according to ISO 32000-1 Chapter 9
2//!
3//! This module provides comprehensive support for custom fonts including Type 1 and TrueType
4//! fonts with proper embedding, encoding, and font descriptor management.
5
6use 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/// Font type enumeration
14#[derive(Debug, Clone, Copy, PartialEq)]
15pub enum FontType {
16    /// Type 1 font
17    Type1,
18    /// TrueType font
19    TrueType,
20    /// Type 3 font (user-defined)
21    Type3,
22    /// Type 0 font (composite)
23    Type0,
24}
25
26/// Font encoding types
27#[derive(Debug, Clone, PartialEq)]
28pub enum FontEncoding {
29    /// Standard encoding
30    StandardEncoding,
31    /// MacRoman encoding
32    MacRomanEncoding,
33    /// WinAnsi encoding
34    WinAnsiEncoding,
35    /// Custom encoding with differences
36    Custom(Vec<EncodingDifference>),
37    /// Identity encoding for CID fonts
38    Identity,
39}
40
41/// Encoding difference entry
42#[derive(Debug, Clone, PartialEq)]
43pub struct EncodingDifference {
44    /// Starting character code
45    pub code: u8,
46    /// Glyph names for consecutive character codes
47    pub names: Vec<String>,
48}
49
50/// Font flags for font descriptor (ISO 32000-1 Table 123)
51#[derive(Debug, Clone, Copy, Default)]
52pub struct FontFlags {
53    /// All glyphs have the same width
54    pub fixed_pitch: bool,
55    /// Glyphs have serifs
56    pub serif: bool,
57    /// Font uses symbolic character set
58    pub symbolic: bool,
59    /// Font is a script font
60    pub script: bool,
61    /// Font uses Adobe standard Latin character set
62    pub non_symbolic: bool,
63    /// Glyphs resemble cursive handwriting
64    pub italic: bool,
65    /// All glyphs have dominant vertical strokes
66    pub all_cap: bool,
67    /// Font is a small-cap font
68    pub small_cap: bool,
69    /// Font weight is bold or black
70    pub force_bold: bool,
71}
72
73impl FontFlags {
74    /// Convert to PDF font flags integer
75    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/// Font descriptor for custom fonts
111#[derive(Debug, Clone)]
112pub struct FontDescriptor {
113    /// Font name
114    pub font_name: String,
115    /// Font family
116    pub font_family: Option<String>,
117    /// Font stretch
118    pub font_stretch: Option<String>,
119    /// Font weight
120    pub font_weight: Option<i32>,
121    /// Font flags
122    pub flags: FontFlags,
123    /// Font bounding box [llx lly urx ury]
124    pub font_bbox: [f64; 4],
125    /// Italic angle in degrees
126    pub italic_angle: f64,
127    /// Ascent (maximum height above baseline)
128    pub ascent: f64,
129    /// Descent (maximum depth below baseline)
130    pub descent: f64,
131    /// Leading (spacing between lines)
132    pub leading: Option<f64>,
133    /// Capital height
134    pub cap_height: f64,
135    /// X-height (height of lowercase x)
136    pub x_height: Option<f64>,
137    /// Stem width
138    pub stem_v: f64,
139    /// Horizontal stem width
140    pub stem_h: Option<f64>,
141    /// Average width of glyphs
142    pub avg_width: Option<f64>,
143    /// Maximum width of glyphs
144    pub max_width: Option<f64>,
145    /// Width of missing character
146    pub missing_width: Option<f64>,
147}
148
149impl FontDescriptor {
150    /// Create a new font descriptor with required fields
151    #[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    /// Convert to PDF dictionary
184    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/// Font metrics for character widths
249#[derive(Debug, Clone)]
250pub struct FontMetrics {
251    /// First character code
252    pub first_char: u8,
253    /// Last character code
254    pub last_char: u8,
255    /// Character widths (in glyph space units)
256    pub widths: Vec<f64>,
257    /// Default width for missing characters
258    pub missing_width: f64,
259}
260
261impl FontMetrics {
262    /// Create new font metrics
263    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    /// Get width for a character
273    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/// Represents a custom font (Type 1 or TrueType)
287#[derive(Debug, Clone)]
288pub struct CustomFont {
289    /// Font name
290    pub name: String,
291    /// Font type
292    pub font_type: FontType,
293    /// Font encoding
294    pub encoding: FontEncoding,
295    /// Font descriptor
296    pub descriptor: FontDescriptor,
297    /// Font metrics
298    pub metrics: FontMetrics,
299    /// Font data (for embedding)
300    pub font_data: Option<Vec<u8>>,
301    /// Font file type for embedding
302    pub font_file_type: Option<FontFileType>,
303    /// Parsed TrueType font (for subsetting)
304    pub truetype_font: Option<TrueTypeFont>,
305    /// Used glyphs for subsetting
306    pub used_glyphs: HashSet<u16>,
307}
308
309/// Font file type for embedding
310#[derive(Debug, Clone, Copy, PartialEq)]
311pub enum FontFileType {
312    /// Type 1 font file
313    Type1,
314    /// TrueType font file
315    TrueType,
316    /// OpenType font file with CFF outlines
317    OpenTypeCFF,
318}
319
320impl CustomFont {
321    /// Create a font from byte data
322    pub fn from_bytes(name: &str, data: Vec<u8>) -> Result<Self> {
323        // Parse TrueType font
324        let ttf = TrueTypeFont::parse(data.clone()).map_err(|e| {
325            PdfError::InvalidStructure(format!("Failed to parse TrueType font: {}", e))
326        })?;
327
328        // Get font name from the font file or use provided name
329        let font_name = ttf.get_font_name().unwrap_or_else(|_| name.to_string());
330
331        // Create font descriptor from TrueType data
332        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], // Font bbox
343            0.0,                              // Italic angle
344            750.0,                            // Ascent
345            -250.0,                           // Descent
346            700.0,                            // Cap height
347            100.0,                            // Stem V
348        );
349
350        // Create metrics with proper Unicode support
351        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, // Use Type0 for Unicode support
383            encoding: FontEncoding::Identity, // Identity encoding for Unicode
384            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    /// Create a new Type 1 font
396    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    /// Create a new TrueType font
416    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    /// Optimize the font for the given text content
436    pub fn optimize_for_text(&mut self, text: &str) {
437        // Check if text contains Unicode characters beyond Latin-1
438        let needs_unicode = text.chars().any(|c| c as u32 > 255);
439
440        if needs_unicode && self.font_type != FontType::Type0 {
441            // Convert to Type0 for Unicode support
442            self.convert_to_type0();
443        }
444
445        // Mark characters as used for subsetting
446        self.mark_characters_used(text);
447    }
448
449    /// Convert font to Type0 for Unicode support
450    fn convert_to_type0(&mut self) {
451        // Only convert TrueType fonts to Type0
452        if self.font_type == FontType::TrueType {
453            self.font_type = FontType::Type0;
454            self.encoding = FontEncoding::Identity;
455
456            // Clear used glyphs as we'll need to rebuild with CIDs
457            self.used_glyphs.clear();
458        }
459    }
460
461    /// Get the glyph mapping (Unicode -> GlyphID) from the font's cmap table
462    pub fn get_glyph_mapping(&self) -> Option<HashMap<u32, u16>> {
463        if let Some(ref ttf) = self.truetype_font {
464            // Parse the cmap table to get Unicode to GlyphID mappings
465            if let Ok(cmap_tables) = ttf.parse_cmap() {
466                // Prefer Windows Unicode mapping (platform 3, encoding 1)
467                // or fallback to Unicode mapping (platform 0)
468                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    /// Load font data from file for embedding
482    pub fn load_font_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
483        let data = fs::read(path.as_ref())?;
484
485        // Detect font file type
486        let file_type = Self::detect_font_file_type(&data)?;
487        self.font_file_type = Some(file_type);
488
489        // For TrueType fonts, parse the font structure
490        if matches!(file_type, FontFileType::TrueType) {
491            match TrueTypeFont::parse(data.clone()) {
492                Ok(ttf) => {
493                    // Update font name from the font file
494                    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                    // Update metrics from the font
500                    if let Ok(cmap_tables) = ttf.parse_cmap() {
501                        // Find best cmap table
502                        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                            // Update character widths
508                            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                                        // Convert from font units to 1000ths of a unit
514                                        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                    // Continue without parsing - will embed full font
532                }
533            }
534        }
535
536        self.font_data = Some(data);
537        Ok(())
538    }
539
540    /// Load TrueType font from file
541    pub fn load_truetype_font<P: AsRef<Path>>(path: P) -> Result<Self> {
542        let data = fs::read(path.as_ref())?;
543
544        // Parse TrueType font
545        let ttf = TrueTypeFont::parse(data.clone()).map_err(|e| {
546            PdfError::InvalidStructure(format!("Failed to parse TrueType font: {}", e))
547        })?;
548
549        // Get font name
550        let font_name = ttf
551            .get_font_name()
552            .unwrap_or_else(|_| "Unknown".to_string());
553
554        // Create font descriptor from TrueType data with real metrics
555        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        // Create metrics
590        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]; // Default widths
614        }
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    /// Mark characters as used for subsetting
633    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    /// Get subset font data
652    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                // No subsetting needed if no glyphs used
660                return Ok(self.font_data.clone());
661            }
662
663            // Create subset
664            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    /// Detect font file type from data
675    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        // Check for TrueType signature
683        let signature = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
684        match signature {
685            0x00010000 | 0x74727565 => Ok(FontFileType::TrueType), // TrueType
686            0x4F54544F => Ok(FontFileType::OpenTypeCFF),           // OpenType with CFF
687            _ => {
688                // Check for Type 1 font (starts with %!PS or %!FontType1)
689                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    /// Convert to PDF font dictionary
701    pub fn to_pdf_dict(&self) -> Dictionary {
702        let mut dict = Dictionary::new();
703
704        // Font type
705        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        // Base font name
720        dict.set("BaseFont", Object::Name(self.name.clone()));
721
722        // Encoding
723        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                // Build differences array
738                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        // Font metrics
755        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        // Font descriptor reference will be added by FontManager
767
768        dict
769    }
770}
771
772/// Font manager for handling custom fonts
773#[derive(Debug, Clone)]
774pub struct FontManager {
775    /// Registered fonts by name
776    fonts: HashMap<String, CustomFont>,
777    /// Font ID counter
778    next_font_id: usize,
779}
780
781impl Default for FontManager {
782    fn default() -> Self {
783        Self::new()
784    }
785}
786
787impl FontManager {
788    /// Create a new font manager
789    pub fn new() -> Self {
790        Self {
791            fonts: HashMap::new(),
792            next_font_id: 1,
793        }
794    }
795
796    /// Register a custom font
797    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    /// Get a registered font
805    pub fn get_font(&self, name: &str) -> Option<&CustomFont> {
806        self.fonts.get(name)
807    }
808
809    /// Get the glyph mapping for a registered font
810    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    /// Get all registered fonts
819    pub fn fonts(&self) -> &HashMap<String, CustomFont> {
820        &self.fonts
821    }
822
823    /// Create font resource dictionary
824    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    /// Create standard fonts from built-in Type 1 fonts
835    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    /// Helvetica character widths (simplified subset)
888    fn helvetica_widths() -> Vec<f64> {
889        // This would contain the full width table for characters 32-255
890        // Simplified for example
891        vec![278.0; 224] // All characters same width for now
892    }
893
894    /// Times Roman character widths (simplified subset)
895    fn times_widths() -> Vec<f64> {
896        // This would contain the full width table for characters 32-255
897        // Simplified for example
898        vec![250.0; 224] // All characters same width for now
899    }
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); // fixed_pitch
922        assert!(value & (1 << 1) != 0); // serif
923        assert!(value & (1 << 6) != 0); // italic
924    }
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); // Before range
964        assert_eq!(metrics.get_width(68), 250.0); // After range
965    }
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        // TrueType signature
1046        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        // Type 1 signature
1051        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        // Invalid data
1056        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}