font_info/sources.rs
1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::Error;
6use freetype_ffi::{FT_Open_Args, FT_Stream, FT_OPEN_PATHNAME, FT_OPEN_STREAM};
7use std::ffi::CString;
8use std::{fmt, ptr};
9
10/// Describes the source of a font asset to be parsed.
11pub enum FontAssetSource {
12 /// Byte stream (e.g. from a VMO)
13 Stream(Box<dyn FTStreamProvider>),
14 /// Path to a local file
15 FilePath(String),
16}
17
18impl fmt::Debug for FontAssetSource {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 match self {
21 FontAssetSource::Stream(_) => write!(f, "FontAssetSource::Stream"),
22 FontAssetSource::FilePath(path) => write!(f, "FontAssetSource::FilePath(\"{}\")", path),
23 }
24 }
25}
26
27/// This struct ensures that the lifetime of `FT_Open_Args` and the resources it depends on is
28/// handled correctly.
29pub struct FTOpenArgs {
30 // All suppressed "dead code" below is used to ensure the lifetime of native_open_args is in
31 // sync with FontAssetSource it is based on.
32
33 // This is the asset source based on which the args are generated. If the asset source is a
34 // stream, this retains the stream provider.
35 #[allow(dead_code)]
36 pub(crate) source: FontAssetSource,
37 // Holds a reference to the C-style string from FontAssetSource, in case it is a string-based
38 // source.
39 #[allow(dead_code)]
40 pathname: Option<CString>,
41 // The FFI type that was obtained based on the asset source. Access it through the AsRef
42 // trait implementation.
43 native_open_args: FT_Open_Args,
44}
45
46/// Allows viewing `FTOpenArgs` as a FFI type.
47impl AsRef<FT_Open_Args> for FTOpenArgs {
48 /// Views `FTOpenArgs` (a Rust type) as a FFI type `FT_Open_Args` for interfacing with low level
49 /// FFI libraries.
50 fn as_ref(&self) -> &FT_Open_Args {
51 &self.native_open_args
52 }
53}
54
55/// Converts a `FontAssetSource` to a wrapper around `FT_Open_Args`, which is required for reading a
56/// font with FreeType.
57impl TryFrom<FontAssetSource> for FTOpenArgs {
58 type Error = Error;
59
60 fn try_from(source: FontAssetSource) -> Result<Self, Error> {
61 let mut pathname: Option<CString> = None;
62 let native_open_args = match source {
63 FontAssetSource::Stream(ref provider) => {
64 FT_Open_Args {
65 flags: FT_OPEN_STREAM,
66 memory_base: ptr::null(),
67 memory_size: 0,
68 pathname: ptr::null(),
69 // Unsafe to call FreeType FFI.
70 // Caller must ensure that the returned `FT_Open_Args` is not used after the
71 // `FontAssetSource` is dropped.
72 stream: unsafe { provider.ft_stream() },
73 driver: ptr::null_mut(),
74 num_params: 0,
75 params: ptr::null_mut(),
76 }
77 }
78 FontAssetSource::FilePath(ref path) => {
79 pathname = Some(CString::new(&path[..])?);
80 FT_Open_Args {
81 flags: FT_OPEN_PATHNAME,
82 memory_base: ptr::null(),
83 memory_size: 0,
84 // This won't ever be `None`, since we're assigning to `pathname` just above.
85 pathname: pathname.as_ref().unwrap().as_ptr(),
86 stream: ptr::null_mut(),
87 driver: ptr::null_mut(),
88 num_params: 0,
89 params: ptr::null_mut(),
90 }
91 }
92 };
93 Ok(FTOpenArgs { source, pathname, native_open_args })
94 }
95}
96
97/// Provides a [FreeType stream](freetype_ffi::FTStream) for reading files.
98pub trait FTStreamProvider {
99 /// Unsafe to call FreeType FFI.
100 /// Caller must ensure that the returned [FT_Stream] is not used after `self` is dropped.
101 unsafe fn ft_stream(&self) -> FT_Stream;
102}
103
104/// Converts a [`fidl_fuchsia_mem::Buffer`] into a `FontAssetSource` by opening a stream from the
105/// VMO represented by the buffer.
106#[cfg(target_os = "fuchsia")]
107impl TryFrom<fidl_fuchsia_mem::Buffer> for FontAssetSource {
108 type Error = Error;
109
110 fn try_from(buffer: fidl_fuchsia_mem::Buffer) -> Result<FontAssetSource, Error> {
111 use crate::vmo_stream::VmoStream;
112 Ok(FontAssetSource::Stream(Box::new(VmoStream::new(buffer.vmo, buffer.size as usize)?)))
113 }
114}