direct_write.rs

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