direct_write.rs

   1use std::{borrow::Cow, mem::ManuallyDrop, sync::Arc};
   2
   3use ::util::ResultExt;
   4use anyhow::{anyhow, Result};
   5use collections::HashMap;
   6use itertools::Itertools;
   7use parking_lot::{RwLock, RwLockUpgradableReadGuard};
   8use smallvec::SmallVec;
   9use windows::{
  10    core::*,
  11    Win32::{
  12        Foundation::*,
  13        Globalization::GetUserDefaultLocaleName,
  14        Graphics::{
  15            Direct2D::{Common::*, *},
  16            DirectWrite::*,
  17            Dxgi::Common::*,
  18            Gdi::LOGFONTW,
  19            Imaging::{D2D::IWICImagingFactory2, *},
  20        },
  21        System::{Com::*, SystemServices::LOCALE_NAME_MAX_LENGTH},
  22        UI::WindowsAndMessaging::*,
  23    },
  24};
  25
  26use crate::*;
  27
  28#[derive(Debug)]
  29struct FontInfo {
  30    font_family: String,
  31    font_face: IDWriteFontFace3,
  32    features: IDWriteTypography,
  33    is_system_font: bool,
  34    is_emoji: bool,
  35}
  36
  37pub(crate) struct DirectWriteTextSystem(RwLock<DirectWriteState>);
  38
  39struct DirectWriteComponent {
  40    locale: String,
  41    factory: IDWriteFactory5,
  42    bitmap_factory: ManuallyDrop<IWICImagingFactory2>,
  43    d2d1_factory: ID2D1Factory,
  44    in_memory_loader: IDWriteInMemoryFontFileLoader,
  45    builder: IDWriteFontSetBuilder1,
  46    text_renderer: Arc<TextRendererWrapper>,
  47    render_context: GlyphRenderContext,
  48}
  49
  50struct GlyphRenderContext {
  51    params: IDWriteRenderingParams3,
  52    dc_target: ID2D1DeviceContext4,
  53}
  54
  55// All use of the IUnknown methods should be "thread-safe".
  56unsafe impl Sync for DirectWriteComponent {}
  57unsafe impl Send for DirectWriteComponent {}
  58
  59struct DirectWriteState {
  60    components: DirectWriteComponent,
  61    system_ui_font_name: SharedString,
  62    system_font_collection: IDWriteFontCollection1,
  63    custom_font_collection: IDWriteFontCollection1,
  64    fonts: Vec<FontInfo>,
  65    font_selections: HashMap<Font, FontId>,
  66    font_id_by_identifier: HashMap<FontIdentifier, FontId>,
  67}
  68
  69#[derive(Debug, Clone, Hash, PartialEq, Eq)]
  70struct FontIdentifier {
  71    postscript_name: String,
  72    weight: i32,
  73    style: i32,
  74}
  75
  76impl DirectWriteComponent {
  77    pub fn new() -> Result<Self> {
  78        unsafe {
  79            let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?;
  80            let bitmap_factory: IWICImagingFactory2 =
  81                CoCreateInstance(&CLSID_WICImagingFactory2, None, CLSCTX_INPROC_SERVER)?;
  82            let bitmap_factory = ManuallyDrop::new(bitmap_factory);
  83            let d2d1_factory: ID2D1Factory =
  84                D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, None)?;
  85            // The `IDWriteInMemoryFontFileLoader` here is supported starting from
  86            // Windows 10 Creators Update, which consequently requires the entire
  87            // `DirectWriteTextSystem` to run on `win10 1703`+.
  88            let in_memory_loader = factory.CreateInMemoryFontFileLoader()?;
  89            factory.RegisterFontFileLoader(&in_memory_loader)?;
  90            let builder = factory.CreateFontSetBuilder()?;
  91            let mut locale_vec = vec![0u16; LOCALE_NAME_MAX_LENGTH as usize];
  92            GetUserDefaultLocaleName(&mut locale_vec);
  93            let locale = String::from_utf16_lossy(&locale_vec);
  94            let text_renderer = Arc::new(TextRendererWrapper::new(&locale));
  95            let render_context = GlyphRenderContext::new(&factory, &d2d1_factory)?;
  96
  97            Ok(DirectWriteComponent {
  98                locale,
  99                factory,
 100                bitmap_factory,
 101                d2d1_factory,
 102                in_memory_loader,
 103                builder,
 104                text_renderer,
 105                render_context,
 106            })
 107        }
 108    }
 109}
 110
 111impl GlyphRenderContext {
 112    pub fn new(factory: &IDWriteFactory5, d2d1_factory: &ID2D1Factory) -> Result<Self> {
 113        unsafe {
 114            let default_params: IDWriteRenderingParams3 =
 115                factory.CreateRenderingParams()?.cast()?;
 116            let gamma = default_params.GetGamma();
 117            let enhanced_contrast = default_params.GetEnhancedContrast();
 118            let gray_contrast = default_params.GetGrayscaleEnhancedContrast();
 119            let cleartype_level = default_params.GetClearTypeLevel();
 120            let grid_fit_mode = default_params.GetGridFitMode();
 121
 122            let params = factory.CreateCustomRenderingParams(
 123                gamma,
 124                enhanced_contrast,
 125                gray_contrast,
 126                cleartype_level,
 127                DWRITE_PIXEL_GEOMETRY_RGB,
 128                DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC,
 129                grid_fit_mode,
 130            )?;
 131            let dc_target = {
 132                let target = d2d1_factory.CreateDCRenderTarget(&get_render_target_property(
 133                    DXGI_FORMAT_B8G8R8A8_UNORM,
 134                    D2D1_ALPHA_MODE_PREMULTIPLIED,
 135                ))?;
 136                let target = target.cast::<ID2D1DeviceContext4>()?;
 137                target.SetTextRenderingParams(&params);
 138                target
 139            };
 140
 141            Ok(Self { params, dc_target })
 142        }
 143    }
 144}
 145
 146impl DirectWriteTextSystem {
 147    pub(crate) fn new() -> Result<Self> {
 148        let components = DirectWriteComponent::new()?;
 149        let system_font_collection = unsafe {
 150            let mut result = std::mem::zeroed();
 151            components
 152                .factory
 153                .GetSystemFontCollection(false, &mut result, true)?;
 154            result.unwrap()
 155        };
 156        let custom_font_set = unsafe { components.builder.CreateFontSet()? };
 157        let custom_font_collection = unsafe {
 158            components
 159                .factory
 160                .CreateFontCollectionFromFontSet(&custom_font_set)?
 161        };
 162        let system_ui_font_name = get_system_ui_font_name();
 163
 164        Ok(Self(RwLock::new(DirectWriteState {
 165            components,
 166            system_ui_font_name,
 167            system_font_collection,
 168            custom_font_collection,
 169            fonts: Vec::new(),
 170            font_selections: HashMap::default(),
 171            font_id_by_identifier: HashMap::default(),
 172        })))
 173    }
 174}
 175
 176impl PlatformTextSystem for DirectWriteTextSystem {
 177    fn add_fonts(&self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {
 178        self.0.write().add_fonts(fonts)
 179    }
 180
 181    fn all_font_names(&self) -> Vec<String> {
 182        self.0.read().all_font_names()
 183    }
 184
 185    fn all_font_families(&self) -> Vec<String> {
 186        self.0.read().all_font_families()
 187    }
 188
 189    fn font_id(&self, font: &Font) -> Result<FontId> {
 190        let lock = self.0.upgradable_read();
 191        if let Some(font_id) = lock.font_selections.get(font) {
 192            Ok(*font_id)
 193        } else {
 194            let mut lock = RwLockUpgradableReadGuard::upgrade(lock);
 195            let font_id = lock.select_font(font);
 196            lock.font_selections.insert(font.clone(), font_id);
 197            Ok(font_id)
 198        }
 199    }
 200
 201    fn font_metrics(&self, font_id: FontId) -> FontMetrics {
 202        self.0.read().font_metrics(font_id)
 203    }
 204
 205    fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
 206        self.0.read().get_typographic_bounds(font_id, glyph_id)
 207    }
 208
 209    fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<Size<f32>> {
 210        self.0.read().get_advance(font_id, glyph_id)
 211    }
 212
 213    fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
 214        self.0.read().glyph_for_char(font_id, ch)
 215    }
 216
 217    fn glyph_raster_bounds(
 218        &self,
 219        params: &RenderGlyphParams,
 220    ) -> anyhow::Result<Bounds<DevicePixels>> {
 221        self.0.read().raster_bounds(params)
 222    }
 223
 224    fn rasterize_glyph(
 225        &self,
 226        params: &RenderGlyphParams,
 227        raster_bounds: Bounds<DevicePixels>,
 228    ) -> anyhow::Result<(Size<DevicePixels>, Vec<u8>)> {
 229        self.0.read().rasterize_glyph(params, raster_bounds)
 230    }
 231
 232    fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout {
 233        self.0
 234            .write()
 235            .layout_line(text, font_size, runs)
 236            .log_err()
 237            .unwrap_or(LineLayout {
 238                font_size,
 239                ..Default::default()
 240            })
 241    }
 242
 243    fn destroy(&self) {
 244        let mut lock = self.0.write();
 245        unsafe { ManuallyDrop::drop(&mut lock.components.bitmap_factory) };
 246    }
 247}
 248
 249impl DirectWriteState {
 250    fn add_fonts(&mut self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {
 251        for font_data in fonts {
 252            match font_data {
 253                Cow::Borrowed(data) => unsafe {
 254                    let font_file = self
 255                        .components
 256                        .in_memory_loader
 257                        .CreateInMemoryFontFileReference(
 258                            &self.components.factory,
 259                            data.as_ptr() as _,
 260                            data.len() as _,
 261                            None,
 262                        )?;
 263                    self.components.builder.AddFontFile(&font_file)?;
 264                },
 265                Cow::Owned(data) => unsafe {
 266                    let font_file = self
 267                        .components
 268                        .in_memory_loader
 269                        .CreateInMemoryFontFileReference(
 270                            &self.components.factory,
 271                            data.as_ptr() as _,
 272                            data.len() as _,
 273                            None,
 274                        )?;
 275                    self.components.builder.AddFontFile(&font_file)?;
 276                },
 277            }
 278        }
 279        let set = unsafe { self.components.builder.CreateFontSet()? };
 280        let collection = unsafe {
 281            self.components
 282                .factory
 283                .CreateFontCollectionFromFontSet(&set)?
 284        };
 285        self.custom_font_collection = collection;
 286
 287        Ok(())
 288    }
 289
 290    unsafe fn generate_font_features(
 291        &self,
 292        font_features: &FontFeatures,
 293    ) -> Result<IDWriteTypography> {
 294        let direct_write_features = self.components.factory.CreateTypography()?;
 295        apply_font_features(&direct_write_features, font_features)?;
 296        Ok(direct_write_features)
 297    }
 298
 299    unsafe fn get_font_id_from_font_collection(
 300        &mut self,
 301        family_name: &str,
 302        font_weight: FontWeight,
 303        font_style: FontStyle,
 304        font_features: &FontFeatures,
 305        is_system_font: bool,
 306    ) -> Option<FontId> {
 307        let collection = if is_system_font {
 308            &self.system_font_collection
 309        } else {
 310            &self.custom_font_collection
 311        };
 312        let fontset = collection.GetFontSet().log_err()?;
 313        let font = fontset
 314            .GetMatchingFonts(
 315                &HSTRING::from(family_name),
 316                font_weight.into(),
 317                DWRITE_FONT_STRETCH_NORMAL,
 318                font_style.into(),
 319            )
 320            .log_err()?;
 321        let total_number = font.GetFontCount();
 322        for index in 0..total_number {
 323            let Some(font_face_ref) = font.GetFontFaceReference(index).log_err() else {
 324                continue;
 325            };
 326            let Some(font_face) = font_face_ref.CreateFontFace().log_err() else {
 327                continue;
 328            };
 329            let Some(identifier) = get_font_identifier(&font_face, &self.components.locale) else {
 330                continue;
 331            };
 332            let is_emoji = font_face.IsColorFont().as_bool();
 333            let Some(direct_write_features) = self.generate_font_features(font_features).log_err()
 334            else {
 335                continue;
 336            };
 337            let font_info = FontInfo {
 338                font_family: family_name.to_owned(),
 339                font_face,
 340                is_system_font,
 341                features: direct_write_features,
 342                is_emoji,
 343            };
 344            let font_id = FontId(self.fonts.len());
 345            self.fonts.push(font_info);
 346            self.font_id_by_identifier.insert(identifier, font_id);
 347            return Some(font_id);
 348        }
 349        None
 350    }
 351
 352    unsafe fn update_system_font_collection(&mut self) {
 353        let mut collection = std::mem::zeroed();
 354        if self
 355            .components
 356            .factory
 357            .GetSystemFontCollection(false, &mut collection, true)
 358            .log_err()
 359            .is_some()
 360        {
 361            self.system_font_collection = collection.unwrap();
 362        }
 363    }
 364
 365    fn select_font(&mut self, target_font: &Font) -> FontId {
 366        unsafe {
 367            if target_font.family == ".SystemUIFont" {
 368                let family = self.system_ui_font_name.clone();
 369                self.find_font_id(
 370                    family.as_ref(),
 371                    target_font.weight,
 372                    target_font.style,
 373                    &target_font.features,
 374                )
 375                .unwrap()
 376            } else {
 377                self.find_font_id(
 378                    target_font.family.as_ref(),
 379                    target_font.weight,
 380                    target_font.style,
 381                    &target_font.features,
 382                )
 383                .unwrap_or_else(|| {
 384                    let family = self.system_ui_font_name.clone();
 385                    log::error!("{} not found, use {} instead.", target_font.family, family);
 386                    self.get_font_id_from_font_collection(
 387                        family.as_ref(),
 388                        target_font.weight,
 389                        target_font.style,
 390                        &target_font.features,
 391                        true,
 392                    )
 393                    .unwrap()
 394                })
 395            }
 396        }
 397    }
 398
 399    unsafe fn find_font_id(
 400        &mut self,
 401        family_name: &str,
 402        weight: FontWeight,
 403        style: FontStyle,
 404        features: &FontFeatures,
 405    ) -> Option<FontId> {
 406        // try to find target font in custom font collection first
 407        self.get_font_id_from_font_collection(family_name, weight, style, features, false)
 408            .or_else(|| {
 409                self.get_font_id_from_font_collection(family_name, weight, style, features, true)
 410            })
 411            .or_else(|| {
 412                self.update_system_font_collection();
 413                self.get_font_id_from_font_collection(family_name, weight, style, features, true)
 414            })
 415    }
 416
 417    fn layout_line(
 418        &mut self,
 419        text: &str,
 420        font_size: Pixels,
 421        font_runs: &[FontRun],
 422    ) -> Result<LineLayout> {
 423        if font_runs.is_empty() {
 424            return Ok(LineLayout {
 425                font_size,
 426                ..Default::default()
 427            });
 428        }
 429        unsafe {
 430            let text_renderer = self.components.text_renderer.clone();
 431            let text_wide = text.encode_utf16().collect_vec();
 432
 433            let mut utf8_offset = 0usize;
 434            let mut utf16_offset = 0u32;
 435            let text_layout = {
 436                let first_run = &font_runs[0];
 437                let font_info = &self.fonts[first_run.font_id.0];
 438                let collection = if font_info.is_system_font {
 439                    &self.system_font_collection
 440                } else {
 441                    &self.custom_font_collection
 442                };
 443                let format = self.components.factory.CreateTextFormat(
 444                    &HSTRING::from(&font_info.font_family),
 445                    collection,
 446                    font_info.font_face.GetWeight(),
 447                    font_info.font_face.GetStyle(),
 448                    DWRITE_FONT_STRETCH_NORMAL,
 449                    font_size.0,
 450                    &HSTRING::from(&self.components.locale),
 451                )?;
 452
 453                let layout = self.components.factory.CreateTextLayout(
 454                    &text_wide,
 455                    &format,
 456                    f32::INFINITY,
 457                    f32::INFINITY,
 458                )?;
 459                let current_text = &text[utf8_offset..(utf8_offset + first_run.len)];
 460                utf8_offset += first_run.len;
 461                let current_text_utf16_length = current_text.encode_utf16().count() as u32;
 462                let text_range = DWRITE_TEXT_RANGE {
 463                    startPosition: utf16_offset,
 464                    length: current_text_utf16_length,
 465                };
 466                layout.SetTypography(&font_info.features, text_range)?;
 467                utf16_offset += current_text_utf16_length;
 468
 469                layout
 470            };
 471
 472            let mut first_run = true;
 473            let mut ascent = Pixels::default();
 474            let mut descent = Pixels::default();
 475            for run in font_runs {
 476                if first_run {
 477                    first_run = false;
 478                    let mut metrics = vec![DWRITE_LINE_METRICS::default(); 4];
 479                    let mut line_count = 0u32;
 480                    text_layout.GetLineMetrics(Some(&mut metrics), &mut line_count as _)?;
 481                    ascent = px(metrics[0].baseline);
 482                    descent = px(metrics[0].height - metrics[0].baseline);
 483                    continue;
 484                }
 485                let font_info = &self.fonts[run.font_id.0];
 486                let current_text = &text[utf8_offset..(utf8_offset + run.len)];
 487                utf8_offset += run.len;
 488                let current_text_utf16_length = current_text.encode_utf16().count() as u32;
 489
 490                let collection = if font_info.is_system_font {
 491                    &self.system_font_collection
 492                } else {
 493                    &self.custom_font_collection
 494                };
 495                let text_range = DWRITE_TEXT_RANGE {
 496                    startPosition: utf16_offset,
 497                    length: current_text_utf16_length,
 498                };
 499                utf16_offset += current_text_utf16_length;
 500                text_layout.SetFontCollection(collection, text_range)?;
 501                text_layout
 502                    .SetFontFamilyName(&HSTRING::from(&font_info.font_family), text_range)?;
 503                text_layout.SetFontSize(font_size.0, text_range)?;
 504                text_layout.SetFontStyle(font_info.font_face.GetStyle(), text_range)?;
 505                text_layout.SetFontWeight(font_info.font_face.GetWeight(), text_range)?;
 506                text_layout.SetTypography(&font_info.features, text_range)?;
 507            }
 508
 509            let mut runs = Vec::new();
 510            let renderer_context = RendererContext {
 511                text_system: self,
 512                index_converter: StringIndexConverter::new(text),
 513                runs: &mut runs,
 514                utf16_index: 0,
 515                width: 0.0,
 516            };
 517            text_layout.Draw(
 518                Some(&renderer_context as *const _ as _),
 519                &text_renderer.0,
 520                0.0,
 521                0.0,
 522            )?;
 523            let width = px(renderer_context.width);
 524
 525            Ok(LineLayout {
 526                font_size,
 527                width,
 528                ascent,
 529                descent,
 530                runs,
 531                len: text.len(),
 532            })
 533        }
 534    }
 535
 536    fn font_metrics(&self, font_id: FontId) -> FontMetrics {
 537        unsafe {
 538            let font_info = &self.fonts[font_id.0];
 539            let mut metrics = std::mem::zeroed();
 540            font_info.font_face.GetMetrics(&mut metrics);
 541
 542            FontMetrics {
 543                units_per_em: metrics.Base.designUnitsPerEm as _,
 544                ascent: metrics.Base.ascent as _,
 545                descent: -(metrics.Base.descent as f32),
 546                line_gap: metrics.Base.lineGap as _,
 547                underline_position: metrics.Base.underlinePosition as _,
 548                underline_thickness: metrics.Base.underlineThickness as _,
 549                cap_height: metrics.Base.capHeight as _,
 550                x_height: metrics.Base.xHeight as _,
 551                bounding_box: Bounds {
 552                    origin: Point {
 553                        x: metrics.glyphBoxLeft as _,
 554                        y: metrics.glyphBoxBottom as _,
 555                    },
 556                    size: Size {
 557                        width: (metrics.glyphBoxRight - metrics.glyphBoxLeft) as _,
 558                        height: (metrics.glyphBoxTop - metrics.glyphBoxBottom) as _,
 559                    },
 560                },
 561            }
 562        }
 563    }
 564
 565    fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
 566        let render_target = &self.components.render_context.dc_target;
 567        unsafe {
 568            render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS);
 569            render_target.SetDpi(96.0 * params.scale_factor, 96.0 * params.scale_factor);
 570        }
 571        let font = &self.fonts[params.font_id.0];
 572        let glyph_id = [params.glyph_id.0 as u16];
 573        let advance = [0.0f32];
 574        let offset = [DWRITE_GLYPH_OFFSET::default()];
 575        let glyph_run = DWRITE_GLYPH_RUN {
 576            fontFace: unsafe { std::mem::transmute_copy(&font.font_face) },
 577            fontEmSize: params.font_size.0,
 578            glyphCount: 1,
 579            glyphIndices: glyph_id.as_ptr(),
 580            glyphAdvances: advance.as_ptr(),
 581            glyphOffsets: offset.as_ptr(),
 582            isSideways: BOOL(0),
 583            bidiLevel: 0,
 584        };
 585        let bounds = unsafe {
 586            render_target.GetGlyphRunWorldBounds(
 587                D2D_POINT_2F { x: 0.0, y: 0.0 },
 588                &glyph_run,
 589                DWRITE_MEASURING_MODE_NATURAL,
 590            )?
 591        };
 592        // todo(windows)
 593        // This is a walkaround, deleted when figured out.
 594        let y_offset;
 595        let extra_height;
 596        if params.is_emoji {
 597            y_offset = 0;
 598            extra_height = 0;
 599        } else {
 600            // make some room for scaler.
 601            y_offset = -1;
 602            extra_height = 2;
 603        }
 604
 605        if bounds.right < bounds.left {
 606            Ok(Bounds {
 607                origin: point(0.into(), 0.into()),
 608                size: size(0.into(), 0.into()),
 609            })
 610        } else {
 611            Ok(Bounds {
 612                origin: point(
 613                    ((bounds.left * params.scale_factor).ceil() as i32).into(),
 614                    ((bounds.top * params.scale_factor).ceil() as i32 + y_offset).into(),
 615                ),
 616                size: size(
 617                    (((bounds.right - bounds.left) * params.scale_factor).ceil() as i32).into(),
 618                    (((bounds.bottom - bounds.top) * params.scale_factor).ceil() as i32
 619                        + extra_height)
 620                        .into(),
 621                ),
 622            })
 623        }
 624    }
 625
 626    fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
 627        let font_info = &self.fonts[font_id.0];
 628        let codepoints = [ch as u32];
 629        let mut glyph_indices = vec![0u16; 1];
 630        unsafe {
 631            font_info
 632                .font_face
 633                .GetGlyphIndices(codepoints.as_ptr(), 1, glyph_indices.as_mut_ptr())
 634                .log_err()
 635        }
 636        .map(|_| GlyphId(glyph_indices[0] as u32))
 637    }
 638
 639    fn rasterize_glyph(
 640        &self,
 641        params: &RenderGlyphParams,
 642        glyph_bounds: Bounds<DevicePixels>,
 643    ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
 644        if glyph_bounds.size.width.0 == 0 || glyph_bounds.size.height.0 == 0 {
 645            return Err(anyhow!("glyph bounds are empty"));
 646        }
 647
 648        let font_info = &self.fonts[params.font_id.0];
 649        let glyph_id = [params.glyph_id.0 as u16];
 650        let advance = [glyph_bounds.size.width.0 as f32];
 651        let offset = [DWRITE_GLYPH_OFFSET {
 652            advanceOffset: -glyph_bounds.origin.x.0 as f32 / params.scale_factor,
 653            ascenderOffset: glyph_bounds.origin.y.0 as f32 / params.scale_factor,
 654        }];
 655        let glyph_run = DWRITE_GLYPH_RUN {
 656            fontFace: unsafe { std::mem::transmute_copy(&font_info.font_face) },
 657            fontEmSize: params.font_size.0,
 658            glyphCount: 1,
 659            glyphIndices: glyph_id.as_ptr(),
 660            glyphAdvances: advance.as_ptr(),
 661            glyphOffsets: offset.as_ptr(),
 662            isSideways: BOOL(0),
 663            bidiLevel: 0,
 664        };
 665
 666        // Add an extra pixel when the subpixel variant isn't zero to make room for anti-aliasing.
 667        let mut bitmap_size = glyph_bounds.size;
 668        if params.subpixel_variant.x > 0 {
 669            bitmap_size.width += DevicePixels(1);
 670        }
 671        if params.subpixel_variant.y > 0 {
 672            bitmap_size.height += DevicePixels(1);
 673        }
 674        let bitmap_size = bitmap_size;
 675
 676        let total_bytes;
 677        let bitmap_format;
 678        let render_target_property;
 679        let bitmap_width;
 680        let bitmap_height;
 681        let bitmap_stride;
 682        let bitmap_dpi;
 683        if params.is_emoji {
 684            total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize * 4;
 685            bitmap_format = &GUID_WICPixelFormat32bppPBGRA;
 686            render_target_property = get_render_target_property(
 687                DXGI_FORMAT_B8G8R8A8_UNORM,
 688                D2D1_ALPHA_MODE_PREMULTIPLIED,
 689            );
 690            bitmap_width = bitmap_size.width.0 as u32;
 691            bitmap_height = bitmap_size.height.0 as u32;
 692            bitmap_stride = bitmap_size.width.0 as u32 * 4;
 693            bitmap_dpi = 96.0;
 694        } else {
 695            total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize;
 696            bitmap_format = &GUID_WICPixelFormat8bppAlpha;
 697            render_target_property =
 698                get_render_target_property(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_STRAIGHT);
 699            bitmap_width = bitmap_size.width.0 as u32 * 2;
 700            bitmap_height = bitmap_size.height.0 as u32 * 2;
 701            bitmap_stride = bitmap_size.width.0 as u32;
 702            bitmap_dpi = 192.0;
 703        }
 704
 705        unsafe {
 706            let bitmap = self.components.bitmap_factory.CreateBitmap(
 707                bitmap_width,
 708                bitmap_height,
 709                bitmap_format,
 710                WICBitmapCacheOnLoad,
 711            )?;
 712            let render_target = self
 713                .components
 714                .d2d1_factory
 715                .CreateWicBitmapRenderTarget(&bitmap, &render_target_property)?;
 716            let brush = render_target.CreateSolidColorBrush(&BRUSH_COLOR, None)?;
 717            let subpixel_shift = params
 718                .subpixel_variant
 719                .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32);
 720            let baseline_origin = D2D_POINT_2F {
 721                x: subpixel_shift.x / params.scale_factor,
 722                y: subpixel_shift.y / params.scale_factor,
 723            };
 724
 725            // This `cast()` action here should never fail since we are running on Win10+, and
 726            // ID2D1DeviceContext4 requires Win8+
 727            let render_target = render_target.cast::<ID2D1DeviceContext4>().unwrap();
 728            render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS);
 729            render_target.SetDpi(
 730                bitmap_dpi * params.scale_factor,
 731                bitmap_dpi * params.scale_factor,
 732            );
 733            render_target.SetTextRenderingParams(&self.components.render_context.params);
 734            render_target.BeginDraw();
 735
 736            if params.is_emoji {
 737                // WARN: only DWRITE_GLYPH_IMAGE_FORMATS_COLR has been tested
 738                let enumerator = self.components.factory.TranslateColorGlyphRun(
 739                    baseline_origin,
 740                    &glyph_run as _,
 741                    None,
 742                    DWRITE_GLYPH_IMAGE_FORMATS_COLR
 743                        | DWRITE_GLYPH_IMAGE_FORMATS_SVG
 744                        | DWRITE_GLYPH_IMAGE_FORMATS_PNG
 745                        | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
 746                        | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8,
 747                    DWRITE_MEASURING_MODE_NATURAL,
 748                    None,
 749                    0,
 750                )?;
 751                while enumerator.MoveNext().is_ok() {
 752                    let Ok(color_glyph) = enumerator.GetCurrentRun() else {
 753                        break;
 754                    };
 755                    let color_glyph = &*color_glyph;
 756                    let brush_color = translate_color(&color_glyph.Base.runColor);
 757                    brush.SetColor(&brush_color);
 758                    match color_glyph.glyphImageFormat {
 759                        DWRITE_GLYPH_IMAGE_FORMATS_PNG
 760                        | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
 761                        | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8 => render_target
 762                            .DrawColorBitmapGlyphRun(
 763                                color_glyph.glyphImageFormat,
 764                                baseline_origin,
 765                                &color_glyph.Base.glyphRun,
 766                                color_glyph.measuringMode,
 767                                D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_DEFAULT,
 768                            ),
 769                        DWRITE_GLYPH_IMAGE_FORMATS_SVG => render_target.DrawSvgGlyphRun(
 770                            baseline_origin,
 771                            &color_glyph.Base.glyphRun,
 772                            &brush,
 773                            None,
 774                            color_glyph.Base.paletteIndex as u32,
 775                            color_glyph.measuringMode,
 776                        ),
 777                        _ => render_target.DrawGlyphRun(
 778                            baseline_origin,
 779                            &color_glyph.Base.glyphRun,
 780                            Some(color_glyph.Base.glyphRunDescription as *const _),
 781                            &brush,
 782                            color_glyph.measuringMode,
 783                        ),
 784                    }
 785                }
 786            } else {
 787                render_target.DrawGlyphRun(
 788                    baseline_origin,
 789                    &glyph_run,
 790                    None,
 791                    &brush,
 792                    DWRITE_MEASURING_MODE_NATURAL,
 793                );
 794            }
 795            render_target.EndDraw(None, None)?;
 796
 797            let mut raw_data = vec![0u8; total_bytes];
 798            if params.is_emoji {
 799                bitmap.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?;
 800                // Convert from BGRA with premultiplied alpha to BGRA with straight alpha.
 801                for pixel in raw_data.chunks_exact_mut(4) {
 802                    let a = pixel[3] as f32 / 255.;
 803                    pixel[0] = (pixel[0] as f32 / a) as u8;
 804                    pixel[1] = (pixel[1] as f32 / a) as u8;
 805                    pixel[2] = (pixel[2] as f32 / a) as u8;
 806                }
 807            } else {
 808                let scaler = self.components.bitmap_factory.CreateBitmapScaler()?;
 809                scaler.Initialize(
 810                    &bitmap,
 811                    bitmap_size.width.0 as u32,
 812                    bitmap_size.height.0 as u32,
 813                    WICBitmapInterpolationModeHighQualityCubic,
 814                )?;
 815                scaler.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?;
 816            }
 817            Ok((bitmap_size, raw_data))
 818        }
 819    }
 820
 821    fn get_typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
 822        unsafe {
 823            let font = &self.fonts[font_id.0].font_face;
 824            let glyph_indices = [glyph_id.0 as u16];
 825            let mut metrics = [DWRITE_GLYPH_METRICS::default()];
 826            font.GetDesignGlyphMetrics(glyph_indices.as_ptr(), 1, metrics.as_mut_ptr(), false)?;
 827
 828            let metrics = &metrics[0];
 829            let advance_width = metrics.advanceWidth as i32;
 830            let advance_height = metrics.advanceHeight as i32;
 831            let left_side_bearing = metrics.leftSideBearing;
 832            let right_side_bearing = metrics.rightSideBearing;
 833            let top_side_bearing = metrics.topSideBearing;
 834            let bottom_side_bearing = metrics.bottomSideBearing;
 835            let vertical_origin_y = metrics.verticalOriginY;
 836
 837            let y_offset = vertical_origin_y + bottom_side_bearing - advance_height;
 838            let width = advance_width - (left_side_bearing + right_side_bearing);
 839            let height = advance_height - (top_side_bearing + bottom_side_bearing);
 840
 841            Ok(Bounds {
 842                origin: Point {
 843                    x: left_side_bearing as f32,
 844                    y: y_offset as f32,
 845                },
 846                size: Size {
 847                    width: width as f32,
 848                    height: height as f32,
 849                },
 850            })
 851        }
 852    }
 853
 854    fn get_advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>> {
 855        unsafe {
 856            let font = &self.fonts[font_id.0].font_face;
 857            let glyph_indices = [glyph_id.0 as u16];
 858            let mut metrics = [DWRITE_GLYPH_METRICS::default()];
 859            font.GetDesignGlyphMetrics(glyph_indices.as_ptr(), 1, metrics.as_mut_ptr(), false)?;
 860
 861            let metrics = &metrics[0];
 862
 863            Ok(Size {
 864                width: metrics.advanceWidth as f32,
 865                height: 0.0,
 866            })
 867        }
 868    }
 869
 870    fn all_font_names(&self) -> Vec<String> {
 871        let mut result =
 872            get_font_names_from_collection(&self.system_font_collection, &self.components.locale);
 873        result.extend(get_font_names_from_collection(
 874            &self.custom_font_collection,
 875            &self.components.locale,
 876        ));
 877        result
 878    }
 879
 880    fn all_font_families(&self) -> Vec<String> {
 881        get_font_names_from_collection(&self.system_font_collection, &self.components.locale)
 882    }
 883}
 884
 885impl Drop for DirectWriteState {
 886    fn drop(&mut self) {
 887        unsafe {
 888            let _ = self
 889                .components
 890                .factory
 891                .UnregisterFontFileLoader(&self.components.in_memory_loader);
 892        }
 893    }
 894}
 895
 896struct TextRendererWrapper(pub IDWriteTextRenderer);
 897
 898impl TextRendererWrapper {
 899    pub fn new(locale_str: &str) -> Self {
 900        let inner = TextRenderer::new(locale_str);
 901        TextRendererWrapper(inner.into())
 902    }
 903}
 904
 905#[implement(IDWriteTextRenderer)]
 906struct TextRenderer {
 907    locale: String,
 908}
 909
 910impl TextRenderer {
 911    pub fn new(locale_str: &str) -> Self {
 912        TextRenderer {
 913            locale: locale_str.to_owned(),
 914        }
 915    }
 916}
 917
 918struct RendererContext<'t, 'a, 'b> {
 919    text_system: &'t mut DirectWriteState,
 920    index_converter: StringIndexConverter<'a>,
 921    runs: &'b mut Vec<ShapedRun>,
 922    utf16_index: usize,
 923    width: f32,
 924}
 925
 926#[allow(non_snake_case)]
 927impl IDWritePixelSnapping_Impl for TextRenderer {
 928    fn IsPixelSnappingDisabled(
 929        &self,
 930        _clientdrawingcontext: *const ::core::ffi::c_void,
 931    ) -> windows::core::Result<BOOL> {
 932        Ok(BOOL(0))
 933    }
 934
 935    fn GetCurrentTransform(
 936        &self,
 937        _clientdrawingcontext: *const ::core::ffi::c_void,
 938        transform: *mut DWRITE_MATRIX,
 939    ) -> windows::core::Result<()> {
 940        unsafe {
 941            *transform = DWRITE_MATRIX {
 942                m11: 1.0,
 943                m12: 0.0,
 944                m21: 0.0,
 945                m22: 1.0,
 946                dx: 0.0,
 947                dy: 0.0,
 948            };
 949        }
 950        Ok(())
 951    }
 952
 953    fn GetPixelsPerDip(
 954        &self,
 955        _clientdrawingcontext: *const ::core::ffi::c_void,
 956    ) -> windows::core::Result<f32> {
 957        Ok(1.0)
 958    }
 959}
 960
 961#[allow(non_snake_case)]
 962impl IDWriteTextRenderer_Impl for TextRenderer {
 963    fn DrawGlyphRun(
 964        &self,
 965        clientdrawingcontext: *const ::core::ffi::c_void,
 966        _baselineoriginx: f32,
 967        _baselineoriginy: f32,
 968        _measuringmode: DWRITE_MEASURING_MODE,
 969        glyphrun: *const DWRITE_GLYPH_RUN,
 970        glyphrundescription: *const DWRITE_GLYPH_RUN_DESCRIPTION,
 971        _clientdrawingeffect: Option<&windows::core::IUnknown>,
 972    ) -> windows::core::Result<()> {
 973        unsafe {
 974            let glyphrun = &*glyphrun;
 975            let glyph_count = glyphrun.glyphCount as usize;
 976            if glyph_count == 0 {
 977                return Ok(());
 978            }
 979            let desc = &*glyphrundescription;
 980            let utf16_length_per_glyph = desc.stringLength as usize / glyph_count;
 981            let context =
 982                &mut *(clientdrawingcontext as *const RendererContext as *mut RendererContext);
 983
 984            if glyphrun.fontFace.is_none() {
 985                return Ok(());
 986            }
 987
 988            let font_face = glyphrun.fontFace.as_ref().unwrap();
 989            // This `cast()` action here should never fail since we are running on Win10+, and
 990            // `IDWriteFontFace3` requires Win10
 991            let font_face = &font_face.cast::<IDWriteFontFace3>().unwrap();
 992            let Some((font_identifier, font_struct, is_emoji)) =
 993                get_font_identifier_and_font_struct(font_face, &self.locale)
 994            else {
 995                return Ok(());
 996            };
 997
 998            let font_id = if let Some(id) = context
 999                .text_system
1000                .font_id_by_identifier
1001                .get(&font_identifier)
1002            {
1003                *id
1004            } else {
1005                context.text_system.select_font(&font_struct)
1006            };
1007            let mut glyphs = SmallVec::new();
1008            for index in 0..glyph_count {
1009                let id = GlyphId(*glyphrun.glyphIndices.add(index) as u32);
1010                context
1011                    .index_converter
1012                    .advance_to_utf16_ix(context.utf16_index);
1013                glyphs.push(ShapedGlyph {
1014                    id,
1015                    position: point(px(context.width), px(0.0)),
1016                    index: context.index_converter.utf8_ix,
1017                    is_emoji,
1018                });
1019                context.utf16_index += utf16_length_per_glyph;
1020                context.width += *glyphrun.glyphAdvances.add(index);
1021            }
1022            context.runs.push(ShapedRun { font_id, glyphs });
1023        }
1024        Ok(())
1025    }
1026
1027    fn DrawUnderline(
1028        &self,
1029        _clientdrawingcontext: *const ::core::ffi::c_void,
1030        _baselineoriginx: f32,
1031        _baselineoriginy: f32,
1032        _underline: *const DWRITE_UNDERLINE,
1033        _clientdrawingeffect: Option<&windows::core::IUnknown>,
1034    ) -> windows::core::Result<()> {
1035        Err(windows::core::Error::new(
1036            E_NOTIMPL,
1037            "DrawUnderline unimplemented",
1038        ))
1039    }
1040
1041    fn DrawStrikethrough(
1042        &self,
1043        _clientdrawingcontext: *const ::core::ffi::c_void,
1044        _baselineoriginx: f32,
1045        _baselineoriginy: f32,
1046        _strikethrough: *const DWRITE_STRIKETHROUGH,
1047        _clientdrawingeffect: Option<&windows::core::IUnknown>,
1048    ) -> windows::core::Result<()> {
1049        Err(windows::core::Error::new(
1050            E_NOTIMPL,
1051            "DrawStrikethrough unimplemented",
1052        ))
1053    }
1054
1055    fn DrawInlineObject(
1056        &self,
1057        _clientdrawingcontext: *const ::core::ffi::c_void,
1058        _originx: f32,
1059        _originy: f32,
1060        _inlineobject: Option<&IDWriteInlineObject>,
1061        _issideways: BOOL,
1062        _isrighttoleft: BOOL,
1063        _clientdrawingeffect: Option<&windows::core::IUnknown>,
1064    ) -> windows::core::Result<()> {
1065        Err(windows::core::Error::new(
1066            E_NOTIMPL,
1067            "DrawInlineObject unimplemented",
1068        ))
1069    }
1070}
1071
1072struct StringIndexConverter<'a> {
1073    text: &'a str,
1074    utf8_ix: usize,
1075    utf16_ix: usize,
1076}
1077
1078impl<'a> StringIndexConverter<'a> {
1079    fn new(text: &'a str) -> Self {
1080        Self {
1081            text,
1082            utf8_ix: 0,
1083            utf16_ix: 0,
1084        }
1085    }
1086
1087    fn advance_to_utf8_ix(&mut self, utf8_target: usize) {
1088        for (ix, c) in self.text[self.utf8_ix..].char_indices() {
1089            if self.utf8_ix + ix >= utf8_target {
1090                self.utf8_ix += ix;
1091                return;
1092            }
1093            self.utf16_ix += c.len_utf16();
1094        }
1095        self.utf8_ix = self.text.len();
1096    }
1097
1098    fn advance_to_utf16_ix(&mut self, utf16_target: usize) {
1099        for (ix, c) in self.text[self.utf8_ix..].char_indices() {
1100            if self.utf16_ix >= utf16_target {
1101                self.utf8_ix += ix;
1102                return;
1103            }
1104            self.utf16_ix += c.len_utf16();
1105        }
1106        self.utf8_ix = self.text.len();
1107    }
1108}
1109
1110impl Into<DWRITE_FONT_STYLE> for FontStyle {
1111    fn into(self) -> DWRITE_FONT_STYLE {
1112        match self {
1113            FontStyle::Normal => DWRITE_FONT_STYLE_NORMAL,
1114            FontStyle::Italic => DWRITE_FONT_STYLE_ITALIC,
1115            FontStyle::Oblique => DWRITE_FONT_STYLE_OBLIQUE,
1116        }
1117    }
1118}
1119
1120impl From<DWRITE_FONT_STYLE> for FontStyle {
1121    fn from(value: DWRITE_FONT_STYLE) -> Self {
1122        match value.0 {
1123            0 => FontStyle::Normal,
1124            1 => FontStyle::Italic,
1125            2 => FontStyle::Oblique,
1126            _ => unreachable!(),
1127        }
1128    }
1129}
1130
1131impl Into<DWRITE_FONT_WEIGHT> for FontWeight {
1132    fn into(self) -> DWRITE_FONT_WEIGHT {
1133        DWRITE_FONT_WEIGHT(self.0 as i32)
1134    }
1135}
1136
1137impl From<DWRITE_FONT_WEIGHT> for FontWeight {
1138    fn from(value: DWRITE_FONT_WEIGHT) -> Self {
1139        FontWeight(value.0 as f32)
1140    }
1141}
1142
1143fn get_font_names_from_collection(
1144    collection: &IDWriteFontCollection1,
1145    locale: &str,
1146) -> Vec<String> {
1147    unsafe {
1148        let mut result = Vec::new();
1149        let family_count = collection.GetFontFamilyCount();
1150        for index in 0..family_count {
1151            let Some(font_family) = collection.GetFontFamily(index).log_err() else {
1152                continue;
1153            };
1154            let Some(localized_family_name) = font_family.GetFamilyNames().log_err() else {
1155                continue;
1156            };
1157            let Some(family_name) = get_name(localized_family_name, locale).log_err() else {
1158                continue;
1159            };
1160            result.push(family_name);
1161        }
1162
1163        result
1164    }
1165}
1166
1167fn get_font_identifier_and_font_struct(
1168    font_face: &IDWriteFontFace3,
1169    locale: &str,
1170) -> Option<(FontIdentifier, Font, bool)> {
1171    let postscript_name = get_postscript_name(font_face, locale).log_err()?;
1172    let localized_family_name = unsafe { font_face.GetFamilyNames().log_err() }?;
1173    let family_name = get_name(localized_family_name, locale).log_err()?;
1174    let weight = unsafe { font_face.GetWeight() };
1175    let style = unsafe { font_face.GetStyle() };
1176    let identifier = FontIdentifier {
1177        postscript_name,
1178        weight: weight.0,
1179        style: style.0,
1180    };
1181    let font_struct = Font {
1182        family: family_name.into(),
1183        features: FontFeatures::default(),
1184        weight: weight.into(),
1185        style: style.into(),
1186    };
1187    let is_emoji = unsafe { font_face.IsColorFont().as_bool() };
1188    Some((identifier, font_struct, is_emoji))
1189}
1190
1191#[inline]
1192fn get_font_identifier(font_face: &IDWriteFontFace3, locale: &str) -> Option<FontIdentifier> {
1193    let weight = unsafe { font_face.GetWeight().0 };
1194    let style = unsafe { font_face.GetStyle().0 };
1195    get_postscript_name(font_face, locale)
1196        .log_err()
1197        .map(|postscript_name| FontIdentifier {
1198            postscript_name,
1199            weight,
1200            style,
1201        })
1202}
1203
1204#[inline]
1205fn get_postscript_name(font_face: &IDWriteFontFace3, locale: &str) -> Result<String> {
1206    let mut info = None;
1207    let mut exists = BOOL(0);
1208    unsafe {
1209        font_face.GetInformationalStrings(
1210            DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME,
1211            &mut info,
1212            &mut exists,
1213        )?
1214    };
1215    if !exists.as_bool() || info.is_none() {
1216        return Err(anyhow!("No postscript name found for font face"));
1217    }
1218
1219    get_name(info.unwrap(), locale)
1220}
1221
1222// https://learn.microsoft.com/en-us/windows/win32/api/dwrite/ne-dwrite-dwrite_font_feature_tag
1223fn apply_font_features(
1224    direct_write_features: &IDWriteTypography,
1225    features: &FontFeatures,
1226) -> Result<()> {
1227    let tag_values = features.tag_value_list();
1228    if tag_values.is_empty() {
1229        return Ok(());
1230    }
1231
1232    // All of these features are enabled by default by DirectWrite.
1233    // If you want to (and can) peek into the source of DirectWrite
1234    let mut feature_liga = make_direct_write_feature("liga", 1);
1235    let mut feature_clig = make_direct_write_feature("clig", 1);
1236    let mut feature_calt = make_direct_write_feature("calt", 1);
1237
1238    for (tag, value) in tag_values {
1239        if tag.as_str() == "liga" && *value == 0 {
1240            feature_liga.parameter = 0;
1241            continue;
1242        }
1243        if tag.as_str() == "clig" && *value == 0 {
1244            feature_clig.parameter = 0;
1245            continue;
1246        }
1247        if tag.as_str() == "calt" && *value == 0 {
1248            feature_calt.parameter = 0;
1249            continue;
1250        }
1251
1252        unsafe {
1253            direct_write_features.AddFontFeature(make_direct_write_feature(&tag, *value))?;
1254        }
1255    }
1256    unsafe {
1257        direct_write_features.AddFontFeature(feature_liga)?;
1258        direct_write_features.AddFontFeature(feature_clig)?;
1259        direct_write_features.AddFontFeature(feature_calt)?;
1260    }
1261
1262    Ok(())
1263}
1264
1265#[inline]
1266fn make_direct_write_feature(feature_name: &str, parameter: u32) -> DWRITE_FONT_FEATURE {
1267    let tag = make_direct_write_tag(feature_name);
1268    DWRITE_FONT_FEATURE {
1269        nameTag: tag,
1270        parameter,
1271    }
1272}
1273
1274#[inline]
1275fn make_open_type_tag(tag_name: &str) -> u32 {
1276    let bytes = tag_name.bytes().collect_vec();
1277    assert_eq!(bytes.len(), 4);
1278    ((bytes[3] as u32) << 24)
1279        | ((bytes[2] as u32) << 16)
1280        | ((bytes[1] as u32) << 8)
1281        | (bytes[0] as u32)
1282}
1283
1284#[inline]
1285fn make_direct_write_tag(tag_name: &str) -> DWRITE_FONT_FEATURE_TAG {
1286    DWRITE_FONT_FEATURE_TAG(make_open_type_tag(tag_name))
1287}
1288
1289#[inline]
1290fn get_name(string: IDWriteLocalizedStrings, locale: &str) -> Result<String> {
1291    let mut locale_name_index = 0u32;
1292    let mut exists = BOOL(0);
1293    unsafe {
1294        string.FindLocaleName(
1295            &HSTRING::from(locale),
1296            &mut locale_name_index,
1297            &mut exists as _,
1298        )?
1299    };
1300    if !exists.as_bool() {
1301        unsafe {
1302            string.FindLocaleName(
1303                DEFAULT_LOCALE_NAME,
1304                &mut locale_name_index as _,
1305                &mut exists as _,
1306            )?
1307        };
1308        if !exists.as_bool() {
1309            return Err(anyhow!("No localised string for {}", locale));
1310        }
1311    }
1312
1313    let name_length = unsafe { string.GetStringLength(locale_name_index) }? as usize;
1314    let mut name_vec = vec![0u16; name_length + 1];
1315    unsafe {
1316        string.GetString(locale_name_index, &mut name_vec)?;
1317    }
1318
1319    Ok(String::from_utf16_lossy(&name_vec[..name_length]))
1320}
1321
1322#[inline]
1323fn translate_color(color: &DWRITE_COLOR_F) -> D2D1_COLOR_F {
1324    D2D1_COLOR_F {
1325        r: color.r,
1326        g: color.g,
1327        b: color.b,
1328        a: color.a,
1329    }
1330}
1331
1332fn get_system_ui_font_name() -> SharedString {
1333    unsafe {
1334        let mut info: LOGFONTW = std::mem::zeroed();
1335        let font_family = if SystemParametersInfoW(
1336            SPI_GETICONTITLELOGFONT,
1337            std::mem::size_of::<LOGFONTW>() as u32,
1338            Some(&mut info as *mut _ as _),
1339            SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS(0),
1340        )
1341        .log_err()
1342        .is_none()
1343        {
1344            // https://learn.microsoft.com/en-us/windows/win32/uxguide/vis-fonts
1345            // Segoe UI is the Windows font intended for user interface text strings.
1346            "Segoe UI".into()
1347        } else {
1348            let font_name = String::from_utf16_lossy(&info.lfFaceName);
1349            font_name.trim_matches(char::from(0)).to_owned().into()
1350        };
1351        log::info!("Use {} as UI font.", font_family);
1352        font_family
1353    }
1354}
1355
1356#[inline]
1357fn get_render_target_property(
1358    pixel_format: DXGI_FORMAT,
1359    alpha_mode: D2D1_ALPHA_MODE,
1360) -> D2D1_RENDER_TARGET_PROPERTIES {
1361    D2D1_RENDER_TARGET_PROPERTIES {
1362        r#type: D2D1_RENDER_TARGET_TYPE_DEFAULT,
1363        pixelFormat: D2D1_PIXEL_FORMAT {
1364            format: pixel_format,
1365            alphaMode: alpha_mode,
1366        },
1367        dpiX: 96.0,
1368        dpiY: 96.0,
1369        usage: D2D1_RENDER_TARGET_USAGE_NONE,
1370        minLevel: D2D1_FEATURE_LEVEL_DEFAULT,
1371    }
1372}
1373
1374const DEFAULT_LOCALE_NAME: PCWSTR = windows::core::w!("en-US");
1375const BRUSH_COLOR: D2D1_COLOR_F = D2D1_COLOR_F {
1376    r: 1.0,
1377    g: 1.0,
1378    b: 1.0,
1379    a: 1.0,
1380};