//! PDF font module
//!
//! Handles PDF font objects, encoding, metrics, and glyph mapping.

use crate::fitz::error::{Error, Result};
use crate::pdf::object::{Dict, Name, Object};
use std::collections::HashMap;

/// Maximum recursion depth when parsing descendant fonts to prevent stack overflow.
const DESCENDANT_FONT_MAX_DEPTH: usize = 32;

/// Fallback glyph width (in PDF units) when no width is available.
/// Standard PDF fonts typically use 1000 units per em; 1000.0 is a common default.
const FALLBACK_GLYPH_WIDTH: f64 = 1000.0;

/// Extract numeric value from PDF object, supporting both Int and Real types.
fn get_number(obj: &Object) -> Option<f64> {
    obj.as_int().map(|i| i as f64).or_else(|| obj.as_real())
}

// ============================================================================
// Font Descriptor Flags
// ============================================================================

pub const FONT_FLAG_FIXED_PITCH: u32 = 1 << 0;
pub const FONT_FLAG_SERIF: u32 = 1 << 1;
pub const FONT_FLAG_SYMBOLIC: u32 = 1 << 2;
pub const FONT_FLAG_SCRIPT: u32 = 1 << 3;
pub const FONT_FLAG_NONSYMBOLIC: u32 = 1 << 5;
pub const FONT_FLAG_ITALIC: u32 = 1 << 6;
pub const FONT_FLAG_ALL_CAP: u32 = 1 << 16;
pub const FONT_FLAG_SMALL_CAP: u32 = 1 << 17;
pub const FONT_FLAG_FORCE_BOLD: u32 = 1 << 18;

// ============================================================================
// Standard 14 Fonts
// ============================================================================

pub const STANDARD_14_FONTS: [&str; 14] = [
    "Courier",
    "Courier-Bold",
    "Courier-Oblique",
    "Courier-BoldOblique",
    "Helvetica",
    "Helvetica-Bold",
    "Helvetica-Oblique",
    "Helvetica-BoldOblique",
    "Times-Roman",
    "Times-Bold",
    "Times-Italic",
    "Times-BoldItalic",
    "Symbol",
    "ZapfDingbats",
];

// ============================================================================
// FontType
// ============================================================================

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FontType {
    Type1,
    TrueType,
    Type3,
    Type0,
    CIDFontType0,
    CIDFontType2,
    MMType1,
    OpenType,
}

impl FontType {
    pub fn from_subtype(s: &str) -> Self {
        match s {
            "Type1" => Self::Type1,
            "TrueType" => Self::TrueType,
            "Type3" => Self::Type3,
            "Type0" => Self::Type0,
            "CIDFontType0" => Self::CIDFontType0,
            "CIDFontType2" => Self::CIDFontType2,
            "MMType1" => Self::MMType1,
            "OpenType" => Self::OpenType,
            _ => Self::Type1,
        }
    }
}

// ============================================================================
// Encoding
// ============================================================================

#[derive(Debug, Clone)]
pub enum Encoding {
    StandardEncoding,
    MacRomanEncoding,
    WinAnsiEncoding,
    MacExpertEncoding,
    IdentityH,
    IdentityV,
    Custom {
        base: Box<Encoding>,
        differences: HashMap<u8, String>,
    },
}

impl Encoding {
    pub fn from_pdf_object(obj: &Object) -> Result<Self> {
        match obj {
            Object::Name(n) => {
                let s = n.as_str();
                Ok(match s {
                    "StandardEncoding" => Self::StandardEncoding,
                    "MacRomanEncoding" => Self::MacRomanEncoding,
                    "WinAnsiEncoding" => Self::WinAnsiEncoding,
                    "MacExpertEncoding" => Self::MacExpertEncoding,
                    "Identity-H" => Self::IdentityH,
                    "Identity-V" => Self::IdentityV,
                    _ => return Err(Error::font(format!("Unknown encoding: {}", s))),
                })
            }
            Object::Dict(d) => {
                let base_enc = d
                    .get(&Name::new("BaseEncoding"))
                    .and_then(|o| o.as_name())
                    .map(|n| n.as_str());
                let base = match base_enc {
                    Some("StandardEncoding") => Self::StandardEncoding,
                    Some("MacRomanEncoding") => Self::MacRomanEncoding,
                    Some("WinAnsiEncoding") => Self::WinAnsiEncoding,
                    Some("MacExpertEncoding") => Self::MacExpertEncoding,
                    _ => Self::StandardEncoding,
                };
                let mut differences = HashMap::new();
                if let Some(diff_obj) = d.get(&Name::new("Differences")) {
                    if let Some(arr) = diff_obj.as_array() {
                        let mut current_code: Option<u8> = None;
                        for item in arr {
                            if let Some(i) = item.as_int() {
                                if (0..=255).contains(&i) {
                                    current_code = Some(i as u8);
                                }
                                // Skip out-of-range values; do not update current_code
                            } else if let Some(n) = item.as_name() {
                                if let Some(code) = current_code {
                                    differences.insert(code, n.as_str().to_string());
                                    current_code = code.checked_add(1);
                                }
                            }
                        }
                    }
                }
                Ok(Self::Custom {
                    base: Box::new(base),
                    differences,
                })
            }
            _ => Err(Error::font("Encoding must be name or dict".to_string())),
        }
    }

    pub fn decode_char(&self, code: u8) -> Option<char> {
        match self {
            Self::StandardEncoding => decode_standard_encoding(code),
            Self::MacRomanEncoding => decode_mac_roman_encoding(code),
            Self::WinAnsiEncoding => decode_win_ansi_encoding(code),
            Self::MacExpertEncoding => decode_mac_expert_encoding(code),
            Self::IdentityH | Self::IdentityV => None, // CID encodings
            Self::Custom { base, differences } => {
                if let Some(name) = differences.get(&code) {
                    glyph_name_to_char(name)
                } else {
                    base.decode_char(code)
                }
            }
        }
    }
}

fn glyph_name_to_char(name: &str) -> Option<char> {
    // Common glyph name to Unicode mappings (Adobe glyph list / PDF spec)
    match name {
        "space" => Some(' '),
        "exclam" => Some('!'),
        "quotedbl" => Some('"'),
        "numbersign" => Some('#'),
        "dollar" => Some('$'),
        "percent" => Some('%'),
        "ampersand" => Some('&'),
        "quotesingle" => Some('\''),
        "quoteleft" => Some('\u{2018}'), // LEFT SINGLE QUOTATION MARK
        "quoteright" => Some('\u{2019}'), // RIGHT SINGLE QUOTATION MARK
        "quotedblleft" => Some('\u{201C}'), // LEFT DOUBLE QUOTATION MARK
        "quotedblright" => Some('\u{201D}'), // RIGHT DOUBLE QUOTATION MARK
        "parenleft" => Some('('),
        "parenright" => Some(')'),
        "asterisk" => Some('*'),
        "plus" => Some('+'),
        "comma" => Some(','),
        "hyphen" | "minus" => Some('-'),
        "endash" => Some('\u{2013}'), // EN DASH
        "emdash" => Some('\u{2014}'), // EM DASH
        "period" => Some('.'),
        "slash" => Some('/'),
        "zero" => Some('0'),
        "one" => Some('1'),
        "two" => Some('2'),
        "three" => Some('3'),
        "four" => Some('4'),
        "five" => Some('5'),
        "six" => Some('6'),
        "seven" => Some('7'),
        "eight" => Some('8'),
        "nine" => Some('9'),
        "colon" => Some(':'),
        "semicolon" => Some(';'),
        "less" => Some('<'),
        "equal" => Some('='),
        "greater" => Some('>'),
        "question" => Some('?'),
        "at" => Some('@'),
        "bullet" => Some('\u{2022}'),
        "ellipsis" => Some('\u{2026}'),
        "trademark" => Some('\u{2122}'),
        "copyright" => Some('\u{00A9}'),
        "registered" => Some('\u{00AE}'),
        "degree" => Some('\u{00B0}'),
        "plusminus" => Some('\u{00B1}'),
        "multiply" => Some('\u{00D7}'),
        "divide" => Some('\u{00F7}'),
        "fi" => Some('\u{FB01}'), // LATIN SMALL LIGATURE FI
        "fl" => Some('\u{FB02}'), // LATIN SMALL LIGATURE FL
        "A" => Some('A'),
        "B" => Some('B'),
        "C" => Some('C'),
        "D" => Some('D'),
        "E" => Some('E'),
        "F" => Some('F'),
        "G" => Some('G'),
        "H" => Some('H'),
        "I" => Some('I'),
        "J" => Some('J'),
        "K" => Some('K'),
        "L" => Some('L'),
        "M" => Some('M'),
        "N" => Some('N'),
        "O" => Some('O'),
        "P" => Some('P'),
        "Q" => Some('Q'),
        "R" => Some('R'),
        "S" => Some('S'),
        "T" => Some('T'),
        "U" => Some('U'),
        "V" => Some('V'),
        "W" => Some('W'),
        "X" => Some('X'),
        "Y" => Some('Y'),
        "Z" => Some('Z'),
        "a" => Some('a'),
        "b" => Some('b'),
        "c" => Some('c'),
        "d" => Some('d'),
        "e" => Some('e'),
        "f" => Some('f'),
        "g" => Some('g'),
        "h" => Some('h'),
        "i" => Some('i'),
        "j" => Some('j'),
        "k" => Some('k'),
        "l" => Some('l'),
        "m" => Some('m'),
        "n" => Some('n'),
        "o" => Some('o'),
        "p" => Some('p'),
        "q" => Some('q'),
        "r" => Some('r'),
        "s" => Some('s'),
        "t" => Some('t'),
        "u" => Some('u'),
        "v" => Some('v'),
        "w" => Some('w'),
        "x" => Some('x'),
        "y" => Some('y'),
        "z" => Some('z'),
        // Accented Latin (Adobe glyph list)
        "Agrave" => Some('\u{00C0}'),
        "Aacute" => Some('\u{00C1}'),
        "Acircumflex" => Some('\u{00C2}'),
        "Atilde" => Some('\u{00C3}'),
        "Adieresis" => Some('\u{00C4}'),
        "Aring" => Some('\u{00C5}'),
        "AE" => Some('\u{00C6}'),
        "Ccedilla" => Some('\u{00C7}'),
        "Egrave" => Some('\u{00C8}'),
        "Eacute" => Some('\u{00C9}'),
        "Ecircumflex" => Some('\u{00CA}'),
        "Edieresis" => Some('\u{00CB}'),
        "Igrave" => Some('\u{00CC}'),
        "Iacute" => Some('\u{00CD}'),
        "Icircumflex" => Some('\u{00CE}'),
        "Idieresis" => Some('\u{00CF}'),
        "Eth" => Some('\u{00D0}'),
        "Ntilde" => Some('\u{00D1}'),
        "Ograve" => Some('\u{00D2}'),
        "Oacute" => Some('\u{00D3}'),
        "Ocircumflex" => Some('\u{00D4}'),
        "Otilde" => Some('\u{00D5}'),
        "Odieresis" => Some('\u{00D6}'),
        "Oslash" => Some('\u{00D8}'),
        "Ugrave" => Some('\u{00D9}'),
        "Uacute" => Some('\u{00DA}'),
        "Ucircumflex" => Some('\u{00DB}'),
        "Udieresis" => Some('\u{00DC}'),
        "Yacute" => Some('\u{00DD}'),
        "Thorn" => Some('\u{00DE}'),
        "germandbls" => Some('\u{00DF}'),
        "agrave" => Some('\u{00E0}'),
        "aacute" => Some('\u{00E1}'),
        "acircumflex" => Some('\u{00E2}'),
        "atilde" => Some('\u{00E3}'),
        "adieresis" => Some('\u{00E4}'),
        "aring" => Some('\u{00E5}'),
        "ae" => Some('\u{00E6}'),
        "ccedilla" => Some('\u{00E7}'),
        "egrave" => Some('\u{00E8}'),
        "eacute" => Some('\u{00E9}'),
        "ecircumflex" => Some('\u{00EA}'),
        "edieresis" => Some('\u{00EB}'),
        "igrave" => Some('\u{00EC}'),
        "iacute" => Some('\u{00ED}'),
        "icircumflex" => Some('\u{00EE}'),
        "idieresis" => Some('\u{00EF}'),
        "eth" => Some('\u{00F0}'),
        "ntilde" => Some('\u{00F1}'),
        "ograve" => Some('\u{00F2}'),
        "oacute" => Some('\u{00F3}'),
        "ocircumflex" => Some('\u{00F4}'),
        "otilde" => Some('\u{00F5}'),
        "odieresis" => Some('\u{00F6}'),
        "oslash" => Some('\u{00F8}'),
        "ugrave" => Some('\u{00F9}'),
        "uacute" => Some('\u{00FA}'),
        "ucircumflex" => Some('\u{00FB}'),
        "udieresis" => Some('\u{00FC}'),
        "yacute" => Some('\u{00FD}'),
        "thorn" => Some('\u{00FE}'),
        "ydieresis" => Some('\u{00FF}'),
        _ => None,
    }
}

fn decode_standard_encoding(code: u8) -> Option<char> {
    // Adobe Standard Encoding - from Unicode stdenc.txt
    match code {
        0x00..=0x1F => None,
        0x20 => Some(' '),
        0x21 => Some('!'),
        0x22 => Some('"'),
        0x23 => Some('#'),
        0x24 => Some('$'),
        0x25 => Some('%'),
        0x26 => Some('&'),
        0x27 => Some('\u{2019}'), // RIGHT SINGLE QUOTATION MARK
        0x28 => Some('('),
        0x29 => Some(')'),
        0x2A => Some('*'),
        0x2B => Some('+'),
        0x2C => Some(','),
        0x2D => Some('-'),
        0x2E => Some('.'),
        0x2F => Some('/'),
        0x30..=0x39 => Some(char::from(b'0' + (code - 0x30))),
        0x3A => Some(':'),
        0x3B => Some(';'),
        0x3C => Some('<'),
        0x3D => Some('='),
        0x3E => Some('>'),
        0x3F => Some('?'),
        0x40 => Some('@'),
        0x41..=0x5A => Some(char::from(b'A' + (code - 0x41))),
        0x5B => Some('['),
        0x5C => Some('\\'),
        0x5D => Some(']'),
        0x5E => Some('^'),
        0x5F => Some('_'),
        0x60 => Some('\u{2018}'), // LEFT SINGLE QUOTATION MARK
        0x61..=0x7A => Some(char::from(b'a' + (code - 0x61))),
        0x7B => Some('{'),
        0x7C => Some('|'),
        0x7D => Some('}'),
        0x7E => Some('~'),
        0xA1 => Some('\u{00A1}'),
        0xA2 => Some('\u{00A2}'),
        0xA3 => Some('\u{00A3}'),
        0xA4 => Some('\u{2044}'),
        0xA5 => Some('\u{00A5}'),
        0xA6 => Some('\u{0192}'),
        0xA7 => Some('\u{00A7}'),
        0xA8 => Some('\u{00A4}'),
        0xA9 => Some('\''),
        0xAA => Some('\u{201C}'),
        0xAB => Some('\u{00AB}'),
        0xAC => Some('\u{2039}'),
        0xAD => Some('\u{203A}'),
        0xAE => Some('\u{FB01}'),
        0xAF => Some('\u{FB02}'),
        0xB1 => Some('\u{2013}'),
        0xB2 => Some('\u{2020}'),
        0xB3 => Some('\u{2021}'),
        0xB4 => Some('\u{00B7}'),
        0xB6 => Some('\u{00B6}'),
        0xB7 => Some('\u{2022}'),
        0xB8 => Some('\u{201A}'),
        0xB9 => Some('\u{201E}'),
        0xBA => Some('\u{201D}'),
        0xBB => Some('\u{00BB}'),
        0xBC => Some('\u{2026}'),
        0xBD => Some('\u{2030}'),
        0xBF => Some('\u{00BF}'),
        0xC1 => Some('`'),
        0xC2 => Some('\u{00B4}'),
        0xC3 => Some('\u{02C6}'),
        0xC4 => Some('\u{02DC}'),
        0xC5 => Some('\u{00AF}'),
        0xC6 => Some('\u{02D8}'),
        0xC7 => Some('\u{02D9}'),
        0xC8 => Some('\u{00A8}'),
        0xCA => Some('\u{02DA}'),
        0xCB => Some('\u{00B8}'),
        0xCD => Some('\u{02DD}'),
        0xCE => Some('\u{02DB}'),
        0xCF => Some('\u{02C7}'),
        0xD0 => Some('\u{2014}'),
        0xE1 => Some('\u{00C6}'),
        0xE3 => Some('\u{00AA}'),
        0xE8 => Some('\u{0141}'),
        0xE9 => Some('\u{00D8}'),
        0xEA => Some('\u{0152}'),
        0xEB => Some('\u{00BA}'),
        0xF1 => Some('\u{00E6}'),
        0xF5 => Some('\u{0131}'),
        0xF8 => Some('\u{0142}'),
        0xF9 => Some('\u{00F8}'),
        0xFA => Some('\u{0153}'),
        0xFB => Some('\u{00DF}'),
        _ => None,
    }
}

fn decode_win_ansi_encoding(code: u8) -> Option<char> {
    // WinAnsiEncoding = Windows-1252
    if code < 0x80 {
        return Some(code as char);
    }
    match code {
        0x80 => Some('\u{20AC}'),
        0x82 => Some('\u{201A}'),
        0x83 => Some('\u{0192}'),
        0x84 => Some('\u{201E}'),
        0x85 => Some('\u{2026}'),
        0x86 => Some('\u{2020}'),
        0x87 => Some('\u{2021}'),
        0x88 => Some('\u{02C6}'),
        0x89 => Some('\u{2030}'),
        0x8A => Some('\u{0160}'),
        0x8B => Some('\u{2039}'),
        0x8C => Some('\u{0152}'),
        0x8E => Some('\u{017D}'),
        0x91 => Some('\u{2018}'),
        0x92 => Some('\u{2019}'),
        0x93 => Some('\u{201C}'),
        0x94 => Some('\u{201D}'),
        0x95 => Some('\u{2022}'),
        0x96 => Some('\u{2013}'),
        0x97 => Some('\u{2014}'),
        0x98 => Some('\u{02DC}'),
        0x99 => Some('\u{2122}'),
        0x9A => Some('\u{0161}'),
        0x9B => Some('\u{203A}'),
        0x9C => Some('\u{0153}'),
        0x9E => Some('\u{017E}'),
        0x9F => Some('\u{0178}'),
        0xA0 => Some('\u{00A0}'),
        0xA1..=0xFF => char::from_u32(code as u32),
        _ => None,
    }
}

/// MacRoman (Mac OS Roman) to Unicode mapping for codes 0x80-0xFF.
/// From Unicode ROMAN.TXT (Apple mapping).
const MAC_ROMAN_HIGH: [u32; 128] = [
    0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1, // 0x80-0x87
    0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8, // 0x88-0x8F
    0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3, // 0x90-0x97
    0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC, // 0x98-0x9F
    0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF, // 0xA0-0xA7
    0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8, // 0xA8-0xAF
    0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211, // 0xB0-0xB7
    0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8, // 0xB8-0xBF
    0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB, // 0xC0-0xC7
    0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153, // 0xC8-0xCF
    0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA, // 0xD0-0xD7
    0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02, // 0xD8-0xDF
    0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1, // 0xE0-0xE7
    0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4, // 0xE8-0xEF
    0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC, // 0xF0-0xF7
    0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7, // 0xF8-0xFF
];

fn decode_mac_roman_encoding(code: u8) -> Option<char> {
    if code < 0x80 {
        return Some(code as char);
    }
    let idx = (code as usize).saturating_sub(0x80);
    MAC_ROMAN_HIGH.get(idx).and_then(|&cp| char::from_u32(cp))
}

/// MacExpertEncoding decodes typographic variants.
/// TODO: MacExpertEncoding uses different glyph names (e.g. "a.smcp" for small caps);
/// full support would require a dedicated mapping. Currently falls back to StandardEncoding.
fn decode_mac_expert_encoding(code: u8) -> Option<char> {
    decode_standard_encoding(code)
}

// ============================================================================
// FontDescriptor
// ============================================================================

#[derive(Debug, Clone)]
pub struct FontDescriptor {
    pub font_name: String,
    pub flags: u32,
    pub font_bbox: [f64; 4],
    pub italic_angle: f64,
    pub ascent: f64,
    pub descent: f64,
    pub cap_height: f64,
    pub stem_v: f64,
    pub missing_width: f64,
    pub font_file: Option<Vec<u8>>,
}

impl FontDescriptor {
    pub fn from_pdf_dict(dict: &Dict) -> Result<Self> {
        let font_name = dict
            .get(&Name::new("FontName"))
            .and_then(|o| o.as_name())
            .map(|n| n.as_str().to_string())
            .unwrap_or_default();

        let flags = dict
            .get(&Name::new("Flags"))
            .and_then(|o| o.as_int())
            .unwrap_or(0) as u32;

        let font_bbox = dict
            .get(&Name::new("FontBBox"))
            .and_then(|o| o.as_array())
            .map(|a| {
                [
                    a.get(0).and_then(get_number).unwrap_or(0.0),
                    a.get(1).and_then(get_number).unwrap_or(0.0),
                    a.get(2).and_then(get_number).unwrap_or(0.0),
                    a.get(3).and_then(get_number).unwrap_or(0.0),
                ]
            })
            .unwrap_or([0.0, 0.0, 0.0, 0.0]);

        let italic_angle = dict
            .get(&Name::new("ItalicAngle"))
            .and_then(get_number)
            .unwrap_or(0.0);

        let ascent = dict
            .get(&Name::new("Ascent"))
            .and_then(get_number)
            .unwrap_or(0.0);

        let descent = dict
            .get(&Name::new("Descent"))
            .and_then(get_number)
            .unwrap_or(0.0);

        let cap_height = dict
            .get(&Name::new("CapHeight"))
            .and_then(get_number)
            .unwrap_or(0.0);

        let stem_v = dict
            .get(&Name::new("StemV"))
            .and_then(get_number)
            .unwrap_or(0.0);

        let missing_width = dict
            .get(&Name::new("MissingWidth"))
            .and_then(get_number)
            .unwrap_or(0.0);

        // FontFile/FontFile2/FontFile3 are usually indirect Object::Ref references.
        // We only extract data from inline Object::Stream; unresolved refs yield None.
        let font_file = dict
            .get(&Name::new("FontFile"))
            .or_else(|| dict.get(&Name::new("FontFile2")))
            .or_else(|| dict.get(&Name::new("FontFile3")))
            .and_then(|o| {
                match o {
                    Object::Stream { data, .. } => Some(data.clone()),
                    Object::Ref(_) => {
                        // Indirect reference: cannot resolve without document context
                        None
                    }
                    _ => None,
                }
            });

        Ok(Self {
            font_name,
            flags,
            font_bbox,
            italic_angle,
            ascent,
            descent,
            cap_height,
            stem_v,
            missing_width,
            font_file,
        })
    }
}

// ============================================================================
// PdfFont
// ============================================================================

#[derive(Debug, Clone)]
pub struct PdfFont {
    font_type: FontType,
    base_font: String,
    encoding: Encoding,
    descriptor: Option<FontDescriptor>,
    widths: Vec<f64>,
    first_char: u32,
    last_char: u32,
    default_width: f64,
    to_unicode: Option<String>,
    descendant_fonts: Vec<PdfFont>,
    is_standard_14: bool,
}

impl PdfFont {
    pub fn from_pdf_dict(dict: &Dict) -> Result<Self> {
        Self::from_pdf_dict_with_depth(dict, 0)
    }

    fn from_pdf_dict_with_depth(dict: &Dict, depth: usize) -> Result<Self> {
        if depth >= DESCENDANT_FONT_MAX_DEPTH {
            return Err(Error::font(
                "Descendant font recursion depth limit exceeded".to_string(),
            ));
        }

        let type_name = dict
            .get(&Name::new("Type"))
            .and_then(|o| o.as_name())
            .map(|n| n.as_str());
        if type_name != Some("Font") {
            return Err(Error::font("Font dict must have /Type Font".to_string()));
        }

        let subtype = dict
            .get(&Name::new("Subtype"))
            .and_then(|o| o.as_name())
            .map(|n| n.as_str())
            .unwrap_or("Type1");
        let font_type = FontType::from_subtype(subtype);

        let base_font = dict
            .get(&Name::new("BaseFont"))
            .and_then(|o| o.as_name())
            .map(|n| n.as_str().to_string())
            .unwrap_or_default();

        let encoding = dict
            .get(&Name::new("Encoding"))
            .map(|o| Encoding::from_pdf_object(o))
            .transpose()?
            .unwrap_or(Encoding::StandardEncoding);

        let descriptor = dict
            .get(&Name::new("FontDescriptor"))
            .and_then(|o| o.as_dict())
            .map(|d| FontDescriptor::from_pdf_dict(d))
            .transpose()?;

        let first_char = dict
            .get(&Name::new("FirstChar"))
            .and_then(get_number)
            .map(|n| n.clamp(0.0, 255.0) as u32)
            .unwrap_or(0);

        let last_char = dict
            .get(&Name::new("LastChar"))
            .and_then(get_number)
            .map(|n| n.clamp(0.0, 255.0) as u32)
            .unwrap_or(255);

        let widths = dict
            .get(&Name::new("Widths"))
            .and_then(|o| o.as_array())
            .map(|a| a.iter().filter_map(get_number).collect::<Vec<f64>>())
            .unwrap_or_default();

        let default_width = descriptor
            .as_ref()
            .map(|d| d.missing_width)
            .filter(|&w| w > 0.0)
            .unwrap_or(
                if font_type == FontType::CIDFontType0 || font_type == FontType::CIDFontType2 {
                    FALLBACK_GLYPH_WIDTH
                } else {
                    0.0
                },
            );

        // ToUnicode is typically a stream (CMap); we also handle Object::Ref and Object::Name.
        let to_unicode = dict.get(&Name::new("ToUnicode")).and_then(|o| {
            match o {
                Object::Stream { data, .. } => String::from_utf8(data.clone()).ok(),
                Object::Name(n) => Some(n.as_str().to_string()),
                Object::Ref(_) => {
                    // Indirect reference: cannot resolve without document context
                    None
                }
                _ => None,
            }
        });

        let descendant_fonts = dict
            .get(&Name::new("DescendantFonts"))
            .and_then(|o| o.as_array())
            .map(|a| {
                a.iter()
                    .filter_map(|o| {
                        o.as_dict()
                            .and_then(|d| PdfFont::from_pdf_dict_with_depth(d, depth + 1).ok())
                    })
                    .collect::<Vec<PdfFont>>()
            })
            .unwrap_or_default();

        let is_standard_14 = Self::is_standard_14(&base_font);

        Ok(Self {
            font_type,
            base_font,
            encoding,
            descriptor,
            widths,
            first_char,
            last_char,
            default_width,
            to_unicode,
            descendant_fonts,
            is_standard_14,
        })
    }

    pub fn glyph_width(&self, char_code: u32) -> f64 {
        // 1. Custom widths from font dictionary (FirstChar/LastChar/Widths) take priority
        if char_code >= self.first_char && char_code <= self.last_char {
            let idx = (char_code - self.first_char) as usize;
            if let Some(&w) = self.widths.get(idx) {
                return w;
            }
        }

        // 2. Standard 14 fonts: try built-in metrics when no custom width
        if self.is_standard_14 {
            if let Some(widths) = standard_14_widths(&self.base_font) {
                let idx = char_code as usize;
                if let Some(&w) = widths.get(idx) {
                    return w;
                }
            }
        }

        // 3. default_width, descriptor.missing_width, or FALLBACK_GLYPH_WIDTH
        if self.default_width > 0.0 {
            return self.default_width;
        }

        self.descriptor
            .as_ref()
            .map(|d| d.missing_width)
            .filter(|&w| w > 0.0)
            .unwrap_or(FALLBACK_GLYPH_WIDTH)
    }

    pub fn is_standard_14(name: &str) -> bool {
        STANDARD_14_FONTS.contains(&name)
    }

    pub fn font_type(&self) -> &FontType {
        &self.font_type
    }

    pub fn base_font(&self) -> &str {
        &self.base_font
    }

    pub fn encoding(&self) -> &Encoding {
        &self.encoding
    }

    pub fn descendant_fonts(&self) -> &[PdfFont] {
        &self.descendant_fonts
    }
}

// ============================================================================
// Standard 14 Widths
// ============================================================================

pub fn standard_14_widths(font_name: &str) -> Option<Vec<f64>> {
    let widths: Vec<f64> = match font_name {
        "Courier" | "Courier-Bold" | "Courier-Oblique" | "Courier-BoldOblique" => {
            (0..256).map(|_| 600.0).collect()
        }
        "Helvetica" | "Helvetica-Bold" | "Helvetica-Oblique" | "Helvetica-BoldOblique" => {
            (0..256).map(|i| helvetica_width(i as u8)).collect()
        }
        "Times-Roman" | "Times-Bold" | "Times-Italic" | "Times-BoldItalic" => {
            (0..256).map(|i| times_width(i as u8)).collect()
        }
        "Symbol" | "ZapfDingbats" => (0..256).map(|_| 500.0).collect(),
        _ => return None,
    };
    Some(widths)
}

/// Helvetica glyph widths (PDF units) for ASCII 32-127. From Adobe AFM.
/// Default 556 for codes outside 32-127.
const HELVETICA_WIDTHS: [f64; 96] = [
    278.0, 278.0, 355.0, 556.0, 556.0, 889.0, 667.0, 222.0, // 32-39 space!"#$%&'
    333.0, 333.0, 389.0, 584.0, 278.0, 333.0, 278.0, 278.0, // 40-47 ()*+,-./
    556.0, 556.0, 556.0, 556.0, 556.0, 556.0, 556.0, 556.0, // 48-55 0-7
    556.0, 556.0, 278.0, 278.0, 584.0, 584.0, 584.0, 556.0, // 56-63 89:;<=>?
    1015.0, 667.0, 667.0, 722.0, 722.0, 611.0, 556.0, 778.0, // 64-71 @ABCDEFG
    722.0, 278.0, 500.0, 667.0, 556.0, 833.0, 722.0, 778.0, // 72-79 HIJKLMNO
    667.0, 778.0, 722.0, 667.0, 611.0, 722.0, 667.0, 944.0, // 80-87 PQRSTUVW
    667.0, 667.0, 611.0, 278.0, 278.0, 278.0, 469.0, 556.0, // 88-95 XYZ[\]^_
    222.0, 556.0, 556.0, 500.0, 556.0, 556.0, 278.0, 556.0, // 96-103 `abcdefg
    556.0, 222.0, 222.0, 500.0, 222.0, 833.0, 556.0, 556.0, // 104-111 hijklmno
    556.0, 556.0, 333.0, 500.0, 278.0, 556.0, 500.0, 722.0, // 112-119 pqrstuvw
    500.0, 500.0, 500.0, 334.0, 260.0, 334.0, 584.0, 556.0, // 120-127 xyz{|}~
];

fn helvetica_width(code: u8) -> f64 {
    if (32..=127).contains(&code) {
        HELVETICA_WIDTHS
            .get((code as usize).saturating_sub(32))
            .copied()
            .unwrap_or(556.0)
    } else {
        556.0
    }
}

/// Times-Roman glyph widths (PDF units) for ASCII 32-127. From Adobe AFM.
/// Default 500 for codes outside 32-127.
const TIMES_WIDTHS: [f64; 96] = [
    250.0, 333.0, 408.0, 500.0, 500.0, 833.0, 778.0, 333.0, // 32-39 space!"#$%&'
    333.0, 333.0, 500.0, 564.0, 250.0, 333.0, 250.0, 278.0, // 40-47 ()*+,-./
    500.0, 500.0, 500.0, 500.0, 500.0, 500.0, 500.0, 500.0, // 48-55 0-7
    500.0, 500.0, 278.0, 278.0, 564.0, 564.0, 564.0, 444.0, // 56-63 89:;<=>?
    921.0, 722.0, 667.0, 667.0, 722.0, 611.0, 556.0, 722.0, // 64-71 @ABCDEFG
    722.0, 333.0, 389.0, 722.0, 611.0, 889.0, 722.0, 722.0, // 72-79 HIJKLMNO
    556.0, 722.0, 667.0, 556.0, 611.0, 722.0, 722.0, 944.0, // 80-87 PQRSTUVW
    722.0, 722.0, 611.0, 333.0, 333.0, 333.0, 469.0, 500.0, // 88-95 XYZ[\]^_
    333.0, 444.0, 500.0, 444.0, 500.0, 444.0, 333.0, 500.0, // 96-103 `abcdefg
    500.0, 278.0, 278.0, 500.0, 278.0, 778.0, 500.0, 500.0, // 104-111 hijklmno
    500.0, 500.0, 333.0, 389.0, 278.0, 500.0, 500.0, 722.0, // 112-119 pqrstuvw
    500.0, 500.0, 444.0, 480.0, 200.0, 480.0, 541.0, 500.0, // 120-127 xyz{|}~
];

fn times_width(code: u8) -> f64 {
    if (32..=127).contains(&code) {
        TIMES_WIDTHS
            .get((code as usize).saturating_sub(32))
            .copied()
            .unwrap_or(500.0)
    } else {
        500.0
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::pdf::object::{Name, Object};

    fn make_dict(entries: Vec<(&str, Object)>) -> Dict {
        entries
            .into_iter()
            .map(|(k, v)| (Name::new(k), v))
            .collect()
    }

    #[test]
    fn test_font_type_parse_type1() {
        let dict = make_dict(vec![
            ("Type", Object::Name(Name::new("Font"))),
            ("Subtype", Object::Name(Name::new("Type1"))),
            ("BaseFont", Object::Name(Name::new("Helvetica"))),
        ]);
        let font = PdfFont::from_pdf_dict(&dict).unwrap();
        assert_eq!(font.font_type(), &FontType::Type1);
        assert_eq!(font.base_font(), "Helvetica");
    }

    #[test]
    fn test_font_type_parse_truetype() {
        let dict = make_dict(vec![
            ("Type", Object::Name(Name::new("Font"))),
            ("Subtype", Object::Name(Name::new("TrueType"))),
            ("BaseFont", Object::Name(Name::new("Arial"))),
        ]);
        let font = PdfFont::from_pdf_dict(&dict).unwrap();
        assert_eq!(font.font_type(), &FontType::TrueType);
    }

    #[test]
    fn test_font_type_parse_type0() {
        let mut desc_dict = Dict::new();
        desc_dict.insert(Name::new("Type"), Object::Name(Name::new("Font")));
        desc_dict.insert(
            Name::new("Subtype"),
            Object::Name(Name::new("CIDFontType0")),
        );
        desc_dict.insert(
            Name::new("BaseFont"),
            Object::Name(Name::new("KozMin-Regular")),
        );

        let dict = make_dict(vec![
            ("Type", Object::Name(Name::new("Font"))),
            ("Subtype", Object::Name(Name::new("Type0"))),
            ("BaseFont", Object::Name(Name::new("KozMin-Regular"))),
            (
                "DescendantFonts",
                Object::Array(vec![Object::Dict(desc_dict)]),
            ),
        ]);
        let font = PdfFont::from_pdf_dict(&dict).unwrap();
        assert_eq!(font.font_type(), &FontType::Type0);
        assert_eq!(font.descendant_fonts().len(), 1);
    }

    #[test]
    fn test_standard_14_detection() {
        assert!(PdfFont::is_standard_14("Helvetica"));
        assert!(PdfFont::is_standard_14("Times-Roman"));
        assert!(PdfFont::is_standard_14("Courier"));
        assert!(PdfFont::is_standard_14("Symbol"));
        assert!(PdfFont::is_standard_14("ZapfDingbats"));
        assert!(!PdfFont::is_standard_14("Arial"));
        assert!(!PdfFont::is_standard_14("CustomFont"));
    }

    #[test]
    fn test_glyph_width_in_range() {
        let dict = make_dict(vec![
            ("Type", Object::Name(Name::new("Font"))),
            ("Subtype", Object::Name(Name::new("Type1"))),
            ("BaseFont", Object::Name(Name::new("CustomFont"))),
            ("FirstChar", Object::Int(32)),
            ("LastChar", Object::Int(34)),
            (
                "Widths",
                Object::Array(vec![
                    Object::Real(278.0),
                    Object::Real(500.0),
                    Object::Real(500.0),
                ]),
            ),
        ]);
        let font = PdfFont::from_pdf_dict(&dict).unwrap();
        assert_eq!(font.glyph_width(32), 278.0);
        assert_eq!(font.glyph_width(33), 500.0);
        assert_eq!(font.glyph_width(34), 500.0);
    }

    #[test]
    fn test_glyph_width_out_of_range() {
        let dict = make_dict(vec![
            ("Type", Object::Name(Name::new("Font"))),
            ("Subtype", Object::Name(Name::new("Type1"))),
            ("BaseFont", Object::Name(Name::new("CustomFont"))),
            ("FirstChar", Object::Int(0)),
            ("LastChar", Object::Int(2)),
            (
                "Widths",
                Object::Array(vec![
                    Object::Real(100.0),
                    Object::Real(200.0),
                    Object::Real(300.0),
                ]),
            ),
        ]);
        let font = PdfFont::from_pdf_dict(&dict).unwrap();
        assert_eq!(font.glyph_width(5), 1000.0);
    }

    #[test]
    fn test_glyph_width_standard_14() {
        let dict = make_dict(vec![
            ("Type", Object::Name(Name::new("Font"))),
            ("Subtype", Object::Name(Name::new("Type1"))),
            ("BaseFont", Object::Name(Name::new("Courier"))),
        ]);
        let font = PdfFont::from_pdf_dict(&dict).unwrap();
        assert!(font.is_standard_14);
        assert_eq!(font.glyph_width(65), 600.0);
    }

    #[test]
    fn test_glyph_width_custom_overrides_standard_14() {
        // Helvetica 'A' (65) is normally 667 in standard_14; custom Widths must override
        let dict = make_dict(vec![
            ("Type", Object::Name(Name::new("Font"))),
            ("Subtype", Object::Name(Name::new("Type1"))),
            ("BaseFont", Object::Name(Name::new("Helvetica"))),
            ("FirstChar", Object::Int(65)),
            ("LastChar", Object::Int(65)),
            ("Widths", Object::Array(vec![Object::Real(800.0)])),
        ]);
        let font = PdfFont::from_pdf_dict(&dict).unwrap();
        assert!(font.is_standard_14);
        assert_eq!(
            font.glyph_width(65),
            800.0,
            "custom width must override standard_14"
        );
        assert_eq!(
            font.glyph_width(66),
            667.0,
            "no custom for 66, use standard_14"
        );
    }

    #[test]
    fn test_glyph_width_no_panic_out_of_bounds() {
        let dict = make_dict(vec![
            ("Type", Object::Name(Name::new("Font"))),
            ("Subtype", Object::Name(Name::new("Type1"))),
            ("BaseFont", Object::Name(Name::new("Courier"))),
        ]);
        let font = PdfFont::from_pdf_dict(&dict).unwrap();
        // char_code far beyond widths array (256 elements) must not panic
        let w = font.glyph_width(99999);
        assert!(w > 0.0, "should return fallback width");
    }

    #[test]
    fn test_encoding_parse_named() {
        let enc = Encoding::from_pdf_object(&Object::Name(Name::new("WinAnsiEncoding"))).unwrap();
        assert!(matches!(enc, Encoding::WinAnsiEncoding));

        let enc = Encoding::from_pdf_object(&Object::Name(Name::new("Identity-H"))).unwrap();
        assert!(matches!(enc, Encoding::IdentityH));
    }

    #[test]
    fn test_encoding_parse_custom() {
        let mut enc_dict = Dict::new();
        enc_dict.insert(
            Name::new("BaseEncoding"),
            Object::Name(Name::new("WinAnsiEncoding")),
        );
        enc_dict.insert(
            Name::new("Differences"),
            Object::Array(vec![
                Object::Int(65),
                Object::Name(Name::new("Agrave")),
                Object::Name(Name::new("Aacute")),
            ]),
        );
        let enc = Encoding::from_pdf_object(&Object::Dict(enc_dict)).unwrap();
        if let Encoding::Custom { base, differences } = enc {
            assert!(matches!(*base, Encoding::WinAnsiEncoding));
            assert_eq!(differences.len(), 2);
        } else {
            panic!("Expected Custom encoding");
        }
    }

    #[test]
    fn test_win_ansi_decode() {
        let enc = Encoding::WinAnsiEncoding;
        assert_eq!(enc.decode_char(b'A'), Some('A'));
        assert_eq!(enc.decode_char(b'Z'), Some('Z'));
        assert_eq!(enc.decode_char(b'a'), Some('a'));
        assert_eq!(enc.decode_char(b'z'), Some('z'));
        assert_eq!(enc.decode_char(b'0'), Some('0'));
        assert_eq!(enc.decode_char(b'9'), Some('9'));
        assert_eq!(enc.decode_char(b' '), Some(' '));
        assert_eq!(enc.decode_char(0x80), Some('\u{20AC}'));
    }

    #[test]
    fn test_standard_encoding_quotes() {
        let enc = Encoding::StandardEncoding;
        // 0x27 (39) = quoteright -> U+2019 RIGHT SINGLE QUOTATION MARK
        assert_eq!(enc.decode_char(0x27), Some('\u{2019}'));
        // 0x60 (96) = quoteleft -> U+2018 LEFT SINGLE QUOTATION MARK
        assert_eq!(enc.decode_char(0x60), Some('\u{2018}'));
    }

    #[test]
    fn test_glyph_name_custom_encoding() {
        let mut enc_dict = Dict::new();
        enc_dict.insert(
            Name::new("BaseEncoding"),
            Object::Name(Name::new("StandardEncoding")),
        );
        enc_dict.insert(
            Name::new("Differences"),
            Object::Array(vec![
                Object::Int(200),
                Object::Name(Name::new("fi")),
                Object::Name(Name::new("fl")),
                Object::Name(Name::new("endash")),
                Object::Name(Name::new("emdash")),
                Object::Name(Name::new("bullet")),
                Object::Name(Name::new("ellipsis")),
                Object::Name(Name::new("quotedblleft")),
                Object::Name(Name::new("quotedblright")),
                Object::Name(Name::new("quoteleft")),
                Object::Name(Name::new("quoteright")),
                Object::Name(Name::new("trademark")),
                Object::Name(Name::new("copyright")),
                Object::Name(Name::new("registered")),
                Object::Name(Name::new("degree")),
                Object::Name(Name::new("Agrave")),
                Object::Name(Name::new("ydieresis")),
            ]),
        );
        let enc = Encoding::from_pdf_object(&Object::Dict(enc_dict)).unwrap();
        if let Encoding::Custom { differences, .. } = &enc {
            assert_eq!(differences.len(), 16); // codes 200-215
        } else {
            panic!("Expected Custom encoding");
        }
        // Verify glyph names decode correctly via Custom encoding
        assert_eq!(enc.decode_char(200), Some('\u{FB01}')); // fi
        assert_eq!(enc.decode_char(201), Some('\u{FB02}')); // fl
        assert_eq!(enc.decode_char(202), Some('\u{2013}')); // endash
        assert_eq!(enc.decode_char(203), Some('\u{2014}')); // emdash
        assert_eq!(enc.decode_char(204), Some('\u{2022}')); // bullet
        assert_eq!(enc.decode_char(205), Some('\u{2026}')); // ellipsis
        assert_eq!(enc.decode_char(206), Some('\u{201C}')); // quotedblleft
        assert_eq!(enc.decode_char(207), Some('\u{201D}')); // quotedblright
        assert_eq!(enc.decode_char(208), Some('\u{2018}')); // quoteleft
        assert_eq!(enc.decode_char(209), Some('\u{2019}')); // quoteright
        assert_eq!(enc.decode_char(210), Some('\u{2122}')); // trademark
        assert_eq!(enc.decode_char(211), Some('\u{00A9}')); // copyright
        assert_eq!(enc.decode_char(212), Some('\u{00AE}')); // registered
        assert_eq!(enc.decode_char(213), Some('\u{00B0}')); // degree
        assert_eq!(enc.decode_char(214), Some('\u{00C0}')); // Agrave
        assert_eq!(enc.decode_char(215), Some('\u{00FF}')); // ydieresis
    }

    #[test]
    fn test_font_descriptor_parse() {
        let mut fd_dict = Dict::new();
        fd_dict.insert(
            Name::new("FontName"),
            Object::Name(Name::new("Helvetica-Bold")),
        );
        fd_dict.insert(Name::new("Flags"), Object::Int(4));
        fd_dict.insert(
            Name::new("FontBBox"),
            Object::Array(vec![
                Object::Real(-170.0),
                Object::Real(-228.0),
                Object::Real(1003.0),
                Object::Real(962.0),
            ]),
        );
        fd_dict.insert(Name::new("ItalicAngle"), Object::Real(0.0));
        fd_dict.insert(Name::new("Ascent"), Object::Real(718.0));
        fd_dict.insert(Name::new("Descent"), Object::Real(-207.0));
        fd_dict.insert(Name::new("CapHeight"), Object::Real(718.0));
        fd_dict.insert(Name::new("StemV"), Object::Real(140.0));
        fd_dict.insert(Name::new("MissingWidth"), Object::Real(500.0));

        let fd = FontDescriptor::from_pdf_dict(&fd_dict).unwrap();
        assert_eq!(fd.font_name, "Helvetica-Bold");
        assert_eq!(fd.flags, 4);
        assert_eq!(fd.font_bbox[0], -170.0);
        assert_eq!(fd.ascent, 718.0);
        assert_eq!(fd.missing_width, 500.0);
    }

    #[test]
    fn test_font_flags_constants() {
        assert_eq!(FONT_FLAG_FIXED_PITCH, 1);
        assert_eq!(FONT_FLAG_SERIF, 2);
        assert_eq!(FONT_FLAG_SYMBOLIC, 4);
        assert_eq!(FONT_FLAG_ITALIC, 64);
    }

    #[test]
    fn test_empty_font_dict() {
        let dict = make_dict(vec![("Type", Object::Name(Name::new("Font")))]);
        let font = PdfFont::from_pdf_dict(&dict).unwrap();
        assert_eq!(font.base_font(), "");
        assert_eq!(font.first_char, 0);
        assert_eq!(font.last_char, 255);
    }

    #[test]
    fn test_font_dict_missing_type() {
        let dict = make_dict(vec![
            ("Subtype", Object::Name(Name::new("Type1"))),
            ("BaseFont", Object::Name(Name::new("Helvetica"))),
        ]);
        assert!(PdfFont::from_pdf_dict(&dict).is_err());
    }

    #[test]
    fn test_standard_14_widths() {
        let widths = standard_14_widths("Courier").unwrap();
        assert_eq!(widths.len(), 256);
        assert!(widths.iter().all(|&w| (w - 600.0).abs() < 0.001));

        let widths = standard_14_widths("Helvetica").unwrap();
        assert_eq!(widths.len(), 256);
    }

    #[test]
    fn test_type0_descendant_fonts() {
        let mut cid_dict = Dict::new();
        cid_dict.insert(Name::new("Type"), Object::Name(Name::new("Font")));
        cid_dict.insert(
            Name::new("Subtype"),
            Object::Name(Name::new("CIDFontType2")),
        );
        cid_dict.insert(
            Name::new("BaseFont"),
            Object::Name(Name::new("Arial-UnicodeMS")),
        );

        let dict = make_dict(vec![
            ("Type", Object::Name(Name::new("Font"))),
            ("Subtype", Object::Name(Name::new("Type0"))),
            (
                "BaseFont",
                Object::Name(Name::new("Arial-UnicodeMS-Identity-H")),
            ),
            (
                "DescendantFonts",
                Object::Array(vec![Object::Dict(cid_dict)]),
            ),
        ]);
        let font = PdfFont::from_pdf_dict(&dict).unwrap();
        assert_eq!(font.font_type(), &FontType::Type0);
        assert_eq!(font.descendant_fonts().len(), 1);
        assert_eq!(
            font.descendant_fonts()[0].font_type(),
            &FontType::CIDFontType2
        );
    }
}
