mod font_id;
mod font_load;
mod rasterize;
mod shaping;
mod system_fonts;
use crate::{
FontHandle, FontMetrics, GlyphBitmap, GlyphBounds, ShapedLine, TextBackend, TextError,
backend::Font, types::FontDescriptor,
};
use std::cell::RefCell;
use std::collections::HashMap;
use std::marker::PhantomData;
use windows::Win32::Graphics::DirectWrite::{
DWRITE_FACTORY_TYPE_SHARED, DWriteCreateFactory, IDWriteFactory, IDWriteFactory5,
IDWriteFontFace, IDWriteInMemoryFontFileLoader, IDWriteTextFormat,
};
use windows::core::Interface;
pub struct DirectWriteBackend {
factory: IDWriteFactory5,
loader: IDWriteInMemoryFontFileLoader,
font_registry: RefCell<HashMap<FontHandle, Box<DirectWriteFont>>>,
_not_send: PhantomData<*const ()>,
}
impl DirectWriteBackend {
pub fn new() -> Result<Self, TextError> {
let factory_base: IDWriteFactory =
unsafe { DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED) }
.map_err(|e| TextError::BackendInit(format!("DWriteCreateFactory: {}", e)))?;
let factory: IDWriteFactory5 = factory_base
.cast()
.map_err(|e| TextError::BackendInit(format!("Cast to IDWriteFactory5: {}", e)))?;
let loader: IDWriteInMemoryFontFileLoader = unsafe {
factory.CreateInMemoryFontFileLoader()
}
.map_err(|e| TextError::BackendInit(format!("CreateInMemoryFontFileLoader: {}", e)))?;
unsafe { factory.RegisterFontFileLoader(&loader) }
.map_err(|e| TextError::BackendInit(format!("RegisterFontFileLoader: {}", e)))?;
Ok(Self {
factory,
loader,
font_registry: RefCell::new(HashMap::new()),
_not_send: PhantomData,
})
}
fn build_substitute_font(
face: IDWriteFontFace,
size_lpx: f32,
scale: f32,
handle: FontHandle,
) -> DirectWriteFont {
let metrics = font_load::extract_metrics(&face, size_lpx);
DirectWriteFont {
font_face: face,
em_size_dip: size_lpx,
pixels_per_dip: scale,
size_lpx,
scale,
metrics,
text_format: None,
handle,
}
}
fn shape_with_direction(
&self,
font: &DirectWriteFont,
text: &str,
forced_direction: Option<crate::types::Direction>,
) -> Result<ShapedLine, TextError> {
let text_format = font.text_format.as_ref().ok_or_else(|| {
TextError::ShapingFailed(
"DirectWriteFont has no IDWriteTextFormat (substitute-only)".into(),
)
})?;
let result = shaping::shape_line(
&self.factory,
text_format,
text,
&font.metrics,
font.size_lpx,
font.scale,
font.handle,
forced_direction,
)?;
if !result.captured_faces.is_empty() {
let mut reg = self.font_registry.borrow_mut();
for cf in result.captured_faces {
reg.entry(cf.handle).or_insert_with(|| {
Box::new(Self::build_substitute_font(
cf.face,
font.size_lpx,
font.scale,
cf.handle,
))
});
}
}
Ok(result.line)
}
}
impl Drop for DirectWriteBackend {
fn drop(&mut self) {
if let Err(e) = unsafe { self.factory.UnregisterFontFileLoader(&self.loader) } {
log::warn!("DirectWrite font loader unregister failed: {e}");
}
}
}
pub struct DirectWriteFont {
pub(crate) font_face: IDWriteFontFace,
pub(crate) em_size_dip: f32,
pub(crate) pixels_per_dip: f32,
pub(crate) size_lpx: f32,
pub(crate) scale: f32,
pub(crate) metrics: FontMetrics,
pub(crate) text_format: Option<IDWriteTextFormat>,
pub(crate) handle: FontHandle,
}
impl Font for DirectWriteFont {
fn handle(&self) -> FontHandle {
self.handle
}
fn metrics(&self) -> FontMetrics {
self.metrics
}
fn size_lpx(&self) -> f32 {
self.size_lpx
}
fn scale(&self) -> f32 {
self.scale
}
}
impl TextBackend for DirectWriteBackend {
type Font = DirectWriteFont;
fn load_font(
&mut self,
family: &str,
size_lpx: f32,
scale: f32,
) -> Result<Self::Font, TextError> {
font_load::load_system_font(&self.factory, family, size_lpx, scale)
}
fn load_font_from_bytes(
&mut self,
bytes: &'static [u8],
size_lpx: f32,
scale: f32,
) -> Result<Self::Font, TextError> {
font_load::load_font_from_bytes(&self.factory, &self.loader, bytes, size_lpx, scale)
}
fn shape_line(&self, font: &Self::Font, text: &str) -> Result<ShapedLine, TextError> {
self.shape_with_direction(font, text, None)
}
fn shape_segment(
&self,
font: &Self::Font,
text: &str,
direction: crate::types::Direction,
) -> Result<ShapedLine, TextError> {
self.shape_with_direction(font, text, Some(direction))
}
fn font_for(&self, handle: FontHandle) -> Option<&Self::Font> {
let map = self.font_registry.borrow();
let ptr: *const DirectWriteFont = map.get(&handle).map(|b| &**b as *const _)?;
drop(map);
Some(unsafe { &*ptr })
}
fn rasterize_glyph(
&self,
font: &Self::Font,
glyph_id: u32,
variant: u8,
) -> Result<GlyphBitmap, TextError> {
let glyph_id_u16 =
u16::try_from(glyph_id).map_err(|_| TextError::GlyphNotFound { glyph_id })?;
rasterize::rasterize(
&self.factory,
&font.font_face,
font.em_size_dip,
font.pixels_per_dip,
glyph_id_u16,
variant,
)
}
fn glyph_raster_bounds(
&self,
font: &Self::Font,
glyph_id: u32,
) -> Result<GlyphBounds, TextError> {
let glyph_id_u16 =
u16::try_from(glyph_id).map_err(|_| TextError::GlyphNotFound { glyph_id })?;
rasterize::get_glyph_bounds(
&self.factory,
&font.font_face,
font.em_size_dip,
glyph_id_u16,
)
}
fn enumerate_system_fonts(&self) -> Result<Vec<FontDescriptor>, TextError> {
system_fonts::enumerate_system_fonts(&self.factory)
}
}