Skip to main content

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::{CmapSubtable, 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    /// CFF/OpenType font
21    CFF,
22    /// Type 3 font (user-defined)
23    Type3,
24    /// Type 0 font (composite)
25    Type0,
26}
27
28/// Font encoding types
29#[derive(Debug, Clone, PartialEq)]
30pub enum FontEncoding {
31    /// Standard encoding
32    StandardEncoding,
33    /// MacRoman encoding
34    MacRomanEncoding,
35    /// WinAnsi encoding
36    WinAnsiEncoding,
37    /// Custom encoding with differences
38    Custom(Vec<EncodingDifference>),
39    /// Identity encoding for CID fonts
40    Identity,
41}
42
43/// Encoding difference entry
44#[derive(Debug, Clone, PartialEq)]
45pub struct EncodingDifference {
46    /// Starting character code
47    pub code: u8,
48    /// Glyph names for consecutive character codes
49    pub names: Vec<String>,
50}
51
52/// Font flags for font descriptor (ISO 32000-1 Table 123)
53#[derive(Debug, Clone, Copy, Default)]
54pub struct FontFlags {
55    /// All glyphs have the same width
56    pub fixed_pitch: bool,
57    /// Glyphs have serifs
58    pub serif: bool,
59    /// Font uses symbolic character set
60    pub symbolic: bool,
61    /// Font is a script font
62    pub script: bool,
63    /// Font uses Adobe standard Latin character set
64    pub non_symbolic: bool,
65    /// Glyphs resemble cursive handwriting
66    pub italic: bool,
67    /// All glyphs have dominant vertical strokes
68    pub all_cap: bool,
69    /// Font is a small-cap font
70    pub small_cap: bool,
71    /// Font weight is bold or black
72    pub force_bold: bool,
73}
74
75impl FontFlags {
76    /// Convert to PDF font flags integer
77    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/// Font descriptor for custom fonts
113#[derive(Debug, Clone)]
114pub struct FontDescriptor {
115    /// Font name
116    pub font_name: String,
117    /// Font family
118    pub font_family: Option<String>,
119    /// Font stretch
120    pub font_stretch: Option<String>,
121    /// Font weight
122    pub font_weight: Option<i32>,
123    /// Font flags
124    pub flags: FontFlags,
125    /// Font bounding box [llx lly urx ury]
126    pub font_bbox: [f64; 4],
127    /// Italic angle in degrees
128    pub italic_angle: f64,
129    /// Ascent (maximum height above baseline)
130    pub ascent: f64,
131    /// Descent (maximum depth below baseline)
132    pub descent: f64,
133    /// Leading (spacing between lines)
134    pub leading: Option<f64>,
135    /// Capital height
136    pub cap_height: f64,
137    /// X-height (height of lowercase x)
138    pub x_height: Option<f64>,
139    /// Stem width
140    pub stem_v: f64,
141    /// Horizontal stem width
142    pub stem_h: Option<f64>,
143    /// Average width of glyphs
144    pub avg_width: Option<f64>,
145    /// Maximum width of glyphs
146    pub max_width: Option<f64>,
147    /// Width of missing character
148    pub missing_width: Option<f64>,
149}
150
151impl FontDescriptor {
152    /// Create a new font descriptor with required fields
153    #[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    /// Convert to PDF dictionary
186    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/// Font metrics for character widths
251#[derive(Debug, Clone)]
252pub struct FontMetrics {
253    /// First character code
254    pub first_char: u8,
255    /// Last character code
256    pub last_char: u8,
257    /// Character widths (in glyph space units)
258    pub widths: Vec<f64>,
259    /// Default width for missing characters
260    pub missing_width: f64,
261}
262
263impl FontMetrics {
264    /// Create new font metrics
265    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    /// Get width for a character
275    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/// Represents a custom font (Type 1 or TrueType)
289#[derive(Debug, Clone)]
290pub struct CustomFont {
291    /// Font name
292    pub name: String,
293    /// Font type
294    pub font_type: FontType,
295    /// Font encoding
296    pub encoding: FontEncoding,
297    /// Font descriptor
298    pub descriptor: FontDescriptor,
299    /// Font metrics
300    pub metrics: FontMetrics,
301    /// Font data (for embedding)
302    pub font_data: Option<Vec<u8>>,
303    /// Font file type for embedding
304    pub font_file_type: Option<FontFileType>,
305    /// Parsed TrueType font (for subsetting)
306    pub truetype_font: Option<TrueTypeFont>,
307    /// Used glyphs for subsetting
308    pub used_glyphs: HashSet<u16>,
309}
310
311/// Font file type for embedding
312#[derive(Debug, Clone, Copy, PartialEq)]
313pub enum FontFileType {
314    /// Type 1 font file
315    Type1,
316    /// TrueType font file
317    TrueType,
318    /// OpenType font file with CFF outlines
319    OpenTypeCFF,
320}
321
322impl CustomFont {
323    /// Create a font from byte data
324    pub fn from_bytes(name: &str, data: Vec<u8>) -> Result<Self> {
325        // Parse TrueType font
326        let ttf = TrueTypeFont::parse(data.clone()).map_err(|e| {
327            PdfError::InvalidStructure(format!("Failed to parse TrueType font: {}", e))
328        })?;
329
330        // Get font name from the font file or use provided name
331        let font_name = ttf.get_font_name().unwrap_or_else(|_| name.to_string());
332
333        // Create font descriptor from TrueType data
334        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], // Font bbox
345            0.0,                              // Italic angle
346            750.0,                            // Ascent
347            -250.0,                           // Descent
348            700.0,                            // Cap height
349            100.0,                            // Stem V
350        );
351
352        // Create metrics with proper Unicode support (including CJK)
353        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                // Get widths for common Unicode ranges including CJK
357                let ranges = [
358                    (0x0020, 0x007F), // Basic Latin
359                    (0x00A0, 0x00FF), // Latin-1 Supplement
360                    (0x3000, 0x303F), // CJK Symbols and Punctuation
361                    (0x3040, 0x309F), // Hiragana
362                    (0x30A0, 0x30FF), // Katakana
363                    (0x4E00, 0x9FFF), // CJK Unified Ideographs (Common)
364                ];
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        // For legacy compatibility, create a simple width array for ASCII range
383        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); // Default width for CJK
387            widths.push(width);
388        }
389
390        let metrics = FontMetrics {
391            first_char: 32,
392            last_char: 255,
393            widths,
394            missing_width: 500.0, // Larger default for CJK characters
395        };
396
397        let font = Self {
398            name: name.to_string(),
399            font_type: FontType::Type0, // Use Type0 for Unicode support
400            encoding: FontEncoding::Identity, // Identity encoding for Unicode
401            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    /// Create a new Type 1 font
413    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    /// Create a new TrueType font
433    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    /// Create a new CFF/OpenType font
453    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    /// Optimize the font for the given text content
473    pub fn optimize_for_text(&mut self, text: &str) {
474        // Check if text contains Unicode characters beyond Latin-1
475        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            // Convert to Type0 for Unicode support (CFF fonts already support Unicode)
479            self.convert_to_type0();
480        }
481
482        // Mark characters as used for subsetting
483        self.mark_characters_used(text);
484    }
485
486    /// Convert font to Type0 for Unicode support
487    fn convert_to_type0(&mut self) {
488        // Convert TrueType fonts to Type0, CFF fonts already use Type0 semantics
489        if self.font_type == FontType::TrueType {
490            self.font_type = FontType::Type0;
491            self.encoding = FontEncoding::Identity;
492
493            // Clear used glyphs as we'll need to rebuild with CIDs
494            self.used_glyphs.clear();
495        }
496    }
497
498    /// Get the glyph mapping (Unicode -> GlyphID) from the font's cmap table
499    pub fn get_glyph_mapping(&self) -> Option<HashMap<u32, u16>> {
500        if let Some(ref ttf) = self.truetype_font {
501            // Parse the cmap table to get Unicode to GlyphID mappings
502            if let Ok(cmap_tables) = ttf.parse_cmap() {
503                // Prefer Windows Unicode mapping (platform 3, encoding 1)
504                // or fallback to Unicode mapping (platform 0)
505                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    /// Load font data from file for embedding
514    pub fn load_font_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
515        let data = fs::read(path.as_ref())?;
516
517        // Detect font file type
518        let file_type = Self::detect_font_file_type(&data)?;
519        self.font_file_type = Some(file_type);
520
521        // For TrueType fonts, parse the font structure
522        if matches!(file_type, FontFileType::TrueType) {
523            match TrueTypeFont::parse(data.clone()) {
524                Ok(ttf) => {
525                    // Update font name from the font file
526                    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                    // Update metrics from the font
532                    if let Ok(cmap_tables) = ttf.parse_cmap() {
533                        // Find best cmap table (prefer Format 12 for CJK)
534                        if let Some(cmap) = CmapSubtable::select_best_or_first(&cmap_tables) {
535                            // Update character widths
536                            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                                        // Convert from font units to 1000ths of a unit
542                                        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                    // Continue without parsing - will embed full font
560                }
561            }
562        }
563
564        self.font_data = Some(data);
565        Ok(())
566    }
567
568    /// Load TrueType font from file
569    pub fn load_truetype_font<P: AsRef<Path>>(path: P) -> Result<Self> {
570        let data = fs::read(path.as_ref())?;
571
572        // Parse TrueType font
573        let ttf = TrueTypeFont::parse(data.clone()).map_err(|e| {
574            PdfError::InvalidStructure(format!("Failed to parse TrueType font: {}", e))
575        })?;
576
577        // Get font name
578        let font_name = ttf
579            .get_font_name()
580            .unwrap_or_else(|_| "Unknown".to_string());
581
582        // Create font descriptor from TrueType data with real metrics
583        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        // Create metrics
618        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]; // Default widths
638        }
639
640        let metrics = FontMetrics::new(32, 255, widths, 250.0);
641
642        // Check if font is CFF/OpenType
643        let mut font = if ttf.is_cff {
644            // CFF fonts use Identity encoding and Type0 for Unicode support
645            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            // Standard TrueType font
650            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    /// Mark characters as used for subsetting
667    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    /// Get subset font data
682    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                // No subsetting needed if no glyphs used
690                return Ok(self.font_data.clone());
691            }
692
693            // Create subset
694            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    /// Detect font file type from data
705    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        // Check for TrueType signature
713        let signature = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
714        match signature {
715            0x00010000 | 0x74727565 => Ok(FontFileType::TrueType), // TrueType
716            0x4F54544F => Ok(FontFileType::OpenTypeCFF),           // OpenType with CFF
717            _ => {
718                // Check for Type 1 font (starts with %!PS or %!FontType1)
719                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    /// Convert to PDF font dictionary
731    pub fn to_pdf_dict(&self) -> Dictionary {
732        let mut dict = Dictionary::new();
733
734        // Font type
735        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", // CFF fonts use Type0 for Unicode support
743                    FontType::Type3 => "Type3",
744                    FontType::Type0 => "Type0",
745                }
746                .to_string(),
747            ),
748        );
749
750        // Base font name
751        dict.set("BaseFont", Object::Name(self.name.clone()));
752
753        // Encoding
754        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                // Build differences array
769                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        // Font metrics
786        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        // Font descriptor reference will be added by FontManager
798
799        dict
800    }
801}
802
803/// Font manager for handling custom fonts
804#[derive(Debug, Clone)]
805pub struct FontManager {
806    /// Registered fonts by name
807    fonts: HashMap<String, CustomFont>,
808    /// Font ID counter
809    next_font_id: usize,
810}
811
812impl Default for FontManager {
813    fn default() -> Self {
814        Self::new()
815    }
816}
817
818impl FontManager {
819    /// Create a new font manager
820    pub fn new() -> Self {
821        Self {
822            fonts: HashMap::new(),
823            next_font_id: 1,
824        }
825    }
826
827    /// Register a custom font
828    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    /// Get a registered font
836    pub fn get_font(&self, name: &str) -> Option<&CustomFont> {
837        self.fonts.get(name)
838    }
839
840    /// Get the glyph mapping for a registered font
841    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    /// Get all registered fonts
850    pub fn fonts(&self) -> &HashMap<String, CustomFont> {
851        &self.fonts
852    }
853
854    /// Create font resource dictionary
855    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    /// Create standard fonts from built-in Type 1 fonts
866    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    /// Helvetica character widths (simplified subset)
919    fn helvetica_widths() -> Vec<f64> {
920        // This would contain the full width table for characters 32-255
921        // Simplified for example
922        vec![278.0; 224] // All characters same width for now
923    }
924
925    /// Times Roman character widths (simplified subset)
926    fn times_widths() -> Vec<f64> {
927        // This would contain the full width table for characters 32-255
928        // Simplified for example
929        vec![250.0; 224] // All characters same width for now
930    }
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); // fixed_pitch
953        assert!(value & (1 << 1) != 0); // serif
954        assert!(value & (1 << 6) != 0); // italic
955    }
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); // Before range
995        assert_eq!(metrics.get_width(68), 250.0); // After range
996    }
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        // TrueType signature
1077        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        // Type 1 signature
1082        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        // Invalid data
1087        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}