Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Draft]: Add missing tables to OpenType definition #226

Draft
wants to merge 55 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
586a78b
Test-fonts folder and Cargo.lock checkin
archaephyrryx Oct 10, 2024
d8cc5f5
Add prep, gasp to OpenType def and extend API-helper accordingly
archaephyrryx Oct 14, 2024
2dcfa5e
Update HELPERS.md
archaephyrryx Oct 17, 2024
85e880d
GDEF spec (ItemVariationStore unimplemented), no API helper support
archaephyrryx Oct 17, 2024
82b87f0
Extract oft_metrics to own file, define and generate Gdef table metri…
archaephyrryx Oct 23, 2024
9fc43f2
Finish printing of GDEF metrics
archaephyrryx Oct 27, 2024
6b628e3
Implement majority of GPOS and display API
archaephyrryx Nov 13, 2024
abd9ddb
Display fixes for GPOS
archaephyrryx Nov 13, 2024
e7e6f61
Tweaks to output
archaephyrryx Nov 14, 2024
166247e
SinglePos subtable in format def and API, add test-font
archaephyrryx Nov 15, 2024
ac606fa
Add rustfmt::skip attr to generated module, fmt everything else
archaephyrryx Nov 15, 2024
317e5c3
Adds PairPos subtable in def and otf_metrics, cleanup, new test-font
archaephyrryx Nov 19, 2024
4483978
CursivePos implementation and API support, new test-font
archaephyrryx Nov 20, 2024
b870ff8
Implement GSUB, scaffolding for lookup subtables
archaephyrryx Nov 20, 2024
ab76d45
Avoid printing default-valued LookupFlag
archaephyrryx Nov 20, 2024
e568f7a
Partial implementation of SequenceContext subtable
archaephyrryx Nov 21, 2024
dd4ae42
Add type-unification tricks to explicate transitive error-chain
archaephyrryx Nov 25, 2024
81e885d
Fix SOF shift bug in SequenceContext definition
archaephyrryx Nov 25, 2024
bc061e9
ChainedSequenceContexts support, otf_metrics tweaks
archaephyrryx Nov 27, 2024
580478f
Add SingleSubst, refine offset16, refactor helpers and usage patterns
archaephyrryx Nov 28, 2024
020e27b
partial fixes for oft_metrics based on revamped format implementation
archaephyrryx Nov 28, 2024
45cd668
Fixes overloaded typename for LookupFlag format definition
archaephyrryx Nov 29, 2024
4ce9e1b
Minor refactor of prepend_field_flags_bits8 in opentype
archaephyrryx Nov 29, 2024
0e0c5e2
Miscellaneous renamings, refactorings, and doc-changes (spellcheck)
archaephyrryx Dec 2, 2024
f0f1faa
Redesign of otf-metrics to handle semi-nullable offset links
archaephyrryx Dec 3, 2024
142d97f
Fixes missing offset-linkage in rule_set.chained_seq_rules
archaephyrryx Dec 4, 2024
e09dca5
AlternateSubst, add guard to avoid underflow for bad offsets
archaephyrryx Dec 5, 2024
279131d
Add --fast flag for fontinfo
archaephyrryx Dec 5, 2024
cb3e7c6
Fix for cmap_subtable_format6 length>65535, minor cleanup
archaephyrryx Dec 9, 2024
61840eb
Fix bad SOF-offset for table-dirs in TTC, rename sample_codegen
archaephyrryx Dec 9, 2024
739fd16
Fix otf_metrics based on breaking changes to opentype.rs
archaephyrryx Dec 9, 2024
aae5df6
Add quick comment to explain TraitSet::Copy
archaephyrryx Dec 10, 2024
98c2b5a
fontinfo verbosity, derive builder, doodle --as-format flag
archaephyrryx Dec 10, 2024
423178b
Reverts red-herring anti-fix to 'start of font' offset interpretation
archaephyrryx Dec 10, 2024
4816aec
Adds base address parameter to WithRelativeOffset, -q flag in fontinfo
archaephyrryx Dec 11, 2024
9ce9e4f
Adds frankenpax TTC font to test-fonts directory
archaephyrryx Dec 11, 2024
a05547d
Relax assertion that MultipleSubst glyphCount != 0
archaephyrryx Dec 11, 2024
a2adb31
Handle non-standard delta-values, refactor UintSet
archaephyrryx Dec 12, 2024
7e7dcbb
Allow OpenType fonts to have TrueType-specific "true" sfntVersion
archaephyrryx Dec 12, 2024
854a0f0
LigatureSubst support, CoverageTable iteration
archaephyrryx Dec 15, 2024
ae955e6
ReverseChainContextSingleSubst support
archaephyrryx Dec 16, 2024
57cba4c
Groundwork FormatRefs to facilitate writing Pos/Subst Extension
archaephyrryx Dec 19, 2024
5c24d7d
Implementation of MarkBase and MarkLig Pos subtables (broken on one f…
archaephyrryx Jan 6, 2025
e22d14c
Fixed MarkLigPos, partial impl (Format layer only) for MarkMarkPos
archaephyrryx Jan 7, 2025
d75ed2c
Full implementation of MarkMarkPos
archaephyrryx Jan 7, 2025
1888a1f
Support for layout extension subtables
archaephyrryx Jan 8, 2025
a1f9827
Inherit extension lookup type for LookupTable lookup_type field value
archaephyrryx Jan 8, 2025
753e79a
Add Format layer for v1.1 layout table fields, test font for FeatureV…
archaephyrryx Jan 8, 2025
010a886
Implementation and partial API for Base table
archaephyrryx Jan 12, 2025
e5b5a31
ItemVariationStore stub implementation
archaephyrryx Jan 12, 2025
f278888
Add missing field to GDEF v1.3 to fix offset bug
archaephyrryx Jan 13, 2025
b49980a
Refactor item_var_store display logic for GDEF
archaephyrryx Jan 14, 2025
695cc3f
vhea and vmtx implementation (reusing horizontal equivalents)
archaephyrryx Jan 14, 2025
77aad92
Prototype for `kern` table
archaephyrryx Jan 15, 2025
d041db3
Kern subtable implementation in API, edge-case fix for wrapping u16
archaephyrryx Jan 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[alias]
cg = "run --bin doodle -- format --output rust --dest generated/sample_codegen.rs"
cg = "run --bin doodle -- format --output rust --dest generated/gencode.rs"

[profile.profiling]
inherits = "release"
Expand Down
96 changes: 88 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
anyhow = "1.0"
clap = { version = "4.2", features = ["derive"] }
num-traits = "0.2.19"
num-traits = "0.2"
72 changes: 59 additions & 13 deletions HELPERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

```rust
/// Returns whichever of two pair-typed values has a larger first element, left-biased if equal
pub fn expr_max_keyval(a: Expr, b: Expr) -> Expr {
pub fn expr_max_key_val(a: Expr, b: Expr) -> Expr {
expr_if_else(
expr_gte(tuple_proj(a.clone(), 0), tuple_proj(b.clone(), 0)),
a,
Expand Down Expand Up @@ -53,7 +53,7 @@ pub fn expr_maximum_by(
[
(
pat_some(bind("x")),
expr_some(expr_max_keyval(var("x"), var("y"))),
expr_some(expr_max_key_val(var("x"), var("y"))),
),
(pat_none(), expr_some(var("y"))),
],
Expand Down Expand Up @@ -84,13 +84,14 @@ pub fn expr_sum_as_u64(seq: Expr) -> Expr {
add(var("acc"), Expr::AsU64(Box::new(var("y")))),
),
Expr::U64(0),
ValueType::UMAX,
ValueType::Base(BaseType::U64),
seq,
)
}

/// Evaluates to `true` if any element of `haystack` equals `needle`, otherwise `false`.
pub fn is_elem(needle: Expr, haystack: Expr) -> Expr {
// REVIEW - we have no mechanism for early-short-circuiting in Expr::LeftFold processing, which may be required to achieve the desired performance
left_fold(
lambda_tuple(
["acc", "stalk"],
Expand All @@ -103,7 +104,7 @@ pub fn is_elem(needle: Expr, haystack: Expr) -> Expr {
}

/// Returns a copied sequence with only the first occurrence of any given element modulo integer equality.
pub fn dedup(seq: Expr, vt_elem: ValueType) -> Expr {
pub fn int_seq_unique(seq: Expr, vt_elem: ValueType) -> Expr {
flat_map_list(
lambda_tuple(
["prefix", "x"],
Expand Down Expand Up @@ -168,13 +169,13 @@ pub fn fmt_unwrap(expr: Expr) -> Format {

/// Performs a table lookup operation over a sequence of entries `seq` of type `elem_type`.
///
/// Uses `f_getkey` to map from each entry to its key, and `query_key` as the query key.
/// Uses `f_get_key` to map from each entry to its key, and `query_key` as the query key.
///
/// Even if mulitple matching entries exist, only the first one is returned, as an Option.
/// Even if multiple matching entries exist, only the first one is returned, as an Option.
pub fn table_find(
seq: Expr,
elem_type: ValueType,
f_getkey: impl Fn(Expr) -> Expr,
f_get_key: impl Fn(Expr) -> Expr,
query_key: Expr,
) -> Expr {
let match0_or1 = flat_map_list(
Expand All @@ -187,7 +188,7 @@ pub fn table_find(
(
Pattern::Wildcard,
expr_match(
expr_eq(f_getkey(tuple_proj(var("list-entry"), 1)), query_key),
expr_eq(f_get_key(tuple_proj(var("list-entry"), 1)), query_key),
[
(Pattern::Bool(true), Expr::Seq(vec![var("entry")])),
(Pattern::Bool(false), Expr::Seq(Vec::new())),
Expand Down Expand Up @@ -220,8 +221,9 @@ pub fn seq_to_opt(empty_or_singleton: Expr) -> Expr {



/// Shortcut for discarding a Format's return value but perserving its effect on the overall parse
/// Shortcut for discarding a Format's return value but preserving its effect on the overall parse
pub fn void(f: Format) -> Format {
// REVIEW - the need for a dummy capture-variable suggests the utility of a `LetFormat` variation without a capture
chain(f, "_", Format::EMPTY)
}

Expand All @@ -247,12 +249,12 @@ pub fn binary_fallback(f: Format) -> Format {
}

/// Shortcut for computing a standalone `Format::Pos` that we immediately consume without ever needing to reuse,
/// which discards the `Format::Pos` token via `map`
/// which discards the `Format::Pos` token via `LetFormat`
///
/// The `pos_varname` parameter is the verbatim name of the variable that `f` internally uses to refer to the parsed `Format::Pos`.
/// The `pos_var` parameter is the verbatim name of the variable that `f` internally uses to refer to the parsed `Format::Pos`.
#[inline]
pub fn with_pos(pos_varname: &'static str, f: Format) -> Format {
chain(Format::Pos, pos_varname, f)
pub fn with_pos(pos_var: &'static str, f: Format) -> Format {
chain(Format::Pos, pos_var, f)
}

/// Helper for parsing `(f, suffix)` where we only want to see the `f` component
Expand All @@ -266,4 +268,48 @@ pub fn discard_suffix(f: Format, suffix: Format) -> Format {
pub fn fmt_try_index(seq: Expr, index: Expr) -> Format {
fmt_unwrap(index_checked(seq, index))
}

/// Similar to [`chain`], but for cases where the result of `f0` is not used.
#[inline]
pub fn keep_last(f0: Format, f: Format) -> Format {
// TODO: implement a first-class Format variant for this case
Format::LetFormat(Box::new(f0), Label::Borrowed("_"), Box::new(f))
}

/// Dual method to [`keep_last`], where the order of evaluation is the same
/// but the return value is that of the first format, not the last.
pub fn keep_first(f: Format, f1: Format) -> Format {
// NOTE: compared to `keep_last`, there is far less of a reason to create a Format primitive to avoid this construction
chain(f, "x", keep_last(f1, compute(var("x"))))
}

/// Helper function for splicing together two separate records, with the option to filter the fields included from each
/// or reorder them internally, but not across the record as a whole (i.e. all fields from the first will strictly precede those of the second).
pub fn merge_record_subsets<const N: usize, const M: usize>(first: (Expr, [&'static str; N]), second: (Expr, [&'static str; M])) -> Expr {
let (first_expr, first_fields) = first;
let (second_expr, second_fields) = second;

let mut accum_fields = Vec::with_capacity(N + M);
let mut included_fields = BTreeSet::new();

for field_name in first_fields.into_iter() {
if !included_fields.insert(field_name) {
unreachable!("duplicated field in merge_records: `{field_name}`");
}
accum_fields.push((
Label::Borrowed(field_name),
record_proj(first_expr.clone(), field_name),
));
}
for field_name in second_fields.into_iter() {
if !included_fields.insert(field_name) {
unreachable!("duplicated field in merge_records: `{field_name}`");
}
accum_fields.push((
Label::Borrowed(field_name),
record_proj(second_expr.clone(), field_name),
))
}
Expr::Record(accum_fields)
}
```
33 changes: 17 additions & 16 deletions doodle-formats/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ use doodle::{Format, FormatModule, FormatRef};

pub mod base;

mod deflate;
mod elf;
mod gif;
mod gzip;
mod jpeg;
mod mpeg4;
mod opentype;
mod peano;
mod png;
mod riff;
mod tar;
mod text;
mod tiff;
mod waldo;
mod zlib;
pub mod deflate;
pub mod elf;
pub mod gif;
pub mod gzip;
pub mod jpeg;
pub mod mpeg4;
pub mod opentype;
pub mod peano;
pub mod png;
pub mod riff;
pub mod tar;
pub mod text;
pub mod tiff;
pub mod waldo;
pub mod zlib;

pub fn main(module: &mut FormatModule) -> FormatRef {
let base = base::main(module);
Expand Down Expand Up @@ -51,7 +51,7 @@ pub fn main(module: &mut FormatModule) -> FormatRef {
var("gzip-raw"),
"item",
Format::DecodeBytes(
Box::new(record_projs(var("item"), &["data", "inflate"])),
Box::new(record_lens(var("item"), &["data", "inflate"])),
Box::new(tar.call()),
),
),
Expand Down Expand Up @@ -109,6 +109,7 @@ mod test {
(
"mask",
Format::WithRelativeOffset(
None,
Box::new(var("len")),
Box::new(Format::Byte(mask_bytes)),
),
Expand Down
Loading