diff options
Diffstat (limited to 'third_party/rust/dwrote')
25 files changed, 3143 insertions, 0 deletions
diff --git a/third_party/rust/dwrote/.cargo-checksum.json b/third_party/rust/dwrote/.cargo-checksum.json new file mode 100644 index 0000000000..7fcb22b5d9 --- /dev/null +++ b/third_party/rust/dwrote/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"2475bea1fc2ab1cb052f254fb2bcf1bfcaba5aafe8b9acea297db498d8ec81fd","README.md":"d69d75705e2582721cbfb2d3b4b2af052c71679057a0b2ac53a22c03f1755bba","appveyor.yml":"6a91cdfbcc8c363ead82321b3be509bb7bf9cf8b90c63518d6484371abcfed48","src/bitmap_render_target.rs":"f677fa828e7466b7e8f9e5feaba7e97bd7512a2f8ee8920e5a4afad9984a9050","src/com_helpers.rs":"b3c7c545646b86b00bcf355da5ed6672596aa050b68f0b62020d8f89839267ab","src/font.rs":"295dbf78b75df4cb8a009fe381892dd00a3ef3ab49e64f9bef0477f46444d134","src/font_collection.rs":"187474bbca4845ffeb52a7535f6ff476e2cf9dd7310849ed801763e7220bdcc2","src/font_collection_impl.rs":"b27b0ae87b09e131eb807e1dee4521bff3cef66964e5494b965f12f40986d3a9","src/font_face.rs":"f5ad21c5814a094647e96cc4d08762d5da04c051344d51aa7c243d3bd86e6e11","src/font_fallback.rs":"c0c39fffdcc6747ed7513e60cbc16510515bf5b6e38d90ca9f5647bfe7c9471c","src/font_family.rs":"5abb5064933042fb5023c5f223ae4ca1942fa6f26d326279257b33a6fdb9a89c","src/font_file.rs":"d6e371f59635d908c8181bcdd3aca3ec1f4690fb7f66f7672b21fae4631a86aa","src/font_file_loader_impl.rs":"db7f93e52f4f071e5face8e8ed9d34c061d928da376b5de4c461b33d217a68ac","src/gdi_interop.rs":"7d33373f8b5af1e0ffac035b0093d72857624a5ab8696658172cd5c8b11edb2a","src/geometry_sink_impl.rs":"447fdc3179b738d981bad86052eb5820bc4d3a1f6fdd811b25617dd0847b1519","src/glyph_run_analysis.rs":"c3ad300f601f50a6b7fc8534e8b5a15c36f99c8ac495310d5e7d804e573d9c95","src/helpers.rs":"0d854b4fe40b80e9ab681a705e32bf430a16ed2f86f472b5a21243d3bb8ef242","src/lib.rs":"54b0e94f800d9c6fb8dff6e6d57f11aa9d6f78b8a63a6c5e9458bd16b2880991","src/outline_builder.rs":"0f5c842b2ffe75e21d68f93be6a3834c120bd43303d4e490fdfee9f4f964cdc4","src/rendering_params.rs":"b04db3a35fb762d8d7b2a2d660a2fd452597b3b7c522b09279d93ce889c117cd","src/test.rs":"43e8b213119c2a48d0ab252da9944c28aeaf693cedb1d69a19c8427cd474eaf9","src/text_analysis_source.rs":"8ff78ccb1271caf06aabcac6ce707685e470a7aaca0c743bf33565f85fc0ecb1","src/text_analysis_source_impl.rs":"2c435ee312b3196c17493c2ba6407a2ceae3a860a5eb83b6279ad929329d0de8","src/types.rs":"9374d8c5bac80e5e0b66188c5ff4f5f49264e15db837049932d4fd703c33746e"},"package":"439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b"}
\ No newline at end of file diff --git a/third_party/rust/dwrote/Cargo.toml b/third_party/rust/dwrote/Cargo.toml new file mode 100644 index 0000000000..6fac8fcff0 --- /dev/null +++ b/third_party/rust/dwrote/Cargo.toml @@ -0,0 +1,47 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "dwrote" +version = "0.11.0" +authors = ["The Servo Project Developers", "Vladimir Vukicevic <vladimir@pobox.com>"] +description = "Lightweight binding to DirectWrite." +license = "MPL-2.0" +repository = "https://github.com/servo/dwrote-rs" + +[lib] +name = "dwrote" +[dependencies.lazy_static] +version = "1" + +[dependencies.libc] +version = "0.2" + +[dependencies.serde] +version = "1.0" +optional = true + +[dependencies.serde_derive] +version = "1.0" +optional = true + +[dependencies.winapi] +version = "0.3.6" +features = ["dwrite", "dwrite_1", "dwrite_3", "winnt", "unknwnbase", "libloaderapi", "winnls"] + +[dependencies.wio] +version = "0.2" + +[features] +default = ["serde_serialization"] +serde_serialization = ["serde", "serde_derive"] diff --git a/third_party/rust/dwrote/README.md b/third_party/rust/dwrote/README.md new file mode 100644 index 0000000000..d436740898 --- /dev/null +++ b/third_party/rust/dwrote/README.md @@ -0,0 +1,12 @@ +# dwrote +A thin wrapper around Windows DirectWrite for Rust + +Dwrote provides access to pieces of DirectWrite needed by WebRender +and Servo. It can be easily extended to other parts of DirectWrite, +but full mapping of the DirectWrite API is not a goal (pull requests +accepted, of course). + +There are a few additional helper functions on individual types that +don't exist in DirectWrite, and a few have had their signatures changed, +but for the most part this library attempts to replicate the DirectWrite +API. diff --git a/third_party/rust/dwrote/appveyor.yml b/third_party/rust/dwrote/appveyor.yml new file mode 100644 index 0000000000..21508dc49c --- /dev/null +++ b/third_party/rust/dwrote/appveyor.yml @@ -0,0 +1,122 @@ +# Appveyor configuration template for Rust using rustup for Rust installation +# https://github.com/starkat99/appveyor-rust + +## Operating System (VM environment) ## + +# Rust needs at least Visual Studio 2013 Appveyor OS for MSVC targets. +os: Visual Studio 2015 + +## Build Matrix ## + +# This configuration will setup a build for each channel & target combination (12 windows +# combinations in all). +# +# There are 3 channels: stable, beta, and nightly. +# +# Alternatively, the full version may be specified for the channel to build using that specific +# version (e.g. channel: 1.5.0) +# +# The values for target are the set of windows Rust build targets. Each value is of the form +# +# ARCH-pc-windows-TOOLCHAIN +# +# Where ARCH is the target architecture, either x86_64 or i686, and TOOLCHAIN is the linker +# toolchain to use, either msvc or gnu. See https://www.rust-lang.org/downloads.html#win-foot for +# a description of the toolchain differences. +# See https://github.com/rust-lang-nursery/rustup.rs/#toolchain-specification for description of +# toolchains and host triples. +# +# Comment out channel/target combos you do not wish to build in CI. +# +# You may use the `cargoflags` and `RUSTFLAGS` variables to set additional flags for cargo commands +# and rustc, respectively. For instance, you can uncomment the cargoflags lines in the nightly +# channels to enable unstable features when building for nightly. Or you could add additional +# matrix entries to test different combinations of features. +environment: + matrix: + +### MSVC Toolchains ### + + # Stable 64-bit MSVC + - channel: stable + target: x86_64-pc-windows-msvc + # Stable 32-bit MSVC + # - channel: stable + # target: i686-pc-windows-msvc + # Beta 64-bit MSVC + # - channel: beta + # target: x86_64-pc-windows-msvc + # Beta 32-bit MSVC + # - channel: beta + # target: i686-pc-windows-msvc + # Nightly 64-bit MSVC + # - channel: nightly + # target: x86_64-pc-windows-msvc + #cargoflags: --features "unstable" + # Nightly 32-bit MSVC + # - channel: nightly + # target: i686-pc-windows-msvc + #cargoflags: --features "unstable" + +### GNU Toolchains ### + + # Stable 64-bit GNU + # - channel: stable + # target: x86_64-pc-windows-gnu + # Stable 32-bit GNU + # - channel: stable + # target: i686-pc-windows-gnu + # Beta 64-bit GNU + # - channel: beta + # target: x86_64-pc-windows-gnu + # Beta 32-bit GNU + # - channel: beta + # target: i686-pc-windows-gnu + # Nightly 64-bit GNU + # - channel: nightly + # target: x86_64-pc-windows-gnu + # #cargoflags: --features "unstable" + # Nightly 32-bit GNU + # - channel: nightly + # target: i686-pc-windows-gnu + #cargoflags: --features "unstable" + +### Allowed failures ### + +# See Appveyor documentation for specific details. In short, place any channel or targets you wish +# to allow build failures on (usually nightly at least is a wise choice). This will prevent a build +# or test failure in the matching channels/targets from failing the entire build. +matrix: + allow_failures: + - channel: nightly + +# If you only care about stable channel build failures, uncomment the following line: + #- channel: beta + +## Install Script ## + +# This is the most important part of the Appveyor configuration. This installs the version of Rust +# specified by the 'channel' and 'target' environment variables from the build matrix. This uses +# rustup to install Rust. +# +# For simple configurations, instead of using the build matrix, you can simply set the +# default-toolchain and default-host manually here. +install: + - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe + - rustup-init -yv --default-toolchain %channel% --default-host %target% + - set PATH=%PATH%;%USERPROFILE%\.cargo\bin + - rustc -vV + - cargo -vV + +## Build Script ## + +# 'cargo test' takes care of building for us, so disable Appveyor's build stage. This prevents +# the "directory does not contain a project or solution file" error. +build: false + +# Uses 'cargo test' to run tests and build. Alternatively, the project may call compiled programs +#directly or perform other testing commands. Rust will automatically be placed in the PATH +# environment variable. +test_script: + - cargo check --no-default-features + - cargo test --verbose %cargoflags% 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, +} |