80 lines
3.2 KiB
Markdown
80 lines
3.2 KiB
Markdown
# read-fonts
|
|
|
|
This crate handles parsing and reading of OpenType fonts. It is intended to be a
|
|
high performance implementation suitable for tasks such as [shaping][], while
|
|
still providing a convenient, high-level API.
|
|
|
|
## Safety
|
|
|
|
Unsafe code is forbidden by a `#![forbid(unsafe_code)]` attribute in the root
|
|
of the library.
|
|
|
|
## codegen
|
|
|
|
Much of the code in this crate is generated automatically. This generated code
|
|
lives in the `generated` directory. Each file in this directory is included
|
|
in a module in the `src` directory, using Rust's [`include!`] macro. This allows
|
|
us to separate the generated code from any custom implementation code, while
|
|
allowing them to exist in the same module.
|
|
|
|
### what we generate
|
|
|
|
With certain exceptions that require manual handling, we generate code for each
|
|
*table*, *record*, *enum*, and *flags* type for each portion of the spec that we
|
|
cover.
|
|
|
|
## tables
|
|
|
|
All tables are aliases of the type `TableRef<Marker>`, where `Marker` is a
|
|
struct that indicates the type of the table. For instance, the [GDEF table][Gdef]
|
|
is defined as `TableRef<GdefMarker>`. `TableRef` itself is a wrapper around a
|
|
slice of bytes, with the *marker* type providing typed access to those bytes.
|
|
|
|
The marker type can only be created from a specific byte slice, and is always
|
|
associated with that slice. It is created through a `parse` method that performs
|
|
a one-time validation of the slice, ensuring that all expected fields are
|
|
present. This includes bounds checking any arrays, as well as ensuring the
|
|
presence of fields the existence of which may depend on the table's version.
|
|
|
|
### variable lengths and version-dependent fields
|
|
|
|
**n.b**: *the design described below has not been benchmarked against the
|
|
alternatives, and may change*
|
|
|
|
For fields that have variable length, or which only exist in certain table
|
|
versions, the marker struct has a corresponding field where that length or
|
|
offset is stored. This means that at runtime there is no need to double check a
|
|
version or length.
|
|
|
|
### TableRef methods
|
|
|
|
For each table, we define methods on the type `TableRef<Marker>` that provide
|
|
access to that table's fields. These methods use the marker type to determine
|
|
the byte range of a given field, and then interpret those bytes as the
|
|
appropriate type.
|
|
|
|
## records
|
|
|
|
Unlike tables, which are essentially a set of methods for reading into a byte
|
|
slice, *records* are **in general** represented as simple packed structs
|
|
containing scalar types in big-endian encodings. This means that, in general,
|
|
records are zerocopy types that can be cast from raw bytes.
|
|
|
|
The exception to this is when a record has variable length; in this case the
|
|
record is still a simple struct, but cannot be cast from raw bytes, and must be
|
|
copied.
|
|
|
|
## flags and enums
|
|
|
|
For flags, we generate a type modeled on those generated by the [`bitflags!` macro][bitflags].
|
|
For enums, we generate a raw Rust enum.
|
|
|
|
|
|
|
|
|
|
[shaping]: https://fonts.google.com/knowledge/glossary/shaping
|
|
[`include!`]: http://doc.rust-lang.org/1.63.0/std/macro.include.html
|
|
[gdef-marker]: https://github.com/googlefonts/fontations/blob/main/read-fonts/generated/generated_gdef.rs#L11
|
|
[Gdef]: https://github.com/googlefonts/fontations/blob/main/read-fonts/generated/generated_gdef.rs#L77
|
|
[bitflags]: https://docs.rs/bitflags/latest/bitflags/
|