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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
use crate::{
    ImtError, ImtErrorSrc, ImtErrorTy, ImtGlyph, ImtLang, ImtParser, ImtRaster, ImtRasterOpts,
    ImtScript, ImtShapeOpts, ImtShaper,
};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::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_gpu<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_gpu(family, weight, raster_ops, device, queue, bytes)
    }

    pub fn from_file_cpu<F: Into<String>, P: AsRef<Path>>(
        family: F,
        weight: ImtWeight,
        raster_ops: ImtRasterOpts,
        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_cpu(family, weight, raster_ops, bytes)
    }

    pub fn from_bytes_gpu<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_gpu(device, queue, raster_ops)?;

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

    pub fn from_bytes_cpu<F: Into<String>>(
        family: F,
        weight: ImtWeight,
        raster_ops: ImtRasterOpts,
        bytes: Vec<u8>,
    ) -> Result<ImtFont, ImtError> {
        let parser = ImtParser::new(bytes)?;
        let shaper = ImtShaper::new()?;
        let raster = ImtRaster::new_cpu(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| {
                let bitmap_metrics = g.bitmap.metrics();

                ImtGlyph {
                    x: (g.shaped.position.x * font_props.scaler * text_height)
                        + bitmap_metrics.bearing_x,
                    y: (g.shaped.position.y * font_props.scaler * text_height)
                        + bitmap_metrics.bearing_y,
                    w: bitmap_metrics.width,
                    h: bitmap_metrics.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(),
                }
            })
            .collect())
    }
}