direct_write.rs

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