use crate::pdf::writer::PdfWriter;
use std::collections::HashMap;
use std::fs;
use std::io::Write;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct FontId(pub usize);
pub struct Font {
pub name: String,
pub path: String,
pub raw_data: Vec<u8>,
}
pub struct FontManager {
fonts: Vec<Font>,
name_to_id: HashMap<String, FontId>,
}
impl Default for FontManager {
fn default() -> Self {
Self::new()
}
}
impl FontManager {
pub fn new() -> Self {
Self {
fonts: Vec::new(),
name_to_id: HashMap::new(),
}
}
pub fn register_font(&mut self, name: &str, path: &str) -> std::io::Result<FontId> {
let raw_data = fs::read(path)?;
let id = FontId(self.fonts.len());
self.fonts.push(Font {
name: name.to_string(),
path: path.to_string(),
raw_data,
});
self.name_to_id.insert(name.to_string(), id);
Ok(id)
}
pub fn get_font_id(&self, name: &str) -> Option<FontId> {
self.name_to_id.get(name).copied()
}
pub fn get_font(&self, id: FontId) -> &Font {
&self.fonts[id.0]
}
pub fn embed_fonts<W: Write>(&mut self, writer: &mut PdfWriter<W>) -> std::io::Result<String> {
let mut resources = String::new();
for (i, font) in self.fonts.iter().enumerate() {
let res = crate::font::embed::embed_ttf(writer, font)?;
resources.push_str(&format!("/F{} {} 0 R ", i, res));
}
Ok(resources)
}
pub fn string_width(&self, id: FontId, text: &str, size: f64) -> f64 {
let font = self.get_font(id);
if let Ok(face) = ttf_parser::Face::parse(&font.raw_data, 0) {
let units_per_em = face.units_per_em() as f64;
let mut width = 0.0;
for c in text.chars() {
if let Some(glyph_id) = face.glyph_index(c)
&& let Some(w) = face.glyph_hor_advance(glyph_id)
{
width += w as f64;
}
}
return (width / units_per_em) * size;
}
0.0
}
pub fn line_height(&self, id: FontId, size: f64) -> f64 {
let (a, d) = self.get_ascent_descent(id, size);
a - d
}
pub fn get_ascent_descent(&self, id: FontId, size: f64) -> (f64, f64) {
let font = self.get_font(id);
if let Ok(face) = ttf_parser::Face::parse(&font.raw_data, 0) {
let units_per_em = face.units_per_em() as f64;
let ascender = face.ascender() as f64;
let descender = face.descender() as f64;
return (
(ascender / units_per_em) * size,
(descender / units_per_em) * size,
);
}
(size * 0.8, -size * 0.2)
}
pub fn encode_text(&self, id: FontId, text: &str) -> String {
let font = self.get_font(id);
let mut hex = String::with_capacity(text.len() * 4 + 2);
hex.push('<');
if let Ok(face) = ttf_parser::Face::parse(&font.raw_data, 0) {
for c in text.chars() {
let gid = face.glyph_index(c).map(|g| g.0).unwrap_or(0);
hex.push_str(&format!("{:04X}", gid));
}
} else {
for c in text.chars() {
hex.push_str(&format!("{:04X}", c as u32));
}
}
hex.push('>');
hex
}
}