1use crate::CacheKey;
2use crate::font::cid::Type0Font;
3use crate::font::true_type::TrueTypeFont;
4use crate::font::type1::Type1Font;
5use kurbo::BezPath;
6use pdf_font::OutlineBuilder;
7use pdf_font::cmap::BfString;
8use skrifa::GlyphId;
9use skrifa::outline::OutlinePen;
10use std::rc::Rc;
11
12#[derive(Clone)]
14pub struct OutlineFontData {
15 pub data: crate::font::FontData,
17 pub cache_key: u128,
19 pub postscript_name: Option<String>,
21 pub weight: Option<u32>,
23 pub is_italic: bool,
25 pub is_serif: bool,
27 pub is_monospace: bool,
29 pub ascent: Option<f64>,
31 pub descent: Option<f64>,
33 pub cap_height: Option<f64>,
35 pub x_height: Option<f64>,
37}
38
39pub(crate) struct OutlinePath(BezPath);
40
41impl OutlinePath {
42 pub(crate) fn new() -> Self {
43 Self(BezPath::new())
44 }
45
46 pub(crate) fn take(self) -> BezPath {
47 self.0
48 }
49}
50
51impl OutlinePen for OutlinePath {
52 #[inline]
53 fn move_to(&mut self, x: f32, y: f32) {
54 self.0.move_to((x, y));
55 }
56
57 #[inline]
58 fn line_to(&mut self, x: f32, y: f32) {
59 if !self.0.elements().is_empty() {
60 self.0.line_to((x, y));
61 }
62 }
63
64 #[inline]
65 fn quad_to(&mut self, cx: f32, cy: f32, x: f32, y: f32) {
66 if !self.0.elements().is_empty() {
67 self.0.quad_to((cx, cy), (x, y));
68 }
69 }
70
71 #[inline]
72 fn curve_to(&mut self, cx0: f32, cy0: f32, cx1: f32, cy1: f32, x: f32, y: f32) {
73 if !self.0.elements().is_empty() {
74 self.0.curve_to((cx0, cy0), (cx1, cy1), (x, y));
75 }
76 }
77
78 #[inline]
79 fn close(&mut self) {
80 if !self.0.elements().is_empty() {
81 self.0.close_path();
82 }
83 }
84}
85
86impl OutlineBuilder for OutlinePath {
87 fn move_to(&mut self, x: f32, y: f32) {
88 self.0.move_to((x, y));
89 }
90
91 fn line_to(&mut self, x: f32, y: f32) {
92 if !self.0.elements().is_empty() {
93 self.0.line_to((x, y));
94 }
95 }
96
97 fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) {
98 if !self.0.elements().is_empty() {
99 self.0.quad_to((x1, y1), (x, y));
100 }
101 }
102
103 fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) {
104 if !self.0.elements().is_empty() {
105 self.0.curve_to((x1, y1), (x2, y2), (x, y));
106 }
107 }
108
109 fn close(&mut self) {
110 if !self.0.elements().is_empty() {
111 self.0.close_path();
112 }
113 }
114}
115
116#[derive(Debug, Clone)]
117pub(crate) enum OutlineFont {
118 Type1(Rc<Type1Font>),
119 TrueType(Rc<TrueTypeFont>),
120 Type0(Rc<Type0Font>),
121}
122
123impl CacheKey for OutlineFont {
124 fn cache_key(&self) -> u128 {
125 match self {
126 Self::Type1(f) => f.cache_key(),
127 Self::TrueType(t) => t.cache_key(),
128 Self::Type0(t0) => t0.cache_key(),
129 }
130 }
131}
132
133impl OutlineFont {
134 pub(crate) fn outline_glyph(&self, glyph: GlyphId, code: u32) -> BezPath {
135 match self {
136 Self::Type1(t) => t.outline_glyph(glyph),
137 Self::TrueType(t) => t.outline_glyph(glyph),
138 Self::Type0(t) => t.outline_glyph(glyph, code),
139 }
140 }
141
142 pub(crate) fn char_code_to_unicode(&self, char_code: u32) -> Option<BfString> {
143 match self {
144 Self::Type1(t) => t.char_code_to_unicode(char_code),
145 Self::TrueType(t) => t.char_code_to_unicode(char_code),
146 Self::Type0(t) => t.char_code_to_unicode(char_code),
147 }
148 }
149
150 pub(crate) fn glyph_advance_width(&self, char_code: u32) -> Option<f32> {
152 match self {
153 Self::Type1(t) => t.glyph_width(char_code as u8),
154 Self::TrueType(t) => Some(t.glyph_width(char_code as u8)),
155 Self::Type0(t) => Some(t.code_advance(char_code).x as f32),
156 }
157 }
158
159 pub(crate) fn postscript_name(&self) -> Option<String> {
171 match self {
172 Self::Type1(t) => t.postscript_name().map(|s| s.to_string()),
173 Self::TrueType(t) => t.postscript_name().map(|s| s.to_string()),
174 Self::Type0(t) => t.postscript_name().map(|s| s.to_string()),
175 }
176 }
177
178 pub(crate) fn font_data(&self) -> Option<OutlineFontData> {
182 match self {
183 Self::Type1(_) => None,
184 Self::TrueType(t) => {
185 let m = t.font_metrics();
186 Some(OutlineFontData {
187 data: t.font_data()?,
188 cache_key: t.cache_key(),
189 postscript_name: t.postscript_name().map(|s| s.to_string()),
190 weight: t.weight(),
191 is_italic: t.is_italic(),
192 is_serif: t.is_serif(),
193 is_monospace: t.is_monospace(),
194 ascent: m.map(|x| x.0),
195 descent: m.map(|x| x.1),
196 cap_height: m.and_then(|x| x.2),
197 x_height: m.and_then(|x| x.3),
198 })
199 }
200 Self::Type0(t) => {
201 let m = t.font_metrics();
202 Some(OutlineFontData {
203 data: t.font_data()?,
204 cache_key: t.cache_key(),
205 postscript_name: t.postscript_name().map(|s| s.to_string()),
206 weight: t.weight(),
207 is_italic: t.is_italic(),
208 is_serif: t.is_serif(),
209 is_monospace: t.is_monospace(),
210 ascent: m.map(|x| x.0),
211 descent: m.map(|x| x.1),
212 cap_height: m.and_then(|x| x.2),
213 x_height: m.and_then(|x| x.3),
214 })
215 }
216 }
217 }
218
219 pub(crate) fn font_metrics(&self) -> Option<(f64, f64, Option<f64>, Option<f64>)> {
222 match self {
223 Self::Type1(t) => t.font_metrics(),
224 Self::TrueType(t) => t.font_metrics(),
225 Self::Type0(t) => t.font_metrics(),
226 }
227 }
228}