summaryrefslogtreecommitdiffstats
path: root/third_party/rust/dwrote/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 11:44:51 +0000
commit9e3c08db40b8916968b9f30096c7be3f00ce9647 (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /third_party/rust/dwrote/src
parentInitial commit. (diff)
downloadthunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.tar.xz
thunderbird-9e3c08db40b8916968b9f30096c7be3f00ce9647.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/dwrote/src')
-rw-r--r--third_party/rust/dwrote/src/bitmap_render_target.rs129
-rw-r--r--third_party/rust/dwrote/src/com_helpers.rs127
-rw-r--r--third_party/rust/dwrote/src/font.rs182
-rw-r--r--third_party/rust/dwrote/src/font_collection.rs171
-rw-r--r--third_party/rust/dwrote/src/font_collection_impl.rs157
-rw-r--r--third_party/rust/dwrote/src/font_face.rs377
-rw-r--r--third_party/rust/dwrote/src/font_fallback.rs94
-rw-r--r--third_party/rust/dwrote/src/font_family.rs79
-rw-r--r--third_party/rust/dwrote/src/font_file.rs256
-rw-r--r--third_party/rust/dwrote/src/font_file_loader_impl.rs237
-rw-r--r--third_party/rust/dwrote/src/gdi_interop.rs46
-rw-r--r--third_party/rust/dwrote/src/geometry_sink_impl.rs127
-rw-r--r--third_party/rust/dwrote/src/glyph_run_analysis.rs107
-rw-r--r--third_party/rust/dwrote/src/helpers.rs68
-rw-r--r--third_party/rust/dwrote/src/lib.rs167
-rw-r--r--third_party/rust/dwrote/src/outline_builder.rs6
-rw-r--r--third_party/rust/dwrote/src/rendering_params.rs35
-rw-r--r--third_party/rust/dwrote/src/test.rs167
-rw-r--r--third_party/rust/dwrote/src/text_analysis_source.rs63
-rw-r--r--third_party/rust/dwrote/src/text_analysis_source_impl.rs240
-rw-r--r--third_party/rust/dwrote/src/types.rs126
21 files changed, 2961 insertions, 0 deletions
diff --git a/third_party/rust/dwrote/src/bitmap_render_target.rs b/third_party/rust/dwrote/src/bitmap_render_target.rs
new file mode 100644
index 0000000000..d4753b851f
--- /dev/null
+++ b/third_party/rust/dwrote/src/bitmap_render_target.rs
@@ -0,0 +1,129 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::cell::UnsafeCell;
+use std::mem::{size_of, zeroed};
+use std::slice;
+use winapi::ctypes::c_void;
+use winapi::shared::windef::{HDC, RECT};
+use winapi::um::dcommon::DWRITE_MEASURING_MODE;
+use winapi::um::dwrite::IDWriteBitmapRenderTarget;
+use winapi::um::dwrite::{DWRITE_GLYPH_OFFSET, DWRITE_GLYPH_RUN};
+use winapi::um::wingdi::{GetCurrentObject, GetObjectW, BITMAP, OBJ_BITMAP, RGB};
+use wio::com::ComPtr;
+
+use super::{FontFace, RenderingParams};
+
+pub struct BitmapRenderTarget {
+ native: UnsafeCell<ComPtr<IDWriteBitmapRenderTarget>>,
+}
+
+impl BitmapRenderTarget {
+ pub fn take(native: ComPtr<IDWriteBitmapRenderTarget>) -> BitmapRenderTarget {
+ BitmapRenderTarget {
+ native: UnsafeCell::new(native),
+ }
+ }
+
+ pub unsafe fn as_ptr(&self) -> *mut IDWriteBitmapRenderTarget {
+ (*self.native.get()).as_raw()
+ }
+
+ // A dip is 1/96th of an inch, so this value is the number of pixels per inch divided by 96.
+ pub fn set_pixels_per_dip(&self, ppd: f32) {
+ unsafe {
+ (*self.native.get()).SetPixelsPerDip(ppd);
+ }
+ }
+
+ pub fn get_memory_dc(&self) -> HDC {
+ unsafe { (*self.native.get()).GetMemoryDC() }
+ }
+
+ pub fn draw_glyph_run(
+ &self,
+ baseline_origin_x: f32,
+ baseline_origin_y: f32,
+ measuring_mode: DWRITE_MEASURING_MODE,
+ font_face: &FontFace,
+ em_size: f32,
+ glyph_indices: &[u16],
+ glyph_advances: &[f32],
+ glyph_offsets: &[DWRITE_GLYPH_OFFSET],
+ rendering_params: &RenderingParams,
+ color: &(f32, f32, f32),
+ ) -> RECT {
+ unsafe {
+ assert!(glyph_indices.len() == glyph_advances.len());
+ assert!(glyph_indices.len() == glyph_offsets.len());
+
+ let r = (color.0 * 255.0) as u8;
+ let g = (color.1 * 255.0) as u8;
+ let b = (color.2 * 255.0) as u8;
+
+ let mut glyph_run: DWRITE_GLYPH_RUN = zeroed();
+ glyph_run.fontFace = font_face.as_ptr();
+ glyph_run.fontEmSize = em_size;
+ glyph_run.glyphCount = glyph_indices.len() as u32;
+ glyph_run.glyphIndices = glyph_indices.as_ptr();
+ glyph_run.glyphAdvances = glyph_advances.as_ptr();
+ glyph_run.glyphOffsets = glyph_offsets.as_ptr();
+ glyph_run.isSideways = 0;
+ glyph_run.bidiLevel = 0;
+
+ let mut rect: RECT = zeroed();
+ let hr = (*self.native.get()).DrawGlyphRun(
+ baseline_origin_x,
+ baseline_origin_y,
+ measuring_mode,
+ &glyph_run,
+ rendering_params.as_ptr(),
+ RGB(r, g, b),
+ &mut rect,
+ );
+ assert!(hr == 0);
+ rect
+ }
+ }
+
+ // This function expects to have glyphs rendered in WHITE,
+ // and pulls out a u8 vector of width*height*4 size with
+ // the coverage value (we pull out R) broadcast to the alpha
+ // channel, with the color white. That is, it performs:
+ // RGBX -> xxxR, where xxx = 0xff
+ pub fn get_opaque_values_as_mask(&self) -> Vec<u8> {
+ // Now grossness to pull out the pixels
+ unsafe {
+ let memory_dc = self.get_memory_dc();
+ let mut bitmap: BITMAP = zeroed();
+ let ret = GetObjectW(
+ GetCurrentObject(memory_dc, OBJ_BITMAP),
+ size_of::<BITMAP>() as i32,
+ &mut bitmap as *mut _ as *mut c_void,
+ );
+ assert!(ret == size_of::<BITMAP>() as i32);
+ assert!(bitmap.bmBitsPixel == 32);
+
+ let width = bitmap.bmWidth as usize;
+ let stride = bitmap.bmWidthBytes as usize;
+ let height = bitmap.bmHeight as usize;
+
+ let mut out_bytes: Vec<u8> = vec![0; width * height * 4];
+ let out_u32 =
+ slice::from_raw_parts_mut(out_bytes.as_mut_ptr() as *mut u32, width * height);
+
+ for row in 0..height {
+ let in_offset = (row * stride) as isize;
+ let in_u32 =
+ slice::from_raw_parts(bitmap.bmBits.offset(in_offset) as *const u32, width);
+ for col in 0..width {
+ let r = in_u32[col] & 0xff;
+ out_u32[width * row + col] = (r << 24) | (0x00ffffffu32);
+ }
+ }
+
+ out_bytes
+ }
+ }
+}
diff --git a/third_party/rust/dwrote/src/com_helpers.rs b/third_party/rust/dwrote/src/com_helpers.rs
new file mode 100644
index 0000000000..b4bcf16641
--- /dev/null
+++ b/third_party/rust/dwrote/src/com_helpers.rs
@@ -0,0 +1,127 @@
+// This is only handy for implementing a single-interface-implementing IUnknown.
+
+macro_rules! implement_iunknown {
+ ($interface:ident, $typ:ident) => {
+ IUnknownVtbl {
+ QueryInterface: {
+ #[allow(non_snake_case)]
+ unsafe extern "system" fn QueryInterface(
+ unknown_this: *mut IUnknown,
+ riid: REFIID,
+ ppv_object: *mut *mut c_void,
+ ) -> HRESULT {
+ use $crate::winapi::Interface;
+ let this = if $crate::winapi::shared::guiddef::IsEqualGUID(
+ &*riid,
+ &$interface::uuidof(),
+ ) {
+ mem::transmute(unknown_this)
+ } else if $crate::winapi::shared::guiddef::IsEqualGUID(
+ &*riid,
+ &IUnknown::uuidof(),
+ ) {
+ mem::transmute(unknown_this)
+ } else {
+ return $crate::winapi::shared::winerror::E_NOINTERFACE;
+ };
+
+ (*unknown_this).AddRef();
+ *ppv_object = this;
+ return S_OK;
+ }
+ QueryInterface
+ },
+ AddRef: {
+ unsafe extern "system" fn AddRef(unknown_this: *mut IUnknown) -> ULONG {
+ let this = $typ::from_interface(unknown_this);
+ let count = this.refcount.fetch_add(1, atomic::Ordering::Relaxed) + 1;
+ count as ULONG
+ }
+ AddRef
+ },
+ Release: {
+ unsafe extern "system" fn Release(unknown_this: *mut IUnknown) -> ULONG {
+ let this = $typ::from_interface(unknown_this);
+ let count = this.refcount.fetch_sub(1, atomic::Ordering::Release) - 1;
+ if count == 0 {
+ <$typ as Com<$interface>>::destroy(unknown_this as *mut $interface);
+ }
+ count as ULONG
+ }
+ Release
+ },
+ }
+ };
+ (static $interface:ident, $typ:ident) => {
+ IUnknownVtbl {
+ QueryInterface: {
+ #[allow(non_snake_case)]
+ unsafe extern "system" fn QueryInterface(
+ unknown_this: *mut IUnknown,
+ riid: REFIID,
+ ppvObject: *mut *mut $crate::winapi::ctypes::c_void,
+ ) -> HRESULT {
+ use $crate::winapi::Interface;
+ let this = if $crate::winapi::shared::guiddef::IsEqualGUID(
+ &*riid,
+ &$interface::uuidof(),
+ ) {
+ mem::transmute(unknown_this)
+ } else if $crate::winapi::shared::guiddef::IsEqualGUID(
+ &*riid,
+ &IUnknown::uuidof(),
+ ) {
+ mem::transmute(unknown_this)
+ } else {
+ return $crate::winapi::shared::winerror::E_NOINTERFACE;
+ };
+
+ (*unknown_this).AddRef();
+ *ppvObject = this;
+ return S_OK;
+ }
+ QueryInterface
+ },
+ AddRef: {
+ // FIXME(pcwalton): Uh? Maybe we should actually reference count?
+ #[allow(non_snake_case)]
+ unsafe extern "system" fn AddRef(_: *mut IUnknown) -> ULONG {
+ 1
+ }
+ AddRef
+ },
+ Release: {
+ #[allow(non_snake_case)]
+ unsafe extern "system" fn Release(_: *mut IUnknown) -> ULONG {
+ 1
+ }
+ Release
+ },
+ }
+ };
+}
+
+#[repr(C)]
+pub struct ComRepr<Type, Vtbl>(*const Vtbl, Type);
+
+pub trait Com<Interface>
+where
+ Self: Sized,
+{
+ type Vtbl: 'static;
+
+ fn vtbl() -> &'static Self::Vtbl;
+
+ fn into_interface(self) -> *mut Interface {
+ let com = Box::new(ComRepr(Self::vtbl(), self));
+ Box::into_raw(com) as *mut Interface
+ }
+
+ unsafe fn from_interface<'a>(thing: *mut Interface) -> &'a mut Self {
+ &mut (*(thing as *mut ComRepr<Self, Self::Vtbl>)).1
+ }
+
+ unsafe fn destroy(thing: *mut Interface) {
+ Box::from_raw(thing as *mut ComRepr<Self, Self::Vtbl>);
+ }
+}
diff --git a/third_party/rust/dwrote/src/font.rs b/third_party/rust/dwrote/src/font.rs
new file mode 100644
index 0000000000..eacad1c3bf
--- /dev/null
+++ b/third_party/rust/dwrote/src/font.rs
@@ -0,0 +1,182 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::cell::UnsafeCell;
+use std::mem;
+use std::ptr;
+use winapi::shared::minwindef::{FALSE, TRUE};
+use winapi::shared::winerror::S_OK;
+use winapi::um::dwrite::IDWriteFont;
+use winapi::um::dwrite::IDWriteFontFace;
+use winapi::um::dwrite::IDWriteFontFamily;
+use winapi::um::dwrite::IDWriteLocalizedStrings;
+use winapi::um::dwrite::DWRITE_FONT_METRICS;
+use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_FULL_NAME;
+use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_ID;
+use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME;
+use winapi::um::dwrite::DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME;
+use winapi::um::dwrite_1::{IDWriteFont1, DWRITE_FONT_METRICS1};
+use wio::com::ComPtr;
+
+use super::*;
+use helpers::*;
+
+pub struct Font {
+ native: UnsafeCell<ComPtr<IDWriteFont>>,
+}
+
+impl Font {
+ pub fn take(native: ComPtr<IDWriteFont>) -> Font {
+ Font {
+ native: UnsafeCell::new(native),
+ }
+ }
+
+ pub unsafe fn as_ptr(&self) -> *mut IDWriteFont {
+ (*self.native.get()).as_raw()
+ }
+
+ pub fn to_descriptor(&self) -> FontDescriptor {
+ FontDescriptor {
+ family_name: self.family_name(),
+ stretch: self.stretch(),
+ style: self.style(),
+ weight: self.weight(),
+ }
+ }
+
+ pub fn stretch(&self) -> FontStretch {
+ unsafe { mem::transmute::<u32, FontStretch>((*self.native.get()).GetStretch()) }
+ }
+
+ pub fn style(&self) -> FontStyle {
+ unsafe { mem::transmute::<u32, FontStyle>((*self.native.get()).GetStyle()) }
+ }
+
+ pub fn weight(&self) -> FontWeight {
+ unsafe { FontWeight::from_u32((*self.native.get()).GetWeight()) }
+ }
+
+ pub fn is_monospace(&self) -> Option<bool> {
+ unsafe {
+ let font1: Option<ComPtr<IDWriteFont1>> = (*self.native.get()).cast().ok();
+ font1.map(|font| font.IsMonospacedFont() == TRUE)
+ }
+ }
+
+ pub fn simulations(&self) -> FontSimulations {
+ unsafe { mem::transmute::<u32, FontSimulations>((*self.native.get()).GetSimulations()) }
+ }
+
+ pub fn family_name(&self) -> String {
+ unsafe {
+ let mut family: *mut IDWriteFontFamily = ptr::null_mut();
+ let hr = (*self.native.get()).GetFontFamily(&mut family);
+ assert!(hr == 0);
+
+ FontFamily::take(ComPtr::from_raw(family)).name()
+ }
+ }
+
+ pub fn face_name(&self) -> String {
+ unsafe {
+ let mut names: *mut IDWriteLocalizedStrings = ptr::null_mut();
+ let hr = (*self.native.get()).GetFaceNames(&mut names);
+ assert!(hr == 0);
+
+ get_locale_string(&mut ComPtr::from_raw(names))
+ }
+ }
+
+ pub fn informational_string(&self, id: InformationalStringId) -> Option<String> {
+ unsafe {
+ let mut names: *mut IDWriteLocalizedStrings = ptr::null_mut();
+ let mut exists = FALSE;
+ let id = id as DWRITE_INFORMATIONAL_STRING_ID;
+ let hr = (*self.native.get()).GetInformationalStrings(id, &mut names, &mut exists);
+ assert!(hr == S_OK);
+ if exists == TRUE {
+ Some(get_locale_string(&mut ComPtr::from_raw(names)))
+ } else {
+ None
+ }
+ }
+ }
+
+ pub fn create_font_face(&self) -> FontFace {
+ // FIXME create_font_face should cache the FontFace and return it,
+ // there's a 1:1 relationship
+ unsafe {
+ let mut face: *mut IDWriteFontFace = ptr::null_mut();
+ let hr = (*self.native.get()).CreateFontFace(&mut face);
+ assert!(hr == 0);
+ FontFace::take(ComPtr::from_raw(face))
+ }
+ }
+
+ pub fn metrics(&self) -> FontMetrics {
+ unsafe {
+ let font_1: Option<ComPtr<IDWriteFont1>> = (*self.native.get()).cast().ok();
+ match font_1 {
+ None => {
+ let mut metrics = mem::zeroed();
+ (*self.native.get()).GetMetrics(&mut metrics);
+ FontMetrics::Metrics0(metrics)
+ }
+ Some(font_1) => {
+ let mut metrics_1 = mem::zeroed();
+ font_1.GetMetrics(&mut metrics_1);
+ FontMetrics::Metrics1(metrics_1)
+ }
+ }
+ }
+ }
+}
+
+impl Clone for Font {
+ fn clone(&self) -> Font {
+ unsafe {
+ Font {
+ native: UnsafeCell::new((*self.native.get()).clone()),
+ }
+ }
+ }
+}
+
+#[repr(u32)]
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum InformationalStringId {
+ FullName = DWRITE_INFORMATIONAL_STRING_FULL_NAME,
+ PostscriptName = DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME,
+ PostscriptCidName = DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_CID_NAME,
+}
+
+/// A wrapper around the `DWRITE_FONT_METRICS` and `DWRITE_FONT_METRICS1` types.
+pub enum FontMetrics {
+ /// Windows 7.
+ Metrics0(DWRITE_FONT_METRICS),
+ /// Windows 8 and up.
+ Metrics1(DWRITE_FONT_METRICS1),
+}
+
+impl FontMetrics {
+ /// Convert self to the Metrics0 arm (throwing away additional information)
+ pub fn metrics0(self) -> DWRITE_FONT_METRICS {
+ match self {
+ FontMetrics::Metrics0(metrics) => metrics,
+ FontMetrics::Metrics1(metrics) => DWRITE_FONT_METRICS {
+ designUnitsPerEm: metrics.designUnitsPerEm,
+ ascent: metrics.ascent,
+ descent: metrics.descent,
+ lineGap: metrics.lineGap,
+ capHeight: metrics.capHeight,
+ xHeight: metrics.xHeight,
+ underlinePosition: metrics.underlinePosition,
+ underlineThickness: metrics.underlineThickness,
+ strikethroughPosition: metrics.strikethroughPosition,
+ strikethroughThickness: metrics.strikethroughThickness,
+ }
+ }
+ }
+}
diff --git a/third_party/rust/dwrote/src/font_collection.rs b/third_party/rust/dwrote/src/font_collection.rs
new file mode 100644
index 0000000000..b1c99dbe50
--- /dev/null
+++ b/third_party/rust/dwrote/src/font_collection.rs
@@ -0,0 +1,171 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::cell::UnsafeCell;
+use std::mem;
+use std::ptr;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use winapi::shared::minwindef::{BOOL, FALSE, TRUE};
+use winapi::shared::winerror::S_OK;
+use winapi::um::dwrite::IDWriteFontCollectionLoader;
+use winapi::um::dwrite::{IDWriteFont, IDWriteFontCollection, IDWriteFontFamily};
+use wio::com::ComPtr;
+
+use crate::helpers::*;
+use super::{DWriteFactory, Font, FontDescriptor, FontFace, FontFamily};
+
+static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
+
+pub struct FontCollectionFamilyIterator {
+ collection: ComPtr<IDWriteFontCollection>,
+ curr: u32,
+ count: u32,
+}
+
+impl Iterator for FontCollectionFamilyIterator {
+ type Item = FontFamily;
+ fn next(&mut self) -> Option<FontFamily> {
+ if self.curr == self.count {
+ return None;
+ }
+
+ unsafe {
+ let mut family: *mut IDWriteFontFamily = ptr::null_mut();
+ let hr = self.collection.GetFontFamily(self.curr, &mut family);
+ assert!(hr == 0);
+ self.curr += 1;
+ Some(FontFamily::take(ComPtr::from_raw(family)))
+ }
+ }
+}
+
+pub struct FontCollection {
+ native: UnsafeCell<ComPtr<IDWriteFontCollection>>,
+}
+
+impl FontCollection {
+ pub fn get_system(update: bool) -> FontCollection {
+ unsafe {
+ let mut native: *mut IDWriteFontCollection = ptr::null_mut();
+ let hr = (*DWriteFactory()).GetSystemFontCollection(
+ &mut native,
+ if update { TRUE } else { FALSE },
+ );
+ assert!(hr == 0);
+
+ FontCollection {
+ native: UnsafeCell::new(ComPtr::from_raw(native)),
+ }
+ }
+ }
+
+ pub fn system() -> FontCollection {
+ FontCollection::get_system(false)
+ }
+
+ pub fn take(native: ComPtr<IDWriteFontCollection>) -> FontCollection {
+ FontCollection {
+ native: UnsafeCell::new(native),
+ }
+ }
+
+ pub fn from_loader(collection_loader: ComPtr<IDWriteFontCollectionLoader>) -> FontCollection {
+ unsafe {
+ let factory = DWriteFactory();
+ assert_eq!(
+ (*factory).RegisterFontCollectionLoader(collection_loader.clone().into_raw()),
+ S_OK
+ );
+ let mut collection: *mut IDWriteFontCollection = ptr::null_mut();
+ let id = NEXT_ID.fetch_add(1, Ordering::SeqCst);
+ assert_eq!(
+ (*factory).CreateCustomFontCollection(
+ collection_loader.clone().into_raw(),
+ &id as *const usize as *const _,
+ mem::size_of::<AtomicUsize>() as u32,
+ &mut collection
+ ),
+ S_OK
+ );
+ FontCollection::take(ComPtr::from_raw(collection))
+ }
+ }
+
+ pub unsafe fn as_ptr(&self) -> *mut IDWriteFontCollection {
+ (*self.native.get()).as_raw()
+ }
+
+ pub fn families_iter(&self) -> FontCollectionFamilyIterator {
+ unsafe {
+ FontCollectionFamilyIterator {
+ collection: (*self.native.get()).clone(),
+ curr: 0,
+ count: (*self.native.get()).GetFontFamilyCount(),
+ }
+ }
+ }
+
+ pub fn get_font_family_count(&self) -> u32 {
+ unsafe { (*self.native.get()).GetFontFamilyCount() }
+ }
+
+ pub fn get_font_family(&self, index: u32) -> FontFamily {
+ unsafe {
+ let mut family: *mut IDWriteFontFamily = ptr::null_mut();
+ let hr = (*self.native.get()).GetFontFamily(index, &mut family);
+ assert!(hr == 0);
+ FontFamily::take(ComPtr::from_raw(family))
+ }
+ }
+
+ // Find a font matching the given font descriptor in this
+ // font collection.
+ pub fn get_font_from_descriptor(&self, desc: &FontDescriptor) -> Option<Font> {
+ if let Some(family) = self.get_font_family_by_name(&desc.family_name) {
+ let font = family.get_first_matching_font(desc.weight, desc.stretch, desc.style);
+ // Exact matches only here
+ if font.weight() == desc.weight
+ && font.stretch() == desc.stretch
+ && font.style() == desc.style
+ {
+ return Some(font);
+ }
+ }
+
+ None
+ }
+
+ pub fn get_font_from_face(&self, face: &FontFace) -> Option<Font> {
+ unsafe {
+ let mut font: *mut IDWriteFont = ptr::null_mut();
+ let hr = (*self.native.get()).GetFontFromFontFace(face.as_ptr(), &mut font);
+ if hr != 0 {
+ return None;
+ }
+ Some(Font::take(ComPtr::from_raw(font)))
+ }
+ }
+
+ pub fn get_font_family_by_name(&self, family_name: &str) -> Option<FontFamily> {
+ unsafe {
+ let mut index: u32 = 0;
+ let mut exists: BOOL = FALSE;
+ let hr = (*self.native.get()).FindFamilyName(
+ family_name.to_wide_null().as_ptr(),
+ &mut index,
+ &mut exists,
+ );
+ assert!(hr == 0);
+ if exists == FALSE {
+ return None;
+ }
+
+ let mut family: *mut IDWriteFontFamily = ptr::null_mut();
+ let hr = (*self.native.get()).GetFontFamily(index, &mut family);
+ assert!(hr == 0);
+
+ Some(FontFamily::take(ComPtr::from_raw(family)))
+ }
+ }
+}
diff --git a/third_party/rust/dwrote/src/font_collection_impl.rs b/third_party/rust/dwrote/src/font_collection_impl.rs
new file mode 100644
index 0000000000..8a9d68e890
--- /dev/null
+++ b/third_party/rust/dwrote/src/font_collection_impl.rs
@@ -0,0 +1,157 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// A temporary custom font collection that exists solely for the face-to-font mapping to work.
+
+use std::mem;
+use std::sync::atomic::AtomicUsize;
+use winapi::ctypes::c_void;
+use winapi::shared::guiddef::REFIID;
+use winapi::shared::minwindef::{BOOL, FALSE, TRUE, ULONG};
+use winapi::shared::winerror::{E_INVALIDARG, S_OK};
+use winapi::um::dwrite::IDWriteFactory;
+use winapi::um::dwrite::IDWriteFontCollectionLoader;
+use winapi::um::dwrite::IDWriteFontCollectionLoaderVtbl;
+use winapi::um::dwrite::IDWriteFontFile;
+use winapi::um::dwrite::IDWriteFontFileEnumerator;
+use winapi::um::dwrite::IDWriteFontFileEnumeratorVtbl;
+use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl};
+use winapi::um::winnt::HRESULT;
+use wio::com::ComPtr;
+
+use crate::com_helpers::Com;
+use crate::FontFile;
+
+static FONT_COLLECTION_LOADER_VTBL: IDWriteFontCollectionLoaderVtbl =
+ IDWriteFontCollectionLoaderVtbl {
+ parent: implement_iunknown!(static IDWriteFontCollectionLoader,
+ CustomFontCollectionLoaderImpl),
+ CreateEnumeratorFromKey: CustomFontCollectionLoaderImpl_CreateEnumeratorFromKey,
+ };
+
+#[repr(C)]
+pub struct CustomFontCollectionLoaderImpl {
+ // NB: This must be the first field.
+ _refcount: AtomicUsize,
+ font_files: Vec<ComPtr<IDWriteFontFile>>,
+}
+
+impl Com<IDWriteFontCollectionLoader> for CustomFontCollectionLoaderImpl {
+ type Vtbl = IDWriteFontCollectionLoaderVtbl;
+ #[inline]
+ fn vtbl() -> &'static IDWriteFontCollectionLoaderVtbl {
+ &FONT_COLLECTION_LOADER_VTBL
+ }
+}
+
+impl Com<IUnknown> for CustomFontCollectionLoaderImpl {
+ type Vtbl = IUnknownVtbl;
+ #[inline]
+ fn vtbl() -> &'static IUnknownVtbl {
+ &FONT_COLLECTION_LOADER_VTBL.parent
+ }
+}
+
+impl CustomFontCollectionLoaderImpl {
+ pub fn new(font_files: &[FontFile]) -> ComPtr<IDWriteFontCollectionLoader> {
+ unsafe {
+ ComPtr::from_raw(
+ CustomFontCollectionLoaderImpl {
+ _refcount: AtomicUsize::new(1),
+ font_files: font_files.iter().map(|file| file.as_com_ptr()).collect(),
+ }
+ .into_interface(),
+ )
+ }
+ }
+}
+
+#[allow(non_snake_case)]
+unsafe extern "system" fn CustomFontCollectionLoaderImpl_CreateEnumeratorFromKey(
+ this: *mut IDWriteFontCollectionLoader,
+ _: *mut IDWriteFactory,
+ _: *const c_void,
+ _: u32,
+ out_enumerator: *mut *mut IDWriteFontFileEnumerator,
+) -> HRESULT {
+ let this = CustomFontCollectionLoaderImpl::from_interface(this);
+ let enumerator = CustomFontFileEnumeratorImpl::new((*this).font_files.clone());
+ let enumerator = ComPtr::<IDWriteFontFileEnumerator>::from_raw(enumerator.into_interface());
+ *out_enumerator = enumerator.as_raw();
+ mem::forget(enumerator);
+ S_OK
+}
+
+#[repr(C)]
+struct CustomFontFileEnumeratorImpl {
+ // NB(pcwalton): This must be the first field.
+ _refcount: AtomicUsize,
+ font_files: Vec<ComPtr<IDWriteFontFile>>,
+ index: isize,
+}
+
+impl Com<IDWriteFontFileEnumerator> for CustomFontFileEnumeratorImpl {
+ type Vtbl = IDWriteFontFileEnumeratorVtbl;
+ #[inline]
+ fn vtbl() -> &'static IDWriteFontFileEnumeratorVtbl {
+ &FONT_FILE_ENUMERATOR_VTBL
+ }
+}
+
+impl Com<IUnknown> for CustomFontFileEnumeratorImpl {
+ type Vtbl = IUnknownVtbl;
+ #[inline]
+ fn vtbl() -> &'static IUnknownVtbl {
+ &FONT_FILE_ENUMERATOR_VTBL.parent
+ }
+}
+
+static FONT_FILE_ENUMERATOR_VTBL: IDWriteFontFileEnumeratorVtbl = IDWriteFontFileEnumeratorVtbl {
+ parent: implement_iunknown!(static IDWriteFontFileEnumerator, CustomFontFileEnumeratorImpl),
+ GetCurrentFontFile: CustomFontFileEnumeratorImpl_GetCurrentFontFile,
+ MoveNext: CustomFontFileEnumeratorImpl_MoveNext,
+};
+
+impl CustomFontFileEnumeratorImpl {
+ pub fn new(font_files: Vec<ComPtr<IDWriteFontFile>>) -> CustomFontFileEnumeratorImpl {
+ CustomFontFileEnumeratorImpl {
+ _refcount: AtomicUsize::new(1),
+ font_files,
+ index: -1,
+ }
+ }
+}
+
+#[allow(non_snake_case)]
+unsafe extern "system" fn CustomFontFileEnumeratorImpl_GetCurrentFontFile(
+ this: *mut IDWriteFontFileEnumerator,
+ out_font_file: *mut *mut IDWriteFontFile,
+) -> HRESULT {
+ let this = CustomFontFileEnumeratorImpl::from_interface(this);
+ if (*this).index < 0 || (*this).index >= (*this).font_files.len() as isize {
+ return E_INVALIDARG;
+ }
+ let new_font_file = (*this).font_files[(*this).index as usize].clone();
+ *out_font_file = new_font_file.as_raw();
+ mem::forget(new_font_file);
+ S_OK
+}
+
+#[allow(non_snake_case)]
+unsafe extern "system" fn CustomFontFileEnumeratorImpl_MoveNext(
+ this: *mut IDWriteFontFileEnumerator,
+ has_current_file: *mut BOOL,
+) -> HRESULT {
+ let this = CustomFontFileEnumeratorImpl::from_interface(this);
+ let font_file_count = (*this).font_files.len() as isize;
+ if (*this).index < font_file_count {
+ (*this).index += 1
+ }
+ *has_current_file = if (*this).index >= 0 && (*this).index < font_file_count {
+ TRUE
+ } else {
+ FALSE
+ };
+ S_OK
+}
diff --git a/third_party/rust/dwrote/src/font_face.rs b/third_party/rust/dwrote/src/font_face.rs
new file mode 100644
index 0000000000..be0091e797
--- /dev/null
+++ b/third_party/rust/dwrote/src/font_face.rs
@@ -0,0 +1,377 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::cell::UnsafeCell;
+use std::mem::{self, zeroed};
+use std::ptr;
+use std::slice;
+use winapi::ctypes::c_void;
+use winapi::shared::minwindef::{BOOL, FALSE, TRUE};
+use winapi::shared::winerror::S_OK;
+use winapi::um::dcommon::DWRITE_MEASURING_MODE;
+use winapi::um::dwrite::IDWriteRenderingParams;
+use winapi::um::dwrite::DWRITE_FONT_FACE_TYPE_TRUETYPE;
+use winapi::um::dwrite::{IDWriteFontFace, IDWriteFontFile};
+use winapi::um::dwrite::{DWRITE_FONT_FACE_TYPE_BITMAP, DWRITE_FONT_FACE_TYPE_CFF};
+use winapi::um::dwrite::{DWRITE_FONT_FACE_TYPE_RAW_CFF, DWRITE_FONT_FACE_TYPE_TYPE1};
+use winapi::um::dwrite::{DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION, DWRITE_FONT_FACE_TYPE_VECTOR};
+use winapi::um::dwrite::{DWRITE_FONT_SIMULATIONS, DWRITE_GLYPH_METRICS};
+use winapi::um::dwrite::{DWRITE_GLYPH_OFFSET, DWRITE_MATRIX, DWRITE_RENDERING_MODE};
+use winapi::um::dwrite::{DWRITE_RENDERING_MODE_DEFAULT, DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC};
+use winapi::um::dwrite_1::IDWriteFontFace1;
+use winapi::um::dwrite_3::{IDWriteFontFace5, IDWriteFontResource, DWRITE_FONT_AXIS_VALUE};
+use wio::com::ComPtr;
+
+use super::{DWriteFactory, DefaultDWriteRenderParams, FontFile, FontMetrics};
+use crate::com_helpers::Com;
+use crate::geometry_sink_impl::GeometrySinkImpl;
+use crate::outline_builder::OutlineBuilder;
+
+pub struct FontFace {
+ native: UnsafeCell<ComPtr<IDWriteFontFace>>,
+ face5: UnsafeCell<Option<ComPtr<IDWriteFontFace5>>>,
+}
+
+impl FontFace {
+ pub fn take(native: ComPtr<IDWriteFontFace>) -> FontFace {
+ let cell = UnsafeCell::new(native);
+ FontFace {
+ native: cell,
+ face5: UnsafeCell::new(None),
+ }
+ }
+
+ pub unsafe fn as_ptr(&self) -> *mut IDWriteFontFace {
+ (*self.native.get()).as_raw()
+ }
+
+ unsafe fn get_raw_files(&self) -> Vec<*mut IDWriteFontFile> {
+ let mut number_of_files: u32 = 0;
+ let hr = (*self.native.get()).GetFiles(&mut number_of_files, ptr::null_mut());
+ assert!(hr == 0);
+
+ let mut file_ptrs: Vec<*mut IDWriteFontFile> =
+ vec![ptr::null_mut(); number_of_files as usize];
+ let hr = (*self.native.get()).GetFiles(&mut number_of_files, file_ptrs.as_mut_ptr());
+ assert!(hr == 0);
+ file_ptrs
+ }
+
+ pub fn get_files(&self) -> Vec<FontFile> {
+ unsafe {
+ let file_ptrs = self.get_raw_files();
+ file_ptrs
+ .iter()
+ .map(|p| FontFile::take(ComPtr::from_raw(*p)))
+ .collect()
+ }
+ }
+
+ pub fn create_font_face_with_simulations(
+ &self,
+ simulations: DWRITE_FONT_SIMULATIONS,
+ ) -> FontFace {
+ unsafe {
+ let file_ptrs = self.get_raw_files();
+ let face_type = (*self.native.get()).GetType();
+ let face_index = (*self.native.get()).GetIndex();
+ let mut face: *mut IDWriteFontFace = ptr::null_mut();
+ let hr = (*DWriteFactory()).CreateFontFace(
+ face_type,
+ file_ptrs.len() as u32,
+ file_ptrs.as_ptr(),
+ face_index,
+ simulations,
+ &mut face,
+ );
+ for p in file_ptrs {
+ let _ = ComPtr::<IDWriteFontFile>::from_raw(p);
+ }
+ assert!(hr == 0);
+ FontFace::take(ComPtr::from_raw(face))
+ }
+ }
+
+ pub fn get_glyph_count(&self) -> u16 {
+ unsafe { (*self.native.get()).GetGlyphCount() }
+ }
+
+ pub fn metrics(&self) -> FontMetrics {
+ unsafe {
+ let font_1: Option<ComPtr<IDWriteFontFace1>> = (*self.native.get()).cast().ok();
+ match font_1 {
+ None => {
+ let mut metrics = mem::zeroed();
+ (*self.native.get()).GetMetrics(&mut metrics);
+ FontMetrics::Metrics0(metrics)
+ }
+ Some(font_1) => {
+ let mut metrics_1 = mem::zeroed();
+ font_1.GetMetrics(&mut metrics_1);
+ FontMetrics::Metrics1(metrics_1)
+ }
+ }
+ }
+ }
+
+ pub fn get_glyph_indices(&self, code_points: &[u32]) -> Vec<u16> {
+ unsafe {
+ let mut glyph_indices: Vec<u16> = vec![0; code_points.len()];
+ let hr = (*self.native.get()).GetGlyphIndices(
+ code_points.as_ptr(),
+ code_points.len() as u32,
+ glyph_indices.as_mut_ptr(),
+ );
+ assert!(hr == 0);
+ glyph_indices
+ }
+ }
+
+ pub fn get_design_glyph_metrics(
+ &self,
+ glyph_indices: &[u16],
+ is_sideways: bool,
+ ) -> Vec<DWRITE_GLYPH_METRICS> {
+ unsafe {
+ let mut metrics: Vec<DWRITE_GLYPH_METRICS> = vec![zeroed(); glyph_indices.len()];
+ let hr = (*self.native.get()).GetDesignGlyphMetrics(
+ glyph_indices.as_ptr(),
+ glyph_indices.len() as u32,
+ metrics.as_mut_ptr(),
+ is_sideways as BOOL,
+ );
+ assert!(hr == 0);
+ metrics
+ }
+ }
+
+ pub fn get_gdi_compatible_glyph_metrics(
+ &self,
+ em_size: f32,
+ pixels_per_dip: f32,
+ transform: *const DWRITE_MATRIX,
+ use_gdi_natural: bool,
+ glyph_indices: &[u16],
+ is_sideways: bool,
+ ) -> Vec<DWRITE_GLYPH_METRICS> {
+ unsafe {
+ let mut metrics: Vec<DWRITE_GLYPH_METRICS> = vec![zeroed(); glyph_indices.len()];
+ let hr = (*self.native.get()).GetGdiCompatibleGlyphMetrics(
+ em_size,
+ pixels_per_dip,
+ transform,
+ use_gdi_natural as BOOL,
+ glyph_indices.as_ptr(),
+ glyph_indices.len() as u32,
+ metrics.as_mut_ptr(),
+ is_sideways as BOOL,
+ );
+ assert!(hr == 0);
+ metrics
+ }
+ }
+
+ /// Returns the contents of the OpenType table with the given tag.
+ ///
+ /// NB: The bytes of the tag are reversed! You probably want to use the `u32::swap_bytes()`
+ /// method on the tag value before calling this method.
+ pub fn get_font_table(&self, opentype_table_tag: u32) -> Option<Vec<u8>> {
+ unsafe {
+ let mut table_data_ptr: *const u8 = ptr::null_mut();
+ let mut table_size: u32 = 0;
+ let mut table_context: *mut c_void = ptr::null_mut();
+ let mut exists: BOOL = FALSE;
+
+ let hr = (*self.native.get()).TryGetFontTable(
+ opentype_table_tag,
+ &mut table_data_ptr as *mut *const _ as *mut *const c_void,
+ &mut table_size,
+ &mut table_context,
+ &mut exists,
+ );
+ assert!(hr == 0);
+
+ if exists == FALSE {
+ return None;
+ }
+
+ let table_bytes = slice::from_raw_parts(table_data_ptr, table_size as usize).to_vec();
+
+ (*self.native.get()).ReleaseFontTable(table_context);
+
+ Some(table_bytes)
+ }
+ }
+
+ pub fn get_recommended_rendering_mode(
+ &self,
+ em_size: f32,
+ pixels_per_dip: f32,
+ measure_mode: DWRITE_MEASURING_MODE,
+ rendering_params: *mut IDWriteRenderingParams,
+ ) -> DWRITE_RENDERING_MODE {
+ unsafe {
+ let mut render_mode: DWRITE_RENDERING_MODE = DWRITE_RENDERING_MODE_DEFAULT;
+ let hr = (*self.native.get()).GetRecommendedRenderingMode(
+ em_size,
+ pixels_per_dip,
+ measure_mode,
+ rendering_params,
+ &mut render_mode,
+ );
+
+ if hr != 0 {
+ return DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC;
+ }
+
+ render_mode
+ }
+ }
+
+ pub fn get_recommended_rendering_mode_default_params(
+ &self,
+ em_size: f32,
+ pixels_per_dip: f32,
+ measure_mode: DWRITE_MEASURING_MODE,
+ ) -> DWRITE_RENDERING_MODE {
+ self.get_recommended_rendering_mode(
+ em_size,
+ pixels_per_dip,
+ measure_mode,
+ DefaultDWriteRenderParams(),
+ )
+ }
+
+ pub fn get_glyph_run_outline(
+ &self,
+ em_size: f32,
+ glyph_indices: &[u16],
+ glyph_advances: Option<&[f32]>,
+ glyph_offsets: Option<&[DWRITE_GLYPH_OFFSET]>,
+ is_sideways: bool,
+ is_right_to_left: bool,
+ outline_builder: Box<dyn OutlineBuilder>,
+ ) {
+ unsafe {
+ let glyph_advances = match glyph_advances {
+ None => ptr::null(),
+ Some(glyph_advances) => {
+ assert_eq!(glyph_advances.len(), glyph_indices.len());
+ glyph_advances.as_ptr()
+ }
+ };
+ let glyph_offsets = match glyph_offsets {
+ None => ptr::null(),
+ Some(glyph_offsets) => {
+ assert_eq!(glyph_offsets.len(), glyph_indices.len());
+ glyph_offsets.as_ptr()
+ }
+ };
+ let is_sideways = if is_sideways { TRUE } else { FALSE };
+ let is_right_to_left = if is_right_to_left { TRUE } else { FALSE };
+ let geometry_sink = GeometrySinkImpl::new(outline_builder);
+ let geometry_sink = geometry_sink.into_interface();
+ let hr = (*self.native.get()).GetGlyphRunOutline(
+ em_size,
+ glyph_indices.as_ptr(),
+ glyph_advances,
+ glyph_offsets,
+ glyph_indices.len() as u32,
+ is_sideways,
+ is_right_to_left,
+ geometry_sink,
+ );
+ assert_eq!(hr, S_OK);
+ }
+ }
+
+ #[inline]
+ pub fn get_type(&self) -> FontFaceType {
+ unsafe {
+ match (*self.native.get()).GetType() {
+ DWRITE_FONT_FACE_TYPE_CFF => FontFaceType::Cff,
+ DWRITE_FONT_FACE_TYPE_RAW_CFF => FontFaceType::RawCff,
+ DWRITE_FONT_FACE_TYPE_TRUETYPE => FontFaceType::TrueType,
+ DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION => FontFaceType::TrueTypeCollection,
+ DWRITE_FONT_FACE_TYPE_TYPE1 => FontFaceType::Type1,
+ DWRITE_FONT_FACE_TYPE_VECTOR => FontFaceType::Vector,
+ DWRITE_FONT_FACE_TYPE_BITMAP => FontFaceType::Bitmap,
+ _ => FontFaceType::Unknown,
+ }
+ }
+ }
+
+ #[inline]
+ pub fn get_index(&self) -> u32 {
+ unsafe { (*self.native.get()).GetIndex() }
+ }
+
+ #[inline]
+ unsafe fn get_face5(&self) -> Option<ComPtr<IDWriteFontFace5>> {
+ if (*self.face5.get()).is_none() {
+ *self.face5.get() = (*self.native.get()).cast().ok()
+ }
+ (*self.face5.get()).clone()
+ }
+
+ pub fn has_variations(&self) -> bool {
+ unsafe {
+ match self.get_face5() {
+ Some(face5) => face5.HasVariations() == TRUE,
+ None => false,
+ }
+ }
+ }
+
+ pub fn create_font_face_with_variations(
+ &self,
+ simulations: DWRITE_FONT_SIMULATIONS,
+ axis_values: &[DWRITE_FONT_AXIS_VALUE],
+ ) -> Option<FontFace> {
+ unsafe {
+ if let Some(face5) = self.get_face5() {
+ let mut resource: *mut IDWriteFontResource = ptr::null_mut();
+ let hr = face5.GetFontResource(&mut resource);
+ if hr == S_OK && !resource.is_null() {
+ let resource = ComPtr::from_raw(resource);
+ let mut var_face: *mut IDWriteFontFace5 = ptr::null_mut();
+ let hr = resource.CreateFontFace(
+ simulations,
+ axis_values.as_ptr(),
+ axis_values.len() as u32,
+ &mut var_face,
+ );
+ if hr == S_OK && !var_face.is_null() {
+ let var_face = ComPtr::from_raw(var_face).cast().unwrap();
+ return Some(FontFace::take(var_face));
+ }
+ }
+ }
+ None
+ }
+ }
+}
+
+impl Clone for FontFace {
+ fn clone(&self) -> FontFace {
+ unsafe {
+ FontFace {
+ native: UnsafeCell::new((*self.native.get()).clone()),
+ face5: UnsafeCell::new(None),
+ }
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum FontFaceType {
+ Unknown,
+ Cff,
+ RawCff,
+ TrueType,
+ TrueTypeCollection,
+ Type1,
+ Vector,
+ Bitmap,
+}
diff --git a/third_party/rust/dwrote/src/font_fallback.rs b/third_party/rust/dwrote/src/font_fallback.rs
new file mode 100644
index 0000000000..1fa1338a29
--- /dev/null
+++ b/third_party/rust/dwrote/src/font_fallback.rs
@@ -0,0 +1,94 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::cell::UnsafeCell;
+use std::ptr::null_mut;
+use winapi::um::dwrite_2::{IDWriteFactory2, IDWriteFontFallback};
+use wio::com::ComPtr;
+
+use super::*;
+
+pub struct FontFallback {
+ native: UnsafeCell<ComPtr<IDWriteFontFallback>>,
+}
+
+pub struct FallbackResult {
+ /// Length of mapped substring, in utf-16 code units.
+ pub mapped_length: usize,
+ /// The font that should be used to render the substring.
+ pub mapped_font: Option<Font>,
+ /// The scale factor to apply.
+ pub scale: f32,
+}
+
+impl FontFallback {
+ pub fn get_system_fallback() -> Option<FontFallback> {
+ unsafe {
+ let factory = ComPtr::from_raw(DWriteFactory());
+ let factory2: Option<ComPtr<IDWriteFactory2>> = factory.cast().ok();
+ std::mem::forget(factory);
+ let factory2 = factory2?;
+ let mut native = null_mut();
+ let hr = factory2.GetSystemFontFallback(&mut native);
+ assert_eq!(hr, 0);
+ Some(Self::take(ComPtr::from_raw(native)))
+ }
+ }
+
+ pub fn take(native: ComPtr<IDWriteFontFallback>) -> FontFallback {
+ FontFallback {
+ native: UnsafeCell::new(native),
+ }
+ }
+
+ // TODO: I'm following crate conventions for unsafe, but it's bullshit
+ pub unsafe fn as_ptr(&self) -> *mut IDWriteFontFallback {
+ (*self.native.get()).as_raw()
+ }
+
+ // TODO: map_characters (main function)
+ pub fn map_characters(
+ &self,
+ text_analysis_source: &TextAnalysisSource,
+ text_position: u32,
+ text_length: u32,
+ base_font: &FontCollection,
+ base_family: Option<&str>,
+ base_weight: FontWeight,
+ base_style: FontStyle,
+ base_stretch: FontStretch,
+ ) -> FallbackResult {
+ unsafe {
+ let mut font = null_mut();
+ let mut mapped_length = 0;
+ let mut scale = 0.0;
+ let hr = (*self.as_ptr()).MapCharacters(
+ text_analysis_source.as_ptr(),
+ text_position,
+ text_length,
+ base_font.as_ptr(),
+ base_family
+ .map(|s| s.to_wide_null().as_mut_ptr())
+ .unwrap_or(null_mut()),
+ base_weight.t(),
+ base_style.t(),
+ base_stretch.t(),
+ &mut mapped_length,
+ &mut font,
+ &mut scale,
+ );
+ assert_eq!(hr, 0);
+ let mapped_font = if font.is_null() {
+ None
+ } else {
+ Some(Font::take(ComPtr::from_raw(font)))
+ };
+ FallbackResult {
+ mapped_length: mapped_length as usize,
+ mapped_font,
+ scale,
+ }
+ }
+ }
+}
diff --git a/third_party/rust/dwrote/src/font_family.rs b/third_party/rust/dwrote/src/font_family.rs
new file mode 100644
index 0000000000..72f13b6a5a
--- /dev/null
+++ b/third_party/rust/dwrote/src/font_family.rs
@@ -0,0 +1,79 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::cell::UnsafeCell;
+use std::ptr;
+use winapi::um::dwrite::IDWriteLocalizedStrings;
+use winapi::um::dwrite::{IDWriteFont, IDWriteFontCollection, IDWriteFontFamily};
+use wio::com::ComPtr;
+
+use super::*;
+use helpers::*;
+
+pub struct FontFamily {
+ native: UnsafeCell<ComPtr<IDWriteFontFamily>>,
+}
+
+impl FontFamily {
+ pub fn take(native: ComPtr<IDWriteFontFamily>) -> FontFamily {
+ FontFamily {
+ native: UnsafeCell::new(native),
+ }
+ }
+
+ pub unsafe fn as_ptr(&self) -> *mut IDWriteFontFamily {
+ (*self.native.get()).as_raw()
+ }
+
+ pub fn name(&self) -> String {
+ unsafe {
+ let mut family_names: *mut IDWriteLocalizedStrings = ptr::null_mut();
+ let hr = (*self.native.get()).GetFamilyNames(&mut family_names);
+ assert!(hr == 0);
+
+ get_locale_string(&mut ComPtr::from_raw(family_names))
+ }
+ }
+
+ pub fn get_first_matching_font(
+ &self,
+ weight: FontWeight,
+ stretch: FontStretch,
+ style: FontStyle,
+ ) -> Font {
+ unsafe {
+ let mut font: *mut IDWriteFont = ptr::null_mut();
+ let hr = (*self.native.get()).GetFirstMatchingFont(
+ weight.t(),
+ stretch.t(),
+ style.t(),
+ &mut font,
+ );
+ assert!(hr == 0);
+ Font::take(ComPtr::from_raw(font))
+ }
+ }
+
+ pub fn get_font_collection(&self) -> FontCollection {
+ unsafe {
+ let mut collection: *mut IDWriteFontCollection = ptr::null_mut();
+ let hr = (*self.native.get()).GetFontCollection(&mut collection);
+ assert!(hr == 0);
+ FontCollection::take(ComPtr::from_raw(collection))
+ }
+ }
+
+ pub fn get_font_count(&self) -> u32 {
+ unsafe { (*self.native.get()).GetFontCount() }
+ }
+
+ pub fn get_font(&self, index: u32) -> Font {
+ unsafe {
+ let mut font: *mut IDWriteFont = ptr::null_mut();
+ let hr = (*self.native.get()).GetFont(index, &mut font);
+ assert!(hr == 0);
+ Font::take(ComPtr::from_raw(font))
+ }
+ }
+}
diff --git a/third_party/rust/dwrote/src/font_file.rs b/third_party/rust/dwrote/src/font_file.rs
new file mode 100644
index 0000000000..04ac7fb0a5
--- /dev/null
+++ b/third_party/rust/dwrote/src/font_file.rs
@@ -0,0 +1,256 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::cell::UnsafeCell;
+use std::ffi::OsString;
+use std::os::windows::ffi::{OsStrExt, OsStringExt};
+use std::path::Path;
+use std::path::PathBuf;
+use std::ptr;
+use std::slice;
+use std::sync::Arc;
+use winapi::ctypes::c_void;
+use winapi::um::dwrite::{IDWriteFontFace, IDWriteFontFile, IDWriteFontFileStream};
+use winapi::um::dwrite::{IDWriteFontFileLoader, IDWriteLocalFontFileLoader};
+use winapi::um::dwrite::{DWRITE_FONT_FACE_TYPE, DWRITE_FONT_FILE_TYPE_UNKNOWN};
+use winapi::um::dwrite::{DWRITE_FONT_FACE_TYPE_UNKNOWN, DWRITE_FONT_SIMULATIONS};
+use winapi::um::winnt::HRESULT;
+use wio::com::ComPtr;
+
+use super::DWriteFactory;
+use crate::font_face::FontFace;
+use crate::font_file_loader_impl::DataFontHelper;
+
+pub struct FontFile {
+ native: UnsafeCell<ComPtr<IDWriteFontFile>>,
+ stream: UnsafeCell<Option<ComPtr<IDWriteFontFileStream>>>,
+ data_key: usize,
+ face_type: DWRITE_FONT_FACE_TYPE,
+}
+
+impl FontFile {
+ pub fn new_from_path<P>(path: P) -> Option<FontFile>
+ where
+ P: AsRef<Path>,
+ {
+ unsafe {
+ let mut path: Vec<u16> = path.as_ref().as_os_str().encode_wide().collect();
+ path.push(0);
+
+ let mut font_file: *mut IDWriteFontFile = ptr::null_mut();
+ let hr = (*DWriteFactory()).CreateFontFileReference(
+ path.as_ptr(),
+ ptr::null(),
+ &mut font_file,
+ );
+ if hr != 0 || font_file.is_null() {
+ return None;
+ }
+
+ let mut ff = FontFile {
+ native: UnsafeCell::new(ComPtr::from_raw(font_file)),
+ stream: UnsafeCell::new(None),
+ data_key: 0,
+ face_type: DWRITE_FONT_FACE_TYPE_UNKNOWN,
+ };
+
+ if ff.analyze() == 0 {
+ None
+ } else {
+ Some(ff)
+ }
+ }
+ }
+
+ pub fn new_from_data(data: Arc<Vec<u8>>) -> Option<FontFile> {
+ let (font_file, font_file_stream, key) = DataFontHelper::register_font_data(data);
+
+ let mut ff = FontFile {
+ native: UnsafeCell::new(font_file),
+ stream: UnsafeCell::new(Some(font_file_stream)),
+ data_key: key,
+ face_type: DWRITE_FONT_FACE_TYPE_UNKNOWN,
+ };
+
+ if ff.analyze() == 0 {
+ None
+ } else {
+ Some(ff)
+ }
+ }
+
+ pub fn analyze_data(data: Arc<Vec<u8>>) -> u32 {
+ let (font_file, font_file_stream, key) = DataFontHelper::register_font_data(data);
+
+ let mut ff = FontFile {
+ native: UnsafeCell::new(font_file),
+ stream: UnsafeCell::new(Some(font_file_stream)),
+ data_key: key,
+ face_type: DWRITE_FONT_FACE_TYPE_UNKNOWN,
+ };
+
+ ff.analyze()
+ }
+
+ fn analyze(&mut self) -> u32 {
+ let mut face_type = DWRITE_FONT_FACE_TYPE_UNKNOWN;
+ let mut num_faces = 0;
+ unsafe {
+ let mut supported = 0;
+ let mut _file_type = DWRITE_FONT_FILE_TYPE_UNKNOWN;
+
+ let hr = (*self.native.get()).Analyze(
+ &mut supported,
+ &mut _file_type,
+ &mut face_type,
+ &mut num_faces,
+ );
+ if hr != 0 || supported == 0 {
+ return 0;
+ }
+ }
+ self.face_type = face_type;
+ num_faces as u32
+ }
+
+ pub fn take(native: ComPtr<IDWriteFontFile>) -> FontFile {
+ let mut ff = FontFile {
+ native: UnsafeCell::new(native),
+ stream: UnsafeCell::new(None),
+ data_key: 0,
+ face_type: DWRITE_FONT_FACE_TYPE_UNKNOWN,
+ };
+ ff.analyze();
+ ff
+ }
+
+ pub fn data_key(&self) -> Option<usize> {
+ if self.data_key != 0 {
+ Some(self.data_key)
+ } else {
+ None
+ }
+ }
+
+ pub(crate) unsafe fn as_com_ptr(&self) -> ComPtr<IDWriteFontFile> {
+ (*self.native.get()).clone()
+ }
+
+ // This is a helper to read the contents of this FontFile,
+ // without requiring callers to deal with loaders, keys,
+ // or streams.
+ pub fn get_font_file_bytes(&self) -> Vec<u8> {
+ unsafe {
+ let mut ref_key: *const c_void = ptr::null();
+ let mut ref_key_size: u32 = 0;
+ let hr = (*self.native.get()).GetReferenceKey(&mut ref_key, &mut ref_key_size);
+ assert!(hr == 0);
+
+ let mut loader: *mut IDWriteFontFileLoader = ptr::null_mut();
+ let hr = (*self.native.get()).GetLoader(&mut loader);
+ assert!(hr == 0);
+ let loader = ComPtr::from_raw(loader);
+
+ let mut stream: *mut IDWriteFontFileStream = ptr::null_mut();
+ let hr = loader.CreateStreamFromKey(ref_key, ref_key_size, &mut stream);
+ assert!(hr == 0);
+ let stream = ComPtr::from_raw(stream);
+
+ let mut file_size: u64 = 0;
+ let hr = stream.GetFileSize(&mut file_size);
+ assert!(hr == 0);
+
+ let mut fragment_start: *const c_void = ptr::null();
+ let mut fragment_context: *mut c_void = ptr::null_mut();
+ let hr =
+ stream.ReadFileFragment(&mut fragment_start, 0, file_size, &mut fragment_context);
+ assert!(hr == 0);
+
+ let in_ptr = slice::from_raw_parts(fragment_start as *const u8, file_size as usize);
+ let bytes = in_ptr.to_vec();
+
+ stream.ReleaseFileFragment(fragment_context);
+
+ bytes
+ }
+ }
+
+ // This is a helper to get the path of a font file,
+ // without requiring callers to deal with loaders.
+ pub fn get_font_file_path(&self) -> Option<PathBuf> {
+ unsafe {
+ let mut ref_key: *const c_void = ptr::null();
+ let mut ref_key_size: u32 = 0;
+ let hr = (*self.native.get()).GetReferenceKey(&mut ref_key, &mut ref_key_size);
+ assert!(hr == 0);
+
+ let mut loader: *mut IDWriteFontFileLoader = ptr::null_mut();
+ let hr = (*self.native.get()).GetLoader(&mut loader);
+ assert!(hr == 0);
+ let loader = ComPtr::from_raw(loader);
+
+ let local_loader: ComPtr<IDWriteLocalFontFileLoader> = match loader.cast() {
+ Ok(local_loader) => local_loader,
+ Err(_) => return None,
+ };
+
+ let mut file_path_len = 0;
+ let hr =
+ local_loader.GetFilePathLengthFromKey(ref_key, ref_key_size, &mut file_path_len);
+ assert_eq!(hr, 0);
+
+ let mut file_path_buf = vec![0; file_path_len as usize + 1];
+ let hr = local_loader.GetFilePathFromKey(
+ ref_key,
+ ref_key_size,
+ file_path_buf.as_mut_ptr(),
+ file_path_len + 1,
+ );
+ assert_eq!(hr, 0);
+
+ if let Some(&0) = file_path_buf.last() {
+ file_path_buf.pop();
+ }
+
+ Some(PathBuf::from(OsString::from_wide(&file_path_buf)))
+ }
+ }
+
+ pub fn create_face(
+ &self,
+ face_index: u32,
+ simulations: DWRITE_FONT_SIMULATIONS,
+ ) -> Result<FontFace, HRESULT> {
+ unsafe {
+ let mut face: *mut IDWriteFontFace = ptr::null_mut();
+ let ptr = self.as_com_ptr();
+ let hr = (*DWriteFactory()).CreateFontFace(
+ self.face_type,
+ 1,
+ &ptr.as_raw(),
+ face_index,
+ simulations,
+ &mut face,
+ );
+ if hr != 0 {
+ Err(hr)
+ } else {
+ Ok(FontFace::take(ComPtr::from_raw(face)))
+ }
+ }
+ }
+}
+
+impl Clone for FontFile {
+ fn clone(&self) -> FontFile {
+ unsafe {
+ FontFile {
+ native: UnsafeCell::new((*self.native.get()).clone()),
+ stream: UnsafeCell::new((*self.stream.get()).clone()),
+ data_key: self.data_key,
+ face_type: self.face_type,
+ }
+ }
+ }
+}
diff --git a/third_party/rust/dwrote/src/font_file_loader_impl.rs b/third_party/rust/dwrote/src/font_file_loader_impl.rs
new file mode 100644
index 0000000000..30230d0db6
--- /dev/null
+++ b/third_party/rust/dwrote/src/font_file_loader_impl.rs
@@ -0,0 +1,237 @@
+#![allow(non_snake_case, non_upper_case_globals)]
+
+use std::collections::HashMap;
+use std::marker::Send;
+use std::sync::atomic::AtomicUsize;
+use std::sync::{atomic, Arc, Mutex};
+use std::{mem, ptr};
+use winapi::ctypes::c_void;
+use winapi::shared::basetsd::{UINT32, UINT64};
+use winapi::shared::guiddef::REFIID;
+use winapi::shared::minwindef::ULONG;
+use winapi::shared::winerror::{E_FAIL, E_INVALIDARG, E_NOTIMPL, S_OK};
+use winapi::um::dwrite::IDWriteFontFile;
+use winapi::um::dwrite::{IDWriteFontFileLoader, IDWriteFontFileLoaderVtbl};
+use winapi::um::dwrite::{IDWriteFontFileStream, IDWriteFontFileStreamVtbl};
+use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl};
+use winapi::um::winnt::HRESULT;
+use wio::com::ComPtr;
+
+use super::DWriteFactory;
+use crate::com_helpers::*;
+
+struct FontFileLoader;
+
+const FontFileLoaderVtbl: &'static IDWriteFontFileLoaderVtbl = &IDWriteFontFileLoaderVtbl {
+ parent: implement_iunknown!(static IDWriteFontFileLoader, FontFileLoader),
+ CreateStreamFromKey: {
+ unsafe extern "system" fn CreateStreamFromKey(
+ _This: *mut IDWriteFontFileLoader,
+ fontFileReferenceKey: *const c_void,
+ fontFileReferenceKeySize: UINT32,
+ fontFileStream: *mut *mut IDWriteFontFileStream,
+ ) -> HRESULT {
+ if fontFileReferenceKey.is_null() || fontFileStream.is_null() {
+ return E_INVALIDARG;
+ }
+ assert!(fontFileReferenceKeySize == mem::size_of::<usize>() as UINT32);
+ let key = *(fontFileReferenceKey as *const usize);
+ let stream = match FONT_FILE_STREAM_MAP.lock().unwrap().get(&key) {
+ None => {
+ *fontFileStream = ptr::null_mut();
+ return E_FAIL;
+ }
+ Some(&FontFileStreamPtr(file_stream)) => file_stream,
+ };
+
+ // This is an addref getter, so make sure to do that!
+ (*stream).AddRef();
+
+ *fontFileStream = stream;
+ S_OK
+ }
+ CreateStreamFromKey
+ },
+};
+
+impl Com<IDWriteFontFileLoader> for FontFileLoader {
+ type Vtbl = IDWriteFontFileLoaderVtbl;
+ fn vtbl() -> &'static IDWriteFontFileLoaderVtbl {
+ FontFileLoaderVtbl
+ }
+}
+
+impl Com<IUnknown> for FontFileLoader {
+ type Vtbl = IUnknownVtbl;
+ fn vtbl() -> &'static IUnknownVtbl {
+ &FontFileLoaderVtbl.parent
+ }
+}
+
+impl FontFileLoader {
+ pub fn new() -> FontFileLoader {
+ FontFileLoader
+ }
+}
+
+unsafe impl Send for FontFileLoader {}
+unsafe impl Sync for FontFileLoader {}
+
+struct FontFileStream {
+ refcount: atomic::AtomicUsize,
+ key: usize,
+ data: Arc<Vec<u8>>,
+}
+
+const FontFileStreamVtbl: &'static IDWriteFontFileStreamVtbl = &IDWriteFontFileStreamVtbl {
+ parent: implement_iunknown!(IDWriteFontFileStream, FontFileStream),
+ ReadFileFragment: {
+ unsafe extern "system" fn ReadFileFragment(
+ This: *mut IDWriteFontFileStream,
+ fragmentStart: *mut *const c_void,
+ fileOffset: UINT64,
+ fragmentSize: UINT64,
+ fragmentContext: *mut *mut c_void,
+ ) -> HRESULT {
+ let this = FontFileStream::from_interface(This);
+ *fragmentContext = ptr::null_mut();
+ if (fileOffset + fragmentSize) as usize > this.data.len() {
+ return E_INVALIDARG;
+ }
+ let index = fileOffset as usize;
+ *fragmentStart = this.data[index..].as_ptr() as *const c_void;
+ S_OK
+ }
+ ReadFileFragment
+ },
+ ReleaseFileFragment: {
+ unsafe extern "system" fn ReleaseFileFragment(
+ _This: *mut IDWriteFontFileStream,
+ _fragmentContext: *mut c_void,
+ ) {
+ }
+ ReleaseFileFragment
+ },
+ GetFileSize: {
+ unsafe extern "system" fn GetFileSize(
+ This: *mut IDWriteFontFileStream,
+ fileSize: *mut UINT64,
+ ) -> HRESULT {
+ let this = FontFileStream::from_interface(This);
+ *fileSize = this.data.len() as UINT64;
+ S_OK
+ }
+ GetFileSize
+ },
+ GetLastWriteTime: {
+ unsafe extern "system" fn GetLastWriteTime(
+ _This: *mut IDWriteFontFileStream,
+ _lastWriteTime: *mut UINT64,
+ ) -> HRESULT {
+ E_NOTIMPL
+ }
+ GetLastWriteTime
+ },
+};
+
+impl FontFileStream {
+ pub fn new(key: usize, data: Arc<Vec<u8>>) -> FontFileStream {
+ FontFileStream {
+ refcount: AtomicUsize::new(1),
+ key,
+ data,
+ }
+ }
+}
+
+impl Drop for FontFileStream {
+ fn drop(&mut self) {
+ DataFontHelper::unregister_font_data(self.key);
+ }
+}
+
+impl Com<IDWriteFontFileStream> for FontFileStream {
+ type Vtbl = IDWriteFontFileStreamVtbl;
+ fn vtbl() -> &'static IDWriteFontFileStreamVtbl {
+ FontFileStreamVtbl
+ }
+}
+
+impl Com<IUnknown> for FontFileStream {
+ type Vtbl = IUnknownVtbl;
+ fn vtbl() -> &'static IUnknownVtbl {
+ &FontFileStreamVtbl.parent
+ }
+}
+
+struct FontFileStreamPtr(*mut IDWriteFontFileStream);
+
+unsafe impl Send for FontFileStreamPtr {}
+
+static mut FONT_FILE_KEY: atomic::AtomicUsize = AtomicUsize::new(0);
+
+#[derive(Clone)]
+struct FontFileLoaderWrapper(ComPtr<IDWriteFontFileLoader>);
+
+unsafe impl Send for FontFileLoaderWrapper {}
+unsafe impl Sync for FontFileLoaderWrapper {}
+
+lazy_static! {
+ static ref FONT_FILE_STREAM_MAP: Mutex<HashMap<usize, FontFileStreamPtr>> =
+ { Mutex::new(HashMap::new()) };
+ static ref FONT_FILE_LOADER: Mutex<FontFileLoaderWrapper> = {
+ unsafe {
+ let ffl_native = FontFileLoader::new();
+ let ffl = ComPtr::<IDWriteFontFileLoader>::from_raw(ffl_native.into_interface());
+ let hr = (*DWriteFactory()).RegisterFontFileLoader(ffl.as_raw());
+ assert!(hr == 0);
+ Mutex::new(FontFileLoaderWrapper(ffl))
+ }
+ };
+}
+
+pub struct DataFontHelper;
+
+impl DataFontHelper {
+ pub fn register_font_data(
+ font_data: Arc<Vec<u8>>,
+ ) -> (
+ ComPtr<IDWriteFontFile>,
+ ComPtr<IDWriteFontFileStream>,
+ usize,
+ ) {
+ unsafe {
+ let key = FONT_FILE_KEY.fetch_add(1, atomic::Ordering::Relaxed);
+ let font_file_stream_native = FontFileStream::new(key, font_data);
+ let font_file_stream: ComPtr<IDWriteFontFileStream> =
+ ComPtr::from_raw(font_file_stream_native.into_interface());
+
+ {
+ let mut map = FONT_FILE_STREAM_MAP.lock().unwrap();
+ map.insert(key, FontFileStreamPtr(font_file_stream.as_raw()));
+ }
+
+ let mut font_file: *mut IDWriteFontFile = ptr::null_mut();
+ {
+ let loader = FONT_FILE_LOADER.lock().unwrap();
+ let hr = (*DWriteFactory()).CreateCustomFontFileReference(
+ mem::transmute(&key),
+ mem::size_of::<usize>() as UINT32,
+ loader.0.as_raw(),
+ &mut font_file,
+ );
+ assert!(hr == S_OK);
+ }
+ let font_file = ComPtr::from_raw(font_file);
+
+ (font_file, font_file_stream, key)
+ }
+ }
+
+ fn unregister_font_data(key: usize) {
+ let mut map = FONT_FILE_STREAM_MAP.lock().unwrap();
+ if map.remove(&key).is_none() {
+ panic!("unregister_font_data: trying to unregister key that is no longer registered");
+ }
+ }
+}
diff --git a/third_party/rust/dwrote/src/gdi_interop.rs b/third_party/rust/dwrote/src/gdi_interop.rs
new file mode 100644
index 0000000000..fcf27947c8
--- /dev/null
+++ b/third_party/rust/dwrote/src/gdi_interop.rs
@@ -0,0 +1,46 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::cell::UnsafeCell;
+use std::ptr;
+use winapi::um::dwrite::IDWriteBitmapRenderTarget;
+use winapi::um::dwrite::IDWriteGdiInterop;
+use wio::com::ComPtr;
+
+use super::{BitmapRenderTarget, DWriteFactory};
+
+pub struct GdiInterop {
+ native: UnsafeCell<ComPtr<IDWriteGdiInterop>>,
+}
+
+impl GdiInterop {
+ pub fn create() -> GdiInterop {
+ unsafe {
+ let mut native: *mut IDWriteGdiInterop = ptr::null_mut();
+ let hr = (*DWriteFactory()).GetGdiInterop(&mut native);
+ assert!(hr == 0);
+ GdiInterop::take(ComPtr::from_raw(native))
+ }
+ }
+
+ pub fn take(native: ComPtr<IDWriteGdiInterop>) -> GdiInterop {
+ GdiInterop {
+ native: UnsafeCell::new(native),
+ }
+ }
+
+ pub fn create_bitmap_render_target(&self, width: u32, height: u32) -> BitmapRenderTarget {
+ unsafe {
+ let mut native: *mut IDWriteBitmapRenderTarget = ptr::null_mut();
+ let hr = (*self.native.get()).CreateBitmapRenderTarget(
+ ptr::null_mut(),
+ width,
+ height,
+ &mut native,
+ );
+ assert!(hr == 0);
+ BitmapRenderTarget::take(ComPtr::from_raw(native))
+ }
+ }
+}
diff --git a/third_party/rust/dwrote/src/geometry_sink_impl.rs b/third_party/rust/dwrote/src/geometry_sink_impl.rs
new file mode 100644
index 0000000000..2f2f7550a2
--- /dev/null
+++ b/third_party/rust/dwrote/src/geometry_sink_impl.rs
@@ -0,0 +1,127 @@
+#![allow(non_snake_case, non_upper_case_globals)]
+
+use std::mem;
+use std::slice;
+use std::sync::atomic::AtomicUsize;
+use winapi::shared::guiddef::REFIID;
+use winapi::shared::minwindef::{UINT, ULONG};
+use winapi::shared::winerror::S_OK;
+use winapi::um::d2d1::{ID2D1SimplifiedGeometrySink, ID2D1SimplifiedGeometrySinkVtbl};
+use winapi::um::d2d1::{D2D1_BEZIER_SEGMENT, D2D1_FIGURE_BEGIN, D2D1_FIGURE_END};
+use winapi::um::d2d1::{D2D1_FIGURE_END_CLOSED, D2D1_FILL_MODE, D2D1_PATH_SEGMENT, D2D1_POINT_2F};
+use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl};
+use winapi::um::winnt::HRESULT;
+
+use crate::com_helpers::Com;
+use crate::outline_builder::OutlineBuilder;
+
+static GEOMETRY_SINK_VTBL: ID2D1SimplifiedGeometrySinkVtbl = ID2D1SimplifiedGeometrySinkVtbl {
+ parent: implement_iunknown!(static ID2D1SimplifiedGeometrySink, GeometrySinkImpl),
+ BeginFigure: GeometrySinkImpl_BeginFigure,
+ EndFigure: GeometrySinkImpl_EndFigure,
+ AddLines: GeometrySinkImpl_AddLines,
+ AddBeziers: GeometrySinkImpl_AddBeziers,
+ Close: GeometrySinkImpl_Close,
+ SetFillMode: GeometrySinkImpl_SetFillMode,
+ SetSegmentFlags: GeometrySinkImpl_SetSegmentFlags,
+};
+
+#[repr(C)]
+pub struct GeometrySinkImpl {
+ // NB: This must be the first field.
+ _refcount: AtomicUsize,
+ outline_builder: Box<dyn OutlineBuilder>,
+}
+
+impl Com<ID2D1SimplifiedGeometrySink> for GeometrySinkImpl {
+ type Vtbl = ID2D1SimplifiedGeometrySinkVtbl;
+ #[inline]
+ fn vtbl() -> &'static ID2D1SimplifiedGeometrySinkVtbl {
+ &GEOMETRY_SINK_VTBL
+ }
+}
+
+impl Com<IUnknown> for GeometrySinkImpl {
+ type Vtbl = IUnknownVtbl;
+ #[inline]
+ fn vtbl() -> &'static IUnknownVtbl {
+ &GEOMETRY_SINK_VTBL.parent
+ }
+}
+
+impl GeometrySinkImpl {
+ pub fn new(outline_builder: Box<dyn OutlineBuilder>) -> GeometrySinkImpl {
+ GeometrySinkImpl {
+ _refcount: AtomicUsize::new(1),
+ outline_builder,
+ }
+ }
+}
+
+unsafe extern "system" fn GeometrySinkImpl_BeginFigure(
+ this: *mut ID2D1SimplifiedGeometrySink,
+ start_point: D2D1_POINT_2F,
+ _: D2D1_FIGURE_BEGIN,
+) {
+ let this = GeometrySinkImpl::from_interface(this);
+ (*this)
+ .outline_builder
+ .move_to(start_point.x, start_point.y)
+}
+
+unsafe extern "system" fn GeometrySinkImpl_EndFigure(
+ this: *mut ID2D1SimplifiedGeometrySink,
+ figure_end: D2D1_FIGURE_END,
+) {
+ let this = GeometrySinkImpl::from_interface(this);
+ if figure_end == D2D1_FIGURE_END_CLOSED {
+ (*this).outline_builder.close()
+ }
+}
+
+unsafe extern "system" fn GeometrySinkImpl_AddLines(
+ this: *mut ID2D1SimplifiedGeometrySink,
+ points: *const D2D1_POINT_2F,
+ points_count: UINT,
+) {
+ let this = GeometrySinkImpl::from_interface(this);
+ let points = slice::from_raw_parts(points, points_count as usize);
+ for point in points {
+ (*this).outline_builder.line_to(point.x, point.y)
+ }
+}
+
+unsafe extern "system" fn GeometrySinkImpl_AddBeziers(
+ this: *mut ID2D1SimplifiedGeometrySink,
+ beziers: *const D2D1_BEZIER_SEGMENT,
+ beziers_count: UINT,
+) {
+ let this = GeometrySinkImpl::from_interface(this);
+ let beziers = slice::from_raw_parts(beziers, beziers_count as usize);
+ for bezier in beziers {
+ (*this).outline_builder.curve_to(
+ bezier.point1.x,
+ bezier.point1.y,
+ bezier.point2.x,
+ bezier.point2.y,
+ bezier.point3.x,
+ bezier.point3.y,
+ )
+ }
+}
+
+unsafe extern "system" fn GeometrySinkImpl_Close(_: *mut ID2D1SimplifiedGeometrySink) -> HRESULT {
+ S_OK
+}
+
+unsafe extern "system" fn GeometrySinkImpl_SetFillMode(
+ _: *mut ID2D1SimplifiedGeometrySink,
+ _: D2D1_FILL_MODE,
+) {
+}
+
+unsafe extern "system" fn GeometrySinkImpl_SetSegmentFlags(
+ _: *mut ID2D1SimplifiedGeometrySink,
+ _: D2D1_PATH_SEGMENT,
+) {
+}
diff --git a/third_party/rust/dwrote/src/glyph_run_analysis.rs b/third_party/rust/dwrote/src/glyph_run_analysis.rs
new file mode 100644
index 0000000000..5224c60bc9
--- /dev/null
+++ b/third_party/rust/dwrote/src/glyph_run_analysis.rs
@@ -0,0 +1,107 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::cell::UnsafeCell;
+use std::mem;
+use std::ptr;
+use winapi::shared::windef::RECT;
+use winapi::um::dcommon::DWRITE_MEASURING_MODE;
+use winapi::um::dwrite::DWRITE_TEXTURE_CLEARTYPE_3x1;
+use winapi::um::dwrite::IDWriteGlyphRunAnalysis;
+use winapi::um::dwrite::{DWRITE_TEXTURE_ALIASED_1x1, DWRITE_GLYPH_RUN, DWRITE_TEXTURE_TYPE};
+use winapi::um::dwrite::{DWRITE_MATRIX, DWRITE_RENDERING_MODE};
+use winapi::um::winnt::HRESULT;
+use wio::com::ComPtr;
+
+use super::DWriteFactory;
+
+pub struct GlyphRunAnalysis {
+ native: UnsafeCell<ComPtr<IDWriteGlyphRunAnalysis>>,
+}
+
+impl GlyphRunAnalysis {
+ pub fn create(
+ glyph_run: &DWRITE_GLYPH_RUN,
+ pixels_per_dip: f32,
+ transform: Option<DWRITE_MATRIX>,
+ rendering_mode: DWRITE_RENDERING_MODE,
+ measuring_mode: DWRITE_MEASURING_MODE,
+ baseline_x: f32,
+ baseline_y: f32,
+ ) -> Result<GlyphRunAnalysis, HRESULT> {
+ unsafe {
+ let mut native: *mut IDWriteGlyphRunAnalysis = ptr::null_mut();
+ let hr = (*DWriteFactory()).CreateGlyphRunAnalysis(
+ glyph_run as *const DWRITE_GLYPH_RUN,
+ pixels_per_dip,
+ transform
+ .as_ref()
+ .map(|x| x as *const _)
+ .unwrap_or(ptr::null()),
+ rendering_mode,
+ measuring_mode,
+ baseline_x,
+ baseline_y,
+ &mut native,
+ );
+ if hr != 0 {
+ Err(hr)
+ } else {
+ Ok(GlyphRunAnalysis::take(ComPtr::from_raw(native)))
+ }
+ }
+ }
+
+ pub fn take(native: ComPtr<IDWriteGlyphRunAnalysis>) -> GlyphRunAnalysis {
+ GlyphRunAnalysis {
+ native: UnsafeCell::new(native),
+ }
+ }
+
+ pub fn get_alpha_texture_bounds(
+ &self,
+ texture_type: DWRITE_TEXTURE_TYPE,
+ ) -> Result<RECT, HRESULT> {
+ unsafe {
+ let mut rect: RECT = mem::zeroed();
+ rect.left = 1234;
+ rect.top = 1234;
+ let hr = (*self.native.get()).GetAlphaTextureBounds(texture_type, &mut rect);
+ if hr != 0 {
+ Err(hr)
+ } else {
+ Ok(rect)
+ }
+ }
+ }
+
+ pub fn create_alpha_texture(
+ &self,
+ texture_type: DWRITE_TEXTURE_TYPE,
+ rect: RECT,
+ ) -> Result<Vec<u8>, HRESULT> {
+ unsafe {
+ let rect_pixels = (rect.right - rect.left) * (rect.bottom - rect.top);
+ let rect_bytes = rect_pixels
+ * match texture_type {
+ DWRITE_TEXTURE_ALIASED_1x1 => 1,
+ DWRITE_TEXTURE_CLEARTYPE_3x1 => 3,
+ _ => panic!("bad texture type specified"),
+ };
+
+ let mut out_bytes: Vec<u8> = vec![0; rect_bytes as usize];
+ let hr = (*self.native.get()).CreateAlphaTexture(
+ texture_type,
+ &rect,
+ out_bytes.as_mut_ptr(),
+ out_bytes.len() as u32,
+ );
+ if hr != 0 {
+ Err(hr)
+ } else {
+ Ok(out_bytes)
+ }
+ }
+ }
+}
diff --git a/third_party/rust/dwrote/src/helpers.rs b/third_party/rust/dwrote/src/helpers.rs
new file mode 100644
index 0000000000..804ff9e40c
--- /dev/null
+++ b/third_party/rust/dwrote/src/helpers.rs
@@ -0,0 +1,68 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::ffi::OsStr;
+use std::os::windows::ffi::OsStrExt;
+use winapi::ctypes::wchar_t;
+use winapi::shared::minwindef::{BOOL, FALSE};
+use winapi::shared::winerror::S_OK;
+use winapi::um::dwrite::IDWriteLocalizedStrings;
+use winapi::um::winnls::GetUserDefaultLocaleName;
+use wio::com::ComPtr;
+
+lazy_static! {
+ static ref SYSTEM_LOCALE: Vec<wchar_t> = {
+ unsafe {
+ let mut locale: Vec<wchar_t> = vec![0; 85];
+ GetUserDefaultLocaleName(locale.as_mut_ptr(), locale.len() as i32 - 1);
+ locale
+ }
+ };
+ static ref EN_US_LOCALE: Vec<wchar_t> = { OsStr::new("en-us").to_wide_null() };
+}
+
+pub fn get_locale_string(strings: &mut ComPtr<IDWriteLocalizedStrings>) -> String {
+ unsafe {
+ let mut index: u32 = 0;
+ let mut exists: BOOL = FALSE;
+ let hr = strings.FindLocaleName((*SYSTEM_LOCALE).as_ptr(), &mut index, &mut exists);
+ if hr != S_OK || exists == FALSE {
+ let hr = strings.FindLocaleName((*EN_US_LOCALE).as_ptr(), &mut index, &mut exists);
+ if hr != S_OK || exists == FALSE {
+ // Ultimately fall back to first locale on list
+ index = 0;
+ }
+ }
+
+ let mut length: u32 = 0;
+ let hr = strings.GetStringLength(index, &mut length);
+ assert!(hr == 0);
+
+ let mut name: Vec<wchar_t> = Vec::with_capacity(length as usize + 1);
+ let hr = strings.GetString(index, name.as_mut_ptr(), length + 1);
+ assert!(hr == 0);
+ name.set_len(length as usize);
+
+ String::from_utf16(&name).ok().unwrap()
+ }
+}
+
+// ToWide from https://github.com/retep998/wio-rs/blob/master/src/wide.rs
+
+pub trait ToWide {
+ fn to_wide(&self) -> Vec<u16>;
+ fn to_wide_null(&self) -> Vec<u16>;
+}
+
+impl<T> ToWide for T
+where
+ T: AsRef<OsStr>,
+{
+ fn to_wide(&self) -> Vec<u16> {
+ self.as_ref().encode_wide().collect()
+ }
+ fn to_wide_null(&self) -> Vec<u16> {
+ self.as_ref().encode_wide().chain(Some(0)).collect()
+ }
+}
diff --git a/third_party/rust/dwrote/src/lib.rs b/third_party/rust/dwrote/src/lib.rs
new file mode 100644
index 0000000000..b392bcaf5a
--- /dev/null
+++ b/third_party/rust/dwrote/src/lib.rs
@@ -0,0 +1,167 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#![allow(non_upper_case_globals)]
+
+#[cfg(feature = "serde_serialization")]
+extern crate serde;
+#[cfg_attr(feature = "serde_serialization", macro_use)]
+#[cfg(feature = "serde_serialization")]
+extern crate serde_derive;
+
+#[macro_use]
+extern crate lazy_static;
+extern crate libc;
+extern crate winapi;
+
+include!("types.rs");
+
+use std::ffi::CString;
+use std::ptr;
+use winapi::Interface;
+use winapi::shared::guiddef::REFIID;
+use winapi::shared::winerror::S_OK;
+use winapi::um::dwrite::DWRITE_FACTORY_TYPE;
+use winapi::um::dwrite::DWRITE_FACTORY_TYPE_SHARED;
+use winapi::um::dwrite::IDWriteFactory;
+use winapi::um::dwrite::IDWriteRenderingParams;
+use winapi::um::unknwnbase::IUnknown;
+use winapi::um::winnt::LPCSTR;
+
+pub use winapi::um::winnt::HRESULT;
+
+mod helpers;
+use helpers::ToWide;
+use std::os::raw::c_void;
+
+#[cfg(test)]
+mod test;
+
+// We still use the DWrite structs for things like metrics; re-export them
+// here
+pub use winapi::shared::windef::RECT;
+pub use winapi::um::dcommon::DWRITE_MEASURING_MODE;
+pub use winapi::um::dcommon::{
+ DWRITE_MEASURING_MODE_GDI_CLASSIC, DWRITE_MEASURING_MODE_GDI_NATURAL,
+ DWRITE_MEASURING_MODE_NATURAL,
+};
+pub use winapi::um::dwrite::DWRITE_FONT_METRICS as FontMetrics0;
+pub use winapi::um::dwrite::DWRITE_FONT_SIMULATIONS;
+pub use winapi::um::dwrite::DWRITE_GLYPH_OFFSET as GlyphOffset;
+pub use winapi::um::dwrite::DWRITE_RENDERING_MODE;
+pub use winapi::um::dwrite::DWRITE_TEXTURE_TYPE;
+pub use winapi::um::dwrite::{DWRITE_TEXTURE_ALIASED_1x1, DWRITE_TEXTURE_CLEARTYPE_3x1};
+pub use winapi::um::dwrite::{
+ DWRITE_FONT_SIMULATIONS_BOLD, DWRITE_FONT_SIMULATIONS_NONE, DWRITE_FONT_SIMULATIONS_OBLIQUE,
+};
+pub use winapi::um::dwrite::{DWRITE_GLYPH_RUN, DWRITE_MATRIX};
+pub use winapi::um::dwrite::{
+ DWRITE_RENDERING_MODE_ALIASED, DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC,
+ DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL, DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL,
+ DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, DWRITE_RENDERING_MODE_DEFAULT,
+ DWRITE_RENDERING_MODE_GDI_CLASSIC, DWRITE_RENDERING_MODE_GDI_NATURAL,
+ DWRITE_RENDERING_MODE_NATURAL, DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC,
+ DWRITE_RENDERING_MODE_OUTLINE,
+};
+pub use winapi::um::dwrite_1::DWRITE_FONT_METRICS1 as FontMetrics1;
+pub use winapi::um::dwrite_3::DWRITE_FONT_AXIS_VALUE;
+use winapi::um::libloaderapi::{GetProcAddress, LoadLibraryW};
+
+#[macro_use]
+mod com_helpers;
+
+mod bitmap_render_target;
+pub use bitmap_render_target::BitmapRenderTarget;
+mod font;
+pub use font::{Font, FontMetrics, InformationalStringId};
+mod font_collection;
+pub use font_collection::FontCollection;
+mod font_face;
+pub use font_face::{FontFace, FontFaceType};
+mod font_fallback;
+pub use font_fallback::{FallbackResult, FontFallback};
+mod font_family;
+pub use font_family::FontFamily;
+mod font_file;
+pub use font_file::FontFile;
+mod gdi_interop;
+pub use gdi_interop::GdiInterop;
+mod outline_builder;
+pub use outline_builder::OutlineBuilder;
+mod rendering_params;
+pub use rendering_params::RenderingParams;
+mod text_analysis_source;
+pub use text_analysis_source::TextAnalysisSource;
+mod glyph_run_analysis;
+pub use glyph_run_analysis::GlyphRunAnalysis;
+
+// This is an internal implementation of FontFileLoader, for our utility
+// functions. We don't wrap the DWriteFontFileLoader interface and
+// related things.
+mod font_file_loader_impl;
+
+// This is an implementation of `FontCollectionLoader` for client code.
+mod font_collection_impl;
+pub use font_collection_impl::CustomFontCollectionLoaderImpl;
+
+// This is an implementation of `TextAnalysisSource` for client code.
+mod text_analysis_source_impl;
+pub use text_analysis_source_impl::{
+ CustomTextAnalysisSourceImpl, NumberSubstitution, TextAnalysisSourceMethods,
+};
+
+// This is an internal implementation of `GeometrySink` so that we can
+// expose `IDWriteGeometrySink` in an idiomatic way.
+mod geometry_sink_impl;
+
+lazy_static! {
+ static ref DWRITE_FACTORY_RAW_PTR: usize = {
+ unsafe {
+ type DWriteCreateFactoryType =
+ extern "system" fn(DWRITE_FACTORY_TYPE, REFIID, *mut *mut IUnknown) -> HRESULT;
+
+ let dwrite_dll = LoadLibraryW("dwrite.dll".to_wide_null().as_ptr());
+ assert!(!dwrite_dll.is_null());
+ let create_factory_name = CString::new("DWriteCreateFactory").unwrap();
+ let dwrite_create_factory_ptr =
+ GetProcAddress(dwrite_dll, create_factory_name.as_ptr() as LPCSTR);
+ assert!(!dwrite_create_factory_ptr.is_null());
+
+ let dwrite_create_factory = mem::transmute::<*const c_void, DWriteCreateFactoryType>(
+ dwrite_create_factory_ptr as *const _,
+ );
+
+ let mut factory: *mut IDWriteFactory = ptr::null_mut();
+ let hr = dwrite_create_factory(
+ DWRITE_FACTORY_TYPE_SHARED,
+ &IDWriteFactory::uuidof(),
+ &mut factory as *mut *mut IDWriteFactory as *mut *mut IUnknown,
+ );
+ assert!(hr == S_OK);
+ factory as usize
+ }
+ };
+ static ref DEFAULT_DWRITE_RENDERING_PARAMS_RAW_PTR: usize = {
+ unsafe {
+ let mut default_rendering_params: *mut IDWriteRenderingParams = ptr::null_mut();
+ let hr = (*DWriteFactory()).CreateRenderingParams(&mut default_rendering_params);
+ assert!(hr == S_OK);
+ default_rendering_params as usize
+ }
+ };
+} // end lazy static
+
+// FIXME vlad would be nice to return, say, FactoryPtr<IDWriteFactory>
+// that has a DerefMut impl, so that we can write
+// DWriteFactory().SomeOperation() as opposed to
+// (*DWriteFactory()).SomeOperation()
+#[allow(non_snake_case)]
+fn DWriteFactory() -> *mut IDWriteFactory {
+ (*DWRITE_FACTORY_RAW_PTR) as *mut IDWriteFactory
+}
+
+#[allow(non_snake_case)]
+fn DefaultDWriteRenderParams() -> *mut IDWriteRenderingParams {
+ (*DEFAULT_DWRITE_RENDERING_PARAMS_RAW_PTR) as *mut IDWriteRenderingParams
+}
diff --git a/third_party/rust/dwrote/src/outline_builder.rs b/third_party/rust/dwrote/src/outline_builder.rs
new file mode 100644
index 0000000000..7e7716b4f0
--- /dev/null
+++ b/third_party/rust/dwrote/src/outline_builder.rs
@@ -0,0 +1,6 @@
+pub trait OutlineBuilder {
+ fn move_to(&mut self, x: f32, y: f32);
+ fn line_to(&mut self, x: f32, y: f32);
+ fn curve_to(&mut self, cp0x: f32, cp0y: f32, cp1x: f32, cp1y: f32, x: f32, y: f32);
+ fn close(&mut self);
+}
diff --git a/third_party/rust/dwrote/src/rendering_params.rs b/third_party/rust/dwrote/src/rendering_params.rs
new file mode 100644
index 0000000000..977c38a34a
--- /dev/null
+++ b/third_party/rust/dwrote/src/rendering_params.rs
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::cell::UnsafeCell;
+use std::ptr;
+use winapi::um::dwrite::IDWriteRenderingParams;
+use wio::com::ComPtr;
+
+use super::DWriteFactory;
+
+pub struct RenderingParams {
+ native: UnsafeCell<ComPtr<IDWriteRenderingParams>>,
+}
+
+impl RenderingParams {
+ pub fn create_for_primary_monitor() -> RenderingParams {
+ unsafe {
+ let mut native: *mut IDWriteRenderingParams = ptr::null_mut();
+ let hr = (*DWriteFactory()).CreateRenderingParams(&mut native);
+ assert!(hr == 0);
+ RenderingParams::take(ComPtr::from_raw(native))
+ }
+ }
+
+ pub fn take(native: ComPtr<IDWriteRenderingParams>) -> RenderingParams {
+ RenderingParams {
+ native: UnsafeCell::new(native),
+ }
+ }
+
+ pub unsafe fn as_ptr(&self) -> *mut IDWriteRenderingParams {
+ (*self.native.get()).as_raw()
+ }
+}
diff --git a/third_party/rust/dwrote/src/test.rs b/third_party/rust/dwrote/src/test.rs
new file mode 100644
index 0000000000..1396ab6df6
--- /dev/null
+++ b/third_party/rust/dwrote/src/test.rs
@@ -0,0 +1,167 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use super::*;
+use std::sync::Arc;
+
+#[test]
+fn test_system_family_iter() {
+ let system_fc = FontCollection::system();
+ let count = system_fc.families_iter().count();
+ assert!(count > 0);
+ assert!(system_fc
+ .families_iter()
+ .find(|f| f.name() == "Arial")
+ .is_some());
+}
+
+#[test]
+fn test_descriptor_round_trip() {
+ let system_fc = FontCollection::system();
+
+ let arial_family = system_fc.get_font_family_by_name("Arial").unwrap();
+ let arial_font = arial_family.get_first_matching_font(
+ FontWeight::Regular,
+ FontStretch::Normal,
+ FontStyle::Normal,
+ );
+
+ let descriptor = arial_font.to_descriptor();
+ assert!(descriptor.family_name == "Arial");
+
+ let arial_font_2 = system_fc.get_font_from_descriptor(&descriptor).unwrap();
+ let descriptor2 = arial_font_2.to_descriptor();
+ assert_eq!(descriptor, descriptor2);
+}
+
+#[test]
+fn test_get_font_file_bytes() {
+ let system_fc = FontCollection::system();
+
+ let arial_family = system_fc.get_font_family_by_name("Arial").unwrap();
+ let arial_font = arial_family.get_first_matching_font(
+ FontWeight::Regular,
+ FontStretch::Normal,
+ FontStyle::Normal,
+ );
+ let face = arial_font.create_font_face();
+ let files = face.get_files();
+ assert!(files.len() > 0);
+
+ let bytes = files[0].get_font_file_bytes();
+ assert!(bytes.len() > 0);
+}
+
+#[test]
+fn test_font_file_is_monospace() {
+ let system_fc = FontCollection::system();
+
+ let arial_family = system_fc.get_font_family_by_name("Arial").unwrap();
+ let arial_font = arial_family.get_first_matching_font(
+ FontWeight::Regular,
+ FontStretch::Normal,
+ FontStyle::Normal,
+ );
+ assert!(arial_font.is_monospace() == Some(false));
+
+ let courier_new_family = system_fc.get_font_family_by_name("Courier New").unwrap();
+ let courier_new_font = courier_new_family.get_first_matching_font(
+ FontWeight::Regular,
+ FontStretch::Normal,
+ FontStyle::Normal,
+ );
+ assert!(courier_new_font.is_monospace() == Some(true));
+}
+
+#[test]
+fn test_create_font_file_from_bytes() {
+ let system_fc = FontCollection::system();
+
+ let arial_family = system_fc.get_font_family_by_name("Arial").unwrap();
+ let arial_font = arial_family.get_first_matching_font(
+ FontWeight::Regular,
+ FontStretch::Normal,
+ FontStyle::Normal,
+ );
+ let face = arial_font.create_font_face();
+ let files = face.get_files();
+ assert!(files.len() > 0);
+
+ let bytes = files[0].get_font_file_bytes();
+ assert!(bytes.len() > 0);
+
+ // now go back
+ let new_font = FontFile::new_from_data(Arc::new(bytes));
+ assert!(new_font.is_some());
+
+ let _new_font = new_font.unwrap();
+}
+
+#[test]
+fn test_glyph_image() {
+ let system_fc = FontCollection::system();
+ let arial_family = system_fc.get_font_family_by_name("Arial").unwrap();
+ let arial_font = arial_family.get_first_matching_font(
+ FontWeight::Regular,
+ FontStretch::Normal,
+ FontStyle::Normal,
+ );
+
+ let face = arial_font.create_font_face();
+ let a_index = face.get_glyph_indices(&['A' as u32])[0];
+
+ let gm = face.get_design_glyph_metrics(&[a_index], false)[0];
+
+ let device_pixel_ratio = 1.0f32;
+ let em_size = 10.0f32;
+
+ let design_units_per_em = match face.metrics() {
+ FontMetrics::Metrics0(ref metrics) => metrics.designUnitsPerEm,
+ FontMetrics::Metrics1(ref metrics) => metrics.designUnitsPerEm,
+ };
+ let design_units_per_pixel = design_units_per_em as f32 / 16.;
+
+ let scaled_design_units_to_pixels = (em_size * device_pixel_ratio) / design_units_per_pixel;
+
+ let width = (gm.advanceWidth as i32 - (gm.leftSideBearing + gm.rightSideBearing)) as f32
+ * scaled_design_units_to_pixels;
+ let height = (gm.advanceHeight as i32 - (gm.topSideBearing + gm.bottomSideBearing)) as f32
+ * scaled_design_units_to_pixels;
+ let x = (-gm.leftSideBearing) as f32 * scaled_design_units_to_pixels;
+ let y = (gm.verticalOriginY - gm.topSideBearing) as f32 * scaled_design_units_to_pixels;
+
+ // FIXME I'm pretty sure we need to do a proper RoundOut type
+ // operation on this rect to properly handle any aliasing
+ let left_i = x.floor() as i32;
+ let top_i = (height - y).floor() as i32;
+ let width_u = width.ceil() as u32;
+ let height_u = height.ceil() as u32;
+
+ println!(
+ "GlyphDimensions: {} {} {} {}",
+ left_i, top_i, width_u, height_u
+ );
+
+ let gdi_interop = GdiInterop::create();
+ let rt = gdi_interop.create_bitmap_render_target(width_u, height_u);
+ let rp = RenderingParams::create_for_primary_monitor();
+ rt.set_pixels_per_dip(device_pixel_ratio);
+ rt.draw_glyph_run(
+ x as f32,
+ y as f32,
+ DWRITE_MEASURING_MODE_NATURAL,
+ &face,
+ em_size,
+ &[a_index],
+ &[0f32],
+ &[GlyphOffset {
+ advanceOffset: 0.,
+ ascenderOffset: 0.,
+ }],
+ &rp,
+ &(255.0f32, 255.0f32, 255.0f32),
+ );
+ let bytes = rt.get_opaque_values_as_mask();
+ println!("bytes length: {}", bytes.len());
+}
diff --git a/third_party/rust/dwrote/src/text_analysis_source.rs b/third_party/rust/dwrote/src/text_analysis_source.rs
new file mode 100644
index 0000000000..cd575d62d2
--- /dev/null
+++ b/third_party/rust/dwrote/src/text_analysis_source.rs
@@ -0,0 +1,63 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::borrow::Cow;
+use std::marker::PhantomData;
+use winapi::ctypes::wchar_t;
+use winapi::um::dwrite::IDWriteTextAnalysisSource;
+use wio::com::ComPtr;
+
+use super::*;
+use crate::com_helpers::Com;
+
+pub struct TextAnalysisSource<'a> {
+ native: ComPtr<IDWriteTextAnalysisSource>,
+ phantom: PhantomData<CustomTextAnalysisSourceImpl<'a>>
+}
+
+impl<'a> TextAnalysisSource<'a> {
+ /// Create a new custom TextAnalysisSource for the given text and a trait
+ /// implementation.
+ ///
+ /// Note: this method has no NumberSubsitution specified. See
+ /// `from_text_and_number_subst` if you need number substitution.
+ pub fn from_text(
+ inner: Box<dyn TextAnalysisSourceMethods + 'a>,
+ text: Cow<'a, [wchar_t]>,
+ ) -> TextAnalysisSource<'a> {
+ let native = unsafe {
+ ComPtr::from_raw(
+ CustomTextAnalysisSourceImpl::from_text_native(inner, text).into_interface()
+ )
+ };
+ TextAnalysisSource { native, phantom: PhantomData }
+ }
+
+ /// Create a new custom TextAnalysisSource for the given text and a trait
+ /// implementation.
+ ///
+ /// Note: this method only supports a single `NumberSubstitution` for the
+ /// entire string.
+ pub fn from_text_and_number_subst(
+ inner: Box<dyn TextAnalysisSourceMethods + 'a>,
+ text: Cow<'a, [wchar_t]>,
+ number_subst: NumberSubstitution,
+ ) -> TextAnalysisSource<'a> {
+ let native = unsafe {
+ ComPtr::from_raw(
+ CustomTextAnalysisSourceImpl::from_text_and_number_subst_native(
+ inner,
+ text,
+ number_subst,
+ )
+ .into_interface()
+ )
+ };
+ TextAnalysisSource { native, phantom: PhantomData }
+ }
+
+ pub fn as_ptr(&self) -> *mut IDWriteTextAnalysisSource {
+ self.native.as_raw()
+ }
+}
diff --git a/third_party/rust/dwrote/src/text_analysis_source_impl.rs b/third_party/rust/dwrote/src/text_analysis_source_impl.rs
new file mode 100644
index 0000000000..44f8a65b5b
--- /dev/null
+++ b/third_party/rust/dwrote/src/text_analysis_source_impl.rs
@@ -0,0 +1,240 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! A custom implementation of the "text analysis source" interface so that
+//! we can convey data to the `FontFallback::map_characters` method.
+
+#![allow(non_snake_case)]
+
+use std::borrow::Cow;
+use std::ffi::OsStr;
+use std::mem;
+use std::os::windows::ffi::OsStrExt;
+use std::ptr::{self, null};
+use std::sync::atomic::AtomicUsize;
+use winapi::ctypes::wchar_t;
+use winapi::shared::basetsd::UINT32;
+use winapi::shared::guiddef::REFIID;
+use winapi::shared::minwindef::{FALSE, TRUE, ULONG};
+use winapi::shared::ntdef::LOCALE_NAME_MAX_LENGTH;
+use winapi::shared::winerror::{E_INVALIDARG, S_OK};
+use winapi::um::dwrite::IDWriteNumberSubstitution;
+use winapi::um::dwrite::IDWriteTextAnalysisSource;
+use winapi::um::dwrite::IDWriteTextAnalysisSourceVtbl;
+use winapi::um::dwrite::DWRITE_NUMBER_SUBSTITUTION_METHOD;
+use winapi::um::dwrite::DWRITE_READING_DIRECTION;
+use winapi::um::unknwnbase::{IUnknown, IUnknownVtbl};
+use winapi::um::winnt::HRESULT;
+use wio::com::ComPtr;
+
+use super::DWriteFactory;
+use crate::com_helpers::Com;
+use crate::helpers::ToWide;
+
+/// The Rust side of a custom text analysis source implementation.
+pub trait TextAnalysisSourceMethods {
+ /// Determine the locale for a range of text.
+ ///
+ /// Return locale and length of text (in utf-16 code units) for which the
+ /// locale is valid.
+ fn get_locale_name<'a>(&'a self, text_position: u32) -> (Cow<'a, str>, u32);
+
+ /// Get the text direction for the paragraph.
+ fn get_paragraph_reading_direction(&self) -> DWRITE_READING_DIRECTION;
+}
+
+#[repr(C)]
+pub struct CustomTextAnalysisSourceImpl<'a> {
+ // NB: This must be the first field.
+ _refcount: AtomicUsize,
+ inner: Box<dyn TextAnalysisSourceMethods + 'a>,
+ text: Cow<'a, [wchar_t]>,
+ number_subst: Option<NumberSubstitution>,
+ locale_buf: [wchar_t; LOCALE_NAME_MAX_LENGTH],
+}
+
+/// A wrapped version of an `IDWriteNumberSubstitution` object.
+pub struct NumberSubstitution {
+ native: ComPtr<IDWriteNumberSubstitution>,
+}
+
+// TODO: implement Clone, for convenience and efficiency?
+
+static TEXT_ANALYSIS_SOURCE_VTBL: IDWriteTextAnalysisSourceVtbl = IDWriteTextAnalysisSourceVtbl {
+ parent: implement_iunknown!(static IDWriteTextAnalysisSource, CustomTextAnalysisSourceImpl),
+ GetLocaleName: CustomTextAnalysisSourceImpl_GetLocaleName,
+ GetNumberSubstitution: CustomTextAnalysisSourceImpl_GetNumberSubstitution,
+ GetParagraphReadingDirection: CustomTextAnalysisSourceImpl_GetParagraphReadingDirection,
+ GetTextAtPosition: CustomTextAnalysisSourceImpl_GetTextAtPosition,
+ GetTextBeforePosition: CustomTextAnalysisSourceImpl_GetTextBeforePosition,
+};
+
+impl<'a> CustomTextAnalysisSourceImpl<'a> {
+ /// Create a new custom TextAnalysisSource for the given text and a trait
+ /// implementation.
+ ///
+ /// Note: this method has no NumberSubsitution specified. See
+ /// `from_text_and_number_subst_native` if you need number substitution.
+ pub fn from_text_native(
+ inner: Box<dyn TextAnalysisSourceMethods + 'a>,
+ text: Cow<'a, [wchar_t]>,
+ ) -> CustomTextAnalysisSourceImpl<'a> {
+ assert!(text.len() <= (std::u32::MAX as usize));
+ CustomTextAnalysisSourceImpl {
+ _refcount: AtomicUsize::new(1),
+ inner,
+ text,
+ number_subst: None,
+ locale_buf: [0u16; LOCALE_NAME_MAX_LENGTH],
+ }
+ }
+
+ /// Create a new custom TextAnalysisSource for the given text and a trait
+ /// implementation.
+ ///
+ /// Note: this method only supports a single `NumberSubstitution` for the
+ /// entire string.
+ pub fn from_text_and_number_subst_native(
+ inner: Box<dyn TextAnalysisSourceMethods + 'a>,
+ text: Cow<'a, [wchar_t]>,
+ number_subst: NumberSubstitution,
+ ) -> CustomTextAnalysisSourceImpl<'a> {
+ assert!(text.len() <= (std::u32::MAX as usize));
+ CustomTextAnalysisSourceImpl {
+ _refcount: AtomicUsize::new(1),
+ inner,
+ text,
+ number_subst: Some(number_subst),
+ locale_buf: [0u16; LOCALE_NAME_MAX_LENGTH],
+ }
+ }
+}
+
+impl Com<IDWriteTextAnalysisSource> for CustomTextAnalysisSourceImpl<'_> {
+ type Vtbl = IDWriteTextAnalysisSourceVtbl;
+ #[inline]
+ fn vtbl() -> &'static IDWriteTextAnalysisSourceVtbl {
+ &TEXT_ANALYSIS_SOURCE_VTBL
+ }
+}
+
+impl Com<IUnknown> for CustomTextAnalysisSourceImpl<'_> {
+ type Vtbl = IUnknownVtbl;
+ #[inline]
+ fn vtbl() -> &'static IUnknownVtbl {
+ &TEXT_ANALYSIS_SOURCE_VTBL.parent
+ }
+}
+
+unsafe extern "system" fn CustomTextAnalysisSourceImpl_GetLocaleName(
+ this: *mut IDWriteTextAnalysisSource,
+ text_position: UINT32,
+ text_length: *mut UINT32,
+ locale_name: *mut *const wchar_t,
+) -> HRESULT {
+ let this = CustomTextAnalysisSourceImpl::from_interface(this);
+ let (locale, text_len) = this.inner.get_locale_name(text_position);
+
+ // Copy the locale data into the buffer
+ for (i, c) in OsStr::new(&*locale).encode_wide().chain(Some(0)).enumerate() {
+ // -1 here is deliberate: it ensures that we never write to the last character in
+ // this.locale_buf, so that the buffer is always null-terminated.
+ if i >= this.locale_buf.len() - 1 {
+ break
+ }
+
+ *this.locale_buf.get_unchecked_mut(i) = c;
+ }
+
+ *text_length = text_len;
+ *locale_name = this.locale_buf.as_ptr();
+ S_OK
+}
+
+unsafe extern "system" fn CustomTextAnalysisSourceImpl_GetNumberSubstitution(
+ this: *mut IDWriteTextAnalysisSource,
+ text_position: UINT32,
+ text_length: *mut UINT32,
+ number_substitution: *mut *mut IDWriteNumberSubstitution,
+) -> HRESULT {
+ let this = CustomTextAnalysisSourceImpl::from_interface(this);
+ if text_position >= (this.text.len() as u32) {
+ return E_INVALIDARG;
+ }
+
+ *text_length = (this.text.len() as UINT32) - text_position;
+ *number_substitution = match &this.number_subst {
+ Some(number_subst) => {
+ let com_ptr = &number_subst.native;
+ com_ptr.AddRef();
+ com_ptr.as_raw()
+ },
+ None => std::ptr::null_mut()
+ };
+
+ S_OK
+}
+
+unsafe extern "system" fn CustomTextAnalysisSourceImpl_GetParagraphReadingDirection(
+ this: *mut IDWriteTextAnalysisSource,
+) -> DWRITE_READING_DIRECTION {
+ let this = CustomTextAnalysisSourceImpl::from_interface(this);
+ this.inner.get_paragraph_reading_direction()
+}
+
+unsafe extern "system" fn CustomTextAnalysisSourceImpl_GetTextAtPosition(
+ this: *mut IDWriteTextAnalysisSource,
+ text_position: UINT32,
+ text_string: *mut *const wchar_t,
+ text_length: *mut UINT32,
+) -> HRESULT {
+ let this = CustomTextAnalysisSourceImpl::from_interface(this);
+ if text_position >= (this.text.len() as u32) {
+ *text_string = null();
+ *text_length = 0;
+ return S_OK;
+ }
+ *text_string = this.text.as_ptr().add(text_position as usize);
+ *text_length = (this.text.len() as UINT32) - text_position;
+ S_OK
+}
+
+unsafe extern "system" fn CustomTextAnalysisSourceImpl_GetTextBeforePosition(
+ this: *mut IDWriteTextAnalysisSource,
+ text_position: UINT32,
+ text_string: *mut *const wchar_t,
+ text_length: *mut UINT32,
+) -> HRESULT {
+ let this = CustomTextAnalysisSourceImpl::from_interface(this);
+ if text_position == 0 || text_position > (this.text.len() as u32) {
+ *text_string = null();
+ *text_length = 0;
+ return S_OK;
+ }
+ *text_string = this.text.as_ptr();
+ *text_length = text_position;
+ S_OK
+}
+
+impl NumberSubstitution {
+ pub fn new(
+ subst_method: DWRITE_NUMBER_SUBSTITUTION_METHOD,
+ locale: &str,
+ ignore_user_overrides: bool,
+ ) -> NumberSubstitution {
+ unsafe {
+ let mut native: *mut IDWriteNumberSubstitution = ptr::null_mut();
+ let hr = (*DWriteFactory()).CreateNumberSubstitution(
+ subst_method,
+ locale.to_wide_null().as_ptr(),
+ if ignore_user_overrides { TRUE } else { FALSE },
+ &mut native,
+ );
+ assert_eq!(hr, 0, "error creating number substitution");
+ NumberSubstitution {
+ native: ComPtr::from_raw(native),
+ }
+ }
+ }
+}
diff --git a/third_party/rust/dwrote/src/types.rs b/third_party/rust/dwrote/src/types.rs
new file mode 100644
index 0000000000..d488a0805e
--- /dev/null
+++ b/third_party/rust/dwrote/src/types.rs
@@ -0,0 +1,126 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* this is include!()'d in lib.rs */
+use std::mem;
+use winapi::um::dwrite::{DWRITE_FONT_STYLE, DWRITE_FONT_WEIGHT, DWRITE_FONT_STRETCH};
+
+// mirrors DWRITE_FONT_WEIGHT
+#[cfg_attr(feature = "serde_serialization", derive(Deserialize, Serialize))]
+#[derive(PartialEq, Debug, Clone, Copy)]
+pub enum FontWeight {
+ Thin,
+ ExtraLight,
+ Light,
+ SemiLight,
+ Regular,
+ Medium,
+ SemiBold,
+ Bold,
+ ExtraBold,
+ Black,
+ ExtraBlack,
+ Unknown(u32)
+}
+
+impl FontWeight {
+ fn t(&self) -> DWRITE_FONT_WEIGHT {
+ unsafe { mem::transmute::<u32, DWRITE_FONT_WEIGHT>(self.to_u32()) }
+ }
+ pub fn to_u32(&self) -> u32 {
+ match self {
+ FontWeight::Thin=> 100,
+ FontWeight::ExtraLight=> 200,
+ FontWeight::Light=> 300,
+ FontWeight::SemiLight=> 350,
+ FontWeight::Regular=> 400,
+ FontWeight::Medium=> 500,
+ FontWeight::SemiBold=> 600,
+ FontWeight::Bold=> 700,
+ FontWeight::ExtraBold=> 800,
+ FontWeight::Black=> 900,
+ FontWeight::ExtraBlack=> 950,
+ FontWeight::Unknown(v) => *v as u32
+ }
+ }
+ pub fn from_u32(v: u32) -> FontWeight {
+ match v {
+ 100 => FontWeight::Thin,
+ 200 => FontWeight::ExtraLight,
+ 300 => FontWeight::Light,
+ 350 => FontWeight::SemiLight,
+ 400 => FontWeight::Regular,
+ 500 => FontWeight::Medium,
+ 600 => FontWeight::SemiBold,
+ 700 => FontWeight::Bold,
+ 800 => FontWeight::ExtraBold,
+ 900 => FontWeight::Black,
+ 950 => FontWeight::ExtraBlack,
+ _ => FontWeight::Unknown(v)
+ }
+ }
+}
+
+// mirrors DWRITE_FONT_STRETCH
+#[repr(u32)]
+#[cfg_attr(feature = "serde_serialization", derive(Deserialize, Serialize))]
+#[derive(PartialEq, Debug, Clone, Copy)]
+pub enum FontStretch {
+ Undefined = 0,
+ UltraCondensed = 1,
+ ExtraCondensed = 2,
+ Condensed = 3,
+ SemiCondensed = 4,
+ Normal = 5,
+ SemiExpanded = 6,
+ Expanded = 7,
+ ExtraExpanded = 8,
+ UltraExpanded = 9,
+}
+
+impl FontStretch {
+ fn t(&self) -> DWRITE_FONT_STRETCH {
+ unsafe { mem::transmute::<FontStretch, DWRITE_FONT_STRETCH>(*self) }
+ }
+ pub fn to_u32(&self) -> u32 { unsafe { mem::transmute::<FontStretch, u32>(*self) } }
+ pub fn from_u32(v: u32) -> FontStretch { unsafe { mem::transmute::<u32, FontStretch>(v) } }
+}
+
+// mirrors DWRITE_FONT_STYLE
+#[repr(u32)]
+#[cfg_attr(feature = "serde_serialization", derive(Deserialize, Serialize))]
+#[derive(PartialEq, Debug, Clone, Copy)]
+pub enum FontStyle {
+ Normal = 0,
+ Oblique = 1,
+ Italic = 2,
+}
+
+impl FontStyle {
+ fn t(&self) -> DWRITE_FONT_STYLE {
+ unsafe { mem::transmute::<FontStyle, DWRITE_FONT_STYLE>(*self) }
+ }
+ pub fn to_u32(&self) -> u32 { unsafe { mem::transmute::<FontStyle, u32>(*self) } }
+ pub fn from_u32(v: u32) -> FontStyle { unsafe { mem::transmute::<u32, FontStyle>(v) } }
+}
+
+// mirrors DWRITE_FONT_SIMULATIONS
+#[repr(u32)]
+#[derive(PartialEq, Debug, Clone, Copy)]
+pub enum FontSimulations {
+ None = winapi::um::dwrite::DWRITE_FONT_SIMULATIONS_NONE,
+ Bold = winapi::um::dwrite::DWRITE_FONT_SIMULATIONS_BOLD,
+ Oblique = winapi::um::dwrite::DWRITE_FONT_SIMULATIONS_OBLIQUE,
+ BoldOblique = winapi::um::dwrite::DWRITE_FONT_SIMULATIONS_BOLD |
+ winapi::um::dwrite::DWRITE_FONT_SIMULATIONS_OBLIQUE,
+}
+
+#[cfg_attr(feature = "serde_serialization", derive(Deserialize, Serialize))]
+#[derive(PartialEq, Debug, Clone)]
+pub struct FontDescriptor {
+ pub family_name: String,
+ pub weight: FontWeight,
+ pub stretch: FontStretch,
+ pub style: FontStyle,
+}