Skip to main content

ttf_view/
lib.rs

1//! A TrueType/OpenType font parsing/viewing library and a CLI tool.
2//!
3//! # Features
4//!
5//! Not much is implemented yet, but here's a few features that [`ttf-parser`] and [`read-fonts`]
6//! don't have:
7//!
8//! - Two-byte codepoint mappings (other crates only support Unicode).
9//! - Supports more character encodings (such as Shift JIS, GB 18030, Big5, EUC-KR).
10//! - Provides raw low-level access to all structures (with &, and not just getters).
11//! - Faster and easier access to metrics in the 'hmtx' table.
12//! - Actually implements the 'cmap' format 2 subtable (deprecated, but still used by some fonts).
13//!
14//! Just like `ttf-parser` and `read-fonts`, this is zero-copy and no-alloc too. Also, validation of
15//! the table data only happens when you access the table (`dir.hmtx()`), instead of doing bounds
16//! checks on every single field access like `ttf-parser` and `read-fonts` do, for some reason.
17//!
18//! The documentation will also be better, more descriptive and with examples. I want the entire
19//! crate to be comprehensible through documentation alone, without the need to look through what
20//! the language server shows you a type can do.
21//!
22//! # Reading a font file
23//!
24//! [`TableDirectory`][tables::TableDirectory] is the entry point and main hub for OpenType tables.
25//! Use [`TableDirectory::new(bytes)`][tables::TableDirectory::new] to read a font file. Note that
26//! the validation of specific tables happens separately and is not cached.
27//!
28//! ```no_run
29//! use ttf_view::tables::TableDirectory;
30//!
31//! let data = std::fs::read("MyFont.ttf").unwrap();
32//! let dir = TableDirectory::new(&data).expect("font should be well-formed");
33//!
34//! if let Ok(cmap) = dir.cmap() {
35//!     // ...
36//! }
37//! ```
38//!
39//! See the [`tables`] module for more information about tables that you can access.
40//!
41//! # Notes on how to access tables
42//!
43//! A table should generally be accessed through a type like [`Os_2<'_>`](tables::os_2::Os_2). This
44//! type automatically dereferences to [`Os_2Base`][tables::os_2::Os_2Base], which contains fields
45//! that are available in all versions of `'OS/2'` table. You can access specific versions of the
46//! table through methods like `v0`, `v1`, `v4` on it, which return references to the raw data with
47//! fields exactly as specified by OpenType. Additionally, each version of the table automatically
48//! dereferences to the previous version, e.g. `Os_2V1` to `Os_2V0`, `Os_2V0` to `Os_2Base`.
49//!
50//! ```no_run
51//! # use ttf_view::tables::{TableDirectory, os_2::Os_2};
52//! # let data = std::fs::read("MyFont.ttf").unwrap();
53//! # let dir = unsafe { TableDirectory::new_unchecked(&data) };
54//! #
55//! let os_2: Os_2<'_> = dir.os_2().unwrap();
56//! println!("Vendor ID: {}", os_2.ach_vend_id); // a field in Os_2Base
57//!
58//! if let Some(v4) = os_2.v4() { // v4 is &Os_2V4
59//!     println!("Last char idx: {}", v4.us_last_char_index); // a field in Os_2Base
60//!     println!("Default char: {}", v4.us_default_char); // a field in Os_2V4
61//! }
62//! ```
63//!
64//! Note about types: if a type is suffixed with `Raw`, then it means it doesn't provide much info
65//! on its own, and you should generally use a higher-level type without this suffix. For example,
66//! [`TableRecordRaw`][tables::TableRecordRaw] only specifies an offset from the font's root, so it
67//! can't provide the actual table's data. But [`TableRecord`](tables::TableRecord) is a
68//! higher-level wrapper which does have a reference to the font, and can provide its table's data.
69//!
70//! ```no_run
71//! # use ttf_view::{
72//! #     tables::{TableDirectory, TableRecord, TableRecordRaw, head::Head},
73//! #     types::tags,
74//! # };
75//! # let data = std::fs::read("MyFont.ttf").unwrap();
76//! # let dir = unsafe { TableDirectory::new_unchecked(&data) };
77//! #
78//! let record_raw: &TableRecordRaw = dir.table_record_raw(tags::head).unwrap();
79//! // can access fields just fine
80//! println!("'head' length: {}", record_raw.length);
81//! println!("'head' offset: {}", record_raw.offset);
82//! // but can't access the actual table
83//!
84//! let record: TableRecord<'_> = dir.table_record(tags::head).unwrap();
85//! // auto-derefs to &TableRecordRaw for field access
86//! println!("'head' length: {}", record_raw.length);
87//! println!("'head' offset: {}", record_raw.offset);
88//! // and can also access the actual table
89//! let bytes = record.table_as_bytes();
90//! let head = record.table_as::<Head<'_>>().unwrap();
91//! ```
92//!
93//! [`ttf-parser`]: https://docs.rs/ttf-parser/latest/ttf_parser/
94//! [`read-fonts`]: https://docs.rs/read-fonts/latest/read_fonts/
95
96#![feature(const_trait_impl)]
97#![feature(const_result_trait_fn)]
98#![feature(const_slice_from_ptr_range)]
99#![feature(const_slice_make_iter)]
100#![feature(const_option_ops)]
101#![feature(const_convert)]
102#![feature(const_default)]
103#![feature(const_clone)]
104#![feature(const_index)]
105#![feature(const_iter)]
106#![feature(const_cmp)]
107#![feature(const_ops)]
108#![feature(const_try)]
109#![feature(derive_const)]
110#![feature(bstr)]
111#![feature(debug_closure_helpers)]
112#![feature(formatting_options)]
113#![feature(slice_from_ptr_range)]
114#![feature(iter_advance_by)]
115#![feature(exact_size_is_empty)]
116#![feature(try_trait_v2)]
117#![feature(integer_casts)]
118#![feature(widening_mul)]
119#![allow(clippy::manual_non_exhaustive)]
120#![allow(clippy::missing_safety_doc)] // TODO: remove when adding docs
121
122pub mod platform;
123pub mod tables;
124pub mod types;
125
126mod util;