summaryrefslogtreecommitdiff
path: root/font/src/directwrite/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'font/src/directwrite/mod.rs')
-rw-r--r--font/src/directwrite/mod.rs335
1 files changed, 0 insertions, 335 deletions
diff --git a/font/src/directwrite/mod.rs b/font/src/directwrite/mod.rs
deleted file mode 100644
index d139b35e..00000000
--- a/font/src/directwrite/mod.rs
+++ /dev/null
@@ -1,335 +0,0 @@
-//! Rasterization powered by DirectWrite.
-
-use std::borrow::Cow;
-use std::collections::HashMap;
-use std::ffi::OsString;
-use std::fmt::{self, Display, Formatter};
-use std::os::windows::ffi::OsStringExt;
-
-use dwrote::{
- FontCollection, FontFace, FontFallback, FontStretch, FontStyle, FontWeight, GlyphOffset,
- GlyphRunAnalysis, TextAnalysisSource, TextAnalysisSourceMethods, DWRITE_GLYPH_RUN,
-};
-
-use winapi::shared::ntdef::{HRESULT, LOCALE_NAME_MAX_LENGTH};
-use winapi::um::dwrite;
-use winapi::um::winnls::GetUserDefaultLocaleName;
-
-use super::{
- BitmapBuffer, FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight,
-};
-
-/// Cached DirectWrite font.
-struct Font {
- face: FontFace,
- family_name: String,
- weight: FontWeight,
- style: FontStyle,
- stretch: FontStretch,
-}
-
-pub struct DirectWriteRasterizer {
- fonts: HashMap<FontKey, Font>,
- keys: HashMap<FontDesc, FontKey>,
- device_pixel_ratio: f32,
- available_fonts: FontCollection,
- fallback_sequence: Option<FontFallback>,
-}
-
-impl DirectWriteRasterizer {
- fn rasterize_glyph(
- &self,
- face: &FontFace,
- size: Size,
- c: char,
- ) -> Result<RasterizedGlyph, Error> {
- let glyph_index = self.get_glyph_index(face, c)?;
-
- let em_size = em_size(size);
-
- let glyph_run = DWRITE_GLYPH_RUN {
- fontFace: unsafe { face.as_ptr() },
- fontEmSize: em_size,
- glyphCount: 1,
- glyphIndices: &glyph_index,
- glyphAdvances: &0.0,
- glyphOffsets: &GlyphOffset::default(),
- isSideways: 0,
- bidiLevel: 0,
- };
-
- let rendering_mode = face.get_recommended_rendering_mode_default_params(
- em_size,
- self.device_pixel_ratio,
- dwrote::DWRITE_MEASURING_MODE_NATURAL,
- );
-
- let glyph_analysis = GlyphRunAnalysis::create(
- &glyph_run,
- self.device_pixel_ratio,
- None,
- rendering_mode,
- dwrote::DWRITE_MEASURING_MODE_NATURAL,
- 0.0,
- 0.0,
- )
- .map_err(Error::DirectWriteError)?;
-
- let bounds = glyph_analysis
- .get_alpha_texture_bounds(dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1)
- .map_err(Error::DirectWriteError)?;
-
- let buf = glyph_analysis
- .create_alpha_texture(dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1, bounds)
- .map_err(Error::DirectWriteError)?;
-
- Ok(RasterizedGlyph {
- c,
- width: (bounds.right - bounds.left) as i32,
- height: (bounds.bottom - bounds.top) as i32,
- top: -bounds.top,
- left: bounds.left,
- buf: BitmapBuffer::RGB(buf),
- })
- }
-
- fn get_loaded_font(&self, font_key: FontKey) -> Result<&Font, Error> {
- self.fonts.get(&font_key).ok_or(Error::FontNotLoaded)
- }
-
- fn get_glyph_index(&self, face: &FontFace, c: char) -> Result<u16, Error> {
- let idx = *face
- .get_glyph_indices(&[c as u32])
- .first()
- // DirectWrite returns 0 if the glyph does not exist in the font.
- .filter(|glyph_index| **glyph_index != 0)
- .ok_or_else(|| Error::MissingGlyph(c))?;
-
- Ok(idx)
- }
-
- fn get_fallback_font(&self, loaded_font: &Font, c: char) -> Option<dwrote::Font> {
- let fallback = self.fallback_sequence.as_ref()?;
-
- let mut buf = [0u16; 2];
- c.encode_utf16(&mut buf);
-
- let length = c.len_utf16() as u32;
- let utf16_codepoints = &buf[..length as usize];
-
- let locale = get_current_locale();
-
- let text_analysis_source_data = TextAnalysisSourceData { locale: &locale, length };
- let text_analysis_source = TextAnalysisSource::from_text(
- Box::new(text_analysis_source_data),
- Cow::Borrowed(utf16_codepoints),
- );
-
- let fallback_result = fallback.map_characters(
- &text_analysis_source,
- 0,
- length,
- &self.available_fonts,
- Some(&loaded_font.family_name),
- loaded_font.weight,
- loaded_font.style,
- loaded_font.stretch,
- );
-
- fallback_result.mapped_font
- }
-}
-
-impl crate::Rasterize for DirectWriteRasterizer {
- type Err = Error;
-
- fn new(device_pixel_ratio: f32, _: bool) -> Result<DirectWriteRasterizer, Error> {
- Ok(DirectWriteRasterizer {
- fonts: HashMap::new(),
- keys: HashMap::new(),
- device_pixel_ratio,
- available_fonts: FontCollection::system(),
- fallback_sequence: FontFallback::get_system_fallback(),
- })
- }
-
- fn metrics(&self, key: FontKey, size: Size) -> Result<Metrics, Error> {
- let face = &self.get_loaded_font(key)?.face;
- let vmetrics = face.metrics().metrics0();
-
- let scale = em_size(size) * self.device_pixel_ratio / f32::from(vmetrics.designUnitsPerEm);
-
- let underline_position = f32::from(vmetrics.underlinePosition) * scale;
- let underline_thickness = f32::from(vmetrics.underlineThickness) * scale;
-
- let strikeout_position = f32::from(vmetrics.strikethroughPosition) * scale;
- let strikeout_thickness = f32::from(vmetrics.strikethroughThickness) * scale;
-
- let ascent = f32::from(vmetrics.ascent) * scale;
- let descent = -f32::from(vmetrics.descent) * scale;
- let line_gap = f32::from(vmetrics.lineGap) * scale;
-
- let line_height = f64::from(ascent - descent + line_gap);
-
- // Since all monospace characters have the same width, we use `!` for horizontal metrics.
- let c = '!';
- let glyph_index = self.get_glyph_index(face, c)?;
-
- let glyph_metrics = face.get_design_glyph_metrics(&[glyph_index], false);
- let hmetrics = glyph_metrics.first().ok_or_else(|| Error::MissingGlyph(c))?;
-
- let average_advance = f64::from(hmetrics.advanceWidth) * f64::from(scale);
-
- Ok(Metrics {
- descent,
- average_advance,
- line_height,
- underline_position,
- underline_thickness,
- strikeout_position,
- strikeout_thickness,
- })
- }
-
- fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result<FontKey, Error> {
- // Fast path if face is already loaded.
- if let Some(key) = self.keys.get(desc) {
- return Ok(*key);
- }
-
- let family = self
- .available_fonts
- .get_font_family_by_name(&desc.name)
- .ok_or_else(|| Error::MissingFont(desc.clone()))?;
-
- let font = match desc.style {
- Style::Description { weight, slant } => {
- // This searches for the "best" font - should mean we don't have to worry about
- // fallbacks if our exact desired weight/style isn't available.
- Ok(family.get_first_matching_font(weight.into(), FontStretch::Normal, slant.into()))
- },
- Style::Specific(ref style) => {
- let mut idx = 0;
- let count = family.get_font_count();
-
- loop {
- if idx == count {
- break Err(Error::MissingFont(desc.clone()));
- }
-
- let font = family.get_font(idx);
-
- if font.face_name() == *style {
- break Ok(font);
- }
-
- idx += 1;
- }
- },
- }?;
-
- let key = FontKey::next();
- self.keys.insert(desc.clone(), key);
- self.fonts.insert(key, font.into());
-
- Ok(key)
- }
-
- fn get_glyph(&mut self, glyph: GlyphKey) -> Result<RasterizedGlyph, Error> {
- let loaded_font = self.get_loaded_font(glyph.font_key)?;
-
- match self.rasterize_glyph(&loaded_font.face, glyph.size, glyph.c) {
- Err(err @ Error::MissingGlyph(_)) => {
- let fallback_font = self.get_fallback_font(&loaded_font, glyph.c).ok_or(err)?;
- self.rasterize_glyph(&fallback_font.create_font_face(), glyph.size, glyph.c)
- },
- result => result,
- }
- }
-
- fn update_dpr(&mut self, device_pixel_ratio: f32) {
- self.device_pixel_ratio = device_pixel_ratio;
- }
-}
-
-#[derive(Debug)]
-pub enum Error {
- MissingFont(FontDesc),
- MissingGlyph(char),
- FontNotLoaded,
- DirectWriteError(HRESULT),
-}
-
-impl std::error::Error for Error {}
-
-impl Display for Error {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- match self {
- Error::MissingGlyph(c) => write!(f, "Glyph not found for char {:?}", c),
- Error::MissingFont(desc) => write!(f, "Unable to find the font {}", desc),
- Error::FontNotLoaded => f.write_str("Tried to use a font that hasn't been loaded"),
- Error::DirectWriteError(hresult) => {
- write!(f, "A DirectWrite rendering error occurred: {:#X}", hresult)
- },
- }
- }
-}
-
-fn em_size(size: Size) -> f32 {
- size.as_f32_pts() * (96.0 / 72.0)
-}
-
-impl From<dwrote::Font> for Font {
- fn from(font: dwrote::Font) -> Font {
- Font {
- face: font.create_font_face(),
- family_name: font.family_name(),
- weight: font.weight(),
- style: font.style(),
- stretch: font.stretch(),
- }
- }
-}
-
-impl From<Weight> for FontWeight {
- fn from(weight: Weight) -> FontWeight {
- match weight {
- Weight::Bold => FontWeight::Bold,
- Weight::Normal => FontWeight::Regular,
- }
- }
-}
-
-impl From<Slant> for FontStyle {
- fn from(slant: Slant) -> FontStyle {
- match slant {
- Slant::Oblique => FontStyle::Oblique,
- Slant::Italic => FontStyle::Italic,
- Slant::Normal => FontStyle::Normal,
- }
- }
-}
-
-fn get_current_locale() -> String {
- let mut buf = vec![0u16; LOCALE_NAME_MAX_LENGTH];
- let len = unsafe { GetUserDefaultLocaleName(buf.as_mut_ptr(), buf.len() as i32) as usize };
-
- // `len` includes null byte, which we don't need in Rust.
- OsString::from_wide(&buf[..len - 1]).into_string().expect("Locale not valid unicode")
-}
-
-/// Font fallback information for dwrote's TextAnalysisSource.
-struct TextAnalysisSourceData<'a> {
- locale: &'a str,
- length: u32,
-}
-
-impl TextAnalysisSourceMethods for TextAnalysisSourceData<'_> {
- fn get_locale_name(&self, _text_position: u32) -> (Cow<str>, u32) {
- (Cow::Borrowed(self.locale), self.length)
- }
-
- fn get_paragraph_reading_direction(&self) -> dwrite::DWRITE_READING_DIRECTION {
- dwrite::DWRITE_READING_DIRECTION_LEFT_TO_RIGHT
- }
-}