blob: d962a085cac3cdbce9004af6f991d1be68c3ec97 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
anyhow::Error,
freetype_ffi::{FT_Open_Args, FT_Stream, FT_OPEN_PATHNAME, FT_OPEN_STREAM},
std::{ffi::CString, fmt, ptr},
};
/// Describes the source of a font asset to be parsed.
pub enum FontAssetSource {
/// Byte stream (e.g. from a VMO)
Stream(Box<dyn FTStreamProvider>),
/// Path to a local file
FilePath(String),
}
impl fmt::Debug for FontAssetSource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FontAssetSource::Stream(_) => write!(f, "FontAssetSource::Stream"),
FontAssetSource::FilePath(path) => write!(f, "FontAssetSource::FilePath(\"{}\")", path),
}
}
}
/// This struct ensures that the lifetime of `FT_Open_Args` and the resources it depends on is
/// handled correctly.
pub struct FTOpenArgs {
// All suppressed "dead code" below is used to ensure the lifetime of native_open_args is in
// sync with FontAssetSource it is based on.
// This is the asset source based on which the args are generated. If the asset source is a
// stream, this retains the stream provider.
#[allow(dead_code)]
pub(crate) source: FontAssetSource,
// Holds a reference to the C-style string from FontAssetSource, in case it is a string-based
// source.
#[allow(dead_code)]
pathname: Option<CString>,
// The FFI type that was obtained based on the asset source. Access it through the AsRef
// trait implementation.
native_open_args: FT_Open_Args,
}
/// Allows viewing `FTOpenArgs` as a FFI type.
impl AsRef<FT_Open_Args> for FTOpenArgs {
/// Views `FTOpenArgs` (a Rust type) as a FFI type `FT_Open_Args` for interfacing with low level
/// FFI libraries.
fn as_ref(&self) -> &FT_Open_Args {
&self.native_open_args
}
}
/// Converts a `FontAssetSource` to a wrapper around `FT_Open_Args`, which is required for reading a
/// font with FreeType.
impl TryFrom<FontAssetSource> for FTOpenArgs {
type Error = Error;
fn try_from(source: FontAssetSource) -> Result<Self, Error> {
let mut pathname: Option<CString> = None;
let native_open_args = match source {
FontAssetSource::Stream(ref provider) => {
FT_Open_Args {
flags: FT_OPEN_STREAM,
memory_base: ptr::null(),
memory_size: 0,
pathname: ptr::null(),
// Unsafe to call FreeType FFI.
// Caller must ensure that the returned `FT_Open_Args` is not used after the
// `FontAssetSource` is dropped.
stream: unsafe { provider.ft_stream() },
driver: ptr::null_mut(),
num_params: 0,
params: ptr::null_mut(),
}
}
FontAssetSource::FilePath(ref path) => {
pathname = Some(CString::new(&path[..])?);
FT_Open_Args {
flags: FT_OPEN_PATHNAME,
memory_base: ptr::null(),
memory_size: 0,
// This won't ever be `None`, since we're assigning to `pathname` just above.
pathname: pathname.as_ref().unwrap().as_ptr(),
stream: ptr::null_mut(),
driver: ptr::null_mut(),
num_params: 0,
params: ptr::null_mut(),
}
}
};
Ok(FTOpenArgs { source, pathname, native_open_args })
}
}
/// Provides a [FreeType stream](freetype_ffi::FTStream) for reading files.
pub trait FTStreamProvider {
/// Unsafe to call FreeType FFI.
/// Caller must ensure that the returned [FT_Stream] is not used after `self` is dropped.
unsafe fn ft_stream(&self) -> FT_Stream;
}
/// Converts a [`fidl_fuchsia_mem::Buffer`] into a `FontAssetSource` by opening a stream from the
/// VMO represented by the buffer.
#[cfg(target_os = "fuchsia")]
impl TryFrom<fidl_fuchsia_mem::Buffer> for FontAssetSource {
type Error = Error;
fn try_from(buffer: fidl_fuchsia_mem::Buffer) -> Result<FontAssetSource, Error> {
use crate::vmo_stream::VmoStream;
Ok(FontAssetSource::Stream(Box::new(VmoStream::new(buffer.vmo, buffer.size as usize)?)))
}
}