use { crate::{ cmm::cmm_eotf::Eotf, format::ARGB8888, gfx_api::{GfxContext, GfxTexture}, pango::{ CairoContext, CairoImageSurface, PangoCairoContext, PangoFontDescription, PangoLayout, consts::{CAIRO_FORMAT_ARGB32, CAIRO_OPERATOR_SOURCE}, }, rect::Rect, theme::Color, }, std::{ops::Neg, rc::Rc, sync::Arc}, }; struct Data { image: Rc, cctx: Rc, _pctx: Rc, _fd: PangoFontDescription, layout: PangoLayout, } fn create_data(font: &str, width: i32, height: i32, scale: Option) -> Option { let image = CairoImageSurface::new_image_surface(CAIRO_FORMAT_ARGB32, width, height).ok()?; let cctx = image.create_context().ok()?; let pctx = cctx.create_pango_context().ok()?; let mut fd = PangoFontDescription::from_string(font); if let Some(scale) = scale { fd.set_size((fd.size() as f64 * scale).round() as _); } let layout = pctx.create_layout().ok()?; layout.set_font_description(&fd); Some(Data { image, cctx, _pctx: pctx, _fd: fd, layout, }) } fn measure(font: &str, text: &str, scale: Option, full: bool) -> Option { let data = create_data(font, 1, 1, scale)?; data.layout.set_text(text); let mut res = TextMeasurement::default(); res.ink_rect = data.layout.inc_pixel_rect(); if full { res.logical_rect = data.layout.logical_pixel_rect(); res.baseline = data.layout.pixel_baseline(); } Some(res) } #[derive(Debug, Copy, Clone, Default)] pub struct TextMeasurement { pub ink_rect: Rect, pub logical_rect: Rect, pub baseline: i32, } pub fn render( ctx: &Rc, height: Option, font: &Arc, text: &str, color: Color, scale: Option, include_measurements: bool, ) -> Option<(Rc, TextMeasurement)> { let measurement = measure(font, text, scale, include_measurements)?; let y = match height { Some(_) => None, _ => Some(measurement.ink_rect.y1().neg()), }; let x = measurement.ink_rect.x1().neg(); let width = measurement.ink_rect.width(); let height = height.unwrap_or(measurement.ink_rect.height()); let data = create_data(font, width, height, scale)?; data.layout.set_text(text); let font_height = data.layout.pixel_size().1; let [r, g, b, a] = color.to_array(Eotf::Gamma22); data.cctx.set_operator(CAIRO_OPERATOR_SOURCE); data.cctx.set_source_rgba(r as _, g as _, b as _, a as _); let y = y.unwrap_or((height - font_height) / 2); data.cctx.move_to(x as f64, y as f64); data.layout.show_layout(); data.image.flush(); let bytes = data.image.data().ok()?; ctx.clone() .shmem_texture( None, bytes, ARGB8888, width, height, data.image.stride(), None, ) .ok() .map(|t| (t as _, measurement)) }