1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use crate::{
	ImtError, ImtErrorSrc, ImtErrorTy, ImtGlyph, ImtLang, ImtParser, ImtRaster, ImtRasterOpts,
	ImtScript, ImtShapeOpts, ImtShaper,
};
use std::{fs::File, io::Read, path::Path, sync::Arc};
use vulkano::device::{Device, Queue};

#[derive(Debug, Clone, PartialEq, Hash, Eq)]
pub enum ImtWeight {
	Thin,
	ExtraLight,
	Light,
	Normal,
	Medium,
	SemiBold,
	Bold,
	ExtraBold,
	UltraBold,
}

#[derive(Debug, Clone, PartialEq, Hash, Eq)]
pub(crate) struct ImtFontKey {
	pub family: String,
	pub weight: ImtWeight,
}

pub struct ImtFont {
	family: String,
	weight: ImtWeight,
	parser: ImtParser,
	shaper: ImtShaper,
	raster: ImtRaster,
}

impl ImtFont {
	pub fn from_file<F: Into<String>, P: AsRef<Path>>(
		family: F,
		weight: ImtWeight,
		raster_ops: ImtRasterOpts,
		device: Arc<Device>,
		queue: Arc<Queue>,
		path: P,
	) -> Result<ImtFont, ImtError> {
		let mut handle = File::open(path.as_ref())
			.map_err(|_| ImtError::src_and_ty(ImtErrorSrc::File, ImtErrorTy::FileRead))?;
		let mut bytes = Vec::new();
		handle
			.read_to_end(&mut bytes)
			.map_err(|_| ImtError::src_and_ty(ImtErrorSrc::File, ImtErrorTy::FileRead))?;
		Self::from_bytes(family, weight, raster_ops, device, queue, bytes)
	}

	pub fn from_bytes<F: Into<String>>(
		family: F,
		weight: ImtWeight,
		raster_ops: ImtRasterOpts,
		device: Arc<Device>,
		queue: Arc<Queue>,
		bytes: Vec<u8>,
	) -> Result<ImtFont, ImtError> {
		let parser = ImtParser::new(bytes)?;
		let shaper = ImtShaper::new()?;
		let raster = ImtRaster::new(device, queue, raster_ops)?;

		Ok(ImtFont {
			family: family.into(),
			weight,
			parser,
			shaper,
			raster,
		})
	}

	pub(crate) fn key(&self) -> ImtFontKey {
		ImtFontKey {
			family: self.family.clone(),
			weight: self.weight.clone(),
		}
	}

	pub fn glyphs_for_text<T: AsRef<str>>(
		&self,
		text_height: f32,
		shape_ops: ImtShapeOpts,
		text: T,
	) -> Result<Vec<ImtGlyph>, ImtError> {
		// TODO: Auto detect script/lang or require params to specify?
		let script = ImtScript::Default;
		let lang = ImtLang::Default;
		let parsed_glyphs = self.parser.retreive_text(text, script, lang)?;
		let shaped_glyphs = self.shaper.shape_parsed_glyphs(
			&self.parser,
			script,
			lang,
			shape_ops,
			parsed_glyphs,
		)?;
		let rastered_glyphs =
			self.raster.raster_shaped_glyphs(&self.parser, text_height, shaped_glyphs)?;
		let font_props = self.parser.font_props();

		Ok(rastered_glyphs
			.into_iter()
			.map(|g| {
				ImtGlyph {
					x: (g.shaped.position.x * font_props.scaler * text_height)
						+ g.bitmap.bearing_x,
					y: (g.shaped.position.y * font_props.scaler * text_height)
						+ g.bitmap.bearing_y,
					w: g.bitmap.width,
					h: g.bitmap.height,
					crop_x: g.shaped.x_overflow * font_props.scaler * text_height,
					crop_y: g.shaped.y_overflow * font_props.scaler * text_height,
					family: self.family.clone(),
					weight: self.weight.clone(),
					index: g.shaped.parsed.inner.glyph_index,
					bitmap: g.bitmap.data.clone(),
				}
			})
			.collect())
	}
}