use std::fmt;
use std::sync::Arc;
use otf_pdf_doc::Store;
use otf_pdf_syntax::{Dict, Name, ObjId, Object};
use crate::matrix::Matrix;
pub trait Font: Send + Sync + fmt::Debug {
fn codes(&self, bytes: &[u8]) -> Vec<(u32, usize)>;
fn width(&self, code: u32) -> f64;
fn is_vertical(&self) -> bool {
false
}
fn vertical_advance(&self, _code: u32) -> f64 {
-1.0
}
fn unicode(&self, code: u32) -> Option<String>;
fn name(&self) -> &str;
}
pub trait FontLoader {
fn load(&self, store: &Store, font: &Object) -> Option<Arc<dyn Font>>;
}
#[derive(Debug, Clone)]
pub struct TextState {
pub font: Option<Arc<dyn Font>>,
pub size: f64,
pub char_spacing: f64,
pub word_spacing: f64,
pub horizontal_scaling: f64,
pub leading: f64,
pub rise: f64,
pub render_mode: i64,
}
impl Default for TextState {
fn default() -> Self {
Self {
font: None,
size: 0.0,
char_spacing: 0.0,
word_spacing: 0.0,
horizontal_scaling: 1.0,
leading: 0.0,
rise: 0.0,
render_mode: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct GraphicsState {
pub ctm: Matrix,
pub line_width: f64,
pub text: TextState,
pub clip: Option<[f64; 4]>,
}
impl GraphicsState {
pub fn clip_to(&mut self, points: impl IntoIterator<Item = (f64, f64)>) {
let mut b = [f64::MAX, f64::MAX, f64::MIN, f64::MIN];
for (x, y) in points {
let (px, py) = self.ctm.apply(x, y);
b = [b[0].min(px), b[1].min(py), b[2].max(px), b[3].max(py)];
}
if b[0] > b[2] {
return;
}
self.clip = Some(match self.clip {
Some(c) => [
c[0].max(b[0]),
c[1].max(b[1]),
c[2].min(b[2]),
c[3].min(b[3]),
],
None => b,
});
}
pub fn in_clip(&self, (x, y): (f64, f64), slack: f64) -> bool {
self.clip.is_none_or(|c| {
x >= c[0] - slack && x <= c[2] + slack && y >= c[1] - slack && y <= c[3] + slack
})
}
}
impl Default for GraphicsState {
fn default() -> Self {
Self {
ctm: Matrix::IDENTITY,
line_width: 1.0,
text: TextState::default(),
clip: None,
}
}
}
#[derive(Debug, Clone)]
pub struct Glyph {
pub code: u32,
pub unicode: Option<String>,
pub font: Arc<dyn Font>,
pub trm: Matrix,
pub origin: (f64, f64),
pub end: (f64, f64),
pub size: f64,
pub render_mode: i64,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PathSegment {
MoveTo(f64, f64),
LineTo(f64, f64),
CurveTo(f64, f64, f64, f64, f64, f64),
Close,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FillRule {
NonZero,
EvenOdd,
}
#[derive(Debug, Clone)]
pub struct Image {
pub id: Option<ObjId>,
pub dict: Dict,
pub inline_data: Vec<u8>,
}
#[allow(unused_variables)]
pub trait Device {
fn fill_path(&mut self, path: &[PathSegment], rule: FillRule, state: &GraphicsState) {}
fn stroke_path(&mut self, path: &[PathSegment], state: &GraphicsState) {}
fn clip(&mut self, path: &[PathSegment], rule: FillRule, state: &GraphicsState) {}
fn draw_glyph(&mut self, glyph: &Glyph, state: &GraphicsState) {}
fn draw_image(&mut self, image: &Image, state: &GraphicsState) {}
fn begin_marked_content(&mut self, tag: &Name, properties: Option<&Dict>) {}
fn end_marked_content(&mut self) {}
fn begin_group(&mut self, form: Option<ObjId>, dict: &Dict, state: &GraphicsState) {}
fn end_group(&mut self) {}
}