1use base64::{engine::general_purpose, Engine as _};
2use bin_rs::reader::{BinaryReader, BytesReader};
3use std::collections::HashMap;
4#[cfg(debug_assertions)]
5use std::fs::File;
6use std::io::{Error, ErrorKind, SeekFrom};
7use std::path::PathBuf;
8
9#[cfg(feature = "svg-fonts")]
10use crate::commands::SvgGlyphLayer;
11use crate::commands::{
12 Command as DrawCommand, FontMetrics as DrawFontMetrics, Glyph, GlyphBounds, GlyphFlow,
13 GlyphLayer, GlyphMetrics as DrawGlyphMetrics, GlyphPaint, GlyphRun, PathGlyphLayer,
14 PositionedGlyph, RasterGlyphLayer,
15};
16use crate::fontheader;
17use crate::opentype::color::sbix;
18use crate::opentype::color::svg;
19use crate::opentype::color::{colr, cpal};
20#[cfg(feature = "layout")]
21use crate::opentype::extentions::gdef;
22#[cfg(feature = "layout")]
23use crate::opentype::extentions::gpos;
24#[cfg(feature = "layout")]
25use crate::opentype::extentions::gsub;
26use crate::opentype::outline::glyf::ParsedGlyph;
27use crate::opentype::platforms::PlatformID;
28use crate::opentype::requires::cmap::CmapEncodings;
29use crate::opentype::requires::hhea::HHEA;
30use crate::opentype::requires::hmtx::LongHorMetric;
31use crate::opentype::requires::name::NameID;
32use crate::opentype::requires::vhea::VHEA;
33use crate::opentype::requires::vmtx::VerticalMetric;
34use crate::opentype::requires::*;
35use crate::opentype::{outline::*, OTFHeader};
36use crate::util::sniff_encoded_image_dimensions;
37
38#[cfg(debug_assertions)]
39use std::io::{BufWriter, Write};
40
41#[cfg(feature = "svg-fonts")]
42pub(crate) fn svg_document_to_glyph_layers(
43 document: &svg::SvgGlyphDocument,
44 scale_x: f32,
45 scale_y: f32,
46) -> Vec<GlyphLayer> {
47 let path_layers = crate::svgparse::svg_to_path_layers(&document.payload, scale_x, scale_y);
48 let keep_svg_fallback = crate::svgparse::svg_requires_svg_fallback(&document.payload);
49 if !path_layers.is_empty() {
50 let mut layers: Vec<GlyphLayer> = path_layers.into_iter().map(GlyphLayer::Path).collect();
51 if keep_svg_fallback {
52 layers.push(GlyphLayer::Svg(SvgGlyphLayer {
53 document: document.payload.clone(),
54 view_box_min_x: document.view_box_min_x * scale_x,
55 view_box_min_y: document.view_box_min_y * scale_y,
56 view_box_width: document.view_box_width * scale_x,
57 view_box_height: document.view_box_height * scale_y,
58 width: (document.view_box_width * scale_x).abs().max(1.0),
59 height: (document.view_box_height * scale_y).abs().max(1.0),
60 offset_x: 0.0,
61 offset_y: 0.0,
62 }));
63 }
64 return layers;
65 }
66
67 vec![GlyphLayer::Svg(SvgGlyphLayer {
68 document: document.payload.clone(),
69 view_box_min_x: document.view_box_min_x * scale_x,
70 view_box_min_y: document.view_box_min_y * scale_y,
71 view_box_width: document.view_box_width * scale_x,
72 view_box_height: document.view_box_height * scale_y,
73 width: (document.view_box_width * scale_x).abs().max(1.0),
74 height: (document.view_box_height * scale_y).abs().max(1.0),
75 offset_x: 0.0,
76 offset_y: 0.0,
77 })]
78}
79
80#[derive(Debug, Clone)]
81pub enum PathCommand {
82 MoveTo { x: f64, y: f64 },
83 LineTo { x: f64, y: f64 },
84 QuadTo { cx: f64, cy: f64, x: f64, y: f64 },
85 ClosePath,
86}
87
88#[derive(Debug, Clone)]
89pub enum BitmapGlyphFormat {
90 Png,
91 Jpeg,
92}
93
94#[derive(Debug, Clone)]
95pub struct BitmapGlyphCommands {
96 pub offset_x: f64,
97 pub offset_y: f64,
98 pub width: f64,
99 pub height: f64,
100 pub format: BitmapGlyphFormat,
101 pub data: Vec<u8>,
102}
103
104#[derive(Debug, Clone)]
105pub struct GlyphCommands {
106 pub ch: char,
107 pub glyph_id: usize,
108 pub origin_x: f64,
109 pub origin_y: f64,
110 pub advance_width: f64,
111 pub commands: Vec<PathCommand>,
112 pub bitmap: Option<BitmapGlyphCommands>,
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116pub enum GlyphFormat {
117 OpenTypeGlyph,
118 CFF,
119 CFF2,
120 SVG,
121 Bitmap,
122 Unknown,
123}
124
125#[derive(Debug, Clone)]
126
127pub enum FontLayout {
128 Horizontal(HorizontalLayout),
129 Vertical(VerticalLayout),
130 Unknown,
131}
132
133#[derive(Debug, Clone)]
134pub struct GriphData {
135 glyph_id: usize,
136 pub(crate) open_type_glyf: Option<OpenTypeGlyph>,
137}
138
139#[derive(Debug, Clone)]
140pub struct OpenTypeGlyph {
141 layout: FontLayout,
142 glyph: FontData,
143 variation_coords: Vec<f32>,
144}
145
146#[derive(Debug, Clone)]
147pub enum FontData {
148 Glyph(glyf::Glyph),
149 ParsedGlyph(glyf::ParsedGlyph),
150 CFF(Vec<u8>),
151 CFF2(Vec<u8>),
152 SVG(String),
153 Bitmap(String, Vec<u8>),
154}
155
156#[derive(Debug, Clone)]
157pub struct Font {
158 pub font_type: fontheader::FontHeaders,
159 pub(crate) outline_format: GlyphFormat,
160 pub(crate) cmap: Option<CmapEncodings>, pub(crate) head: Option<head::HEAD>, pub(crate) hhea: Option<hhea::HHEA>, pub(crate) hmtx: Option<hmtx::HMTX>, pub(crate) maxp: Option<maxp::MAXP>, pub(crate) name: Option<name::NAME>, pub(crate) name_table: Option<name::NameTable>,
167 pub(crate) os2: Option<os2::OS2>, pub(crate) post: Option<post::POST>, pub(crate) fvar: Option<fvar::FVAR>,
170 pub(crate) avar: Option<avar::AVAR>,
171 pub(crate) gvar: Option<gvar::GVAR>,
172 pub(crate) loca: Option<loca::LOCA>, pub(crate) glyf: Option<glyf::GLYF>, #[cfg(feature = "cff")]
175 pub(crate) cff: Option<cff::CFF>, pub(crate) hvar: Option<hvar::HVAR>,
177 pub(crate) mvar: Option<mvar::MVAR>,
178 pub(crate) colr: Option<colr::COLR>,
179 pub(crate) cpal: Option<cpal::CPAL>,
180 #[cfg(feature = "layout")]
181 pub(crate) gdef: Option<gdef::GDEF>,
182 #[cfg(feature = "layout")]
183 pub(crate) gpos: Option<gpos::GPOS>,
184 #[cfg(feature = "layout")]
185 pub(crate) gsub: Option<gsub::GSUB>,
186 pub(crate) svg: Option<svg::SVG>,
187 pub(crate) sbix: Option<sbix::SBIX>,
188 pub(crate) vhea: Option<vhea::VHEA>,
189 pub(crate) vvar: Option<vvar::VVAR>,
190 pub(crate) vmtx: Option<vmtx::VMTX>,
191 hmtx_pos: Option<Pointer>,
192 vmtx_pos: Option<Pointer>,
193 loca_pos: Option<Pointer>, glyf_pos: Option<Pointer>, sbix_pos: Option<Pointer>,
196 pub(crate) more_fonts: Box<Vec<Font>>,
197 current_font: usize,
198}
199
200#[derive(Debug, Clone, Copy)]
201enum ResolvedTextUnit {
202 Glyph(ResolvedGlyph),
203 Newline,
204 Tab,
205}
206
207#[derive(Debug, Clone)]
208pub(crate) enum ParsedTextUnit {
209 Glyph {
210 text: String,
211 ch: char,
212 variation_selector: char,
213 },
214 Newline,
215 Tab,
216}
217
218#[derive(Debug, Clone, Copy)]
219struct ResolvedGlyph {
220 ch: char,
221 glyph_id: usize,
222 prefer_color: bool,
223 ligature_components: u16,
224}
225
226#[derive(Debug, Clone, Copy, Default)]
227struct GlyphPositionAdjustment {
228 placement_x: f32,
229 placement_y: f32,
230 advance_x: f32,
231 advance_y: f32,
232}
233
234#[derive(Debug, Clone, Copy)]
235struct GlyphAttachmentPlacement {
236 glyph_index: usize,
237 adjustment: GlyphPositionAdjustment,
238}
239
240#[derive(Debug, Clone, Copy, Default)]
241pub(crate) struct TextUnitSupport {
242 pub(crate) has_glyph: bool,
243 pub(crate) has_outline: bool,
244 pub(crate) has_color: bool,
245}
246
247impl TextUnitSupport {
248 pub(crate) fn is_supported(self) -> bool {
249 self.has_glyph && (self.has_outline || self.has_color)
250 }
251}
252
253impl Font {
254 fn empty() -> Self {
255 Self {
256 font_type: fontheader::FontHeaders::Unknown,
257 outline_format: GlyphFormat::Unknown,
258 cmap: None,
259 head: None,
260 hhea: None,
261 hmtx: None,
262 maxp: None,
263 name: None,
264 name_table: None,
265 os2: None,
266 post: None,
267 fvar: None,
268 avar: None,
269 gvar: None,
270 loca: None,
271 glyf: None,
272 #[cfg(feature = "cff")]
273 cff: None,
274 hvar: None,
275 mvar: None,
276 colr: None,
277 cpal: None,
278 #[cfg(feature = "layout")]
279 gdef: None,
280 #[cfg(feature = "layout")]
281 gpos: None,
282 #[cfg(feature = "layout")]
283 gsub: None,
284 sbix: None,
285 svg: None,
286 vhea: None,
287 vvar: None,
288 vmtx: None,
289 hmtx_pos: None,
290 vmtx_pos: None,
291 loca_pos: None,
292 glyf_pos: None,
293 sbix_pos: None,
294 more_fonts: Box::<Vec<Font>>::default(),
295 current_font: 0,
296 }
297 }
298
299 pub fn get_name_list(&self, locale: &String) -> HashMap<u16, String> {
300 let Some(name_table) = self.current_name_table() else {
301 return HashMap::new();
302 };
303 let platform_id = PlatformID::Windows;
304 let name = name_table.get_name_list(locale, platform_id);
305 if name.is_empty() {
306 let platform_id = PlatformID::Macintosh;
307 name_table.get_name_list(locale, platform_id)
308 } else {
309 name
310 }
311 }
312
313 pub fn get_font_from_file(filename: &PathBuf) -> Result<Self, Error> {
314 font_load_from_file(filename)
315 }
316
317 pub fn get_font_from_buffer(fontdata: &[u8]) -> Result<Self, Error> {
318 let mut reader = BytesReader::new(fontdata);
319 let font_type = fontheader::get_font_type(&mut reader)?;
320 if let fontheader::FontHeaders::WOFF2(header) = font_type {
321 let declared_length = header.length as usize;
322 if declared_length > fontdata.len() {
323 return Err(Error::new(
324 ErrorKind::UnexpectedEof,
325 format!(
326 "WOFF2 buffer is shorter than declared length: {} < {}",
327 fontdata.len(),
328 declared_length
329 ),
330 ));
331 }
332 let mut input = &fontdata[..declared_length];
333 let ttf = woff2::decode::convert_woff2_to_ttf(&mut input).map_err(|err| {
334 Error::new(
335 ErrorKind::InvalidData,
336 format!("Failed to decode WOFF2 font: {err}"),
337 )
338 })?;
339 return Self::get_font_from_buffer(&ttf);
340 }
341
342 reader.seek(SeekFrom::Start(0))?;
343 font_load(&mut reader)
344 }
345
346 pub(crate) fn get_h_metrix_with_coords(&self, id: usize, coordinates: &[f32]) -> LongHorMetric {
347 if self.current_font == 0 {
348 let mut metric = self
349 .hmtx
350 .as_ref()
351 .map(|hmtx| hmtx.get_metrix(id))
352 .unwrap_or(LongHorMetric {
353 advance_width: 0,
354 left_side_bearing: 0,
355 });
356 if let Some(hvar) = self.hvar.as_ref() {
357 if let Some(delta) = hvar.advance_offset(id, coordinates) {
358 metric.advance_width = apply_u16_delta(metric.advance_width, delta);
359 }
360 if let Some(delta) = hvar.left_side_bearing_offset(id, coordinates) {
361 metric.left_side_bearing = apply_i16_delta(metric.left_side_bearing, delta);
362 }
363 }
364 metric
365 } else {
366 let font = &self.more_fonts[self.current_font - 1];
367 let mut metric = font
368 .hmtx
369 .as_ref()
370 .map(|hmtx| hmtx.get_metrix(id))
371 .unwrap_or(LongHorMetric {
372 advance_width: 0,
373 left_side_bearing: 0,
374 });
375 if let Some(hvar) = font.hvar.as_ref() {
376 if let Some(delta) = hvar.advance_offset(id, coordinates) {
377 metric.advance_width = apply_u16_delta(metric.advance_width, delta);
378 }
379 if let Some(delta) = hvar.left_side_bearing_offset(id, coordinates) {
380 metric.left_side_bearing = apply_i16_delta(metric.left_side_bearing, delta);
381 }
382 }
383 metric
384 }
385 }
386
387 pub(crate) fn get_v_metrix_with_coords(
388 &self,
389 id: usize,
390 coordinates: &[f32],
391 ) -> VerticalMetric {
392 if self.current_font == 0 {
393 let mut metric = self
394 .vmtx
395 .as_ref()
396 .map(|vmtx| vmtx.get_metrix(id))
397 .unwrap_or(VerticalMetric {
398 advance_height: 0,
399 top_side_bearing: 0,
400 });
401 if let Some(vvar) = self.vvar.as_ref() {
402 if let Some(delta) = vvar.advance_offset(id, coordinates) {
403 metric.advance_height = apply_u16_delta(metric.advance_height, delta);
404 }
405 if let Some(delta) = vvar.top_side_bearing_offset(id, coordinates) {
406 metric.top_side_bearing = apply_i16_delta(metric.top_side_bearing, delta);
407 }
408 }
409 metric
410 } else {
411 let font = &self.more_fonts[self.current_font - 1];
412 let mut metric = font
413 .vmtx
414 .as_ref()
415 .map(|vmtx| vmtx.get_metrix(id))
416 .unwrap_or(VerticalMetric {
417 advance_height: 0,
418 top_side_bearing: 0,
419 });
420 if let Some(vvar) = font.vvar.as_ref() {
421 if let Some(delta) = vvar.advance_offset(id, coordinates) {
422 metric.advance_height = apply_u16_delta(metric.advance_height, delta);
423 }
424 if let Some(delta) = vvar.top_side_bearing_offset(id, coordinates) {
425 metric.top_side_bearing = apply_i16_delta(metric.top_side_bearing, delta);
426 }
427 }
428 metric
429 }
430 }
431
432 pub fn get_vertical_layout(&self, id: usize) -> Option<VerticalLayout> {
433 self.get_vertical_layout_with_coords(id, &[])
434 }
435
436 pub fn get_vertical_layout_with_coords(
437 &self,
438 id: usize,
439 coordinates: &[f32],
440 ) -> Option<VerticalLayout> {
441 let vhea = self.current_vhea();
442 if let Some(vhea) = vhea {
443 let mut v_metrix = self.get_v_metrix_with_coords(id, coordinates);
444 if let Some(variation) = self.current_gvar_variation(id, coordinates) {
445 if let Some(metric) = variation.vertical_metric {
446 v_metrix = metric;
447 }
448 }
449 return Some(VerticalLayout {
450 tsb: v_metrix.top_side_bearing as isize,
451 advance_height: v_metrix.advance_height as isize,
452 accender: self.metric_value_i16(tag4("vasc"), vhea.get_accender(), coordinates)
453 as isize,
454 descender: self.metric_value_i16(tag4("vdsc"), vhea.get_descender(), coordinates)
455 as isize,
456 line_gap: self.metric_value_i16(tag4("vlgp"), vhea.get_line_gap(), coordinates)
457 as isize,
458 vhea: vhea.clone(),
459 });
460 } else {
461 return None;
462 }
463 }
464
465 pub fn get_horizontal_layout(&self, id: usize) -> HorizontalLayout {
466 self.get_horizontal_layout_with_coords(id, &[])
467 }
468
469 pub fn get_horizontal_layout_with_coords(
470 &self,
471 id: usize,
472 coordinates: &[f32],
473 ) -> HorizontalLayout {
474 let mut h_metrix = self.get_h_metrix_with_coords(id, coordinates);
475 if let Some(variation) = self.current_gvar_variation(id, coordinates) {
476 if let Some(metric) = variation.horizontal_metric {
477 h_metrix = metric;
478 }
479 }
480 let hhea = self.current_hhea().cloned().unwrap_or(HHEA {
481 major_version: 0,
482 minor_version: 0,
483 ascender: 0,
484 descender: 0,
485 line_gap: 0,
486 advance_width_max: 0,
487 min_left_side_bearing: 0,
488 min_right_side_bearing: 0,
489 x_max_extent: 0,
490 caret_slope_rise: 0,
491 caret_slope_run: 0,
492 caret_offset: 0,
493 reserved1: 0,
494 reserved2: 0,
495 reserved3: 0,
496 reserved4: 0,
497 metric_data_format: 0,
498 number_of_hmetrics: 0,
499 });
500 let lsb = h_metrix.left_side_bearing as isize;
501 let advance_width = h_metrix.advance_width as isize;
502
503 let accender =
504 self.metric_value_i16(tag4("hasc"), hhea.get_accender(), coordinates) as isize;
505 let descender =
506 self.metric_value_i16(tag4("hdsc"), hhea.get_descender(), coordinates) as isize;
507 let line_gap =
508 self.metric_value_i16(tag4("hlgp"), hhea.get_line_gap(), coordinates) as isize;
509
510 HorizontalLayout {
511 lsb,
512 advance_width,
513 accender,
514 descender,
515 line_gap,
516 hhea,
517 }
518 }
519
520 pub fn get_glyph_from_id(&self, glyph_id: usize) -> GriphData {
521 self.get_glyph_from_id_axis(glyph_id, false)
522 }
523
524 pub fn get_layout(&self, glyph_id: usize, is_vert: bool) -> FontLayout {
525 self.get_layout_with_coords(glyph_id, is_vert, &[])
526 }
527
528 pub fn get_layout_with_coords(
529 &self,
530 glyph_id: usize,
531 is_vert: bool,
532 coordinates: &[f32],
533 ) -> FontLayout {
534 if is_vert {
535 if let Some(result) = self.get_vertical_layout_with_coords(glyph_id, coordinates) {
536 FontLayout::Vertical(result)
537 } else {
538 FontLayout::Horizontal(
539 self.get_horizontal_layout_with_coords(glyph_id, coordinates),
540 )
541 }
542 } else {
543 FontLayout::Horizontal(self.get_horizontal_layout_with_coords(glyph_id, coordinates))
544 }
545 }
546
547 pub fn get_layout_with_options(
548 &self,
549 glyph_id: usize,
550 is_vert: bool,
551 options: &crate::commands::FontOptions<'_>,
552 ) -> FontLayout {
553 let coordinates = self.normalized_variation_coords(options);
554 self.get_layout_with_coords(glyph_id, is_vert, &coordinates)
555 }
556
557 pub fn get_glyph_with_uvs_axis(&self, ch: char, vs: char, is_vert: bool) -> GriphData {
558 let glyph_id = self.resolve_glyph_id_with_uvs(ch, vs, is_vert).unwrap_or(0);
559 self.get_glyph_from_id_axis(glyph_id, is_vert)
560 }
561
562 pub fn get_glyph_with_uvs(&self, ch: char, vs: char) -> GriphData {
563 self.get_glyph_with_uvs_axis(ch, vs, false)
564 }
565
566 pub fn get_glyph(&self, ch: char) -> GriphData {
567 self.get_glyph_with_uvs(ch, '\u{0}')
568 }
569
570 pub fn get_svg_from_id(
571 &self,
572 glyph_id: usize,
573 fontsize: f64,
574 fontunit: &str,
575 ) -> Result<String, Error> {
576 let layout = self.get_layout(glyph_id, false);
577 #[cfg(feature = "cff")]
578 if let Some(cff) = self.cff.as_ref() {
579 let string = cff.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0)?;
580 return Ok(string);
581 }
582
583 if self.current_outline_format() == GlyphFormat::CFF2 {
584 return Err(Error::new(
585 ErrorKind::Unsupported,
586 "CFF2 outlines are not supported yet",
587 ));
588 }
589
590 let pos = glyph_id as u32;
592 if let Some(glyf) = self.current_glyf() {
593 let glyph = glyf.get_glyph(pos as usize).ok_or_else(|| {
594 Error::new(
595 std::io::ErrorKind::Other,
596 "glyph is none,also you need --features cff".to_string(),
597 )
598 })?;
599 if let Some(sbix) = self.current_sbix() {
600 let result = sbix.get_svg(pos as u32, fontsize, fontunit, &layout, 0.0, 0.0);
601 if let Some(svg) = result {
602 let mut string = "".to_string();
603 #[cfg(debug_assertions)]
604 {
605 string += &format!("<!-- glyf id: {} -->", pos);
606 }
607 string += &svg;
608 return Ok(string);
609 }
610 } else if let Some(svg) = self.current_svg_table() {
611 let result = svg.get_svg(pos as u32, fontsize, fontunit, &layout, 0.0, 0.0);
612 if let Some(svg) = result {
613 let mut string = "".to_string();
614 #[cfg(debug_assertions)]
615 {
616 string += &format!("<!-- glyf id: {} -->", pos);
617 if let FontLayout::Horizontal(layout) = &layout {
618 string += &format!(
619 "<!-- layout {} {} {} {} {} -->\n",
620 layout.lsb,
621 layout.advance_width,
622 layout.accender,
623 layout.descender,
624 layout.line_gap
625 );
626 }
627 }
628 string += &svg;
629 return Ok(string);
630 }
631 }
632
633 let cpal = self.current_cpal();
634 let colr = self.current_colr();
635
636 if let Some(colr) = colr.as_ref() {
637 let layers = colr.get_layer_record(pos as u16);
638 if layers.is_empty() {
639 return Ok(glyf.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0));
640 }
641 let mut string = glyph.get_svg_heder(fontsize, fontunit, &layout);
642 #[cfg(debug_assertions)]
643 {
644 string += &format!("\n<!-- glyf id: {} -->", pos);
645 }
646
647 for layer in layers {
648 let glyf_id = layer.glyph_id as u32;
649 let Some(cpal) = cpal.as_ref() else {
650 return Ok(glyf.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0));
651 };
652 let pallet = cpal.get_pallet(layer.palette_index as usize);
653 #[cfg(debug_assertions)]
654 {
655 string += &format!("<!-- pallet index {} -->\n", layer.palette_index);
656 string += &format!(
657 "<!-- Red {} Green {} Blue {} Alpha {} -->\n",
658 pallet.red, pallet.green, pallet.blue, pallet.alpha
659 );
660 }
661 string += &format!(
662 "<g fill=\"rgba({}, {}, {}, {})\">\n",
663 pallet.red, pallet.green, pallet.blue, pallet.alpha
664 );
665 string += &glyf.get_svg_path(glyf_id as usize, &layout, 0.0, 0.0);
666 string += "</g>\n";
667 }
668 string += "</svg>";
669 Ok(string)
670 } else {
671 #[cfg(debug_assertions)]
672 {
673 let string = glyf.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0);
674 return Ok(format!("<!-- glyf id: {} -->{}", pos, string));
675 }
676 #[cfg(not(debug_assertions))]
677 Ok(glyf.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0))
678 }
679 } else {
680 return Err(Error::new(
681 std::io::ErrorKind::Other,
682 "glyf is none".to_string(),
683 ));
684 }
685 }
686
687 pub fn get_svg_with_uvs_axis(
688 &self,
689 ch: char,
690 vs: char,
691 fontsize: f64,
692 fontunit: &str,
693 is_vert: bool,
694 ) -> Result<String, Error> {
695 #[cfg(feature = "cff")]
700 if let Some(cff) = self.cff.as_ref() {
701 let glyf_data = self.get_glyph_with_uvs_axis(ch, vs, is_vert);
702 let glyph_id = glyf_data.glyph_id;
703 let layout = self.get_layout(glyph_id as usize, is_vert);
704 let string = cff.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0);
705 return string;
706 }
707
708 if self.current_outline_format() == GlyphFormat::CFF2 {
709 return Err(Error::new(
710 ErrorKind::Unsupported,
711 "CFF2 outlines are not supported yet",
712 ));
713 }
714
715 let glyf_data = self.get_glyph_with_uvs_axis(ch, vs, is_vert);
717 let glyph_id = glyf_data.glyph_id;
718
719 let open_type_glyph = glyf_data
720 .open_type_glyf
721 .as_ref()
722 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "glyph is none"))?;
723 let layout = &open_type_glyph.layout;
724 match &open_type_glyph.glyph {
725 FontData::Glyph(glyph) => {
726 if let Some(sbix) = self.current_sbix() {
727 let result =
728 sbix.get_svg(glyph_id as u32, fontsize, fontunit, &layout, 0.0, 0.0);
729 if let Some(svg) = result {
730 let mut string = "".to_string();
731 #[cfg(debug_assertions)]
732 {
733 string += &format!("<!-- {} glyf id: {} -->", ch, glyph_id);
734 }
735 string += &svg;
736 return Ok(string);
737 }
738 } else if let Some(svg) = self.current_svg_table() {
739 let result =
740 svg.get_svg(glyph_id as u32, fontsize, fontunit, &layout, 0.0, 0.0);
741 if let Some(svg) = result {
742 let mut string = "".to_string();
743 #[cfg(debug_assertions)]
744 {
745 string += &format!("<!-- {} glyf id: {} -->", ch, glyph_id);
746 if let FontLayout::Horizontal(layout) = layout {
747 string += &format!(
748 "<!-- layout {} {} {} {} {} -->\n",
749 layout.lsb,
750 layout.advance_width,
751 layout.accender,
752 layout.descender,
753 layout.line_gap
754 );
755 } else if let FontLayout::Vertical(layout) = layout {
756 string += &format!(
757 "<!-- layout vert {} {} {} {} {} -->\n",
758 layout.tsb,
759 layout.advance_height,
760 layout.accender,
761 layout.descender,
762 layout.line_gap
763 );
764 }
765 }
766 string += &svg;
767 return Ok(string);
768 }
769 }
770 let Some(glyf) = self.current_glyf() else {
771 return Err(Error::new(std::io::ErrorKind::Other, "glyf is none"));
772 };
773
774 let cpal = self.current_cpal();
775 let colr = self.current_colr();
776
777 if let Some(colr) = colr.as_ref() {
778 let layers = colr.get_layer_record(glyph_id as u16);
779 if layers.is_empty() {
780 return Ok(glyf.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0));
781 }
782 let mut string = glyph.get_svg_heder(fontsize, fontunit, &layout);
783 #[cfg(debug_assertions)]
784 {
785 string += &format!("\n<!-- {} glyf id: {} -->", ch, glyph_id);
786 }
787
788 for layer in layers {
789 let glyf_id = layer.glyph_id as u32;
790 let Some(cpal) = cpal.as_ref() else {
791 return Ok(glyf.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0));
792 };
793 let pallet = cpal.get_pallet(layer.palette_index as usize);
794 #[cfg(debug_assertions)]
795 {
796 string += &format!("<!-- pallet index {} -->\n", layer.palette_index);
797 string += &format!(
798 "<!-- Red {} Green {} Blue {} Alpha {} -->\n",
799 pallet.red, pallet.green, pallet.blue, pallet.alpha
800 );
801 }
802 string += &format!(
803 "<g fill=\"rgba({}, {}, {}, {})\">\n",
804 pallet.red, pallet.green, pallet.blue, pallet.alpha
805 );
806 string += &glyf.get_svg_path(glyf_id as usize, &layout, 0.0, 0.0);
807 string += "</g>\n";
808 }
809 string += "</svg>";
810 Ok(string)
811 } else {
812 #[cfg(debug_assertions)]
813 {
814 let string = glyf.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0);
815 return Ok(format!("<!-- {} glyf id: {} -->{}", ch, glyph_id, string));
816 }
817 #[cfg(not(debug_assertions))]
818 Ok(glyf.to_svg(glyph_id, fontsize, fontunit, &layout, 0.0, 0.0))
819 }
820 }
821 FontData::ParsedGlyph(parsed) => {
822 let mut string =
823 glyf::Glyph::get_svg_header_from_parsed(parsed, fontsize, fontunit, layout);
824 string += &glyf::Glyph::get_svg_path_parsed(parsed, layout, 0.0, 0.0);
825 string += "\n</svg>";
826 Ok(string)
827 }
828 _ => Err(Error::new(
829 std::io::ErrorKind::Other,
830 "glyf is none".to_string(),
831 )),
832 }
833 }
834
835 pub fn get_svg_with_uvs(
836 &self,
837 ch: char,
838 vs: char,
839 fontsize: f64,
840 fontunit: &str,
841 ) -> Result<String, Error> {
842 self.get_svg_with_uvs_axis(ch, vs, fontsize, fontunit, false)
843 }
844
845 pub fn get_svg(&self, ch: char, fontsize: f64, fontunit: &str) -> Result<String, Error> {
846 self.get_svg_with_uvs(ch, '\u{0}', fontsize, fontunit)
847 }
848
849 fn current_hhea(&self) -> Result<&HHEA, Error> {
850 if self.current_font == 0 {
851 self.hhea
852 .as_ref()
853 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "hhea is none"))
854 } else {
855 self.more_fonts[self.current_font - 1]
856 .hhea
857 .as_ref()
858 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "hhea is none"))
859 }
860 }
861
862 fn current_head(&self) -> Result<&head::HEAD, Error> {
863 if self.current_font == 0 {
864 self.head
865 .as_ref()
866 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "head is none"))
867 } else {
868 self.more_fonts[self.current_font - 1]
869 .head
870 .as_ref()
871 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "head is none"))
872 }
873 }
874
875 fn current_vhea(&self) -> Option<&VHEA> {
876 if self.current_font == 0 {
877 self.vhea.as_ref()
878 } else {
879 self.more_fonts[self.current_font - 1].vhea.as_ref()
880 }
881 }
882
883 fn current_name_table(&self) -> Option<&name::NameTable> {
884 if self.current_font == 0 {
885 self.name_table.as_ref()
886 } else {
887 self.more_fonts[self.current_font - 1].name_table.as_ref()
888 }
889 }
890
891 fn current_cmap(&self) -> Result<&CmapEncodings, Error> {
892 if self.current_font == 0 {
893 self.cmap
894 .as_ref()
895 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "cmap is none"))
896 } else {
897 self.more_fonts[self.current_font - 1]
898 .cmap
899 .as_ref()
900 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "cmap is none"))
901 }
902 }
903
904 fn current_fvar(&self) -> Option<&fvar::FVAR> {
905 if self.current_font == 0 {
906 self.fvar.as_ref()
907 } else {
908 self.more_fonts[self.current_font - 1].fvar.as_ref()
909 }
910 }
911
912 fn current_avar(&self) -> Option<&avar::AVAR> {
913 if self.current_font == 0 {
914 self.avar.as_ref()
915 } else {
916 self.more_fonts[self.current_font - 1].avar.as_ref()
917 }
918 }
919
920 fn current_mvar(&self) -> Option<&mvar::MVAR> {
921 if self.current_font == 0 {
922 self.mvar.as_ref()
923 } else {
924 self.more_fonts[self.current_font - 1].mvar.as_ref()
925 }
926 }
927
928 fn current_gvar(&self) -> Option<&gvar::GVAR> {
929 if self.current_font == 0 {
930 self.gvar.as_ref()
931 } else {
932 self.more_fonts[self.current_font - 1].gvar.as_ref()
933 }
934 }
935
936 fn current_glyf(&self) -> Option<&glyf::GLYF> {
937 if self.current_font == 0 {
938 self.glyf.as_ref()
939 } else {
940 self.more_fonts[self.current_font - 1].glyf.as_ref()
941 }
942 }
943
944 fn current_outline_format(&self) -> GlyphFormat {
945 if self.current_font == 0 {
946 self.outline_format
947 } else {
948 self.more_fonts[self.current_font - 1].outline_format
949 }
950 }
951
952 fn current_colr(&self) -> Option<&colr::COLR> {
953 if self.current_font == 0 {
954 self.colr.as_ref()
955 } else {
956 self.more_fonts[self.current_font - 1].colr.as_ref()
957 }
958 }
959
960 fn current_cpal(&self) -> Option<&cpal::CPAL> {
961 if self.current_font == 0 {
962 self.cpal.as_ref()
963 } else {
964 self.more_fonts[self.current_font - 1].cpal.as_ref()
965 }
966 }
967
968 fn current_sbix(&self) -> Option<&sbix::SBIX> {
969 if self.current_font == 0 {
970 self.sbix.as_ref()
971 } else {
972 self.more_fonts[self.current_font - 1].sbix.as_ref()
973 }
974 }
975
976 fn current_svg_table(&self) -> Option<&svg::SVG> {
977 if self.current_font == 0 {
978 self.svg.as_ref()
979 } else {
980 self.more_fonts[self.current_font - 1].svg.as_ref()
981 }
982 }
983
984 #[cfg(feature = "layout")]
985 fn current_gpos(&self) -> Option<&gpos::GPOS> {
986 if self.current_font == 0 {
987 self.gpos.as_ref()
988 } else {
989 self.more_fonts[self.current_font - 1].gpos.as_ref()
990 }
991 }
992
993 #[cfg(feature = "layout")]
994 fn current_gsub(&self) -> Option<&gsub::GSUB> {
995 if self.current_font == 0 {
996 self.gsub.as_ref()
997 } else {
998 self.more_fonts[self.current_font - 1].gsub.as_ref()
999 }
1000 }
1001
1002 #[cfg(feature = "layout")]
1003 fn current_gdef(&self) -> Option<&gdef::GDEF> {
1004 if self.current_font == 0 {
1005 self.gdef.as_ref()
1006 } else {
1007 self.more_fonts[self.current_font - 1].gdef.as_ref()
1008 }
1009 }
1010
1011 #[cfg(feature = "cff")]
1012 fn current_cff(&self) -> Option<&cff::CFF> {
1013 if self.current_font == 0 {
1014 self.cff.as_ref()
1015 } else {
1016 self.more_fonts[self.current_font - 1].cff.as_ref()
1017 }
1018 }
1019
1020 fn normalized_variation_coords(&self, options: &crate::commands::FontOptions<'_>) -> Vec<f32> {
1021 let Some(fvar) = self.current_fvar() else {
1022 return Vec::new();
1023 };
1024
1025 let mut coordinates = fvar
1026 .axes
1027 .iter()
1028 .map(|axis| {
1029 let value = options
1030 .variations
1031 .iter()
1032 .find(|setting| u32::from_be_bytes(setting.tag) == axis.tag)
1033 .map(|setting| setting.value)
1034 .unwrap_or(axis.default_value);
1035 axis.normalized_value(value)
1036 })
1037 .collect::<Vec<_>>();
1038
1039 if let Some(avar) = self.current_avar() {
1040 for index in 0..coordinates.len() {
1041 avar.map_coordinate(&mut coordinates, index);
1042 }
1043 }
1044
1045 coordinates
1046 }
1047
1048 fn metric_value_i16(&self, tag: u32, base: i16, coordinates: &[f32]) -> i16 {
1049 if coordinates.is_empty() {
1050 return base;
1051 }
1052
1053 if let Some(mvar) = self.current_mvar() {
1054 if let Some(delta) = mvar.metric_offset(tag, coordinates) {
1055 return apply_i16_delta(base, delta);
1056 }
1057 }
1058
1059 base
1060 }
1061
1062 fn get_glyph_from_id_with_options(
1063 &self,
1064 glyph_id: usize,
1065 is_vert: bool,
1066 options: &crate::commands::FontOptions<'_>,
1067 ) -> GriphData {
1068 let coordinates = self.normalized_variation_coords(options);
1069 let mut layout = self.get_layout_with_coords(glyph_id, is_vert, &coordinates);
1070
1071 #[cfg(feature = "cff")]
1072 if let Some(cff) = self.current_cff() {
1073 let string = cff.to_code_with_coords(glyph_id, &layout, &coordinates);
1074 let open_type_glyf = Some(OpenTypeGlyph {
1075 layout,
1076 glyph: FontData::CFF(string.as_bytes().to_vec()),
1077 variation_coords: coordinates.clone(),
1078 });
1079
1080 return GriphData {
1081 glyph_id,
1082 open_type_glyf,
1083 };
1084 }
1085
1086 let open_type_glyph = match self.current_outline_format() {
1087 GlyphFormat::OpenTypeGlyph => {
1088 let glyf = self
1089 .current_glyf()
1090 .expect("glyf outline format should expose glyf table");
1091 let glyph = glyf
1092 .get_glyph(glyph_id)
1093 .expect("glyph id should resolve inside glyf table");
1094 let glyph =
1095 if let Some(variation) = self.current_gvar_variation(glyph_id, &coordinates) {
1096 Self::apply_varied_metrics_to_layout(
1097 &mut layout,
1098 variation.horizontal_metric.as_ref(),
1099 variation.vertical_metric.as_ref(),
1100 );
1101 FontData::ParsedGlyph(variation.parsed)
1102 } else if self.current_gvar().is_some() {
1103 if let Some(parsed) =
1104 self.current_gvar_component_varied_parsed(glyph_id, &coordinates)
1105 {
1106 FontData::ParsedGlyph(parsed)
1107 } else {
1108 FontData::Glyph(glyph.clone())
1109 }
1110 } else {
1111 FontData::Glyph(glyph.clone())
1112 };
1113 OpenTypeGlyph {
1114 layout,
1115 glyph,
1116 variation_coords: coordinates.clone(),
1117 }
1118 }
1119 GlyphFormat::CFF2 => OpenTypeGlyph {
1120 layout,
1121 glyph: FontData::CFF2(Vec::new()),
1122 variation_coords: coordinates.clone(),
1123 },
1124 _ => OpenTypeGlyph {
1125 layout,
1126 glyph: FontData::CFF2(Vec::new()),
1127 variation_coords: coordinates.clone(),
1128 },
1129 };
1130
1131 GriphData {
1132 glyph_id,
1133 open_type_glyf: Some(open_type_glyph),
1134 }
1135 }
1136
1137 fn current_gvar_variation(
1138 &self,
1139 glyph_id: usize,
1140 coordinates: &[f32],
1141 ) -> Option<gvar::GlyphVariationResult> {
1142 let gvar = self.current_gvar()?;
1143 let component_varied = self.current_gvar_component_varied_parsed(glyph_id, coordinates)?;
1144
1145 let horizontal_metric = Some(self.get_h_metrix_with_coords(glyph_id, coordinates));
1146 let vertical_metric = self
1147 .current_vhea()
1148 .map(|_| self.get_v_metrix_with_coords(glyph_id, coordinates));
1149
1150 gvar.apply_to_parsed_glyph_with_metrics(
1151 glyph_id,
1152 coordinates,
1153 &component_varied,
1154 horizontal_metric,
1155 vertical_metric,
1156 )
1157 }
1158
1159 fn current_gvar_component_varied_parsed(
1160 &self,
1161 glyph_id: usize,
1162 coordinates: &[f32],
1163 ) -> Option<ParsedGlyph> {
1164 let gvar = self.current_gvar()?;
1165 let glyf = self.current_glyf()?;
1166 let glyph = glyf.get_glyph(glyph_id)?;
1167 let raw_parsed = glyph.parse();
1168 if raw_parsed.number_of_contours < 0 {
1169 glyf.parse_glyph_with_variation(glyph_id, &|component_glyph_id, parsed| {
1170 if component_glyph_id == glyph_id {
1171 None
1172 } else {
1173 gvar.apply_to_parsed_glyph(component_glyph_id, coordinates, parsed)
1174 }
1175 })
1176 .or(Some(raw_parsed))
1177 } else {
1178 Some(raw_parsed)
1179 }
1180 }
1181
1182 fn apply_varied_metrics_to_layout(
1183 layout: &mut FontLayout,
1184 horizontal_metric: Option<&LongHorMetric>,
1185 vertical_metric: Option<&VerticalMetric>,
1186 ) {
1187 match layout {
1188 FontLayout::Horizontal(current) => {
1189 if let Some(metric) = horizontal_metric {
1190 current.lsb = metric.left_side_bearing as isize;
1191 current.advance_width = metric.advance_width as isize;
1192 }
1193 }
1194 FontLayout::Vertical(current) => {
1195 if let Some(metric) = vertical_metric {
1196 current.tsb = metric.top_side_bearing as isize;
1197 current.advance_height = metric.advance_height as isize;
1198 }
1199 }
1200 FontLayout::Unknown => {}
1201 }
1202 }
1203
1204 fn get_glyph_from_id_axis(&self, glyph_id: usize, is_vert: bool) -> GriphData {
1205 self.get_glyph_from_id_with_options(
1206 glyph_id,
1207 is_vert,
1208 &crate::commands::FontOptions::from_parsed(self),
1209 )
1210 }
1211
1212 fn resolve_glyph_id_with_uvs(&self, ch: char, vs: char, is_vert: bool) -> Result<usize, Error> {
1213 let glyph_id = self
1214 .current_cmap()?
1215 .get_glyph_position_from_uvs(ch as u32, vs as u32) as usize;
1216
1217 #[cfg(feature = "layout")]
1218 {
1219 if is_vert {
1220 if let Some(gsub) = self.current_gsub() {
1221 return Ok(gsub
1222 .lookup_vertical(glyph_id as u16)
1223 .unwrap_or(glyph_id as u16) as usize);
1224 }
1225 }
1226 }
1227
1228 #[cfg(not(feature = "layout"))]
1229 let _ = is_vert;
1230
1231 Ok(glyph_id)
1232 }
1233
1234 fn is_variation_selector(ch: char) -> bool {
1235 (0xfe00..=0xfe0f).contains(&(ch as u32)) || (0xE0100..=0xE01EF).contains(&(ch as u32))
1236 }
1237
1238 fn is_emoji_modifier(ch: char) -> bool {
1239 (0x1F3FB..=0x1F3FF).contains(&(ch as u32))
1240 }
1241
1242 fn is_zero_width_joiner(ch: char) -> bool {
1243 ch == '\u{200D}'
1244 }
1245
1246 fn is_keycap_mark(ch: char) -> bool {
1247 ch == '\u{20E3}'
1248 }
1249
1250 fn is_combining_mark(ch: char) -> bool {
1251 matches!(
1252 ch as u32,
1253 0x0300..=0x036F
1254 | 0x0483..=0x0489
1255 | 0x0591..=0x05BD
1256 | 0x05BF
1257 | 0x05C1..=0x05C2
1258 | 0x05C4..=0x05C5
1259 | 0x05C7
1260 | 0x0610..=0x061A
1261 | 0x064B..=0x065F
1262 | 0x0670
1263 | 0x06D6..=0x06DC
1264 | 0x06DF..=0x06E4
1265 | 0x06E7..=0x06E8
1266 | 0x06EA..=0x06ED
1267 | 0x0711
1268 | 0x0730..=0x074A
1269 | 0x07A6..=0x07B0
1270 | 0x07EB..=0x07F3
1271 | 0x0816..=0x0819
1272 | 0x081B..=0x0823
1273 | 0x0825..=0x0827
1274 | 0x0829..=0x082D
1275 | 0x0859..=0x085B
1276 | 0x08D3..=0x08E1
1277 | 0x08E3..=0x08FF
1278 | 0x0F18..=0x0F19
1279 | 0x0F35
1280 | 0x0F37
1281 | 0x0F39
1282 | 0x0F71..=0x0F7E
1283 | 0x0F80..=0x0F84
1284 | 0x0F86..=0x0F87
1285 | 0x0F8D..=0x0FBC
1286 | 0x1AB0..=0x1AFF
1287 | 0x1DC0..=0x1DFF
1288 | 0x20D0..=0x20FF
1289 | 0x3099..=0x309A
1290 | 0xFE20..=0xFE2F
1291 )
1292 }
1293
1294 fn is_regional_indicator(ch: char) -> bool {
1295 (0x1F1E6..=0x1F1FF).contains(&(ch as u32))
1296 }
1297
1298 fn is_tag_character(ch: char) -> bool {
1299 (0xE0020..=0xE007E).contains(&(ch as u32)) || ch == '\u{E007F}'
1300 }
1301
1302 fn is_default_emoji_scalar(ch: char) -> bool {
1303 matches!(ch as u32, 0x1F000..=0x1FAFF | 0x2600..=0x27BF)
1304 }
1305
1306 fn text_prefers_color_glyph(text: &str) -> bool {
1307 let mut saw_emoji_scalar = false;
1308 for ch in text.chars() {
1309 if Self::is_variation_selector(ch)
1310 || Self::is_emoji_modifier(ch)
1311 || Self::is_zero_width_joiner(ch)
1312 || Self::is_keycap_mark(ch)
1313 || Self::is_regional_indicator(ch)
1314 || Self::is_tag_character(ch)
1315 {
1316 return true;
1317 }
1318 if Self::is_default_emoji_scalar(ch) {
1319 saw_emoji_scalar = true;
1320 }
1321 }
1322 saw_emoji_scalar
1323 }
1324
1325 fn extend_cluster_suffix(chars: &[char], index: &mut usize, text: &mut String) {
1326 while *index < chars.len() {
1327 let ch = chars[*index];
1328 if Self::is_variation_selector(ch)
1329 || Self::is_emoji_modifier(ch)
1330 || Self::is_keycap_mark(ch)
1331 || Self::is_tag_character(ch)
1332 || Self::is_combining_mark(ch)
1333 {
1334 text.push(ch);
1335 *index += 1;
1336 } else {
1337 break;
1338 }
1339 }
1340 }
1341
1342 fn cluster_glyph_scalars(text: &str) -> Vec<(char, char)> {
1343 let chars: Vec<char> = text.chars().collect();
1344 let mut glyphs = Vec::new();
1345 let mut index = 0usize;
1346
1347 while index < chars.len() {
1348 let ch = chars[index];
1349 if Self::is_variation_selector(ch) {
1350 index += 1;
1351 continue;
1352 }
1353
1354 let mut variation_selector = '\0';
1355 if index + 1 < chars.len() && Self::is_variation_selector(chars[index + 1]) {
1356 variation_selector = chars[index + 1];
1357 index += 1;
1358 }
1359
1360 glyphs.push((ch, variation_selector));
1361 index += 1;
1362 }
1363
1364 glyphs
1365 }
1366
1367 fn parse_text_units(text: &str) -> Vec<ParsedTextUnit> {
1368 let chars: Vec<char> = text.chars().collect();
1369 let mut units = Vec::new();
1370 let mut index = 0;
1371
1372 while index < chars.len() {
1373 let ch = chars[index];
1374 match ch {
1375 '\r' => {
1376 index += 1;
1377 }
1378 '\n' => {
1379 units.push(ParsedTextUnit::Newline);
1380 index += 1;
1381 }
1382 '\t' => {
1383 units.push(ParsedTextUnit::Tab);
1384 index += 1;
1385 }
1386 _ if Self::is_variation_selector(ch) => {
1387 index += 1;
1388 }
1389 _ => {
1390 let mut text = String::new();
1391 text.push(ch);
1392 let mut variation_selector = '\0';
1393 if index + 1 < chars.len() && Self::is_variation_selector(chars[index + 1]) {
1394 variation_selector = chars[index + 1];
1395 text.push(variation_selector);
1396 index += 1;
1397 }
1398
1399 index += 1;
1400 Self::extend_cluster_suffix(&chars, &mut index, &mut text);
1401
1402 if Self::is_regional_indicator(ch)
1403 && index < chars.len()
1404 && Self::is_regional_indicator(chars[index])
1405 {
1406 text.push(chars[index]);
1407 index += 1;
1408 Self::extend_cluster_suffix(&chars, &mut index, &mut text);
1409 }
1410
1411 while index < chars.len() && Self::is_zero_width_joiner(chars[index]) {
1412 text.push(chars[index]);
1413 index += 1;
1414 if index >= chars.len() {
1415 break;
1416 }
1417 text.push(chars[index]);
1418 index += 1;
1419 Self::extend_cluster_suffix(&chars, &mut index, &mut text);
1420 }
1421
1422 units.push(ParsedTextUnit::Glyph {
1423 text,
1424 ch,
1425 variation_selector,
1426 });
1427 }
1428 }
1429 }
1430
1431 units
1432 }
1433
1434 pub(crate) fn parse_text_units_for_fallback(text: &str) -> Vec<ParsedTextUnit> {
1435 Self::parse_text_units(text)
1436 }
1437
1438 #[cfg(feature = "layout")]
1439 fn apply_gsub_sequence_stages(
1440 &self,
1441 glyphs: &mut Vec<(usize, usize)>,
1442 locale: Option<&str>,
1443 is_right_to_left: bool,
1444 font_variant: crate::commands::FontVariant,
1445 ) {
1446 let Some(gsub) = self.current_gsub() else {
1447 return;
1448 };
1449
1450 gsub.apply_ccmp_sequence(glyphs);
1455 gsub.apply_variant_sequence(glyphs, locale, font_variant);
1456 if is_right_to_left {
1457 gsub.apply_joining_sequence(glyphs, locale);
1458 gsub.apply_rtl_contextual_sequence(glyphs, locale);
1459 }
1460 }
1461
1462 #[cfg(feature = "layout")]
1463 fn apply_gsub_ligature_stage(
1464 &self,
1465 output: &mut Vec<ResolvedTextUnit>,
1466 expanded_glyphs: &[ResolvedGlyph],
1467 locale: Option<&str>,
1468 is_right_to_left: bool,
1469 ) {
1470 let Some(gsub) = self.current_gsub() else {
1471 output.extend(expanded_glyphs.iter().copied().map(ResolvedTextUnit::Glyph));
1472 return;
1473 };
1474
1475 const MAX_LIGATURE_COMPONENTS: usize = 8;
1476 let glyph_ids: Vec<usize> = expanded_glyphs.iter().map(|glyph| glyph.glyph_id).collect();
1477 let mut index = 0;
1478 while index < expanded_glyphs.len() {
1479 let max_len = (expanded_glyphs.len() - index).min(MAX_LIGATURE_COMPONENTS);
1480 let mut matched = None;
1481 for len in (2..=max_len).rev() {
1482 if is_right_to_left {
1483 if let Some(glyph_id) =
1484 gsub.lookup_rlig_sequence(&glyph_ids[index..index + len], locale)
1485 {
1486 matched = Some((glyph_id, len));
1487 break;
1488 }
1489 }
1490 if let Some(glyph_id) = gsub.lookup_liga_sequence(&glyph_ids[index..index + len]) {
1491 matched = Some((glyph_id, len));
1492 break;
1493 }
1494 }
1495
1496 if let Some((glyph_id, len)) = matched {
1497 output.push(ResolvedTextUnit::Glyph(ResolvedGlyph {
1498 ch: expanded_glyphs[index].ch,
1499 glyph_id,
1500 prefer_color: expanded_glyphs[index].prefer_color,
1501 ligature_components: len as u16,
1502 }));
1503 index += len;
1504 } else {
1505 output.push(ResolvedTextUnit::Glyph(expanded_glyphs[index]));
1506 index += 1;
1507 }
1508 }
1509 }
1510
1511 fn flush_shaped_glyphs(
1512 &self,
1513 output: &mut Vec<ResolvedTextUnit>,
1514 glyphs: &mut Vec<ResolvedGlyph>,
1515 locale: Option<&str>,
1516 is_right_to_left: bool,
1517 font_variant: crate::commands::FontVariant,
1518 ) {
1519 #[cfg(not(feature = "layout"))]
1520 let _ = (locale, is_right_to_left, font_variant);
1521
1522 if glyphs.is_empty() {
1523 return;
1524 }
1525
1526 #[cfg(feature = "layout")]
1527 if self.current_gsub().is_some() {
1528 let mut ccmp_glyphs = glyphs
1529 .iter()
1530 .enumerate()
1531 .map(|(source_index, glyph)| (glyph.glyph_id, source_index))
1532 .collect::<Vec<_>>();
1533 self.apply_gsub_sequence_stages(
1534 &mut ccmp_glyphs,
1535 locale,
1536 is_right_to_left,
1537 font_variant,
1538 );
1539 let expanded_glyphs = ccmp_glyphs
1540 .into_iter()
1541 .map(|(glyph_id, source_index)| ResolvedGlyph {
1542 ch: glyphs[source_index].ch,
1543 glyph_id,
1544 prefer_color: glyphs[source_index].prefer_color,
1545 ligature_components: glyphs[source_index].ligature_components,
1546 })
1547 .collect::<Vec<_>>();
1548 self.apply_gsub_ligature_stage(output, &expanded_glyphs, locale, is_right_to_left);
1549 glyphs.clear();
1550 return;
1551 }
1552
1553 output.extend(glyphs.iter().copied().map(ResolvedTextUnit::Glyph));
1554 glyphs.clear();
1555 }
1556
1557 fn shape_text_units(
1558 &self,
1559 text: &str,
1560 is_vert: bool,
1561 is_right_to_left: bool,
1562 locale: Option<&str>,
1563 font_variant: crate::commands::FontVariant,
1564 ) -> Result<Vec<ResolvedTextUnit>, Error> {
1565 #[cfg(not(feature = "layout"))]
1566 let _ = (locale, is_right_to_left, font_variant);
1567
1568 let mut output = Vec::new();
1569 let mut pending_glyphs = Vec::new();
1570
1571 for unit in Self::parse_text_units(text) {
1572 match unit {
1573 ParsedTextUnit::Newline => {
1574 self.flush_shaped_glyphs(
1575 &mut output,
1576 &mut pending_glyphs,
1577 locale,
1578 is_right_to_left,
1579 font_variant,
1580 );
1581 output.push(ResolvedTextUnit::Newline);
1582 }
1583 ParsedTextUnit::Tab => {
1584 self.flush_shaped_glyphs(
1585 &mut output,
1586 &mut pending_glyphs,
1587 locale,
1588 is_right_to_left,
1589 font_variant,
1590 );
1591 output.push(ResolvedTextUnit::Tab);
1592 }
1593 ParsedTextUnit::Glyph { text, .. } => {
1594 let prefer_color = Self::text_prefers_color_glyph(&text);
1595 for (ch, variation_selector) in Self::cluster_glyph_scalars(&text) {
1596 let glyph_id =
1597 self.resolve_glyph_id_with_uvs(ch, variation_selector, is_vert)?;
1598 #[cfg(feature = "layout")]
1599 let glyph_id = if let Some(locale) = locale {
1600 if let Some(gsub) = self.current_gsub() {
1601 gsub.lookup_locale(glyph_id, locale)
1602 } else {
1603 glyph_id
1604 }
1605 } else {
1606 glyph_id
1607 };
1608 pending_glyphs.push(ResolvedGlyph {
1609 ch,
1610 glyph_id,
1611 prefer_color,
1612 ligature_components: 1,
1613 });
1614 }
1615 }
1616 }
1617 }
1618
1619 self.flush_shaped_glyphs(
1620 &mut output,
1621 &mut pending_glyphs,
1622 locale,
1623 is_right_to_left,
1624 font_variant,
1625 );
1626 Ok(output)
1627 }
1628
1629 pub(crate) fn supports_text_unit(
1630 &self,
1631 unit: &ParsedTextUnit,
1632 text_direction: crate::commands::TextDirection,
1633 locale: Option<&str>,
1634 font_variant: crate::commands::FontVariant,
1635 ) -> bool {
1636 self.text_unit_support(unit, text_direction, locale, font_variant)
1637 .is_supported()
1638 }
1639
1640 pub(crate) fn text_unit_support(
1641 &self,
1642 unit: &ParsedTextUnit,
1643 text_direction: crate::commands::TextDirection,
1644 locale: Option<&str>,
1645 font_variant: crate::commands::FontVariant,
1646 ) -> TextUnitSupport {
1647 #[cfg(not(feature = "layout"))]
1648 let _ = (locale, font_variant);
1649
1650 match unit {
1651 ParsedTextUnit::Newline | ParsedTextUnit::Tab => TextUnitSupport {
1652 has_glyph: true,
1653 has_outline: true,
1654 has_color: false,
1655 },
1656 ParsedTextUnit::Glyph { text, .. } => {
1657 let is_vert = text_direction.is_vertical();
1658 let is_right_to_left = text_direction.is_right_to_left();
1659 let Ok(shaped_units) =
1660 self.shape_text_units(text, is_vert, is_right_to_left, locale, font_variant)
1661 else {
1662 return TextUnitSupport::default();
1663 };
1664
1665 if !shaped_units
1666 .iter()
1667 .any(|unit| matches!(unit, ResolvedTextUnit::Glyph(_)))
1668 {
1669 return TextUnitSupport::default();
1670 }
1671
1672 let mut support = TextUnitSupport::default();
1673 for unit in shaped_units {
1674 let ResolvedTextUnit::Glyph(glyph) = unit else {
1675 continue;
1676 };
1677 if glyph.glyph_id == 0 {
1678 return TextUnitSupport::default();
1679 }
1680
1681 support.has_glyph = true;
1682
1683 #[cfg(feature = "cff")]
1684 if self.current_cff().is_some() {
1685 support.has_outline = true;
1686 }
1687
1688 if self
1689 .current_glyf()
1690 .and_then(|glyf| glyf.get_glyph(glyph.glyph_id))
1691 .is_some()
1692 {
1693 support.has_outline = true;
1694 }
1695
1696 if self
1697 .current_colr()
1698 .map(|colr| !colr.get_layer_record(glyph.glyph_id as u16).is_empty())
1699 .unwrap_or(false)
1700 {
1701 support.has_color = true;
1702 }
1703
1704 if self
1705 .current_sbix()
1706 .and_then(|sbix| sbix.get_raster_glyph(glyph.glyph_id as u32, 16.0, "px"))
1707 .is_some()
1708 {
1709 support.has_color = true;
1710 }
1711
1712 if self
1713 .current_svg_table()
1714 .map(|svg| svg.has_glyph(glyph.glyph_id as u32))
1715 .unwrap_or(false)
1716 {
1717 support.has_color = true;
1718 }
1719
1720 if !support.has_outline && !support.has_color {
1721 return TextUnitSupport::default();
1722 }
1723 }
1724
1725 support
1726 }
1727 }
1728 }
1729
1730 fn push_svg_html_unit(
1731 &self,
1732 svgs: &mut Vec<String>,
1733 unit: ParsedTextUnit,
1734 fontsize: f64,
1735 fontunit: &str,
1736 is_vert: bool,
1737 ) -> Result<(), Error> {
1738 match unit {
1739 ParsedTextUnit::Newline => {
1740 svgs.push("<br>".to_string());
1741 }
1742 ParsedTextUnit::Tab => {
1743 svgs.push(
1744 "<span style=\"width: 4em; display: inline-block;\"></span>\n".to_string(),
1745 );
1746 }
1747 ParsedTextUnit::Glyph {
1748 text,
1749 ch,
1750 variation_selector,
1751 } => {
1752 let svg = if text.chars().count() > 2
1753 || text.contains('\u{200D}')
1754 || text
1755 .chars()
1756 .filter(|ch| Self::is_regional_indicator(*ch))
1757 .count()
1758 > 1
1759 {
1760 self.text2svg(&text, fontsize, fontunit)?
1761 } else {
1762 self.get_svg_with_uvs_axis(ch, variation_selector, fontsize, fontunit, is_vert)?
1763 };
1764 svgs.push(svg);
1765 }
1766 }
1767 Ok(())
1768 }
1769
1770 #[cfg(test)]
1771 pub(crate) fn debug_shape_glyph_ids(
1772 &self,
1773 text: &str,
1774 locale: Option<&str>,
1775 ) -> Result<Vec<usize>, Error> {
1776 let mut glyph_ids = Vec::new();
1777 for unit in self.shape_text_units(
1778 text,
1779 false,
1780 false,
1781 locale,
1782 crate::commands::FontVariant::Normal,
1783 )? {
1784 if let ResolvedTextUnit::Glyph(glyph) = unit {
1785 glyph_ids.push(glyph.glyph_id);
1786 }
1787 }
1788 Ok(glyph_ids)
1789 }
1790
1791 #[cfg(test)]
1792 #[allow(dead_code)]
1793 pub(crate) fn debug_shape_glyph_ids_with_direction(
1794 &self,
1795 text: &str,
1796 locale: Option<&str>,
1797 is_right_to_left: bool,
1798 ) -> Result<Vec<usize>, Error> {
1799 let mut glyph_ids = Vec::new();
1800 for unit in self.shape_text_units(
1801 text,
1802 false,
1803 is_right_to_left,
1804 locale,
1805 crate::commands::FontVariant::Normal,
1806 )? {
1807 if let ResolvedTextUnit::Glyph(glyph) = unit {
1808 glyph_ids.push(glyph.glyph_id);
1809 }
1810 }
1811 Ok(glyph_ids)
1812 }
1813
1814 #[cfg(test)]
1815 #[allow(dead_code)]
1816 pub(crate) fn debug_shape_glyph_ids_with_variant(
1817 &self,
1818 text: &str,
1819 locale: Option<&str>,
1820 font_variant: crate::commands::FontVariant,
1821 ) -> Result<Vec<usize>, Error> {
1822 let mut glyph_ids = Vec::new();
1823 for unit in self.shape_text_units(text, false, false, locale, font_variant)? {
1824 if let ResolvedTextUnit::Glyph(glyph) = unit {
1825 glyph_ids.push(glyph.glyph_id);
1826 }
1827 }
1828 Ok(glyph_ids)
1829 }
1830
1831 fn default_line_height(&self) -> Result<f64, Error> {
1832 self.default_line_height_with_options(&crate::commands::FontOptions::from_parsed(self))
1833 }
1834
1835 fn default_line_height_with_options(
1836 &self,
1837 options: &crate::commands::FontOptions<'_>,
1838 ) -> Result<f64, Error> {
1839 let hhea = self.current_hhea()?;
1840 let coordinates = self.normalized_variation_coords(options);
1841 let ascender = self.metric_value_i16(tag4("hasc"), hhea.get_accender(), &coordinates);
1842 let descender = self.metric_value_i16(tag4("hdsc"), hhea.get_descender(), &coordinates);
1843 let line_gap = self.metric_value_i16(tag4("hlgp"), hhea.get_line_gap(), &coordinates);
1844 Ok((ascender - descender + line_gap) as f64)
1845 }
1846
1847 #[allow(dead_code)]
1848 fn glyph_unit_at(units: &[ResolvedTextUnit], index: usize) -> Option<ResolvedGlyph> {
1849 match units.get(index) {
1850 Some(ResolvedTextUnit::Glyph(glyph)) => Some(*glyph),
1851 _ => None,
1852 }
1853 }
1854
1855 fn resolved_glyph_can_use_outline(
1856 &self,
1857 open_type_glyph: &OpenTypeGlyph,
1858 glyph_id: usize,
1859 ) -> bool {
1860 if self
1861 .current_colr()
1862 .map(|colr| !colr.get_layer_record(glyph_id as u16).is_empty())
1863 .unwrap_or(false)
1864 {
1865 return true;
1866 }
1867
1868 #[cfg(feature = "cff")]
1869 if self.current_cff().is_some() {
1870 return true;
1871 }
1872
1873 if self.current_outline_format() == GlyphFormat::CFF2 {
1874 return false;
1875 }
1876
1877 matches!(
1878 open_type_glyph.glyph,
1879 FontData::Glyph(_) | FontData::ParsedGlyph(_)
1880 )
1881 }
1882
1883 #[cfg(feature = "svg-fonts")]
1884 fn build_svg_layers(
1885 &self,
1886 glyph_id: usize,
1887 layout: &FontLayout,
1888 scale_x: f32,
1889 scale_y: f32,
1890 ) -> Option<Vec<GlyphLayer>> {
1891 let document = self
1892 .current_svg_table()?
1893 .get_glyph_document(glyph_id as u32, layout)?;
1894 Some(svg_document_to_glyph_layers(&document, scale_x, scale_y))
1895 }
1896
1897 fn pair_adjustment_for_index(
1898 &self,
1899 units: &[ResolvedTextUnit],
1900 index: usize,
1901 locale: Option<&str>,
1902 is_vertical: bool,
1903 scale_x: f32,
1904 scale_y: f32,
1905 ) -> GlyphPositionAdjustment {
1906 #[cfg(not(feature = "layout"))]
1907 {
1908 let _ = (units, index, locale, is_vertical, scale_x, scale_y);
1909 GlyphPositionAdjustment::default()
1910 }
1911
1912 #[cfg(feature = "layout")]
1913 {
1914 let Some(gpos) = self.current_gpos() else {
1915 return GlyphPositionAdjustment::default();
1916 };
1917 let Some(current) = Self::glyph_unit_at(units, index) else {
1918 return GlyphPositionAdjustment::default();
1919 };
1920
1921 let mut adjustment = GlyphPositionAdjustment::default();
1922 let previous_index = self.find_previous_spacing_glyph_index(units, index);
1923 let next_index = self.find_next_spacing_glyph_index(units, index);
1924
1925 if let Some(previous_index) = previous_index {
1926 if let Some(previous) = Self::glyph_unit_at(units, previous_index) {
1927 if let Some(pair) = gpos.lookup_pair_adjustment(
1928 previous.glyph_id as u16,
1929 current.glyph_id as u16,
1930 is_vertical,
1931 locale,
1932 ) {
1933 adjustment.placement_x += pair.second.x_placement as f32 * scale_x;
1934 adjustment.placement_y += pair.second.y_placement as f32 * scale_y;
1935 adjustment.advance_x += pair.second.x_advance as f32 * scale_x;
1936 adjustment.advance_y += pair.second.y_advance as f32 * scale_y;
1937 }
1938 }
1939 }
1940
1941 if let Some(next_index) = next_index {
1942 if let Some(next) = Self::glyph_unit_at(units, next_index) {
1943 if let Some(pair) = gpos.lookup_pair_adjustment(
1944 current.glyph_id as u16,
1945 next.glyph_id as u16,
1946 is_vertical,
1947 locale,
1948 ) {
1949 adjustment.placement_x += pair.first.x_placement as f32 * scale_x;
1950 adjustment.placement_y += pair.first.y_placement as f32 * scale_y;
1951 adjustment.advance_x += pair.first.x_advance as f32 * scale_x;
1952 adjustment.advance_y += pair.first.y_advance as f32 * scale_y;
1953 }
1954 }
1955 }
1956
1957 adjustment
1958 }
1959 }
1960
1961 #[cfg(feature = "layout")]
1962 fn mark_attachment_for_index(
1963 &self,
1964 units: &[ResolvedTextUnit],
1965 unit_glyph_indices: &[Option<usize>],
1966 index: usize,
1967 locale: Option<&str>,
1968 scale_x: f32,
1969 scale_y: f32,
1970 ) -> Option<GlyphAttachmentPlacement> {
1971 let gpos = self.current_gpos()?;
1972 let current = Self::glyph_unit_at(units, index)?;
1973 if let Some(previous_mark_unit_index) = self.find_previous_mark_glyph_index(units, index) {
1974 let previous_mark = Self::glyph_unit_at(units, previous_mark_unit_index)?;
1975 let glyph_index = unit_glyph_indices
1976 .get(previous_mark_unit_index)
1977 .and_then(|glyph_index| *glyph_index)?;
1978 if let Some(adjustment) = gpos.lookup_mark_to_mark_adjustment(
1979 previous_mark.glyph_id as u16,
1980 current.glyph_id as u16,
1981 locale,
1982 ) {
1983 return Some(GlyphAttachmentPlacement {
1984 glyph_index,
1985 adjustment: GlyphPositionAdjustment {
1986 placement_x: adjustment.x_placement as f32 * scale_x,
1987 placement_y: adjustment.y_placement as f32 * scale_y,
1988 advance_x: 0.0,
1989 advance_y: 0.0,
1990 },
1991 });
1992 }
1993 }
1994
1995 let base_index = self.find_previous_spacing_glyph_index(units, index)?;
1996 let base = Self::glyph_unit_at(units, base_index)?;
1997 let glyph_index = unit_glyph_indices
1998 .get(base_index)
1999 .and_then(|glyph_index| *glyph_index)?;
2000 let ligature_component_index = base.ligature_components.saturating_sub(1) as usize;
2001 let adjustment = if base.ligature_components > 1 {
2002 gpos.lookup_mark_to_ligature_adjustment(
2003 base.glyph_id as u16,
2004 current.glyph_id as u16,
2005 ligature_component_index,
2006 locale,
2007 )
2008 .or_else(|| {
2009 gpos.lookup_mark_to_base_adjustment(
2010 base.glyph_id as u16,
2011 current.glyph_id as u16,
2012 locale,
2013 )
2014 })?
2015 } else {
2016 gpos.lookup_mark_to_base_adjustment(
2017 base.glyph_id as u16,
2018 current.glyph_id as u16,
2019 locale,
2020 )?
2021 };
2022
2023 Some(GlyphAttachmentPlacement {
2024 glyph_index,
2025 adjustment: GlyphPositionAdjustment {
2026 placement_x: adjustment.x_placement as f32 * scale_x,
2027 placement_y: adjustment.y_placement as f32 * scale_y,
2028 advance_x: 0.0,
2029 advance_y: 0.0,
2030 },
2031 })
2032 }
2033
2034 #[cfg(not(feature = "layout"))]
2035 fn mark_attachment_for_index(
2036 &self,
2037 units: &[ResolvedTextUnit],
2038 unit_glyph_indices: &[Option<usize>],
2039 index: usize,
2040 locale: Option<&str>,
2041 scale_x: f32,
2042 scale_y: f32,
2043 ) -> Option<GlyphAttachmentPlacement> {
2044 let _ = (units, unit_glyph_indices, index, locale, scale_x, scale_y);
2045 None
2046 }
2047
2048 #[cfg(feature = "layout")]
2049 fn find_previous_mark_glyph_index(
2050 &self,
2051 units: &[ResolvedTextUnit],
2052 index: usize,
2053 ) -> Option<usize> {
2054 let cursor = index.checked_sub(1)?;
2055 Self::glyph_unit_at(units, cursor).map(|_| cursor)
2056 }
2057
2058 #[cfg(feature = "layout")]
2059 fn find_previous_spacing_glyph_index(
2060 &self,
2061 units: &[ResolvedTextUnit],
2062 index: usize,
2063 ) -> Option<usize> {
2064 let mut cursor = index.checked_sub(1)?;
2065 loop {
2066 match Self::glyph_unit_at(units, cursor) {
2067 Some(glyph)
2068 if !self.gdef_is_ignored_for_pair_positioning(glyph.glyph_id as u16) =>
2069 {
2070 return Some(cursor);
2071 }
2072 Some(_) => {
2073 cursor = cursor.checked_sub(1)?;
2074 }
2075 None => return None,
2076 }
2077 }
2078 }
2079
2080 #[cfg(feature = "layout")]
2081 fn find_next_spacing_glyph_index(
2082 &self,
2083 units: &[ResolvedTextUnit],
2084 index: usize,
2085 ) -> Option<usize> {
2086 let mut cursor = index + 1;
2087 while cursor < units.len() {
2088 match Self::glyph_unit_at(units, cursor) {
2089 Some(glyph)
2090 if !self.gdef_is_ignored_for_pair_positioning(glyph.glyph_id as u16) =>
2091 {
2092 return Some(cursor);
2093 }
2094 Some(_) => {
2095 cursor += 1;
2096 }
2097 None => return None,
2098 }
2099 }
2100 None
2101 }
2102
2103 #[cfg(feature = "layout")]
2104 fn gdef_is_ignored_for_pair_positioning(&self, glyph_id: u16) -> bool {
2105 self.current_gdef()
2106 .map(|gdef| gdef.is_mark_glyph(glyph_id))
2107 .unwrap_or(false)
2108 }
2109
2110 #[cfg(not(feature = "layout"))]
2111 fn gdef_is_ignored_for_pair_positioning(&self, glyph_id: u16) -> bool {
2112 let _ = glyph_id;
2113 false
2114 }
2115
2116 #[cfg(feature = "layout")]
2117 fn gdef_supports_mark_attachment(&self, glyph_id: u16) -> bool {
2118 self.current_gdef()
2119 .map(|gdef| {
2120 gdef.is_mark_glyph(glyph_id)
2121 && (gdef.mark_attachment_class(glyph_id).is_some()
2122 || gdef.has_attach_points(glyph_id))
2123 })
2124 .unwrap_or(false)
2125 }
2126
2127 #[cfg(not(feature = "layout"))]
2128 fn gdef_supports_mark_attachment(&self, glyph_id: u16) -> bool {
2129 let _ = glyph_id;
2130 false
2131 }
2132
2133 pub fn text2glyph_run(
2134 &self,
2135 text: &str,
2136 options: &crate::commands::FontOptions<'_>,
2137 ) -> Result<GlyphRun, Error> {
2138 let _ = self.current_head()?;
2139
2140 if !options.font_size.is_finite() || options.font_size <= 0.0 {
2141 return Err(Error::new(
2142 ErrorKind::InvalidInput,
2143 "font_size must be a positive finite value",
2144 ));
2145 }
2146
2147 let default_line_height = self.default_line_height_with_options(options)? as f32;
2148 let scale_y = options.font_size / default_line_height.max(1.0);
2149 let scale_x = scale_y * options.font_stretch.0.max(0.0);
2150 let line_height = options.line_height.unwrap_or(options.font_size);
2151 let is_vertical = options.text_direction.is_vertical();
2152 let is_right_to_left = options.text_direction.is_right_to_left();
2153 if !line_height.is_finite() || line_height <= 0.0 {
2154 return Err(Error::new(
2155 ErrorKind::InvalidInput,
2156 "line_height must be a positive finite value",
2157 ));
2158 }
2159
2160 let mut glyphs: Vec<PositionedGlyph> = Vec::new();
2161 let mut cursor_x = 0.0f32;
2162 let mut cursor_y = 0.0f32;
2163 let tab_advance = line_height;
2164 let shaped_units = self.shape_text_units(
2165 text,
2166 is_vertical,
2167 is_right_to_left,
2168 options.locale,
2169 options.font_variant,
2170 )?;
2171 let mut unit_glyph_indices = vec![None; shaped_units.len()];
2172
2173 for (index, unit) in shaped_units.iter().enumerate() {
2174 match *unit {
2175 ResolvedTextUnit::Newline => {
2176 if is_vertical {
2177 cursor_x -= line_height;
2178 cursor_y = 0.0;
2179 } else {
2180 cursor_x = 0.0;
2181 cursor_y += line_height;
2182 }
2183 }
2184 ResolvedTextUnit::Tab => {
2185 if is_vertical {
2186 cursor_y += tab_advance * 4.0;
2187 } else if is_right_to_left {
2188 cursor_x -= tab_advance * 4.0;
2189 } else {
2190 cursor_x += tab_advance * 4.0;
2191 }
2192 }
2193 ResolvedTextUnit::Glyph(resolved) => {
2194 let glyph_data = self.get_glyph_from_id_with_options(
2195 resolved.glyph_id,
2196 is_vertical,
2197 options,
2198 );
2199 let open_type_glyph = glyph_data
2200 .open_type_glyf
2201 .as_ref()
2202 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "glyph is none"))?;
2203 let glyph_id = glyph_data.glyph_id;
2204 let can_use_outline =
2205 self.resolved_glyph_can_use_outline(open_type_glyph, glyph_id);
2206 #[cfg(feature = "svg-fonts")]
2207 let can_use_svg = self
2208 .current_svg_table()
2209 .map(|svg| svg.has_glyph(glyph_id as u32))
2210 .unwrap_or(false);
2211
2212 let layers = if let Some(sbix) = self.current_sbix() {
2213 if let Some(bitmap) =
2214 sbix.get_raster_glyph(glyph_id as u32, options.font_size, "px")
2215 {
2216 if resolved.prefer_color || !can_use_outline {
2217 let mut raster = RasterGlyphLayer::from_encoded(bitmap.glyph_data);
2218 raster.offset_x = bitmap.offset_x * options.font_stretch.0.max(0.0);
2219 raster.offset_y = bitmap.offset_y;
2220 raster.width = bitmap.width;
2221 raster.height = bitmap.height;
2222 vec![GlyphLayer::Raster(raster)]
2223 } else {
2224 self.build_outline_layers(
2225 glyph_id,
2226 open_type_glyph,
2227 scale_x,
2228 scale_y,
2229 resolved.ch,
2230 )?
2231 }
2232 } else {
2233 #[cfg(feature = "svg-fonts")]
2234 {
2235 if can_use_svg && (resolved.prefer_color || !can_use_outline) {
2236 self.build_svg_layers(
2237 glyph_id,
2238 &open_type_glyph.layout,
2239 scale_x,
2240 scale_y,
2241 )
2242 .ok_or_else(|| {
2243 Error::new(
2244 ErrorKind::Unsupported,
2245 format!(
2246 "failed to extract SVG glyph layer for {:?}",
2247 resolved.ch
2248 ),
2249 )
2250 })?
2251 } else {
2252 self.build_outline_layers(
2253 glyph_id,
2254 open_type_glyph,
2255 scale_x,
2256 scale_y,
2257 resolved.ch,
2258 )?
2259 }
2260 }
2261 #[cfg(not(feature = "svg-fonts"))]
2262 {
2263 self.build_outline_layers(
2264 glyph_id,
2265 open_type_glyph,
2266 scale_x,
2267 scale_y,
2268 resolved.ch,
2269 )?
2270 }
2271 }
2272 } else {
2273 #[cfg(feature = "svg-fonts")]
2274 {
2275 if can_use_svg && (resolved.prefer_color || !can_use_outline) {
2276 self.build_svg_layers(
2277 glyph_id,
2278 &open_type_glyph.layout,
2279 scale_x,
2280 scale_y,
2281 )
2282 .ok_or_else(|| {
2283 Error::new(
2284 ErrorKind::Unsupported,
2285 format!(
2286 "failed to extract SVG glyph layer for {:?}",
2287 resolved.ch
2288 ),
2289 )
2290 })?
2291 } else {
2292 self.build_outline_layers(
2293 glyph_id,
2294 open_type_glyph,
2295 scale_x,
2296 scale_y,
2297 resolved.ch,
2298 )?
2299 }
2300 }
2301 #[cfg(not(feature = "svg-fonts"))]
2302 {
2303 self.build_outline_layers(
2304 glyph_id,
2305 open_type_glyph,
2306 scale_x,
2307 scale_y,
2308 resolved.ch,
2309 )?
2310 }
2311 };
2312
2313 let mut metrics =
2314 glyph_metrics_from_layout(&open_type_glyph.layout, scale_x, scale_y);
2315 let adjustment = self.pair_adjustment_for_index(
2316 &shaped_units,
2317 index,
2318 options.locale,
2319 is_vertical,
2320 scale_x,
2321 scale_y,
2322 );
2323 let mark_attachment = self.mark_attachment_for_index(
2324 &shaped_units,
2325 &unit_glyph_indices,
2326 index,
2327 options.locale,
2328 scale_x,
2329 scale_y,
2330 );
2331 metrics.advance_x += adjustment.advance_x;
2332 metrics.advance_y += adjustment.advance_y;
2333 metrics.bounds = glyph_layers_bounds(&layers);
2334 let uses_gpos_mark_attachment = mark_attachment.is_some();
2335 let gdef_attach_glyph_index = if !uses_gpos_mark_attachment
2336 && self.gdef_supports_mark_attachment(glyph_id as u16)
2337 {
2338 self.find_previous_spacing_glyph_index(&shaped_units, index)
2339 .and_then(|unit_index| unit_glyph_indices.get(unit_index))
2340 .and_then(|glyph_index| *glyph_index)
2341 } else {
2342 None
2343 };
2344 let uses_gdef_mark_attachment =
2345 !uses_gpos_mark_attachment && gdef_attach_glyph_index.is_some();
2346 let uses_mark_attachment =
2347 uses_gpos_mark_attachment || uses_gdef_mark_attachment;
2348 let attach_glyph_index = mark_attachment
2349 .map(|attachment| attachment.glyph_index)
2350 .or(gdef_attach_glyph_index);
2351 let origin_x = if uses_mark_attachment {
2352 let base_x = glyphs[attach_glyph_index.expect("checked some")].x;
2353 let placement_x = mark_attachment
2354 .map(|mark| mark.adjustment.placement_x)
2355 .unwrap_or(0.0)
2356 + adjustment.placement_x;
2357 base_x + placement_x
2358 } else if is_right_to_left && !is_vertical {
2359 cursor_x - metrics.advance_x + adjustment.placement_x
2360 } else {
2361 cursor_x + adjustment.placement_x
2362 };
2363 let origin_y = if uses_mark_attachment {
2364 let base_y = glyphs[attach_glyph_index.expect("checked some")].y;
2365 let placement_y = mark_attachment
2366 .map(|mark| mark.adjustment.placement_y)
2367 .unwrap_or(0.0)
2368 + adjustment.placement_y;
2369 base_y + placement_y
2370 } else {
2371 cursor_y + adjustment.placement_y
2372 };
2373 if uses_mark_attachment {
2374 metrics.advance_x = 0.0;
2375 metrics.advance_y = 0.0;
2376 }
2377 let glyph = Glyph {
2378 font: Some(font_metrics_from_layout(&open_type_glyph.layout, scale_y)),
2379 metrics,
2380 layers,
2381 };
2382 glyphs.push(PositionedGlyph::new(glyph, origin_x, origin_y));
2383 unit_glyph_indices[index] = Some(glyphs.len() - 1);
2384 if !uses_mark_attachment {
2385 if is_right_to_left && !is_vertical {
2386 cursor_x -= metrics.advance_x;
2387 } else {
2388 cursor_x += metrics.advance_x;
2389 }
2390 cursor_y += metrics.advance_y;
2391 }
2392 }
2393 }
2394 }
2395
2396 Ok(GlyphRun::new(glyphs))
2397 }
2398
2399 fn build_outline_layers(
2400 &self,
2401 glyph_id: usize,
2402 open_type_glyph: &OpenTypeGlyph,
2403 scale_x: f32,
2404 scale_y: f32,
2405 _ch: char,
2406 ) -> Result<Vec<GlyphLayer>, Error> {
2407 let color_layers =
2408 self.build_colr_layers(glyph_id, &open_type_glyph.layout, scale_x, scale_y);
2409 if !color_layers.is_empty() {
2410 return Ok(color_layers);
2411 }
2412
2413 #[cfg(feature = "cff")]
2414 if let Some(cff) = self.current_cff() {
2415 let commands =
2416 cff.to_path_commands_with_coords(glyph_id, 1.0, &open_type_glyph.variation_coords)?;
2417 let commands = transform_cff_commands(&commands, scale_x, scale_y);
2418 return Ok(vec![GlyphLayer::Path(PathGlyphLayer::new(
2419 commands,
2420 GlyphPaint::CurrentColor,
2421 ))]);
2422 }
2423
2424 if self.current_outline_format() == GlyphFormat::CFF2 {
2425 return Err(Error::new(
2426 ErrorKind::Unsupported,
2427 "CFF2 outlines are not supported yet",
2428 ));
2429 }
2430
2431 match &open_type_glyph.glyph {
2432 FontData::Glyph(_) => {
2433 let glyf = self
2434 .current_glyf()
2435 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "glyf is none"))?;
2436 let commands = glyf.to_path_commands(glyph_id, &open_type_glyph.layout, 0.0, 0.0);
2437 let commands =
2438 transform_glyf_commands(&commands, &open_type_glyph.layout, scale_x, scale_y);
2439 Ok(vec![GlyphLayer::Path(PathGlyphLayer::new(
2440 commands,
2441 GlyphPaint::CurrentColor,
2442 ))])
2443 }
2444 FontData::ParsedGlyph(parsed) => {
2445 let commands =
2446 glyf::Glyph::to_path_commands_parsed(parsed, &open_type_glyph.layout, 0.0, 0.0);
2447 let commands =
2448 transform_glyf_commands(&commands, &open_type_glyph.layout, scale_x, scale_y);
2449 Ok(vec![GlyphLayer::Path(PathGlyphLayer::new(
2450 commands,
2451 GlyphPaint::CurrentColor,
2452 ))])
2453 }
2454 FontData::Bitmap(_, _) => Err(Error::new(
2455 ErrorKind::Unsupported,
2456 "bitmap glyphs are only supported through sbix raster layers",
2457 )),
2458 FontData::SVG(_) => Err(Error::new(
2459 ErrorKind::Unsupported,
2460 "SVG glyph layers are not supported yet",
2461 )),
2462 _ => Err(Error::new(
2463 ErrorKind::Unsupported,
2464 "glyph outlines are not available for this font",
2465 )),
2466 }
2467 }
2468
2469 fn build_colr_layers(
2470 &self,
2471 glyph_id: usize,
2472 layout: &FontLayout,
2473 scale_x: f32,
2474 scale_y: f32,
2475 ) -> Vec<GlyphLayer> {
2476 let (Some(colr), Some(cpal), Some(glyf)) = (
2477 self.current_colr(),
2478 self.current_cpal(),
2479 self.current_glyf(),
2480 ) else {
2481 return Vec::new();
2482 };
2483
2484 let mut layers = Vec::new();
2485 for layer in colr.get_layer_record(glyph_id as u16) {
2486 if glyf.get_glyph(layer.glyph_id as usize).is_none() {
2487 continue;
2488 }
2489 let commands = glyf.to_path_commands(layer.glyph_id as usize, layout, 0.0, 0.0);
2490 let commands = transform_glyf_commands(&commands, layout, scale_x, scale_y);
2491 let color = cpal.get_pallet(layer.palette_index as usize);
2492 let argb = ((color.alpha as u32) << 24)
2493 | ((color.red as u32) << 16)
2494 | ((color.green as u32) << 8)
2495 | color.blue as u32;
2496 layers.push(GlyphLayer::Path(PathGlyphLayer::new(
2497 commands,
2498 GlyphPaint::Solid(argb),
2499 )));
2500 }
2501
2502 layers
2503 }
2504
2505 fn legacy_colr_commands(
2506 &self,
2507 glyph_id: usize,
2508 layout: &FontLayout,
2509 origin_x: f64,
2510 origin_y: f64,
2511 ) -> Vec<PathCommand> {
2512 let (Some(colr), Some(glyf)) = (self.current_colr(), self.current_glyf()) else {
2513 return Vec::new();
2514 };
2515
2516 let mut commands = Vec::new();
2517 for layer in colr.get_layer_record(glyph_id as u16) {
2518 if glyf.get_glyph(layer.glyph_id as usize).is_none() {
2519 continue;
2520 }
2521 commands.extend(glyf.to_path_commands(
2522 layer.glyph_id as usize,
2523 layout,
2524 origin_x,
2525 origin_y,
2526 ));
2527 }
2528 commands
2529 }
2530
2531 pub(crate) fn text2commands(&self, text: &str) -> Result<Vec<GlyphCommands>, Error> {
2532 let mut result = Vec::new();
2533 let mut cursor_x = 0.0;
2534 let mut line_index = 0usize;
2535 let line_height = self
2536 .default_line_height_with_options(&crate::commands::FontOptions::from_parsed(self))?;
2537 let tab_advance = line_height;
2538 let shaped_units = self.shape_text_units(
2539 text,
2540 false,
2541 false,
2542 None,
2543 crate::commands::FontVariant::Normal,
2544 )?;
2545
2546 for (index, unit) in shaped_units.iter().enumerate() {
2547 match *unit {
2548 ResolvedTextUnit::Newline => {
2549 cursor_x = 0.0;
2550 line_index += 1;
2551 }
2552 ResolvedTextUnit::Tab => {
2553 cursor_x += tab_advance * 4.0;
2554 }
2555 ResolvedTextUnit::Glyph(resolved) => {
2556 let glyph_data = self.get_glyph_from_id_with_options(
2557 resolved.glyph_id,
2558 false,
2559 &crate::commands::FontOptions::from_parsed(self),
2560 );
2561 let open_type_glyph = glyph_data
2562 .open_type_glyf
2563 .as_ref()
2564 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "glyph is none"))?;
2565 let adjustment =
2566 self.pair_adjustment_for_index(&shaped_units, index, None, false, 1.0, 1.0);
2567 let origin_y =
2568 -(line_index as f64 * line_height) + adjustment.placement_y as f64;
2569 let advance_width = match &open_type_glyph.layout {
2570 FontLayout::Horizontal(layout) => layout.advance_width as f64,
2571 FontLayout::Vertical(layout) => layout.advance_height as f64,
2572 FontLayout::Unknown => 0.0,
2573 } + adjustment.advance_x as f64;
2574 let origin_x = cursor_x + adjustment.placement_x as f64;
2575 let can_use_outline =
2576 self.resolved_glyph_can_use_outline(open_type_glyph, glyph_data.glyph_id);
2577
2578 if let Some(sbix) = self.current_sbix() {
2579 if let Some(bitmap) = sbix.get_raster_glyph(
2580 glyph_data.glyph_id as u32,
2581 line_height as f32,
2582 "px",
2583 ) {
2584 if resolved.prefer_color || !can_use_outline {
2585 let format = if bitmap.graphic_type == u32::from_be_bytes(*b"png ")
2586 {
2587 BitmapGlyphFormat::Png
2588 } else if bitmap.graphic_type == u32::from_be_bytes(*b"jpg ") {
2589 BitmapGlyphFormat::Jpeg
2590 } else {
2591 return Err(Error::new(
2592 std::io::ErrorKind::Unsupported,
2593 "unsupported sbix image format",
2594 ));
2595 };
2596 let sniffed_dimensions =
2597 sniff_encoded_image_dimensions(&bitmap.glyph_data);
2598 result.push(GlyphCommands {
2599 ch: resolved.ch,
2600 glyph_id: glyph_data.glyph_id,
2601 origin_x,
2602 origin_y,
2603 advance_width,
2604 commands: Vec::new(),
2605 bitmap: Some(BitmapGlyphCommands {
2606 offset_x: bitmap.offset_x as f64,
2607 offset_y: bitmap.offset_y as f64,
2608 width: bitmap
2609 .width
2610 .map(|width| width as f64)
2611 .or_else(|| {
2612 sniffed_dimensions.map(|(_, width, _)| width as f64)
2613 })
2614 .unwrap_or(line_height),
2615 height: bitmap
2616 .height
2617 .map(|height| height as f64)
2618 .or_else(|| {
2619 sniffed_dimensions
2620 .map(|(_, _, height)| height as f64)
2621 })
2622 .unwrap_or(line_height),
2623 format,
2624 data: bitmap.glyph_data,
2625 }),
2626 });
2627 cursor_x += advance_width;
2628 continue;
2629 }
2630 }
2631 }
2632
2633 match &open_type_glyph.glyph {
2634 FontData::Glyph(_) => {
2635 let mut commands = self.legacy_colr_commands(
2636 glyph_data.glyph_id,
2637 &open_type_glyph.layout,
2638 origin_x,
2639 origin_y,
2640 );
2641 if commands.is_empty() {
2642 let glyf = self.current_glyf().ok_or_else(|| {
2643 Error::new(std::io::ErrorKind::Other, "glyf is none")
2644 })?;
2645 commands = glyf.to_path_commands(
2646 glyph_data.glyph_id,
2647 &open_type_glyph.layout,
2648 origin_x,
2649 origin_y,
2650 );
2651 }
2652 result.push(GlyphCommands {
2653 ch: resolved.ch,
2654 glyph_id: glyph_data.glyph_id,
2655 origin_x,
2656 origin_y,
2657 advance_width,
2658 commands,
2659 bitmap: None,
2660 });
2661 cursor_x += advance_width;
2662 }
2663 FontData::ParsedGlyph(parsed) => {
2664 let commands = glyf::Glyph::to_path_commands_parsed(
2665 parsed,
2666 &open_type_glyph.layout,
2667 origin_x,
2668 origin_y,
2669 );
2670 result.push(GlyphCommands {
2671 ch: resolved.ch,
2672 glyph_id: glyph_data.glyph_id,
2673 origin_x,
2674 origin_y,
2675 advance_width,
2676 commands,
2677 bitmap: None,
2678 });
2679 cursor_x += advance_width;
2680 }
2681 FontData::CFF(_) | FontData::CFF2(_) => {
2682 return Err(Error::new(
2683 ErrorKind::Unsupported,
2684 "legacy text2commands does not support CFF/CFF2 outlines",
2685 ));
2686 }
2687 _ => {
2688 return Err(Error::new(
2689 std::io::ErrorKind::Other,
2690 "text2commands supports glyf outlines and sbix bitmap glyphs only",
2691 ));
2692 }
2693 }
2694 }
2695 }
2696 }
2697
2698 Ok(result)
2699 }
2700
2701 #[cfg(test)]
2702 pub(crate) fn text2command(&self, text: &str) -> Result<Vec<GlyphCommands>, Error> {
2703 self.text2commands(text)
2704 }
2705
2706 pub fn measure(&self, text: &str) -> Result<f64, Error> {
2707 self.measure_with_options(text, &crate::commands::FontOptions::from_parsed(self))
2708 }
2709
2710 pub fn measure_with_options(
2711 &self,
2712 text: &str,
2713 options: &crate::commands::FontOptions<'_>,
2714 ) -> Result<f64, Error> {
2715 let mut cursor_x = 0.0;
2716 let mut cursor_y = 0.0;
2717 let mut max_line_width: f64 = 0.0;
2718 let line_height = self.default_line_height_with_options(options)?;
2719 let tab_advance = line_height;
2720 let is_vertical = options.text_direction.is_vertical();
2721 let is_right_to_left = options.text_direction.is_right_to_left();
2722 let shaped_units = self.shape_text_units(
2723 text,
2724 is_vertical,
2725 is_right_to_left,
2726 options.locale,
2727 options.font_variant,
2728 )?;
2729
2730 for (index, unit) in shaped_units.iter().enumerate() {
2731 match *unit {
2732 ResolvedTextUnit::Newline => {
2733 max_line_width = if is_vertical {
2734 max_line_width.max(cursor_y)
2735 } else if is_right_to_left {
2736 max_line_width.max(-cursor_x)
2737 } else {
2738 max_line_width.max(cursor_x)
2739 };
2740 if is_vertical {
2741 cursor_x -= line_height;
2742 cursor_y = 0.0;
2743 } else {
2744 cursor_x = 0.0;
2745 }
2746 }
2747 ResolvedTextUnit::Tab => {
2748 if is_vertical {
2749 cursor_y += tab_advance * 4.0;
2750 } else if is_right_to_left {
2751 cursor_x -= tab_advance * 4.0;
2752 } else {
2753 cursor_x += tab_advance * 4.0;
2754 }
2755 }
2756 ResolvedTextUnit::Glyph(resolved) => {
2757 let glyph_data = self.get_glyph_from_id_with_options(
2758 resolved.glyph_id,
2759 is_vertical,
2760 options,
2761 );
2762 let open_type_glyph = glyph_data
2763 .open_type_glyf
2764 .as_ref()
2765 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "glyph is none"))?;
2766
2767 let adjustment = self.pair_adjustment_for_index(
2768 &shaped_units,
2769 index,
2770 options.locale,
2771 is_vertical,
2772 1.0,
2773 1.0,
2774 );
2775 let (advance_x, advance_y) = match &open_type_glyph.layout {
2776 FontLayout::Horizontal(layout) => (layout.advance_width as f64, 0.0),
2777 FontLayout::Vertical(layout) => (0.0, layout.advance_height as f64),
2778 FontLayout::Unknown => (0.0, 0.0),
2779 };
2780 if is_right_to_left && !is_vertical {
2781 cursor_x -= advance_x + adjustment.advance_x as f64;
2782 } else {
2783 cursor_x += advance_x + adjustment.advance_x as f64;
2784 }
2785 cursor_y += advance_y + adjustment.advance_y as f64;
2786 }
2787 }
2788 }
2789
2790 Ok(if is_vertical {
2791 max_line_width.max(cursor_y)
2792 } else if is_right_to_left {
2793 max_line_width.max(-cursor_x)
2794 } else {
2795 max_line_width.max(cursor_x)
2796 })
2797 }
2798
2799 pub(crate) fn text2svg(
2800 &self,
2801 text: &str,
2802 fontsize: f64,
2803 fontunit: &str,
2804 ) -> Result<String, Error> {
2805 let glyphs = self.text2commands(text)?;
2806 let line_height = self.default_line_height()?;
2807 let mut svg_elements = Vec::new();
2808 let mut min_x = 0.0;
2809 let mut min_y = 0.0;
2810 let mut max_x = 0.0;
2811 let mut max_y = 0.0;
2812 let mut has_point = false;
2813
2814 for glyph in glyphs.iter() {
2815 let d = path_commands_to_svg_path(&glyph.commands);
2816 if !d.is_empty() {
2817 let (glyph_min_x, glyph_min_y, glyph_max_x, glyph_max_y) =
2818 path_command_bounds(&glyph.commands);
2819 if !has_point {
2820 min_x = glyph_min_x;
2821 min_y = glyph_min_y;
2822 max_x = glyph_max_x;
2823 max_y = glyph_max_y;
2824 has_point = true;
2825 } else {
2826 min_x = min_x.min(glyph_min_x);
2827 min_y = min_y.min(glyph_min_y);
2828 max_x = max_x.max(glyph_max_x);
2829 max_y = max_y.max(glyph_max_y);
2830 }
2831 svg_elements.push(format!("<path d=\"{}\" fill=\"currentColor\"/>", d));
2832 }
2833
2834 if let Some(bitmap) = glyph.bitmap.as_ref() {
2835 let glyph_min_x = glyph.origin_x + bitmap.offset_x;
2836 let glyph_min_y = glyph.origin_y + bitmap.offset_y;
2837 let glyph_max_x = glyph_min_x + bitmap.width;
2838 let glyph_max_y = glyph_min_y + bitmap.height;
2839 if !has_point {
2840 min_x = glyph_min_x;
2841 min_y = glyph_min_y;
2842 max_x = glyph_max_x;
2843 max_y = glyph_max_y;
2844 has_point = true;
2845 } else {
2846 min_x = min_x.min(glyph_min_x);
2847 min_y = min_y.min(glyph_min_y);
2848 max_x = max_x.max(glyph_max_x);
2849 max_y = max_y.max(glyph_max_y);
2850 }
2851 svg_elements.push(bitmap_glyph_to_svg_image(glyph, bitmap));
2852 }
2853 }
2854
2855 if !has_point {
2856 let size = format!("0{}", fontunit);
2857 return Ok(format!(
2858 "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{}\" height=\"{}\" viewBox=\"0 0 0 0\"></svg>",
2859 size, size
2860 ));
2861 }
2862
2863 const SVG_EXPORT_PADDING: f64 = 4.0;
2864 min_x -= SVG_EXPORT_PADDING;
2865 min_y -= SVG_EXPORT_PADDING;
2866 let view_width = (max_x - min_x + SVG_EXPORT_PADDING).max(1.0);
2867 let view_height = (max_y - min_y + SVG_EXPORT_PADDING).max(1.0);
2868 let scale = fontsize / line_height.max(1.0);
2869 let width = (view_width * scale).ceil();
2870 let height = (view_height * scale).ceil();
2871
2872 let mut svg = format!(
2873 "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"{}{}\" height=\"{}{}\" viewBox=\"{} {} {} {}\" overflow=\"visible\">",
2874 width, fontunit, height, fontunit, min_x, min_y, view_width, view_height
2875 );
2876 for element in svg_elements {
2877 svg += &element;
2878 }
2879 svg += "</svg>";
2880 Ok(svg)
2881 }
2882
2883 pub fn get_name(&self, name_id: NameID, locale: &String) -> Result<String, Error> {
2884 let name_table = self
2885 .current_name_table()
2886 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "name table is none"))?;
2887 let platform_id = PlatformID::Windows;
2888 name_table.get_name(name_id, locale, platform_id)
2889 }
2890
2891 pub(crate) fn face_name_by_id(&self, name_id: u16) -> Option<String> {
2892 let locale = "en-US".to_string();
2893 let name_table = if self.current_font == 0 {
2894 self.name_table.as_ref()?
2895 } else {
2896 self.more_fonts[self.current_font - 1].name_table.as_ref()?
2897 };
2898
2899 let names = name_table.get_name_list(&locale, PlatformID::Windows);
2900 names
2901 .get(&name_id)
2902 .cloned()
2903 .or_else(|| name_table.default_namelist.get(&name_id).cloned())
2904 .filter(|name| !name.trim().is_empty())
2905 }
2906
2907 pub(crate) fn face_variation_axes(&self) -> Vec<fvar::VariationAxisRecord> {
2908 self.current_fvar()
2909 .map(|fvar| fvar.axes.clone())
2910 .unwrap_or_default()
2911 }
2912
2913 pub(crate) fn face_family_name(&self) -> String {
2914 let locale = "en-US".to_string();
2915 self.get_name(NameID::TypographicFamilyName, &locale)
2916 .ok()
2917 .filter(|name| !name.trim().is_empty())
2918 .or_else(|| {
2919 self.get_name(NameID::FontFamilyName, &locale)
2920 .ok()
2921 .filter(|name| !name.trim().is_empty())
2922 })
2923 .unwrap_or_else(|| "Unknown Family".to_string())
2924 }
2925
2926 pub(crate) fn face_full_name(&self) -> Option<String> {
2927 let locale = "en-US".to_string();
2928 self.get_name(NameID::FullFontName, &locale)
2929 .ok()
2930 .filter(|name| !name.trim().is_empty())
2931 .or_else(|| {
2932 self.get_name(NameID::PostScriptName, &locale)
2933 .ok()
2934 .filter(|name| !name.trim().is_empty())
2935 })
2936 }
2937
2938 pub(crate) fn face_weight_class(&self) -> u16 {
2939 let os2 = if self.current_font == 0 {
2940 self.os2.as_ref()
2941 } else {
2942 self.more_fonts[self.current_font - 1].os2.as_ref()
2943 };
2944 os2.map(|os2| os2.weight_class()).unwrap_or(400)
2945 }
2946
2947 pub(crate) fn face_width_class(&self) -> u16 {
2948 let os2 = if self.current_font == 0 {
2949 self.os2.as_ref()
2950 } else {
2951 self.more_fonts[self.current_font - 1].os2.as_ref()
2952 };
2953 os2.map(|os2| os2.width_class()).unwrap_or(5)
2954 }
2955
2956 pub(crate) fn face_is_italic(&self) -> bool {
2957 let head_mac_style = if self.current_font == 0 {
2958 self.head.as_ref().map(|head| head.mac_style)
2959 } else {
2960 self.more_fonts[self.current_font - 1]
2961 .head
2962 .as_ref()
2963 .map(|head| head.mac_style)
2964 }
2965 .unwrap_or(0);
2966 if head_mac_style & 0x0002 == 0x0002 {
2967 return true;
2968 }
2969
2970 let post_italic_angle = if self.current_font == 0 {
2971 self.post.as_ref().map(|post| post.italic_angle)
2972 } else {
2973 self.more_fonts[self.current_font - 1]
2974 .post
2975 .as_ref()
2976 .map(|post| post.italic_angle)
2977 }
2978 .unwrap_or(0);
2979 if post_italic_angle != 0 {
2980 return true;
2981 }
2982
2983 let os2_selection = if self.current_font == 0 {
2984 self.os2.as_ref().map(|os2| os2.selection_flags())
2985 } else {
2986 self.more_fonts[self.current_font - 1]
2987 .os2
2988 .as_ref()
2989 .map(|os2| os2.selection_flags())
2990 }
2991 .unwrap_or(0);
2992 os2_selection & 0x0001 == 0x0001
2993 }
2994
2995 #[cfg(debug_assertions)]
2996 pub fn get_name_raw(&self) -> String {
2997 let name = if self.current_font == 0 {
2998 self.name.as_ref()
2999 } else {
3000 self.more_fonts[self.current_font - 1].name.as_ref()
3001 };
3002 Self::debug_optional_table_string(name, "name", |name| name.to_string())
3003 }
3004
3005 #[cfg(debug_assertions)]
3006 pub fn get_maxp_raw(&self) -> String {
3007 let maxp = if self.current_font == 0 {
3008 self.maxp.as_ref()
3009 } else {
3010 self.more_fonts[self.current_font - 1].maxp.as_ref()
3011 };
3012 Self::debug_optional_table_string(maxp, "maxp", |maxp| maxp.to_string())
3013 }
3014
3015 #[cfg(debug_assertions)]
3016 pub fn get_header_raw(&self) -> String {
3017 let head = if self.current_font == 0 {
3018 self.head.as_ref()
3019 } else {
3020 self.more_fonts[self.current_font - 1].head.as_ref()
3021 };
3022 Self::debug_optional_table_string(head, "head", |head| head.to_string())
3023 }
3024
3025 #[cfg(debug_assertions)]
3026 pub fn get_os2_raw(&self) -> String {
3027 let os2 = if self.current_font == 0 {
3028 self.os2.as_ref()
3029 } else {
3030 self.more_fonts[self.current_font - 1].os2.as_ref()
3031 };
3032 Self::debug_optional_table_string(os2, "os2", |os2| os2.to_string())
3033 }
3034
3035 #[cfg(debug_assertions)]
3036 pub fn get_hhea_raw(&self) -> String {
3037 let hhea = if self.current_font == 0 {
3038 self.hhea.as_ref()
3039 } else {
3040 self.more_fonts[self.current_font - 1].hhea.as_ref()
3041 };
3042 Self::debug_optional_table_string(hhea, "hhea", |hhea| hhea.to_string())
3043 }
3044
3045 #[cfg(debug_assertions)]
3046 pub fn get_cmap_raw(&self) -> String {
3047 let cmap = if self.current_font == 0 {
3048 self.cmap.as_ref()
3049 } else {
3050 self.more_fonts[self.current_font - 1].cmap.as_ref()
3051 };
3052 let Some(cmap) = cmap else {
3053 return "cmap is none".to_string();
3054 };
3055 let encodings = &cmap.cmap_encodings;
3056 let mut string = String::new();
3057 for encoding in encodings.as_ref().iter() {
3058 string += &format!(
3059 "Encording Record\n{}\n",
3060 encoding.encoding_record.to_string()
3061 );
3062 string += &format!(
3063 "Subtable\n{}\n",
3064 encoding.cmap_subtable.get_part_of_string(10)
3065 );
3066 }
3067 string
3068 }
3069
3070 #[cfg(debug_assertions)]
3071 pub fn get_sbix_raw(&self) -> String {
3072 let sbix = if self.current_font == 0 {
3073 if let Some(sbix) = &self.sbix {
3074 sbix
3075 } else {
3076 return "sbix is none".to_string();
3077 }
3078 } else {
3079 let sbix = self.more_fonts[self.current_font - 1].sbix.as_ref();
3080 if let Some(sbix) = sbix {
3081 sbix
3082 } else {
3083 return "sbix is none".to_string();
3084 }
3085 };
3086 sbix.to_string()
3087 }
3088
3089 #[cfg(debug_assertions)]
3090 pub fn get_svg_raw(&self) -> String {
3091 let svg = if self.current_font == 0 {
3092 if let Some(svg) = &self.svg {
3093 svg
3094 } else {
3095 return "svg is none".to_string();
3096 }
3097 } else {
3098 let svg = self.more_fonts[self.current_font - 1].svg.as_ref();
3099 if let Some(svg) = svg {
3100 svg
3101 } else {
3102 return "svg is none".to_string();
3103 }
3104 };
3105 svg.to_string()
3106 }
3107
3108 #[cfg(debug_assertions)]
3109 pub fn get_post_raw(&self) -> String {
3110 let post = if self.current_font == 0 {
3111 self.post.as_ref()
3112 } else {
3113 self.more_fonts[self.current_font - 1].post.as_ref()
3114 };
3115 Self::debug_optional_table_string(post, "post", |post| post.to_string())
3116 }
3117
3118 #[cfg(debug_assertions)]
3119 fn debug_optional_table_string<T, F>(table: Option<&T>, name: &str, to_string: F) -> String
3120 where
3121 F: FnOnce(&T) -> String,
3122 {
3123 if let Some(table) = table {
3124 to_string(table)
3125 } else {
3126 format!("{} is none", name)
3127 }
3128 }
3129
3130 #[cfg(debug_assertions)]
3131 pub fn get_cpal_raw(&self) -> String {
3132 let cpal = if self.current_font == 0 {
3133 self.cpal.as_ref()
3134 } else {
3135 self.more_fonts[self.current_font - 1].cpal.as_ref()
3136 };
3137 Self::debug_optional_table_string(cpal, "cpal", |cpal| cpal.to_string())
3138 }
3139
3140 #[cfg(debug_assertions)]
3141 pub fn get_colr_raw(&self) -> String {
3142 let colr = if self.current_font == 0 {
3143 self.colr.as_ref()
3144 } else {
3145 self.more_fonts[self.current_font - 1].colr.as_ref()
3146 };
3147 Self::debug_optional_table_string(colr, "colr", |colr| colr.to_string())
3148 }
3149 #[cfg(debug_assertions)]
3150 #[cfg(feature = "layout")]
3151 pub fn get_vhea_raw(&self) -> String {
3152 let vhea = if self.current_font == 0 {
3153 self.vhea.as_ref()
3154 } else {
3155 self.more_fonts[self.current_font - 1].vhea.as_ref()
3156 };
3157 Self::debug_optional_table_string(vhea, "vhea", |vhea| vhea.to_string())
3158 }
3159
3160 #[cfg(debug_assertions)]
3161 #[cfg(feature = "layout")]
3162 pub fn get_gdef_raw(&self) -> String {
3163 let gdef = if self.current_font == 0 {
3164 self.gdef.as_ref()
3165 } else {
3166 self.more_fonts[self.current_font - 1].gdef.as_ref()
3167 };
3168 Self::debug_optional_table_string(gdef, "gdef", |gdef| gdef.to_string())
3169 }
3170
3171 #[cfg(debug_assertions)]
3172 #[cfg(feature = "layout")]
3173 pub fn get_gsub_raw(&self) -> String {
3174 let gsub = if self.current_font == 0 {
3175 self.gsub.as_ref()
3176 } else {
3177 self.more_fonts[self.current_font - 1].gsub.as_ref()
3178 };
3179 Self::debug_optional_table_string(gsub, "gsub", |gsub| gsub.to_string())
3180 }
3181
3182 pub fn get_html_vert(
3183 &self,
3184 string: &str,
3185 fontsize: f64,
3186 fontunit: &str,
3187 ) -> Result<String, Error> {
3188 let mut html = String::new();
3189 html += "<html>\n";
3190 html += "<head>\n";
3191 html += "<meta charset=\"UTF-8\">\n";
3192 html += "<title>fontreader</title>\n";
3193 html += "<style>body {writing-mode: vertical-rl; }</style>\n";
3194 html += "</head>\n";
3195 html += "<body>\n";
3196 let mut svgs = Vec::new();
3197 for unit in Self::parse_text_units(string) {
3198 self.push_svg_html_unit(&mut svgs, unit, fontsize, fontunit, true)?;
3199 }
3200
3201 for svg in svgs {
3202 html += &svg;
3203 }
3204 html += "</body>\n";
3205 html += "</html>\n";
3206 Ok(html)
3207 }
3208
3209 pub fn get_html(&self, string: &str, fontsize: f64, fontunit: &str) -> Result<String, Error> {
3210 let mut html = String::new();
3211 html += "<html>\n";
3212 html += "<head>\n";
3213 html += "<meta charset=\"UTF-8\">\n";
3214 html += "<title>fontreader</title>\n";
3215 html += "</head>\n";
3216 html += "<body>\n";
3217 let mut svgs = Vec::new();
3218 for unit in Self::parse_text_units(string) {
3219 self.push_svg_html_unit(&mut svgs, unit, fontsize, fontunit, false)?;
3220 }
3221 for svg in svgs {
3222 html += &svg;
3223 }
3224 html += "</body>\n";
3225 html += "</html>\n";
3226 Ok(html)
3227 }
3228
3229 pub fn get_info(&self) -> Result<String, Error> {
3230 let mut string = String::new();
3231 let name = self
3232 .name
3233 .as_ref()
3234 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "name table is none"))?;
3235 let font_famiry = name.get_family_name();
3236 let subfamily_name = name.get_subfamily_name();
3237 string += &format!("Font famiry: {} {}\n", font_famiry, subfamily_name);
3238 for more_font in self.more_fonts.iter() {
3239 let Some(name) = more_font.name.as_ref() else {
3240 continue;
3241 };
3242 let font_famiry = name.get_family_name();
3243 let subfamily_name = name.get_subfamily_name();
3244 string += &format!("Font famiry: {} {}\n", font_famiry, subfamily_name);
3245 }
3246 Ok(string)
3247 }
3248
3249 pub fn get_font_count(&self) -> usize {
3250 self.more_fonts.len() + 1
3251 }
3252
3253 pub fn get_font_number(&self) -> usize {
3254 self.current_font
3255 }
3256
3257 pub fn set_font(&mut self, number: usize) -> Result<(), String> {
3258 if number <= self.more_fonts.len() {
3259 self.current_font = number;
3260 Ok(())
3261 } else {
3262 Err("font number is out of range".to_owned())
3263 }
3264 }
3265}
3266
3267#[derive(Debug, Clone)]
3268pub struct HorizontalLayout {
3269 pub lsb: isize,
3270 pub advance_width: isize,
3271 pub accender: isize,
3272 pub descender: isize,
3273 pub line_gap: isize,
3274 #[allow(dead_code)]
3275 pub(crate) hhea: HHEA,
3276}
3277
3278#[derive(Debug, Clone)]
3279pub struct VerticalLayout {
3280 pub tsb: isize,
3281 pub advance_height: isize,
3282 pub accender: isize,
3283 pub descender: isize,
3284 pub line_gap: isize,
3285 #[allow(dead_code)]
3286 pub(crate) vhea: VHEA,
3287}
3288
3289#[derive(Debug, Clone)]
3290struct Pointer {
3291 pub(crate) offset: u32,
3292 pub(crate) length: u32,
3293}
3294
3295fn font_load_from_file(filename: &PathBuf) -> Result<Font, Error> {
3296 #[cfg(target_arch = "wasm32")]
3297 {
3298 let _ = filename;
3299 return Err(Error::new(
3300 ErrorKind::Unsupported,
3301 "file font loading is not supported on wasm32",
3302 ));
3303 }
3304
3305 #[cfg(not(target_arch = "wasm32"))]
3306 {
3307 let fontdata = std::fs::read(filename)?;
3308 Font::get_font_from_buffer(&fontdata)
3309 }
3310}
3311
3312fn path_commands_to_svg_path(commands: &[PathCommand]) -> String {
3313 let mut d = String::new();
3314 for command in commands {
3315 match command {
3316 PathCommand::MoveTo { x, y } => d += &format!("M{} {} ", x, y),
3317 PathCommand::LineTo { x, y } => d += &format!("L{} {} ", x, y),
3318 PathCommand::QuadTo { cx, cy, x, y } => d += &format!("Q{} {} {} {} ", cx, cy, x, y),
3319 PathCommand::ClosePath => d += "Z ",
3320 }
3321 }
3322 d.trim_end().to_string()
3323}
3324
3325fn path_command_bounds(commands: &[PathCommand]) -> (f64, f64, f64, f64) {
3326 let mut min_x = 0.0;
3327 let mut min_y = 0.0;
3328 let mut max_x = 0.0;
3329 let mut max_y = 0.0;
3330 let mut has_point = false;
3331
3332 let mut add_point = |x: f64, y: f64| {
3333 if !has_point {
3334 min_x = x;
3335 min_y = y;
3336 max_x = x;
3337 max_y = y;
3338 has_point = true;
3339 } else {
3340 min_x = min_x.min(x);
3341 min_y = min_y.min(y);
3342 max_x = max_x.max(x);
3343 max_y = max_y.max(y);
3344 }
3345 };
3346
3347 for command in commands {
3348 match command {
3349 PathCommand::MoveTo { x, y } | PathCommand::LineTo { x, y } => add_point(*x, *y),
3350 PathCommand::QuadTo { cx, cy, x, y } => {
3351 add_point(*cx, *cy);
3352 add_point(*x, *y);
3353 }
3354 PathCommand::ClosePath => {}
3355 }
3356 }
3357
3358 (min_x, min_y, max_x, max_y)
3359}
3360
3361fn bitmap_glyph_to_svg_image(glyph: &GlyphCommands, bitmap: &BitmapGlyphCommands) -> String {
3362 let mime = match bitmap.format {
3363 BitmapGlyphFormat::Png => "image/png",
3364 BitmapGlyphFormat::Jpeg => "image/jpeg",
3365 };
3366 let encoded = general_purpose::STANDARD.encode(&bitmap.data);
3367 format!(
3368 "<image x=\"{}\" y=\"{}\" width=\"{}\" height=\"{}\" href=\"data:{};base64,{}\"/>",
3369 glyph.origin_x + bitmap.offset_x,
3370 glyph.origin_y + bitmap.offset_y,
3371 bitmap.width,
3372 bitmap.height,
3373 mime,
3374 encoded
3375 )
3376}
3377
3378fn glyph_baseline_shift(layout: &FontLayout) -> f32 {
3379 match layout {
3380 FontLayout::Horizontal(layout) => (layout.accender + layout.line_gap) as f32,
3381 FontLayout::Vertical(layout) => (layout.accender - layout.descender) as f32,
3382 FontLayout::Unknown => 0.0,
3383 }
3384}
3385
3386fn transform_glyf_commands(
3387 commands: &[PathCommand],
3388 layout: &FontLayout,
3389 scale_x: f32,
3390 scale_y: f32,
3391) -> Vec<DrawCommand> {
3392 let baseline_shift = glyph_baseline_shift(layout) as f64;
3393 commands
3394 .iter()
3395 .map(|command| match command {
3396 PathCommand::MoveTo { x, y } => {
3397 DrawCommand::MoveTo(*x as f32 * scale_x, (*y - baseline_shift) as f32 * scale_y)
3398 }
3399 PathCommand::LineTo { x, y } => {
3400 DrawCommand::Line(*x as f32 * scale_x, (*y - baseline_shift) as f32 * scale_y)
3401 }
3402 PathCommand::QuadTo { cx, cy, x, y } => DrawCommand::Bezier(
3403 (
3404 *cx as f32 * scale_x,
3405 (*cy - baseline_shift) as f32 * scale_y,
3406 ),
3407 (*x as f32 * scale_x, (*y - baseline_shift) as f32 * scale_y),
3408 ),
3409 PathCommand::ClosePath => DrawCommand::Close,
3410 })
3411 .collect()
3412}
3413
3414fn transform_cff_commands(
3415 commands: &[DrawCommand],
3416 scale_x: f32,
3417 scale_y: f32,
3418) -> Vec<DrawCommand> {
3419 commands
3420 .iter()
3421 .map(|command| match command {
3422 DrawCommand::MoveTo(x, y) => DrawCommand::MoveTo(*x * scale_x, *y * scale_y),
3423 DrawCommand::Line(x, y) => DrawCommand::Line(*x * scale_x, *y * scale_y),
3424 DrawCommand::Bezier((cx, cy), (x, y)) => {
3425 DrawCommand::Bezier((*cx * scale_x, *cy * scale_y), (*x * scale_x, *y * scale_y))
3426 }
3427 DrawCommand::CubicBezier((xa, ya), (xb, yb), (xc, yc)) => DrawCommand::CubicBezier(
3428 (*xa * scale_x, *ya * scale_y),
3429 (*xb * scale_x, *yb * scale_y),
3430 (*xc * scale_x, *yc * scale_y),
3431 ),
3432 DrawCommand::Close => DrawCommand::Close,
3433 })
3434 .collect()
3435}
3436
3437fn font_metrics_from_layout(layout: &FontLayout, scale_y: f32) -> DrawFontMetrics {
3438 match layout {
3439 FontLayout::Horizontal(layout) => DrawFontMetrics {
3440 ascent: layout.accender as f32 * scale_y,
3441 descent: (-layout.descender) as f32 * scale_y,
3442 line_gap: layout.line_gap as f32 * scale_y,
3443 flow: GlyphFlow::Horizontal,
3444 },
3445 FontLayout::Vertical(layout) => DrawFontMetrics {
3446 ascent: layout.accender as f32 * scale_y,
3447 descent: (-layout.descender) as f32 * scale_y,
3448 line_gap: layout.line_gap as f32 * scale_y,
3449 flow: GlyphFlow::Vertical,
3450 },
3451 FontLayout::Unknown => DrawFontMetrics {
3452 ascent: 0.0,
3453 descent: 0.0,
3454 line_gap: 0.0,
3455 flow: GlyphFlow::Horizontal,
3456 },
3457 }
3458}
3459
3460fn glyph_metrics_from_layout(layout: &FontLayout, scale_x: f32, scale_y: f32) -> DrawGlyphMetrics {
3461 match layout {
3462 FontLayout::Horizontal(layout) => DrawGlyphMetrics {
3463 advance_x: layout.advance_width as f32 * scale_x,
3464 advance_y: 0.0,
3465 bearing_x: layout.lsb as f32 * scale_x,
3466 bearing_y: layout.accender as f32 * scale_y,
3467 bounds: None,
3468 },
3469 FontLayout::Vertical(layout) => DrawGlyphMetrics {
3470 advance_x: 0.0,
3471 advance_y: layout.advance_height as f32 * scale_y,
3472 bearing_x: 0.0,
3473 bearing_y: layout.accender as f32 * scale_y,
3474 bounds: None,
3475 },
3476 FontLayout::Unknown => DrawGlyphMetrics::default(),
3477 }
3478}
3479
3480fn glyph_layers_bounds(layers: &[GlyphLayer]) -> Option<GlyphBounds> {
3481 let mut bounds = None;
3482
3483 for layer in layers {
3484 match layer {
3485 GlyphLayer::Path(path) => {
3486 for command in path.commands.iter() {
3487 match command {
3488 DrawCommand::MoveTo(x, y) | DrawCommand::Line(x, y) => {
3489 extend_bounds(&mut bounds, *x + path.offset_x, *y + path.offset_y);
3490 }
3491 DrawCommand::Bezier((cx, cy), (x, y)) => {
3492 extend_bounds(&mut bounds, *cx + path.offset_x, *cy + path.offset_y);
3493 extend_bounds(&mut bounds, *x + path.offset_x, *y + path.offset_y);
3494 }
3495 DrawCommand::CubicBezier((xa, ya), (xb, yb), (xc, yc)) => {
3496 extend_bounds(&mut bounds, *xa + path.offset_x, *ya + path.offset_y);
3497 extend_bounds(&mut bounds, *xb + path.offset_x, *yb + path.offset_y);
3498 extend_bounds(&mut bounds, *xc + path.offset_x, *yc + path.offset_y);
3499 }
3500 DrawCommand::Close => {}
3501 }
3502 }
3503 }
3504 GlyphLayer::Raster(raster) => {
3505 if let (Some(width), Some(height)) = (raster.width, raster.height) {
3506 extend_bounds(&mut bounds, raster.offset_x, raster.offset_y);
3507 extend_bounds(
3508 &mut bounds,
3509 raster.offset_x + width as f32,
3510 raster.offset_y + height as f32,
3511 );
3512 }
3513 }
3514 #[cfg(feature = "svg-fonts")]
3515 GlyphLayer::Svg(svg) => {
3516 extend_bounds(
3517 &mut bounds,
3518 svg.offset_x + svg.view_box_min_x,
3519 svg.offset_y + svg.view_box_min_y,
3520 );
3521 extend_bounds(
3522 &mut bounds,
3523 svg.offset_x + svg.view_box_min_x + svg.view_box_width,
3524 svg.offset_y + svg.view_box_min_y + svg.view_box_height,
3525 );
3526 }
3527 }
3528 }
3529
3530 bounds
3531}
3532
3533fn extend_bounds(bounds: &mut Option<GlyphBounds>, x: f32, y: f32) {
3534 if let Some(bounds) = bounds.as_mut() {
3535 bounds.min_x = bounds.min_x.min(x);
3536 bounds.min_y = bounds.min_y.min(y);
3537 bounds.max_x = bounds.max_x.max(x);
3538 bounds.max_y = bounds.max_y.max(y);
3539 } else {
3540 *bounds = Some(GlyphBounds {
3541 min_x: x,
3542 min_y: y,
3543 max_x: x,
3544 max_y: y,
3545 });
3546 }
3547}
3548
3549#[cfg(debug_assertions)]
3550#[allow(dead_code)]
3551fn font_debug(_font: &Font) {
3552 let filename = "test/font.txt";
3554 let file = match File::create(filename) {
3555 Ok(it) => it,
3556 Err(_) => match File::open("test/font.txt") {
3557 Ok(it) => it,
3558 Err(_) => return,
3559 },
3560 };
3561 let mut writer = BufWriter::new(file);
3562
3563 let Some(cmap) = _font.cmap.as_ref() else {
3564 return;
3565 };
3566 let Some(head) = _font.head.as_ref() else {
3567 return;
3568 };
3569 let Some(hhea) = _font.hhea.as_ref() else {
3570 return;
3571 };
3572 let Some(maxp) = _font.maxp.as_ref() else {
3573 return;
3574 };
3575 let Some(hmtx) = _font.hmtx.as_ref() else {
3576 return;
3577 };
3578 let Some(loca) = _font.loca.as_ref() else {
3579 let _ = writeln!(&mut writer, "loca is none. it is not glyf font.");
3580 return;
3581 };
3582 let Some(glyf) = _font.glyf.as_ref() else {
3583 return;
3584 };
3585
3586 let encoding_records = &cmap.get_encoding_engine();
3587 let _ = writeln!(&mut writer, "{}", cmap.cmap);
3588 for i in 0..encoding_records.len() {
3589 let _ = writeln!(&mut writer, "{} {}", i, encoding_records[i].to_string());
3590 }
3591 let _ = writeln!(&mut writer, "{}", head.to_string());
3592 let _ = writeln!(&mut writer, "{}", hhea.to_string());
3593 let _ = writeln!(&mut writer, "{}", maxp.to_string());
3594 let _ = writeln!(&mut writer, "{}", hmtx.to_string());
3595 if let Some(os2) = _font.os2.as_ref() {
3596 let _ = writeln!(&mut writer, "{}", os2.to_string());
3597 }
3598 if let Some(post) = _font.post.as_ref() {
3599 let _ = writeln!(&mut writer, "{}", post.to_string());
3600 }
3601 if let Some(name) = _font.name.as_ref() {
3602 let _ = writeln!(&mut writer, "{}", name.to_string());
3603 }
3604 let _ = writeln!(&mut writer, "{}", loca.to_string());
3605 if let Some(cpal) = _font.cpal.as_ref() {
3606 let _ = writeln!(&mut writer, "{}", cpal.to_string());
3607 }
3608 if let Some(colr) = _font.colr.as_ref() {
3609 let _ = writeln!(&mut writer, "{}", colr.to_string());
3610 }
3611
3612 let _ = writeln!(&mut writer, "long cmap -> griph");
3613 let cmap_encodings = cmap.clone();
3614 for i in 0x0020..0x0ff {
3615 let pos = cmap_encodings.get_glyph_position(i);
3616 let Some(glyph) = glyf.get_glyph(pos as usize) else {
3617 continue;
3618 };
3619 let layout = _font.get_layout(pos as usize, false);
3620 let svg = glyph.to_svg(32.0, "pt", &layout, 0.0, 0.0);
3621 let Some(ch) = char::from_u32(i) else {
3622 continue;
3623 };
3624 let _ = writeln!(&mut writer, "{}:{:04} ", ch, pos);
3625 let _ = writeln!(&mut writer, "{}", glyph.to_string());
3626 let _ = writeln!(&mut writer, "{}:{:?}", i, layout);
3627 let _ = writeln!(&mut writer, "{}", svg);
3628 }
3629 let _ = writeln!(&mut writer);
3630 for i in 0x4e00..0x4eff {
3631 if i as u32 % 16 == 0 {
3632 let _ = writeln!(&mut writer);
3633 }
3634 let pos = cmap_encodings.get_glyph_position(i as u32);
3635 let Some(glyph) = glyf.get_glyph(pos as usize) else {
3636 continue;
3637 };
3638 let layout = _font.get_layout(pos as usize, false);
3639 let svg = glyph.to_svg(100.0, "px", &layout, 0.0, 0.0);
3640 let Some(ch) = char::from_u32(i as u32) else {
3641 continue;
3642 };
3643 let _ = write!(&mut writer, "{}:{:04} ", ch, pos);
3644 let _ = writeln!(&mut writer, "{}", svg);
3645 }
3646 let _ = writeln!(&mut writer);
3647 let i = 0x2a6b2;
3648 let pos = cmap_encodings.get_glyph_position(i as u32);
3649 if let Some(ch) = char::from_u32(i as u32) {
3650 let _ = writeln!(&mut writer, "{}:{:04} ", ch, pos);
3651 }
3652}
3653
3654fn apply_i16_delta(base: i16, delta: f32) -> i16 {
3655 let value = base as f32 + delta.round();
3656 value.clamp(i16::MIN as f32, i16::MAX as f32) as i16
3657}
3658
3659fn apply_u16_delta(base: u16, delta: f32) -> u16 {
3660 let value = base as f32 + delta.round();
3661 value.clamp(0.0, u16::MAX as f32) as u16
3662}
3663
3664fn tag4(tag: &str) -> u32 {
3665 let mut bytes = [0u8; 4];
3666 bytes.copy_from_slice(tag.as_bytes());
3667 u32::from_be_bytes(bytes)
3668}
3669
3670fn font_load<R: BinaryReader>(file: &mut R) -> Result<Font, Error> {
3671 match fontheader::get_font_type(file)? {
3672 fontheader::FontHeaders::OTF(header) => {
3673 let font = from_opentype(file, &header);
3674 #[cfg(debug_assertions)]
3675 {
3676 }
3678 font
3679 }
3680 fontheader::FontHeaders::TTC(header) => {
3681 let num_fonts = header.num_fonts;
3682 let font_collection = header.font_collection.as_ref();
3683 let table = &font_collection[0];
3684 let mut font = from_opentype(file, table);
3685 #[cfg(debug_assertions)]
3686 {
3687 }
3689
3690 let mut fonts = Vec::new();
3691 for i in 1..num_fonts {
3692 let table = &font_collection[i as usize];
3693 if let Ok(font) = from_opentype(file, table) {
3694 fonts.push(font);
3695 }
3696 }
3697 if let Ok(font) = font.as_mut() {
3698 font.more_fonts = Box::new(fonts);
3699 #[cfg(debug_assertions)]
3700 {
3701 }
3703 }
3704 font
3705 }
3706 fontheader::FontHeaders::WOFF(header) => {
3707 let mut font = Font::empty();
3708 font.font_type = fontheader::FontHeaders::WOFF(header.clone());
3709 let woff = crate::woff::WOFF::from(file, header)?;
3710
3711 let mut hmtx_table = None;
3712 let mut loca_table = None;
3713 let mut glyf_table = None;
3714 let mut sbix_table = None;
3715 let mut vmtx_table = None;
3716 for table in woff.tables {
3717 let tag: [u8; 4] = [
3718 (table.tag >> 24) as u8,
3719 (table.tag >> 16) as u8,
3720 (table.tag >> 8) as u8,
3721 table.tag as u8,
3722 ];
3723 match &tag {
3725 b"cmap" => {
3726 let mut reader = BytesReader::new(&table.data);
3727 let cmap_encodings =
3728 CmapEncodings::new(&mut reader, 0, table.data.len() as u32)?;
3729 font.cmap = Some(cmap_encodings);
3730 }
3731 b"head" => {
3732 let mut reader = BytesReader::new(&table.data);
3733 let head = head::HEAD::new(&mut reader, 0, table.data.len() as u32)?;
3734 font.head = Some(head);
3735 }
3736 b"OS/2" => {
3737 let mut reader = BytesReader::new(&table.data);
3738 let os2 = os2::OS2::new(&mut reader, 0, table.data.len() as u32)?;
3739 font.os2 = Some(os2);
3740 }
3741 b"fvar" => {
3742 let mut reader = BytesReader::new(&table.data);
3743 let fvar = fvar::FVAR::new(&mut reader, 0, table.data.len() as u32)?;
3744 font.fvar = Some(fvar);
3745 }
3746 b"gvar" => {
3747 let mut reader = BytesReader::new(&table.data);
3748 let gvar = gvar::GVAR::new(&mut reader, 0, table.data.len() as u32)?;
3749 font.gvar = Some(gvar);
3750 }
3751 b"avar" => {
3752 let mut reader = BytesReader::new(&table.data);
3753 let avar = avar::AVAR::new(&mut reader, 0, table.data.len() as u32)?;
3754 font.avar = Some(avar);
3755 }
3756 b"hhea" => {
3757 let mut reader = BytesReader::new(&table.data);
3758 let hhea = hhea::HHEA::new(&mut reader, 0, table.data.len() as u32)?;
3759 font.hhea = Some(hhea);
3760 }
3761 b"maxp" => {
3762 let mut reader = BytesReader::new(&table.data);
3763 let maxp = maxp::MAXP::new(&mut reader, 0, table.data.len() as u32)?;
3764 font.maxp = Some(maxp);
3765 }
3766 b"hmtx" => {
3767 hmtx_table = Some(table);
3768 }
3769 b"name" => {
3770 let mut reader = BytesReader::new(&table.data);
3771 let name = name::NAME::new(&mut reader, 0, table.data.len() as u32)?;
3772 let name_table = name::NameTable::new(&name);
3773 font.name = Some(name);
3774 font.name_table = Some(name_table);
3775 }
3776 b"post" => {
3777 let mut reader = BytesReader::new(&table.data);
3778 let post = post::POST::new(&mut reader, 0, table.data.len() as u32)?;
3779 font.post = Some(post);
3780 }
3781 b"loca" => {
3782 loca_table = Some(table);
3783 }
3784 b"glyf" => {
3785 glyf_table = Some(table);
3786 }
3787 b"COLR" => {
3788 let mut reader = BytesReader::new(&table.data);
3789 let colr = colr::COLR::new(&mut reader, 0, table.data.len() as u32)?;
3790 font.colr = Some(colr);
3791 }
3792 b"HVAR" => {
3793 let mut reader = BytesReader::new(&table.data);
3794 let hvar = hvar::HVAR::new(&mut reader, 0, table.data.len() as u32)?;
3795 font.hvar = Some(hvar);
3796 }
3797 b"MVAR" => {
3798 let mut reader = BytesReader::new(&table.data);
3799 let mvar = mvar::MVAR::new(&mut reader, 0, table.data.len() as u32)?;
3800 font.mvar = Some(mvar);
3801 }
3802 b"CPAL" => {
3803 let mut reader = BytesReader::new(&table.data);
3804 let cpal = cpal::CPAL::new(&mut reader, 0, table.data.len() as u32)?;
3805 font.cpal = Some(cpal);
3806 }
3807 b"sbix" => {
3808 sbix_table = Some(table);
3809 }
3810 b"SVG " => {
3811 let mut reader = BytesReader::new(&table.data);
3812 let svg = svg::SVG::new(&mut reader, 0, table.data.len() as u32)?;
3813 font.svg = Some(svg);
3814 }
3815 #[cfg(feature = "cff")]
3816 b"CFF " => {
3817 let mut reader = BytesReader::new(&table.data);
3818 let cff = cff::CFF::new(&mut reader, 0, table.data.len() as u32).map_err(
3819 |err| Error::new(std::io::ErrorKind::InvalidData, err.to_string()),
3820 )?;
3821 font.cff = Some(cff);
3822 font.outline_format = GlyphFormat::CFF;
3823 }
3824 #[cfg(feature = "cff")]
3825 b"CFF2" => {
3826 let mut reader = BytesReader::new(&table.data);
3827 let cff = cff::CFF::new(&mut reader, 0, table.data.len() as u32).map_err(
3828 |err| Error::new(std::io::ErrorKind::InvalidData, err.to_string()),
3829 )?;
3830 font.cff = Some(cff);
3831 font.outline_format = GlyphFormat::CFF2;
3832 }
3833 #[cfg(feature = "layout")]
3834 b"GPOS" => {
3835 let mut reader = BytesReader::new(&table.data);
3836 let gpos = gpos::GPOS::new(&mut reader, 0, table.data.len() as u32)?;
3837 font.gpos = Some(gpos);
3838 }
3839 #[cfg(feature = "layout")]
3840 b"GSUB" => {
3841 let mut reader = BytesReader::new(&table.data);
3842 let gsub = gsub::GSUB::new(&mut reader, 0, table.data.len() as u32)?;
3843 font.gsub = Some(gsub);
3844 }
3845 #[cfg(feature = "layout")]
3846 b"GDEF" => {
3847 let mut reader = BytesReader::new(&table.data);
3848 let gdef = gdef::GDEF::new(&mut reader, 0, table.data.len() as usize)?;
3849 font.gdef = Some(gdef);
3850 }
3851 b"vhea" => {
3852 let mut reader = BytesReader::new(&table.data);
3853 let vhea = vhea::VHEA::new(&mut reader, 0, table.data.len() as u32)?;
3854 font.vhea = Some(vhea);
3855 }
3856 b"vmtx" => {
3857 vmtx_table = Some(table);
3858 }
3859 b"VVAR" => {
3860 let mut reader = BytesReader::new(&table.data);
3861 let vvar = vvar::VVAR::new(&mut reader, 0, table.data.len() as u32)?;
3862 font.vvar = Some(vvar);
3863 }
3864 _ => {}
3865 }
3866 }
3867 let hmtx_table = hmtx_table
3868 .as_ref()
3869 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No hmtx table"))?;
3870 let hhea = font
3871 .hhea
3872 .as_ref()
3873 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No hhea table"))?;
3874 let maxp = font
3875 .maxp
3876 .as_ref()
3877 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No maxp table"))?;
3878 let mut reader = BytesReader::new(&hmtx_table.data);
3879 let hmtx = hmtx::HMTX::new(
3880 &mut reader,
3881 0,
3882 hmtx_table.data.len() as u32,
3883 hhea.number_of_hmetrics,
3884 maxp.num_glyphs,
3885 )?;
3886 font.hmtx = Some(hmtx);
3887 if let Some(vmtx_table) = vmtx_table {
3888 let mut reader = BytesReader::new(&vmtx_table.data);
3889 let vhea = font
3890 .vhea
3891 .as_ref()
3892 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No vhea table"))?;
3893 let vmtx = vmtx::VMTX::new(
3894 &mut reader,
3895 0,
3896 vmtx_table.data.len() as u32,
3897 vhea.number_of_vmetrics,
3898 maxp.num_glyphs,
3899 )?;
3900 font.vmtx = Some(vmtx);
3901 }
3902 if let (Some(loca_table), Some(glyf_table)) = (loca_table.as_ref(), glyf_table.as_ref())
3903 {
3904 let mut reader = BytesReader::new(&loca_table.data);
3905 let head = font
3906 .head
3907 .as_ref()
3908 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No head table"))?;
3909 let index_to_loc_format = head.index_to_loc_format as usize;
3910 let loca = loca::LOCA::new_by_size(
3911 &mut reader,
3912 0,
3913 loca_table.data.len() as u32,
3914 index_to_loc_format,
3915 )?;
3916 font.loca = Some(loca);
3917 let mut reader = BytesReader::new(&glyf_table.data);
3918 let loca = font
3919 .loca
3920 .as_ref()
3921 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No loca table"))?;
3922 let glyf = glyf::GLYF::new(&mut reader, 0, glyf_table.data.len() as u32, loca);
3923 font.glyf = Some(glyf);
3924 font.outline_format = GlyphFormat::OpenTypeGlyph;
3925 }
3926
3927 if let Some(sbix_table) = sbix_table {
3928 let mut reader = BytesReader::new(&sbix_table.data);
3929 let num_glyphs = maxp.num_glyphs as u32;
3930 let sbix =
3931 sbix::SBIX::new(&mut reader, 0, sbix_table.data.len() as u32, num_glyphs)?;
3932 font.sbix = Some(sbix);
3933 }
3934 #[cfg(debug_assertions)]
3935 {
3936 }
3938 Ok(font)
3939 }
3940 fontheader::FontHeaders::WOFF2(_) => todo!(),
3941 fontheader::FontHeaders::Unknown => {
3942 Err(Error::new(
3944 std::io::ErrorKind::Other,
3945 "Unknown font type".to_string(),
3946 ))
3947 }
3948 }
3949}
3950
3951fn from_opentype<R: BinaryReader>(file: &mut R, header: &OTFHeader) -> Result<Font, Error> {
3952 let mut font = Font::empty();
3953 font.font_type = fontheader::FontHeaders::OTF(header.clone());
3954
3955 let records = header.table_records.as_ref();
3956
3957 for record in records.iter() {
3958 let tag: [u8; 4] = record.table_tag.to_be_bytes();
3959 match &tag {
3960 b"cmap" => {
3961 let cmap_encodings = CmapEncodings::new(file, record.offset, record.length)?;
3962 font.cmap = Some(cmap_encodings);
3963 }
3964 b"head" => {
3965 let head = head::HEAD::new(file, record.offset, record.length)?;
3966 font.head = Some(head);
3967 }
3968 b"fvar" => {
3969 let fvar = fvar::FVAR::new(file, record.offset, record.length)?;
3970 font.fvar = Some(fvar);
3971 }
3972 b"gvar" => {
3973 let gvar = gvar::GVAR::new(file, record.offset, record.length)?;
3974 font.gvar = Some(gvar);
3975 }
3976 b"avar" => {
3977 let avar = avar::AVAR::new(file, record.offset, record.length)?;
3978 font.avar = Some(avar);
3979 }
3980 b"hhea" => {
3981 let hhea = hhea::HHEA::new(file, record.offset, record.length)?;
3982 font.hhea = Some(hhea);
3983 }
3984 b"hmtx" => {
3985 let htmx_pos = Pointer {
3986 offset: record.offset,
3987 length: record.length,
3988 };
3989 font.hmtx_pos = Some(htmx_pos);
3990 }
3991 b"maxp" => {
3992 let maxp = maxp::MAXP::new(file, record.offset, record.length)?;
3993 font.maxp = Some(maxp);
3994 }
3995 b"name" => {
3996 let name = name::NAME::new(file, record.offset, record.length)?;
3997 let name_table = name::NameTable::new(&name);
3998 font.name = Some(name);
3999 font.name_table = Some(name_table);
4000 }
4001 b"OS/2" => {
4002 let os2 = os2::OS2::new(file, record.offset, record.length)?;
4003 font.os2 = Some(os2);
4004 }
4005 b"post" => {
4006 let post = post::POST::new(file, record.offset, record.length)?;
4007 font.post = Some(post);
4008 }
4009 b"loca" => {
4010 let loca_pos = Pointer {
4011 offset: record.offset,
4012 length: record.length,
4013 };
4014 font.loca_pos = Some(loca_pos);
4015 }
4016 b"glyf" => {
4017 let glyf_pos = Pointer {
4018 offset: record.offset,
4019 length: record.length,
4020 };
4021 font.glyf_pos = Some(glyf_pos);
4022 }
4023 b"COLR" => {
4024 let colr = colr::COLR::new(file, record.offset, record.length)?;
4025 font.colr = Some(colr);
4026 }
4027 b"HVAR" => {
4028 let hvar = hvar::HVAR::new(file, record.offset, record.length)?;
4029 font.hvar = Some(hvar);
4030 }
4031 b"MVAR" => {
4032 let mvar = mvar::MVAR::new(file, record.offset, record.length)?;
4033 font.mvar = Some(mvar);
4034 }
4035 b"CPAL" => {
4036 let cpal = cpal::CPAL::new(file, record.offset, record.length)?;
4037 font.cpal = Some(cpal);
4038 }
4039 b"sbix" => {
4040 let sbix_pos = Pointer {
4041 offset: record.offset,
4042 length: record.length,
4043 };
4044 font.sbix_pos = Some(sbix_pos);
4045 }
4046 b"SVG " => {
4047 let svg = svg::SVG::new(file, record.offset, record.length)?;
4048 font.svg = Some(svg);
4049 }
4050 #[cfg(feature = "cff")]
4051 b"CFF " => {
4052 let cff = cff::CFF::new(file, record.offset, record.length)
4053 .map_err(|err| Error::new(std::io::ErrorKind::InvalidData, err.to_string()))?;
4054 font.cff = Some(cff);
4055 font.outline_format = GlyphFormat::CFF;
4056 }
4057 #[cfg(feature = "cff")]
4058 b"CFF2" => {
4059 let cff = cff::CFF::new(file, record.offset, record.length)
4060 .map_err(|err| Error::new(std::io::ErrorKind::InvalidData, err.to_string()))?;
4061 font.cff = Some(cff);
4062 font.outline_format = GlyphFormat::CFF2;
4063 }
4064 #[cfg(feature = "layout")]
4065 b"GPOS" => {
4066 let gpos = gpos::GPOS::new(file, record.offset, record.length)?;
4067 font.gpos = Some(gpos);
4068 }
4069 #[cfg(feature = "layout")]
4070 b"GSUB" => {
4071 let gsub = gsub::GSUB::new(file, record.offset, record.length)?;
4072 font.gsub = Some(gsub);
4073 #[cfg(debug_assertions)]
4074 {
4075 }
4077 }
4078 #[cfg(feature = "layout")]
4079 b"GDEF" => {
4080 let gdef = gdef::GDEF::new(file, record.offset as u64, record.length as usize)?;
4081 font.gdef = Some(gdef);
4082 #[cfg(debug_assertions)]
4083 {
4084 }
4086 }
4087 #[cfg(feature = "layout")]
4088 b"vhea" => {
4089 let vhea = vhea::VHEA::new(file, record.offset, record.length)?;
4090 font.vhea = Some(vhea);
4091 }
4092 #[cfg(feature = "layout")]
4093 b"vmtx" => {
4094 let vmtx_pos = Pointer {
4095 offset: record.offset,
4096 length: record.length,
4097 };
4098 font.vmtx_pos = Some(vmtx_pos);
4099 }
4100 b"VVAR" => {
4101 let vvar = vvar::VVAR::new(file, record.offset, record.length)?;
4102 font.vvar = Some(vvar);
4103 }
4104 _ => {
4105 debug_assert!(true, "Unknown table tag")
4106 }
4107 }
4108 }
4109
4110 let num_glyphs = font
4111 .maxp
4112 .as_ref()
4113 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No maxp table"))?
4114 .num_glyphs;
4115 let number_of_hmetrics = font
4116 .hhea
4117 .as_ref()
4118 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No hhea table"))?
4119 .number_of_hmetrics;
4120 let hmtx_pointer = font
4121 .hmtx_pos
4122 .as_ref()
4123 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No hmtx table pointer"))?;
4124 let offset = hmtx_pointer.offset;
4125 let length = hmtx_pointer.length;
4126
4127 let hmtx = hmtx::HMTX::new(file, offset, length, number_of_hmetrics, num_glyphs)?;
4128 font.hmtx = Some(hmtx);
4129
4130 if font.vmtx_pos.is_some() {
4131 let number_of_vmetrics = font
4132 .vhea
4133 .as_ref()
4134 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No vhea table"))?
4135 .number_of_vmetrics;
4136 let vmtx_pointer = font
4137 .vmtx_pos
4138 .as_ref()
4139 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No vmtx table pointer"))?;
4140 let offset = vmtx_pointer.offset;
4141 let length = vmtx_pointer.length;
4142 let vmtx = vmtx::VMTX::new(file, offset, length, number_of_vmetrics, num_glyphs)?;
4143 font.vmtx = Some(vmtx);
4144 }
4145
4146 if let Some(offset) = font.loca_pos.as_ref() {
4147 let length = offset.length;
4148 let index_to_loc_format = font
4149 .head
4150 .as_ref()
4151 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No head table"))?
4152 .index_to_loc_format as usize;
4153 let loca = loca::LOCA::new_by_size(file, offset.offset, length, index_to_loc_format)?;
4154 font.loca = Some(loca);
4155 let glyf_pointer = font
4156 .glyf_pos
4157 .as_ref()
4158 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No glyf table pointer"))?;
4159 let offset = glyf_pointer.offset;
4160 let length = glyf_pointer.length;
4161 let loca = font
4162 .loca
4163 .as_ref()
4164 .ok_or_else(|| Error::new(std::io::ErrorKind::Other, "No loca table"))?;
4165 let glyf = glyf::GLYF::new(file, offset, length, loca);
4166 font.glyf = Some(glyf);
4167 font.outline_format = GlyphFormat::OpenTypeGlyph;
4168 }
4169 if let Some(offset) = font.sbix_pos.as_ref() {
4170 let sbix = sbix::SBIX::new(file, offset.offset, offset.length, num_glyphs as u32)?;
4171 font.sbix = Some(sbix);
4172 }
4173
4174 if font.cmap.is_none() {
4175 debug_assert!(true, "No cmap table");
4176 return Err(Error::new(
4177 std::io::ErrorKind::Other,
4178 "No cmap table".to_string(),
4179 ));
4180 }
4181 if font.head.is_none() {
4182 debug_assert!(true, "No head table");
4183 return Err(Error::new(
4184 std::io::ErrorKind::Other,
4185 "No head table".to_string(),
4186 ));
4187 }
4188 if font.hhea.is_none() {
4189 debug_assert!(true, "No hhea table");
4190 return Err(Error::new(
4191 std::io::ErrorKind::Other,
4192 "No hhea table".to_string(),
4193 ));
4194 }
4195 if font.hmtx.is_none() {
4196 debug_assert!(true, "No hmtx table");
4197 return Err(Error::new(
4198 std::io::ErrorKind::Other,
4199 "No hmtx table".to_string(),
4200 ));
4201 }
4202 if font.maxp.is_none() {
4203 debug_assert!(true, "No maxp table");
4204 return Err(Error::new(
4205 std::io::ErrorKind::Other,
4206 "No maxp table".to_string(),
4207 ));
4208 }
4209 if font.name.is_none() {
4210 debug_assert!(true, "No name table");
4211 return Err(Error::new(
4212 std::io::ErrorKind::Other,
4213 "No name table".to_string(),
4214 ));
4215 }
4216
4217 Ok(font)
4218}