#![doc(
html_logo_url = "https://raw.githubusercontent.com/maps4print/azul/master/assets/images/azul_logo_full_min.svg.png",
html_favicon_url = "https://raw.githubusercontent.com/maps4print/azul/master/assets/images/favicon.ico"
)]
#![deny(unused_must_use)]
#![warn(clippy::all)]
#![cfg_attr(not(test), warn(
clippy::pedantic,
clippy::nursery,
clippy::cargo,
// missing_docs, // TODO(docs): re-enable as a dedicated final docs pass; disabled
// // for now so the cleanup focuses on code-quality lints, not doc debt.
missing_debug_implementations,
missing_copy_implementations,
unreachable_pub,
unused_qualifications,
unused_lifetimes,
unused_import_braces,
unused_macro_rules,
unused_crate_dependencies,
meta_variable_misuse,
trivial_casts,
trivial_numeric_casts,
elided_lifetimes_in_paths,
single_use_lifetimes,
variant_size_differences,
non_ascii_idents,
unsafe_op_in_unsafe_fn,
let_underscore_drop,
))]
#![allow(
// `unknown_lints` lets the two forward-compat lints below be listed even on
// the CI toolchain (1.88), where they are not yet known, without emitting an
// "unknown lint" warning of their own. They still apply on newer rustc.
unknown_lints,
mismatched_lifetime_syntaxes, // newer rustc; fires in macro-generated code
function_casts_as_integer, // newer rustc; widget callback pointer identity
// pervasive + feature-sensitive (unused under one feature, live under another):
unused_imports,
unused_variables,
unused_mut,
dead_code,
// design lint, pervasive across the layout solver / renderer:
clippy::too_many_arguments,
// churny / 3rd-party, low value to scope:
clippy::legacy_numeric_constants,
unexpected_cfgs, // web-lift diagnostic cfgs
deprecated, // image crate tiff encoder (only under `tiff`)
// transitive dependency-version dups not resolvable in azul's source —
// syn 1↔2 (proc-macro migration), heck/jni-sys/rustc-hash/rustls-webpki;
// re-audit when the dep tree aligns.
clippy::multiple_crate_versions,
)]
#[macro_use]
extern crate alloc;
extern crate core;
#[cfg(feature = "icons")]
use brotli_decompressor as _;
use lru as _;
#[cfg(feature = "text_layout")]
use unicode_normalization as _;
#[cfg(feature = "xml")]
use xmlwriter as _;
#[cfg(feature = "web_lift")]
#[inline]
pub unsafe fn az_mark(_addr: u32, _val: u32) {
core::ptr::write_volatile(_addr as usize as *mut u32, _val);
}
#[cfg(not(feature = "web_lift"))]
#[inline]
pub const unsafe fn az_mark(_addr: u32, _val: u32) {}
#[cfg(feature = "web_lift")]
#[inline]
#[must_use] pub unsafe fn az_mark_read(_addr: u32) -> u32 {
core::ptr::read_volatile(_addr as usize as *const u32)
}
#[cfg(not(feature = "web_lift"))]
#[inline]
#[must_use] pub const unsafe fn az_mark_read(_addr: u32) -> u32 {
0
}
pub mod font_traits;
pub mod probe;
#[cfg(feature = "image_decoding")]
pub mod image;
#[cfg(feature = "text_layout")]
#[allow(private_interfaces)]
pub mod managers;
#[cfg(feature = "text_layout")]
#[allow(
dropping_references,
private_interfaces,
unreachable_patterns,
unused_parens,
unused_doc_comments,
unused_assignments
)]
pub mod solver3;
#[cfg(feature = "strfmt")]
pub mod fmt;
#[cfg(feature = "strfmt")]
pub use fmt::{FmtArg, FmtArgVec, FmtArgVecDestructor, FmtValue, fmt_string};
#[cfg(feature = "widgets")]
#[allow(unused_assignments, improper_ctypes_definitions)]
pub mod widgets;
#[cfg(feature = "extra")]
pub mod desktop;
#[cfg(any(
feature = "icu",
all(target_os = "macos", feature = "icu_macos"),
all(target_os = "windows", feature = "icu_windows"),
))]
pub mod icu;
#[cfg(any(
feature = "icu",
all(target_os = "macos", feature = "icu_macos"),
all(target_os = "windows", feature = "icu_windows"),
))]
pub use icu::{
DateTimeFieldSet, FormatLength, IcuDate, IcuDateTime, IcuError,
IcuLocalizer, IcuLocalizerHandle, IcuResult, IcuStringVec, IcuTime,
LayoutCallbackInfoIcuExt, ListType, PluralCategory,
};
#[cfg(feature = "fluent")]
pub mod fluent;
#[cfg(feature = "fluent")]
pub use fluent::{
check_fluent_syntax, check_fluent_syntax_bytes, create_fluent_zip,
create_fluent_zip_from_strings, export_to_zip, FluentError,
FluentLanguageInfo, FluentLanguageInfoVec, FluentLoadError, FluentLoadErrorVec,
FluentLocalizerHandle, FluentSyntaxCheckResult,
FluentZipLoadResult,
};
pub use azul_core::url;
pub use azul_core::url::{Url, UrlParseError, ResultUrlUrlParseError};
#[allow(unused_doc_comments, clippy::should_implement_trait)]
pub mod file;
pub use file::{
dir_create, dir_create_all, dir_list, dir_delete, dir_delete_all,
file_append, file_copy, path_exists, file_metadata, file_read, file_read_string,
file_delete, file_rename, file_write, file_write_string,
path_canonicalize, path_extension, path_file_name, path_is_dir, path_is_file,
path_join, path_parent, temp_dir,
DirEntry, DirEntryVec, DirEntryVecDestructor, DirEntryVecDestructorType,
FileError, FileErrorKind, FileMetadata, FilePath, OptionFilePath,
};
pub mod http;
pub use http::{
download_bytes, download_bytes_with_config, http_get,
http_get_with_config, is_url_reachable, HttpError, HttpHeader,
HttpRequestConfig, HttpResponse, HttpResponseTooLargeError, HttpResult,
HttpStatusError,
};
#[cfg(feature = "json")]
pub mod json;
#[cfg(feature = "json")]
pub use json::{
json_parse, json_stringify,
Json, JsonInternal, JsonKeyValue, JsonKeyValueVec, JsonKeyValueVecDestructor, JsonKeyValueVecDestructorType,
JsonParseError, JsonType, JsonVec,
ResultJsonJsonParseError, OptionJson, OptionJsonVec, OptionJsonKeyValueVec,
};
#[cfg(feature = "zip")]
pub mod zip;
#[cfg(feature = "zip")]
pub use zip::{
zip_create, zip_create_from_files, zip_extract_all, zip_list_contents,
ZipFile, ZipFileEntry, ZipFileEntryVec, ZipPathEntry, ZipPathEntryVec,
ZipReadConfig, ZipWriteConfig, ZipReadError, ZipWriteError,
};
pub mod icon;
pub use icon::{
default_icon_resolver,
ImageIconData, FontIconData,
register_image_icon,
register_font_icon,
register_icons_from_zip,
create_default_icon_provider,
register_material_icons,
register_embedded_material_icons,
};
pub use azul_core::icon::{
IconProviderHandle, IconResolverCallbackType,
resolve_icons_in_styled_dom, OptionIconProviderHandle,
};
#[cfg(feature = "text_layout")]
pub mod callbacks;
#[cfg(feature = "cpurender")]
#[allow(clippy::type_complexity)]
pub mod cpurender;
#[cfg(feature = "cpurender")]
pub mod glyph_cache;
#[cfg(feature = "text_layout")]
pub mod default_actions;
#[cfg(feature = "text_layout")]
pub mod event_determination;
#[cfg(feature = "text_layout")]
#[allow(clippy::type_complexity)]
pub mod font;
#[cfg(feature = "text_layout")]
pub mod headless;
#[cfg(feature = "text_layout")]
pub use allsorts::subset::CmapTarget;
#[cfg(feature = "text_layout")]
pub use font::parsed::{
FontParseWarning, FontParseWarningSeverity, FontType, OwnedGlyph, ParsedFont, PdfFontMetrics,
SubsetFont,
};
#[cfg(feature = "text_layout_hyphenation")]
pub use hyphenation;
#[cfg(feature = "text_layout")]
pub mod hit_test;
pub use azul_core::paged;
#[cfg(feature = "text_layout")]
#[allow(private_interfaces, unused_labels, clippy::type_complexity)]
pub mod text3;
#[cfg(feature = "text_layout")]
pub mod thread;
#[cfg(feature = "text_layout")]
#[allow(clippy::non_canonical_partial_ord_impl)]
pub mod timer;
#[cfg(feature = "text_layout")]
pub mod scroll_timer;
#[cfg(feature = "text_layout")]
#[allow(unused_parens)]
pub mod window;
#[cfg(feature = "text_layout")]
pub mod window_state;
#[cfg(feature = "xml")]
#[allow(unused_assignments)]
pub mod xml;
pub use paged::PageMargins;
#[cfg(feature = "text_layout")]
pub use hit_test::{CursorTypeHitTest, FullHitTest};
#[cfg(feature = "text_layout")]
pub use solver3::cache::LayoutCache as Solver3LayoutCache;
#[cfg(feature = "text_layout")]
pub use solver3::display_list::DisplayList as DisplayList3;
#[cfg(feature = "text_layout")]
pub use solver3::layout_document;
#[cfg(feature = "text_layout")]
pub use solver3::paged_layout::layout_document_paged;
#[cfg(feature = "text_layout")]
pub use solver3::{LayoutContext, LayoutError, Result as LayoutResult3};
#[cfg(feature = "text_layout")]
pub use text3::cache::{FontContext, FontManager, TextShapingCache};
#[cfg(feature = "text_layout")]
pub use text3::cache::TextShapingCache as TextLayoutCache;
#[cfg(feature = "font_async_registry")]
pub use rust_fontconfig::registry::FcFontRegistry;
#[cfg(feature = "text_layout")]
pub use window::{CursorBlinkTimerAction, LayoutWindow, ScrollbarDragState, TooltipTimerAction};
#[cfg(feature = "text_layout")]
pub use managers::text_input::{PendingTextEdit, OptionPendingTextEdit};
#[cfg(feature = "text_layout")]
#[allow(clippy::needless_pass_by_value)]
pub fn parse_font_fn(
source: azul_core::resources::LoadedFontSource,
) -> Option<azul_css::props::basic::FontRef> {
use crate::font::parsed::ParsedFont;
ParsedFont::from_bytes(
source.data.as_ref(),
source.index as usize,
&mut Vec::new(), )
.map(parsed_font_to_font_ref)
}
#[cfg(feature = "text_layout")]
pub fn parsed_font_to_font_ref(
parsed_font: ParsedFont,
) -> azul_css::props::basic::FontRef {
use core::ffi::c_void;
extern "C" fn parsed_font_destructor(ptr: *mut c_void) {
unsafe {
drop(Box::from_raw(ptr.cast::<ParsedFont>()));
}
}
let boxed = Box::new(parsed_font);
let raw_ptr = Box::into_raw(boxed) as *const c_void;
azul_css::props::basic::FontRef::new(raw_ptr, parsed_font_destructor)
}
#[cfg(feature = "text_layout")]
#[must_use] pub const fn font_ref_to_parsed_font(
font_ref: &azul_css::props::basic::FontRef,
) -> &ParsedFont {
unsafe { &*font_ref.parsed.cast::<ParsedFont>() }
}
#[cfg(test)]
mod autotest_generated {
use super::*;
#[cfg(not(feature = "web_lift"))]
#[test]
fn az_mark_read_is_zero_for_every_boundary_address() {
let addresses = [
0u32,
1,
0x3_FFFF, 0x4_0000, 0x6_0758, 0xF_0000, 0xF_0001, i32::MIN as u32, (-1i32) as u32, u32::MAX - 1,
u32::MAX,
u32::MAX.wrapping_add(1), ];
for addr in addresses {
assert_eq!(unsafe { az_mark_read(addr) }, 0, "az_mark_read(0x{addr:x})");
}
}
#[cfg(not(feature = "web_lift"))]
#[test]
fn az_mark_read_sweeps_the_whole_address_space_as_zero() {
for addr in (0u32..=u32::MAX).step_by(1 << 24) {
assert_eq!(unsafe { az_mark_read(addr) }, 0);
}
}
#[cfg(not(feature = "web_lift"))]
#[test]
fn az_mark_writes_are_unobservable_without_web_lift() {
let addresses = [0u32, 0x4_0000, 0x6_0758, 0xF_0000, u32::MAX];
let values = [0u32, 1, u32::MAX / 2, u32::MAX - 1, u32::MAX, i32::MIN as u32];
for addr in addresses {
for val in values {
unsafe { az_mark(addr, val) };
assert_eq!(
unsafe { az_mark_read(addr) },
0,
"az_mark(0x{addr:x}, {val}) must not be observable"
);
}
}
for _ in 0..1_000 {
unsafe { az_mark(0x6_0758, u32::MAX) };
}
assert_eq!(unsafe { az_mark_read(0x6_0758) }, 0);
}
#[cfg(not(feature = "web_lift"))]
#[test]
fn az_mark_no_op_variants_are_const_evaluable() {
const _WRITE_MIN: () = unsafe { az_mark(0, 0) };
const _WRITE_MAX: () = unsafe { az_mark(u32::MAX, u32::MAX) };
const READ_ZERO: u32 = unsafe { az_mark_read(0) };
const READ_MAX: u32 = unsafe { az_mark_read(u32::MAX) };
assert_eq!(READ_ZERO, 0);
assert_eq!(READ_MAX, 0);
}
#[cfg(feature = "text_layout")]
const MOCK_MONO: &[u8] = crate::text3::mock_fonts::MOCK_MONO_TTF;
#[cfg(feature = "text_layout")]
fn loaded_source(bytes: Vec<u8>, index: u32) -> azul_core::resources::LoadedFontSource {
azul_core::resources::LoadedFontSource {
data: azul_css::U8Vec::from_vec(bytes),
index,
load_outlines: true,
}
}
#[cfg(feature = "text_layout")]
fn parse_mock() -> ParsedFont {
ParsedFont::from_bytes(MOCK_MONO, 0, &mut Vec::new())
.expect("Azul Mock Mono must parse (positive control)")
}
#[cfg(feature = "text_layout")]
#[test]
fn parse_font_fn_rejects_malformed_input() {
let cases: Vec<(&str, Vec<u8>)> = vec![
("empty", Vec::new()),
("single_nul", vec![0u8]),
("whitespace_only", b" \t\n".to_vec()),
("garbage", (0u8..=255).cycle().take(4096).collect()),
("invalid_utf8", vec![0xFF, 0xFE, 0x00]),
("sfnt_magic_only", vec![0x00, 0x01, 0x00, 0x00]),
("header_only", MOCK_MONO[..12].to_vec()),
("truncated_font", MOCK_MONO[..64].to_vec()),
("half_a_font", MOCK_MONO[..MOCK_MONO.len() / 2].to_vec()),
("unicode_emoji", "\u{1F600}\u{1F600}".repeat(1_000).into_bytes()),
("combining_marks", "e\u{0301}".repeat(10_000).into_bytes()),
("nested_brackets", vec![b'['; 10_000]),
("boundary_numbers", b"0 -0 9223372036854775807 NaN inf -inf 1e309".to_vec()),
("leading_junk_then_font", {
let mut v = b"garbage".to_vec();
v.extend_from_slice(MOCK_MONO);
v
}),
];
for (name, bytes) in cases {
assert!(
parse_font_fn(loaded_source(bytes, 0)).is_none(),
"{name} must not parse into a FontRef"
);
}
}
#[cfg(feature = "text_layout")]
#[test]
fn parse_font_fn_survives_extremely_long_input() {
assert!(parse_font_fn(loaded_source(vec![0u8; 1_000_000], 0)).is_none());
assert!(parse_font_fn(loaded_source(vec![b'a'; 1_000_000], 0)).is_none());
let mut ttcf = b"ttcf".to_vec();
ttcf.extend_from_slice(&vec![0xABu8; 1_000_000]);
assert!(parse_font_fn(loaded_source(ttcf, 0)).is_none());
}
#[cfg(feature = "text_layout")]
#[test]
fn parse_font_fn_parses_the_positive_control() {
let font_ref = parse_font_fn(loaded_source(MOCK_MONO.to_vec(), 0))
.expect("the positive control must parse");
let parsed = font_ref_to_parsed_font(&font_ref);
assert_eq!(parsed.num_glyphs(), 96);
assert_eq!(parsed.num_glyphs(), parsed.maxp_table.num_glyphs);
assert_eq!(parsed.font_metrics.units_per_em, 1000);
assert!(parsed.font_metrics.ascent > 0.0);
assert!(parsed.font_metrics.descent <= 0.0);
assert!(parsed.font_metrics.ascent.is_finite());
assert!(parsed.font_metrics.descent.is_finite());
assert!(parsed.font_metrics.line_gap.is_finite());
assert_eq!(parsed.font_type, FontType::TrueType);
assert_eq!(parsed.original_index, 0);
assert!(parsed.cmap_subtable.is_some());
assert_eq!(parsed.hash, parse_mock().hash);
}
#[cfg(feature = "text_layout")]
#[test]
fn parse_font_fn_ignores_the_load_outlines_flag() {
let with = azul_core::resources::LoadedFontSource {
data: azul_css::U8Vec::from_vec(MOCK_MONO.to_vec()),
index: 0,
load_outlines: true,
};
let without = azul_core::resources::LoadedFontSource {
data: azul_css::U8Vec::from_vec(MOCK_MONO.to_vec()),
index: 0,
load_outlines: false,
};
let a = parse_font_fn(with).expect("parses with outlines");
let b = parse_font_fn(without).expect("parses without outlines");
let (pa, pb) = (font_ref_to_parsed_font(&a), font_ref_to_parsed_font(&b));
assert_eq!(pa.hash, pb.hash);
assert_eq!(pa.num_glyphs(), pb.num_glyphs());
assert_eq!(pa.pdf_font_metrics, pb.pdf_font_metrics);
}
#[cfg(feature = "text_layout")]
#[test]
fn parse_font_fn_with_an_out_of_range_index_is_deterministic() {
let baseline = parse_mock();
for index in [1u32, 2, 0x7FFF_FFFF, u32::MAX - 1, u32::MAX] {
if let Some(font_ref) = parse_font_fn(loaded_source(MOCK_MONO.to_vec(), index)) {
let parsed = font_ref_to_parsed_font(&font_ref);
assert_eq!(
parsed.num_glyphs(),
baseline.num_glyphs(),
"index {index} must not conjure a different face"
);
assert_eq!(parsed.original_index, index as usize);
}
}
}
#[cfg(feature = "text_layout")]
#[test]
fn parse_font_fn_failure_path_is_repeatable() {
for _ in 0..200 {
assert!(parse_font_fn(loaded_source(Vec::new(), 0)).is_none());
assert!(parse_font_fn(loaded_source(vec![0xFF; 3], u32::MAX)).is_none());
}
assert!(parse_font_fn(loaded_source(MOCK_MONO.to_vec(), 0)).is_some());
}
#[cfg(feature = "text_layout")]
#[test]
fn parse_font_fn_mints_a_fresh_identity_per_call() {
let a = parse_font_fn(loaded_source(MOCK_MONO.to_vec(), 0)).expect("parses");
let b = parse_font_fn(loaded_source(MOCK_MONO.to_vec(), 0)).expect("parses");
assert_ne!(a, b, "two parses of the same bytes are two distinct handles");
assert_ne!(a.id, b.id);
assert!(b > a, "ids are monotonically assigned");
assert_eq!(
font_ref_to_parsed_font(&a).hash,
font_ref_to_parsed_font(&b).hash
);
}
#[cfg(feature = "text_layout")]
#[test]
fn parsed_font_font_ref_round_trip_preserves_the_face() {
let original = parse_mock();
let expected_hash = original.hash;
let expected_glyphs = original.num_glyphs();
let expected_metrics = original.pdf_font_metrics;
let expected_upem = original.font_metrics.units_per_em;
let expected_ascent = original.font_metrics.ascent;
let expected_type = original.font_type.clone();
let expected_index = original.original_index;
let font_ref = parsed_font_to_font_ref(original);
let decoded = font_ref_to_parsed_font(&font_ref);
assert_eq!(decoded.hash, expected_hash);
assert_eq!(decoded.num_glyphs(), expected_glyphs);
assert_eq!(decoded.pdf_font_metrics, expected_metrics);
assert_eq!(decoded.font_metrics.units_per_em, expected_upem);
assert!((decoded.font_metrics.ascent - expected_ascent).abs() < f32::EPSILON);
assert_eq!(decoded.font_type, expected_type);
assert_eq!(decoded.original_index, expected_index);
}
#[cfg(feature = "text_layout")]
#[test]
fn parsed_font_to_font_ref_handle_invariants() {
use core::sync::atomic::Ordering as AtomicOrdering;
let font_ref = parsed_font_to_font_ref(parse_mock());
assert!(!font_ref.parsed.is_null());
assert!(!font_ref.copies.is_null());
assert!(font_ref.run_destructor);
assert_ne!(font_ref.id, 0, "id 0 is reserved for un-initialised handles");
assert_eq!(unsafe { (*font_ref.copies).load(AtomicOrdering::SeqCst) }, 1);
assert_eq!(font_ref.get_parsed(), font_ref.parsed);
}
#[cfg(feature = "text_layout")]
#[test]
fn font_ref_to_parsed_font_is_a_stable_reborrow() {
use core::ffi::c_void;
let font_ref = parsed_font_to_font_ref(parse_mock());
let first: *const ParsedFont = font_ref_to_parsed_font(&font_ref);
let second: *const ParsedFont = font_ref_to_parsed_font(&font_ref);
assert!(core::ptr::eq(first, second), "reborrow must be stable");
assert!(core::ptr::eq(first.cast::<c_void>(), font_ref.get_parsed()));
}
#[cfg(feature = "text_layout")]
#[test]
fn cloning_a_font_ref_shares_the_face_and_the_drop_is_refcounted() {
use core::sync::atomic::Ordering as AtomicOrdering;
let original = parsed_font_to_font_ref(parse_mock());
let expected_hash = font_ref_to_parsed_font(&original).hash;
let clone = original.clone();
assert_eq!(clone.id, original.id, "a clone is the same font");
assert_eq!(clone, original);
assert!(core::ptr::eq(clone.parsed, original.parsed));
assert_eq!(unsafe { (*original.copies).load(AtomicOrdering::SeqCst) }, 2);
drop(clone);
assert_eq!(unsafe { (*original.copies).load(AtomicOrdering::SeqCst) }, 1);
assert_eq!(
font_ref_to_parsed_font(&original).hash,
expected_hash,
"the face must survive its clone being dropped"
);
assert_eq!(font_ref_to_parsed_font(&original).num_glyphs(), 96);
}
#[cfg(feature = "text_layout")]
#[test]
fn font_ref_clone_drop_cycles_do_not_double_free() {
use core::sync::atomic::Ordering as AtomicOrdering;
let original = parsed_font_to_font_ref(parse_mock());
let expected_hash = font_ref_to_parsed_font(&original).hash;
for _ in 0..1_000 {
let c = original.clone();
assert_eq!(font_ref_to_parsed_font(&c).hash, expected_hash);
}
let batch: Vec<_> = (0..1_000).map(|_| original.clone()).collect();
assert_eq!(
unsafe { (*original.copies).load(AtomicOrdering::SeqCst) },
1_001
);
drop(batch);
assert_eq!(unsafe { (*original.copies).load(AtomicOrdering::SeqCst) }, 1);
assert_eq!(font_ref_to_parsed_font(&original).hash, expected_hash);
}
#[cfg(feature = "text_layout")]
#[test]
fn font_ref_identity_is_per_handle_not_per_content() {
use std::collections::{BTreeSet, HashSet};
let a = parsed_font_to_font_ref(parse_mock());
let b = parsed_font_to_font_ref(parse_mock());
assert_ne!(a, b);
assert!(a < b, "ids are monotonically assigned, so a precedes b");
assert_eq!(
font_ref_to_parsed_font(&a).hash,
font_ref_to_parsed_font(&b).hash,
"…even though the content hash is the same"
);
let set: HashSet<_> = vec![a.clone(), a.clone(), a.clone(), b.clone()]
.into_iter()
.collect();
assert_eq!(set.len(), 2, "clones dedup, distinct handles do not");
let ordered: BTreeSet<_> = vec![b.clone(), a.clone(), b.clone()].into_iter().collect();
assert_eq!(ordered.len(), 2);
assert_eq!(ordered.iter().next(), Some(&a));
}
#[cfg(feature = "text_layout")]
#[test]
fn many_font_refs_do_not_alias_each_other() {
let refs: Vec<_> = (0..16).map(|_| parsed_font_to_font_ref(parse_mock())).collect();
for (i, a) in refs.iter().enumerate() {
assert_eq!(font_ref_to_parsed_font(a).num_glyphs(), 96);
for b in refs.iter().skip(i + 1) {
assert!(
!core::ptr::eq(a.parsed, b.parsed),
"independently boxed faces must not share a pointer"
);
assert_ne!(a.id, b.id);
}
}
}
}