Source code

Revision control

Copy as Markdown

Other Tools

#![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");
}
}
}