nipdf_cff_parser/
lib.rs

1//! Decode Adobe CFF font files.
2//!
3//! The entry type is `File`, use `File::open()` method
4//! to parses a CFF file and returns `File`
5//!
6//! File is a struct that contains all the data in a CFF file.
7//! derived types borrows data hold by File.
8//!
9//! File::fonts() returns a iterator of `Font` which is a struct
10//! that provides info for that font, such as encoding, charset, etc.
11use log::error;
12use prescript::Encoding;
13use std::borrow::Cow;
14
15mod inner;
16
17pub use inner::{Error, Result};
18
19pub struct Font<'a> {
20    font_data: &'a [u8],
21    name: Cow<'a, str>,
22    top_dict_data: inner::TopDictData<'a>,
23}
24
25impl<'a> Font<'a> {
26    pub fn new(
27        font_data: &'a [u8],
28        name: Cow<'a, str>,
29        top_dict_data: inner::TopDictData<'a>,
30    ) -> Self {
31        Self {
32            font_data,
33            name,
34            top_dict_data,
35        }
36    }
37
38    pub fn name(&self) -> &str {
39        &self.name
40    }
41
42    pub fn encodings(&self) -> Result<Encoding> {
43        let charsets = self.top_dict_data.charsets(self.font_data)?;
44        let (encodings, supplements) = self.top_dict_data.encodings(self.font_data)?;
45        let mut r = encodings.build(&charsets, self.top_dict_data.string_index())?;
46        if let Some(supplements) = supplements {
47            for supp in supplements {
48                supp.apply(self.top_dict_data.string_index(), &mut r)?;
49            }
50        }
51
52        Ok(r)
53    }
54}
55
56/// Iterator of Font.
57pub struct Fonts<'a> {
58    data: &'a [u8],
59    names_index: inner::NameIndex<'a>,
60    top_dict_index: inner::TopDictIndex<'a>,
61    string_index: inner::StringIndex<'a>,
62    idx: usize,
63}
64
65impl<'a> Fonts<'a> {
66    pub fn new(f: &File<'a>) -> Result<Self> {
67        let names_offset = f.header.hdr_size as usize;
68        let buf = &f.data[names_offset..];
69        let (names_index, top_dict_index, string_index) = inner::parse_fonts(buf)?;
70        Ok(Self {
71            data: f.data,
72            names_index,
73            top_dict_index,
74            string_index,
75            idx: 0,
76        })
77    }
78}
79
80impl<'a> Iterator for Fonts<'a> {
81    type Item = Font<'a>;
82
83    fn next(&mut self) -> Option<Self::Item> {
84        if self.idx < self.names_index.len() {
85            let name = self.names_index.get(self.idx).unwrap_or_else(|e| {
86                error!("Error getting name: {}", e);
87                None
88            });
89            let top_dict_data = self.top_dict_index.get(self.idx, self.string_index).ok()?;
90            self.idx += 1;
91            match name {
92                Some(name) => Some(Font::new(self.data, name, top_dict_data)),
93                None => self.next(),
94            }
95        } else {
96            None
97        }
98    }
99}
100
101pub struct File<'a> {
102    header: inner::Header,
103    data: &'a [u8],
104}
105
106impl<'a> File<'a> {
107    pub fn open(data: &'a [u8]) -> Result<Self> {
108        let header = inner::parse_header(data)?;
109        Ok(File { data, header })
110    }
111
112    pub fn iter(&self) -> Result<Fonts<'a>> {
113        Fonts::new(self)
114    }
115
116    pub fn major_version(&self) -> u8 {
117        self.header.major
118    }
119
120    pub fn minor_version(&self) -> u8 {
121        self.header.minor
122    }
123}
124
125#[cfg(test)]
126mod tests;