1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// 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)?)))
    }
}