From b62b304edb92a871cbc2f4655f29982949e6e6f6 Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Sun, 4 Jun 2023 16:11:54 +0900 Subject: [PATCH 01/26] develop README --- README.md | 58 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 5e155e3..03ed1fe 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,14 @@ # `otfed`: OpenType Font Format Encoder & Decoder -This library is intended to be a reformulation and extension of [`otfm`](https://github.com/dbuenzli/otfm). +## Table of Contents -See also: - -* [an extended version of `otfm` for SATySFi](https://github.com/gfngfn/otfm) -* [SATySFi](https://github.com/gfngfn/SATySFi) - - -## Note - -Some unit tests use data extracted from the following fonts: - -* [IPAex明朝 (IPAex Mincho)](https://moji.or.jp/ipafont/): `ipaexm.ttf` - - See the license [here](https://moji.or.jp/ipafont/license/) -* [Latin Modern](http://www.gust.org.pl/projects/e-foundry/latin-modern): `lmroman10-regular.otf` and `lmmono10-regular.otf` - - See the license [here](http://www.gust.org.pl/projects/e-foundry/latin-modern#section-2) -* [Latin Modern Math](https://www.gust.org.pl/projects/e-foundry/lm-math) - - See the license [here](https://www.latex-project.org/lppl/) -* [Junicode](https://junicode.sourceforge.io/) - - See the license [here](https://github.com/psb1558/Junicode-font/blob/master/OFL.txt) +- [Supported features](#supported-features) +- [How to install](#how-to-install) +- [Usage of an example CLI `otfedcli`](#usage-of-an-example-cli-otfedcli) +- [Development status](#development-status) +- [How to develop this library](#how-to-develop-this-library) +- [Remarks](#remarks) ## How to install @@ -121,10 +109,10 @@ $ dune exec otfedcli input/Junicode.ttf subset 0,113,302 output/Junicode-subset. ``` -## Status +## Development status * v: done -* o: no automated test has been given, but seems working correctly for many inputs +* o: no automated test has been given, but seems to be working fine for many inputs * \-: not supported yet @@ -178,7 +166,7 @@ $ dune exec otfedcli input/Junicode.ttf subset 0,113,302 output/Junicode-subset.
-## How to develop +## How to develop this library Assumes that [Dune](https://dune.build/) (≥2.7) is installed. @@ -193,3 +181,29 @@ $ dune build ```console $ dune test ``` + + +## Remarks + +### Origin + +This library has been developed with the intension of reformulating [`otfm`](https://github.com/dbuenzli/otfm). + +See also: + +* [an extended version of `otfm` for SATySFi](https://github.com/gfngfn/otfm) +* [SATySFi](https://github.com/gfngfn/SATySFi) + + +### Data used in unit tests + +Some unit tests use data extracted from the following fonts: + +* [IPAex明朝 (IPAex Mincho)](https://moji.or.jp/ipafont/): `ipaexm.ttf` + - See the license [here](https://moji.or.jp/ipafont/license/) +* [Latin Modern](http://www.gust.org.pl/projects/e-foundry/latin-modern): `lmroman10-regular.otf` and `lmmono10-regular.otf` + - See the license [here](http://www.gust.org.pl/projects/e-foundry/latin-modern#section-2) +* [Latin Modern Math](https://www.gust.org.pl/projects/e-foundry/lm-math) + - See the license [here](https://www.latex-project.org/lppl/) +* [Junicode](https://junicode.sourceforge.io/) + - See the license [here](https://github.com/psb1558/Junicode-font/blob/master/OFL.txt) From 5e8a203429a950754dac3a977b7958e546a0915a Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Sun, 4 Jun 2023 17:16:10 +0900 Subject: [PATCH 02/26] add 'DecodeVhea' and change 'xmax_extent' to 'x_max_extent' --- src/decodeHhea.ml | 4 +-- src/decodeTable.ml | 1 + src/decodeVhea.ml | 72 +++++++++++++++++++++++++++++++++++++++++++ src/encodeHhea.ml | 2 +- src/intermediate.ml | 19 +++++++++++- src/otfed.mli | 45 ++++++++++++++++++++++++++- src/subset.ml | 6 ++-- src/value.ml | 38 ++++++++++++++++++++--- test/testCaseHhea1.ml | 2 +- 9 files changed, 176 insertions(+), 13 deletions(-) create mode 100644 src/decodeVhea.ml diff --git a/src/decodeHhea.ml b/src/decodeHhea.ml index 0b102f2..eea2989 100644 --- a/src/decodeHhea.ml +++ b/src/decodeHhea.ml @@ -23,7 +23,7 @@ let d_hhea : Intermediate.Hhea.t decoder = d_uint16 >>= fun advance_width_max -> d_int16 >>= fun min_left_side_bearing -> d_int16 >>= fun min_right_side_bearing -> - d_int16 >>= fun xmax_extent -> + d_int16 >>= fun x_max_extent -> d_int16 >>= fun caret_slope_rise -> d_int16 >>= fun caret_slope_run -> d_int16 >>= fun caret_offset -> @@ -40,7 +40,7 @@ let d_hhea : Intermediate.Hhea.t decoder = advance_width_max; min_left_side_bearing; min_right_side_bearing; - xmax_extent; + x_max_extent; }; } diff --git a/src/decodeTable.ml b/src/decodeTable.ml index a1e0db9..689ca44 100644 --- a/src/decodeTable.ml +++ b/src/decodeTable.ml @@ -9,6 +9,7 @@ module Name = DecodeName module Gsub = DecodeGsub module Gpos = DecodeGpos module Kern = DecodeKern +module Vhea = DecodeVhea module Math = DecodeMath module Ttf = DecodeTtf module Cff = DecodeCff diff --git a/src/decodeVhea.ml b/src/decodeVhea.ml new file mode 100644 index 0000000..5a5c2d0 --- /dev/null +++ b/src/decodeVhea.ml @@ -0,0 +1,72 @@ + +open Basic +open DecodeOperation.Open +open DecodeBasic + + +let fetch_num_of_long_ver_metrics (core : common_source_core) (table_directory : table_directory) = + let open ResultMonad in + DecodeOperation.seek_required_table table_directory Value.Tag.table_vhea >>= fun (offset, _length) -> + let open DecodeOperation in + d_uint16 |> run core (offset + 34) + + +let d_vhea_metrics_1_0 : Value.Vhea.metrics decoder = + let open DecodeOperation in + d_int16 >>= fun ascent -> + d_int16 >>= fun descent -> + d_int16 >>= fun _line_gap -> + return @@ Value.Vhea.Version1_0{ ascent; descent } + + +let d_vhea_metrics_1_1 : Value.Vhea.metrics decoder = + let open DecodeOperation in + d_int16 >>= fun vert_typo_ascender -> + d_int16 >>= fun vert_typo_descender -> + d_int16 >>= fun vert_typo_line_gap -> + return @@ Value.Vhea.Version1_1{ + vert_typo_ascender; + vert_typo_descender; + vert_typo_line_gap; + } + + +let d_vhea : Intermediate.Vhea.t decoder = + let open DecodeOperation in + d_uint32 >>= fun version -> + begin + if version = !%% 0x00010000L then + d_vhea_metrics_1_0 + else if version = !%% 0x00011000L then + d_vhea_metrics_1_1 + else + err @@ UnknownTableVersion(version) + end >>= fun metrics -> + d_uint16 >>= fun advance_height_max -> + d_int16 >>= fun min_top_side_bearing -> + d_int16 >>= fun min_bottom_side_bearing -> + d_int16 >>= fun y_max_extent -> + d_int16 >>= fun caret_slope_rise -> + d_int16 >>= fun caret_slope_run -> + d_int16 >>= fun caret_offset -> + return Intermediate.Vhea.{ + value = Value.Vhea.{ + metrics; + caret_slope_rise; + caret_slope_run; + caret_offset; + }; + derived = { + advance_height_max; + min_top_side_bearing; + min_bottom_side_bearing; + y_max_extent; + }; + } + + +let get (src : source) : Intermediate.Vhea.t ok = + let open ResultMonad in + let common = get_common_source src in + DecodeOperation.seek_required_table common.table_directory Value.Tag.table_vhea >>= fun (offset, _length) -> + DecodeOperation.run common.core offset d_vhea diff --git a/src/encodeHhea.ml b/src/encodeHhea.ml index 9b9accc..6488fac 100644 --- a/src/encodeHhea.ml +++ b/src/encodeHhea.ml @@ -17,7 +17,7 @@ let e_hhea ~(number_of_h_metrics : int) (ihhea : Intermediate.Hhea.t) : unit enc e_uint16 d.advance_width_max >>= fun () -> e_int16 d.min_left_side_bearing >>= fun () -> e_int16 d.min_right_side_bearing >>= fun () -> - e_int16 d.xmax_extent >>= fun () -> + e_int16 d.x_max_extent >>= fun () -> e_int16 v.caret_slope_rise >>= fun () -> e_int16 v.caret_slope_run >>= fun () -> e_int16 v.caret_offset >>= fun () -> diff --git a/src/intermediate.ml b/src/intermediate.ml index 5527b02..c01a35b 100644 --- a/src/intermediate.ml +++ b/src/intermediate.ml @@ -34,7 +34,7 @@ module Hhea = struct advance_width_max : int; min_left_side_bearing : int; min_right_side_bearing : int; - xmax_extent : int; + x_max_extent : int; } [@@deriving show { with_path = false }] @@ -64,6 +64,23 @@ module Os2 = struct end +module Vhea = struct + type derived = { + advance_height_max : int; + min_top_side_bearing : int; + min_bottom_side_bearing : int; + y_max_extent : int; + } + [@@deriving show { with_path = false }] + + type t = { + value : Value.Vhea.t; + derived : derived; + } + [@@deriving show { with_path = false }] +end + + module Ttf = struct module Maxp = struct type t = { diff --git a/src/otfed.mli b/src/otfed.mli index 0591158..7b04947 100644 --- a/src/otfed.mli +++ b/src/otfed.mli @@ -370,6 +370,29 @@ module Value : sig [@@deriving show] end + (** Defines types for master data in [vhea] tables. *) + module Vhea : sig + type metrics = + | Version1_0 of { + ascent : int; + descent : int; + } + | Version1_1 of { + vert_typo_ascender : int; + vert_typo_descender : int; + vert_typo_line_gap : int; + } + [@@deriving show] + + type t = { + metrics : metrics; + caret_slope_rise : int; + caret_slope_run : int; + caret_offset : int; + } + [@@deriving show] + end + (** Defines types specific to TrueType-based tables. *) module Ttf : sig type contour_element = { @@ -586,7 +609,7 @@ module Intermediate : sig advance_width_max : int; min_left_side_bearing : int; min_right_side_bearing : int; - xmax_extent : int; + x_max_extent : int; } [@@deriving show] @@ -618,6 +641,26 @@ module Intermediate : sig [@@deriving show] end + (** Defines types for representing whole information in [vhea] tables. *) + module Vhea : sig + (** The type for data contained in a single [vhea] table that are derivable + from glyph descriptions or master data in other tables in the font the [vhea] table belongs to. *) + type derived = { + advance_height_max : int; + min_top_side_bearing : int; + min_bottom_side_bearing : int; + y_max_extent : int; + } + [@@deriving show] + + (** The type for representing [vhea] tables. *) + type t = { + value : Value.Vhea.t; + derived : derived; + } + [@@deriving show] + end + (** Defines types for representing TrueType-based tables. *) module Ttf : sig module Maxp : sig diff --git a/src/subset.ml b/src/subset.ml index b26ecbd..6f92426 100644 --- a/src/subset.ml +++ b/src/subset.ml @@ -124,7 +124,7 @@ let update_derived ~current:derived ~new_one:derived_new = advance_width_max = Stdlib.max derived.advance_width_max derived_new.advance_width_max; min_left_side_bearing = Stdlib.min derived.min_left_side_bearing derived_new.min_left_side_bearing; min_right_side_bearing = Stdlib.min derived.min_right_side_bearing derived_new.min_right_side_bearing; - xmax_extent = Stdlib.max derived.xmax_extent derived_new.xmax_extent; + x_max_extent = Stdlib.max derived.x_max_extent derived_new.x_max_extent; } @@ -147,7 +147,7 @@ let get_ttf_hmtx (ihmtx : Decode.Hmtx.t) ((gid, g_opt) : glyph_id * Ttf.glyph_in advance_width_max = aw; min_left_side_bearing = lsb; min_right_side_bearing = rsb; - xmax_extent = extent; + x_max_extent = extent; } in return (derived, entry, aw) @@ -227,7 +227,7 @@ let get_cff_hmtx (cff : Decode.cff_source) (ihmtx : Decode.Hmtx.t) (gid : glyph_ advance_width_max = aw; min_left_side_bearing = lsb; min_right_side_bearing = rsb; - xmax_extent = extent; + x_max_extent = extent; } in return (derived, entry, aw, bbox) diff --git a/src/value.ml b/src/value.ml index ad1a5de..a5aab02 100644 --- a/src/value.ml +++ b/src/value.ml @@ -30,8 +30,14 @@ module Tag : sig val table_prep : t val table_cff : t + val table_cff2 : t val table_vorg : t + val table_dsig : t + val table_kern : t + val table_vhea : t + val table_vmtx : t + val table_base : t val table_gdef : t val table_gpos : t @@ -39,8 +45,6 @@ module Tag : sig val table_jstf : t val table_math : t - val table_kern : t - end = struct type t = wint @@ -89,8 +93,14 @@ end = struct let table_prep = !%% 0x70726570L let table_cff = !%% 0x43464620L + let table_cff2 = !%% 0x43464632L let table_vorg = !%% 0x564F5247L + let table_dsig = !%% 0x44534947L + let table_kern = !%% 0x6B65726EL + let table_vhea = !%% 0x76686561L + let table_vmtx = !%% 0x766D7478L + let table_base = !%% 0x42415345L let table_gdef = !%% 0x47444546L let table_gpos = !%% 0x47504F53L @@ -98,8 +108,6 @@ end = struct let table_jstf = !%% 0x4A535446L let table_math = !%% 0x4d415448L - let table_kern = !%% 0x6B65726EL - end type glyph_id = int @@ -610,6 +618,28 @@ module Name = struct [@@deriving show { with_path = false }] end +module Vhea = struct + type metrics = + | Version1_0 of { + ascent : int; + descent : int; + } + | Version1_1 of { + vert_typo_ascender : int; + vert_typo_descender : int; + vert_typo_line_gap : int; + } + [@@deriving show { with_path = false }] + + type t = { + metrics : metrics; + caret_slope_rise : int; + caret_slope_run : int; + caret_offset : int; + } + [@@deriving show { with_path = false }] +end + module Ttf = struct type contour_element = { on_curve : bool; diff --git a/test/testCaseHhea1.ml b/test/testCaseHhea1.ml index 305ef92..cddc1cd 100644 --- a/test/testCaseHhea1.ml +++ b/test/testCaseHhea1.ml @@ -30,6 +30,6 @@ let unmarshaled = advance_width_max = 2138; min_left_side_bearing = -539; min_right_side_bearing = -542; - xmax_extent = 2048; + x_max_extent = 2048; }; } From 1affc6d617b63c6e32aaef7f0f667c76e23137ad Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Sun, 4 Jun 2023 17:40:55 +0900 Subject: [PATCH 03/26] add a test for decoding 'vhea' tables --- test/otfedTest.ml | 11 +++++++++++ test/testCaseVhea1.ml | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 test/testCaseVhea1.ml diff --git a/test/otfedTest.ml b/test/otfedTest.ml index 0faeded..ebff084 100644 --- a/test/otfedTest.ml +++ b/test/otfedTest.ml @@ -24,6 +24,7 @@ module DecodeName = Otfed__DecodeName module EncodeName = Otfed__EncodeName module DecodePost = Otfed__DecodePost module EncodePost = Otfed__EncodePost +module DecodeVhea = Otfed__DecodeVhea module DecodeTtf = Otfed__DecodeTtf module EncodeTtf = Otfed__EncodeTtf module DecodeCff = Otfed__DecodeCff @@ -94,6 +95,13 @@ let e_os2_tests () = Alcotest.(check encoding) "e_os2" expected got +(** Tests for `DecodeVhea.d_vhea` *) +let d_vhea_tests () = + let got = DecodeVhea.d_vhea |> run_decoder TestCaseVhea1.marshaled in + let expected = Ok(TestCaseVhea1.unmarshaled) in + Alcotest.(check (decoding (of_pp Intermediate.Vhea.pp))) "d_vhea" expected got + + (** Tests for `DecodeTtfMaxp.d_maxp` *) let d_ttf_maxp_tests () = let got = DecodeTtfMaxp.d_maxp |> run_decoder TestCaseTtfMaxp1.marshaled in @@ -436,6 +444,9 @@ let () = ("EncodePost", [ test_case "e_post" `Quick e_post_tests; ]); + ("DecodeVhea", [ + test_case "d_vhea" `Quick d_vhea_tests; + ]); ("DecodeTtf", [ test_case "d_glyph" `Quick d_glyph_tests; test_case "d_loca" `Quick d_loca_tests; diff --git a/test/testCaseVhea1.ml b/test/testCaseVhea1.ml new file mode 100644 index 0000000..ac44adc --- /dev/null +++ b/test/testCaseVhea1.ml @@ -0,0 +1,33 @@ + +open! Otfed__Basic +module Value = Otfed__Value +module Intermediate = Otfed__Intermediate + + +let marshaled = + TestUtil.make_string_even [ + (* `ipaexm.ttf` (offset: 0x773054, length: 0x24) *) + 0x0001; 0x0000; 0x070a; 0x00f6; 0x0000; 0x0800; 0xffb6; 0xfebb; + 0x0945; 0x0000; 0x0001; 0x0000; 0x0000; 0x0000; 0x0000; 0x0000; + 0x0000; 0x0001; + ] + + +let number_of_long_ver_metrics = 1 + + +let unmarshaled = + Intermediate.Vhea.{ + value = Value.Vhea.{ + metrics = Version1_0 { ascent = 1802; descent = 246 }; + caret_slope_rise = 0; + caret_slope_run = 1; + caret_offset = 0; + }; + derived = { + advance_height_max = 2048; + min_top_side_bearing = -74; + min_bottom_side_bearing = -325; + y_max_extent = 2373; + }; + } From fd3fab8c035edcf8c42794b330cd2fc0813a939d Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Sun, 4 Jun 2023 17:44:50 +0900 Subject: [PATCH 04/26] update README about 'vhea' --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 03ed1fe..cdf8156 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ $ dune exec otfedcli input/Junicode.ttf subset 0,113,302 output/Junicode-subset. Optional DSIG---- kern--v (Format 0 only)o - vhea---- + vhea--vv (Version 1.0 only) vmtx---- Advanced From 694362b04f2ff334fce85df11862bd543aedba43 Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Sun, 4 Jun 2023 18:11:42 +0900 Subject: [PATCH 05/26] add '{Decode,Encode}.Vhea' --- README.md | 2 +- src/decodeVhea.ml | 25 +++++++++++++++------ src/encodeTable.ml | 1 + src/encodeVhea.ml | 54 ++++++++++++++++++++++++++++++++++++++++++++++ src/otfed.mli | 6 ++++++ test/otfedTest.ml | 12 +++++++++++ 6 files changed, 93 insertions(+), 7 deletions(-) create mode 100644 src/encodeVhea.ml diff --git a/README.md b/README.md index cdf8156..b6c9dc6 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ $ dune exec otfedcli input/Junicode.ttf subset 0,113,302 output/Junicode-subset. Optional DSIG---- kern--v (Format 0 only)o - vhea--vv (Version 1.0 only) + vheavv (Version 1.0 only)vv (Version 1.0 only) vmtx---- Advanced diff --git a/src/decodeVhea.ml b/src/decodeVhea.ml index 5a5c2d0..317982e 100644 --- a/src/decodeVhea.ml +++ b/src/decodeVhea.ml @@ -6,9 +6,17 @@ open DecodeBasic let fetch_num_of_long_ver_metrics (core : common_source_core) (table_directory : table_directory) = let open ResultMonad in - DecodeOperation.seek_required_table table_directory Value.Tag.table_vhea >>= fun (offset, _length) -> - let open DecodeOperation in - d_uint16 |> run core (offset + 34) + match DecodeOperation.seek_table table_directory Value.Tag.table_vhea with + | None -> + return None + + | Some((offset, _length)) -> + let res = + let open DecodeOperation in + d_uint16 |> run core (offset + 34) + in + res >>= fun num_of_long_ver_metrics -> + return @@ Some(num_of_long_ver_metrics) let d_vhea_metrics_1_0 : Value.Vhea.metrics decoder = @@ -65,8 +73,13 @@ let d_vhea : Intermediate.Vhea.t decoder = } -let get (src : source) : Intermediate.Vhea.t ok = +let get (src : source) : (Intermediate.Vhea.t option) ok = let open ResultMonad in let common = get_common_source src in - DecodeOperation.seek_required_table common.table_directory Value.Tag.table_vhea >>= fun (offset, _length) -> - DecodeOperation.run common.core offset d_vhea + match DecodeOperation.seek_table common.table_directory Value.Tag.table_vhea with + | None -> + return None + + | Some((offset, _length)) -> + DecodeOperation.run common.core offset d_vhea >>= fun ivhea -> + return @@ Some(ivhea) diff --git a/src/encodeTable.ml b/src/encodeTable.ml index 8ffa887..a0db529 100644 --- a/src/encodeTable.ml +++ b/src/encodeTable.ml @@ -6,5 +6,6 @@ module Post = EncodePost module Name = EncodeName module Hmtx = EncodeHmtx module Cmap = EncodeCmap +module Vhea = EncodeVhea module Ttf = EncodeTtf module Cff = EncodeCff diff --git a/src/encodeVhea.ml b/src/encodeVhea.ml new file mode 100644 index 0000000..3c06de3 --- /dev/null +++ b/src/encodeVhea.ml @@ -0,0 +1,54 @@ + +open Basic +open EncodeOperation.Open +open EncodeBasic + + +let e_vhea_table_version_and_metrics (metrics : Value.Vhea.metrics) : unit encoder = + let open EncodeOperation in + match metrics with + | Value.Vhea.Version1_0{ ascent; descent } -> + let line_gap = 0 in + e_uint32 !% 0x00010000 >>= fun () -> + e_int16 ascent >>= fun () -> + e_int16 descent >>= fun () -> + e_int16 line_gap >>= fun () -> + return () + + | Value.Vhea.Version1_1{ vert_typo_ascender; vert_typo_descender; vert_typo_line_gap } -> + e_uint32 !% 0x00011000 >>= fun () -> + e_int16 vert_typo_ascender >>= fun () -> + e_int16 vert_typo_descender >>= fun () -> + e_int16 vert_typo_line_gap >>= fun () -> + return () + + +let e_vhea ~(number_of_long_ver_metrics : int) (ivhea : Intermediate.Vhea.t) : unit encoder = + let open EncodeOperation in + let d = ivhea.Intermediate.Vhea.derived in + let v = ivhea.Intermediate.Vhea.value in + let metric_data_format = 0 in + e_vhea_table_version_and_metrics v.metrics >>= fun () -> + e_uint16 d.advance_height_max >>= fun () -> + e_int16 d.min_top_side_bearing >>= fun () -> + e_int16 d.min_bottom_side_bearing >>= fun () -> + e_int16 d.y_max_extent >>= fun () -> + e_int16 v.caret_slope_rise >>= fun () -> + e_int16 v.caret_slope_run >>= fun () -> + e_int16 v.caret_offset >>= fun () -> + e_int16 0 >>= fun () -> + e_int16 0 >>= fun () -> + e_int16 0 >>= fun () -> + e_int16 0 >>= fun () -> + e_int16 metric_data_format >>= fun () -> + e_uint16 number_of_long_ver_metrics >>= fun () -> + return () + + +let make ~(number_of_long_ver_metrics : int) (ivhea : Intermediate.Vhea.t) : table ok = + let open ResultMonad in + e_vhea ~number_of_long_ver_metrics ivhea |> EncodeOperation.run >>= fun (contents, ()) -> + return { + tag = Value.Tag.table_vhea; + contents; + } diff --git a/src/otfed.mli b/src/otfed.mli index 7b04947..cec8af5 100644 --- a/src/otfed.mli +++ b/src/otfed.mli @@ -1317,6 +1317,12 @@ module Decode : sig val fold : ('a -> kern_info -> bool * 'a) -> ('a -> int -> int -> int -> 'a) -> 'a -> t -> 'a ok end + (** Defines decoding operations for [vhea] tables. *) + module Vhea : sig + (** Gets the [vhea] table of a font if it exists. *) + val get : source -> (Intermediate.Vhea.t option) ok + end + (** Contains decoding operations for [MATH] tables. *) module Math : sig (** Gets the [MATH] table of a font if it exists. *) diff --git a/test/otfedTest.ml b/test/otfedTest.ml index ebff084..fa40158 100644 --- a/test/otfedTest.ml +++ b/test/otfedTest.ml @@ -25,6 +25,7 @@ module EncodeName = Otfed__EncodeName module DecodePost = Otfed__DecodePost module EncodePost = Otfed__EncodePost module DecodeVhea = Otfed__DecodeVhea +module EncodeVhea = Otfed__EncodeVhea module DecodeTtf = Otfed__DecodeTtf module EncodeTtf = Otfed__EncodeTtf module DecodeCff = Otfed__DecodeCff @@ -102,6 +103,14 @@ let d_vhea_tests () = Alcotest.(check (decoding (of_pp Intermediate.Vhea.pp))) "d_vhea" expected got +(** Tests for `EncodeVhea.e_vhea` *) +let e_vhea_tests () = + let number_of_long_ver_metrics = TestCaseVhea1.number_of_long_ver_metrics in + let got = EncodeVhea.e_vhea ~number_of_long_ver_metrics TestCaseVhea1.unmarshaled |> run_encoder in + let expected = Ok(TestCaseVhea1.marshaled) in + Alcotest.(check encoding) "e_vhea" expected got + + (** Tests for `DecodeTtfMaxp.d_maxp` *) let d_ttf_maxp_tests () = let got = DecodeTtfMaxp.d_maxp |> run_decoder TestCaseTtfMaxp1.marshaled in @@ -447,6 +456,9 @@ let () = ("DecodeVhea", [ test_case "d_vhea" `Quick d_vhea_tests; ]); + ("EncodeVhea", [ + test_case "e_vhea" `Quick e_vhea_tests; + ]); ("DecodeTtf", [ test_case "d_glyph" `Quick d_glyph_tests; test_case "d_loca" `Quick d_loca_tests; From 1caded1f15af61cd1d4691ca9bf40f5143d17469 Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Sun, 4 Jun 2023 19:26:31 +0900 Subject: [PATCH 06/26] add 'DecodeVmtx' --- src/decodeError.ml | 1 + src/decodeTable.ml | 1 + src/decodeVmtx.ml | 59 ++++++++++++++++++++++++++++++++++++++++++++++ src/otfed.mli | 22 +++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 src/decodeVmtx.ml diff --git a/src/decodeError.ml b/src/decodeError.ml index 93a927d..ad0478a 100644 --- a/src/decodeError.ml +++ b/src/decodeError.ml @@ -20,6 +20,7 @@ type t = Format.fprintf ppf "InvalidCodePointRange(%a, %a)" pp_uchar uch1 pp_uchar uch2 )] | InvalidLocFormat of int + | NoVerticalHeader | InvalidCompositeFormat of int | InvalidOffsize of int | InvalidFirstOffsetInIndex of wint diff --git a/src/decodeTable.ml b/src/decodeTable.ml index 689ca44..986a363 100644 --- a/src/decodeTable.ml +++ b/src/decodeTable.ml @@ -10,6 +10,7 @@ module Gsub = DecodeGsub module Gpos = DecodeGpos module Kern = DecodeKern module Vhea = DecodeVhea +module Vmtx = DecodeVmtx module Math = DecodeMath module Ttf = DecodeTtf module Cff = DecodeCff diff --git a/src/decodeVmtx.ml b/src/decodeVmtx.ml new file mode 100644 index 0000000..edc4501 --- /dev/null +++ b/src/decodeVmtx.ml @@ -0,0 +1,59 @@ + +open Basic +open Value +open DecodeBasic + + +type info = { + num_glyphs : int; + num_long_ver_metrics : int; +} + +include GeneralTable(struct type t = info end) + + +let get (src : source) : (t option) ok = + let open ResultMonad in + let common = get_common_source src in + match DecodeOperation.seek_table common.table_directory Tag.table_hmtx with + | None -> + return None + + | Some((offset, length)) -> + let num_glyphs = common.num_glyphs in + DecodeVhea.fetch_num_of_long_ver_metrics common.core common.table_directory >>= function + | None -> + err Error.NoVerticalHeader + + | Some(num_long_ver_metrics) -> + let info = { num_glyphs; num_long_ver_metrics } in + return @@ Some(make_scheme common.core offset length info) + + +let d_long_ver_metric = + let open DecodeOperation in + d_uint16 >>= fun advanceHeight -> + d_int16 >>= fun topSideBearing -> + return @@ (advanceHeight, topSideBearing) + + +let d_top_side_bearing = + let open DecodeOperation in + d_int16 + + +let access (vmtx : t) (gid : glyph_id) : ((design_units * design_units) option) ok = + let open ResultMonad in + let { num_glyphs; num_long_ver_metrics } = get_info vmtx in + if gid < 0 || num_glyphs <= gid then + return None + else if gid < num_long_ver_metrics then + let reloffset = 4 * gid in + d_long_ver_metric |> DecodeOperation.run vmtx.core (vmtx.offset + reloffset) >>= fun (ah, tsb) -> + return (Some((ah, tsb))) + else + let reloffset1 = 4 * (num_long_ver_metrics - 1) in + d_long_ver_metric |> DecodeOperation.run vmtx.core (vmtx.offset + reloffset1) >>= fun (ah_last, _) -> + let reloffset2 = 4 * num_long_ver_metrics + 2 * (gid - num_long_ver_metrics) in + d_top_side_bearing |> DecodeOperation.run vmtx.core (vmtx.offset + reloffset2) >>= fun tsb -> + return (Some((ah_last, tsb))) diff --git a/src/otfed.mli b/src/otfed.mli index cec8af5..0735327 100644 --- a/src/otfed.mli +++ b/src/otfed.mli @@ -927,6 +927,7 @@ module Decode : sig | InvalidCodePoint of int | InvalidCodePointRange of Uchar.t * Uchar.t | InvalidLocFormat of int + | NoVerticalHeader | InvalidCompositeFormat of int | InvalidOffsize of int | InvalidFirstOffsetInIndex of wint @@ -1323,6 +1324,22 @@ module Decode : sig val get : source -> (Intermediate.Vhea.t option) ok end + (** Handles intermediate representation of [vmtx] tables for decoding. + Since the operations provided by this module use only sequential sources and + do NOT allocate so much additional memory for the representation, + they are likely to be efficient in time and space. *) + module Vmtx : sig + (** The type for representing [vmtx] tables. *) + type t + + (** Gets the [vmtx] table of a font if it exists. *) + val get : source -> (t option) ok + + (** [access vmtx gid] returns [Some((ah, tsb))] (if [gid] is present in the font) + where [ah] is the advance height of [gid], and [tsb] is the top side bearing of [gid]. *) + val access : t -> Value.glyph_id -> ((Value.design_units * Value.design_units) option) ok + end + (** Contains decoding operations for [MATH] tables. *) module Math : sig (** Gets the [MATH] table of a font if it exists. *) @@ -1487,6 +1504,11 @@ module Encode : sig val make : Value.Cmap.t -> table ok end + (** Defines encoding operations for [vhea] tables. *) + module Vhea : sig + val make : number_of_long_ver_metrics:int -> Intermediate.Vhea.t -> table ok + end + (** Defines encoding operations for tables specific to TrueType-based fonts. *) module Ttf : sig (** Defines encoding operations for [maxp] tables of TrueType-based fonts. *) From a9d49dba26aeb73b5383a8a9c8824dc60f4219ad Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Sun, 4 Jun 2023 19:53:13 +0900 Subject: [PATCH 07/26] add a test for 'DecodeVmtx' --- test/otfedTest.ml | 22 ++++++++++++ test/testCaseVmtx1.ml | 81 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 test/testCaseVmtx1.ml diff --git a/test/otfedTest.ml b/test/otfedTest.ml index fa40158..c7f61e3 100644 --- a/test/otfedTest.ml +++ b/test/otfedTest.ml @@ -26,6 +26,7 @@ module DecodePost = Otfed__DecodePost module EncodePost = Otfed__EncodePost module DecodeVhea = Otfed__DecodeVhea module EncodeVhea = Otfed__EncodeVhea +module DecodeVmtx = Otfed__DecodeVmtx module DecodeTtf = Otfed__DecodeTtf module EncodeTtf = Otfed__EncodeTtf module DecodeCff = Otfed__DecodeCff @@ -110,6 +111,24 @@ let e_vhea_tests () = let expected = Ok(TestCaseVhea1.marshaled) in Alcotest.(check encoding) "e_vhea" expected got +(** Tests for `DecodeVmtx.access` *) +let access_vmtx_tests () = + let vmtx = + let data = TestCaseVmtx1.marshaled in + let length = String.length data in + let core = DecodeBasic.{ data = data; max = length; } in + DecodeVmtx.make_scheme core 0 length + { + num_glyphs = TestCaseVmtx1.number_of_glyphs; + num_long_ver_metrics = TestCaseVmtx1.number_of_long_ver_metrics; + } + in + TestCaseVmtx1.test_cases |> List.iteri (fun index (gid, pair_opt) -> + let title = Printf.sprintf "access_vmtx (%d)" index in + let got = DecodeVmtx.access vmtx gid in + let expected = Ok(pair_opt) in + Alcotest.(check (decoding (option (pair int int)))) title expected got + ) (** Tests for `DecodeTtfMaxp.d_maxp` *) let d_ttf_maxp_tests () = @@ -459,6 +478,9 @@ let () = ("EncodeVhea", [ test_case "e_vhea" `Quick e_vhea_tests; ]); + ("DecodeVmtx", [ + test_case "access_vmtx" `Quick access_vmtx_tests; + ]); ("DecodeTtf", [ test_case "d_glyph" `Quick d_glyph_tests; test_case "d_loca" `Quick d_loca_tests; diff --git a/test/testCaseVmtx1.ml b/test/testCaseVmtx1.ml new file mode 100644 index 0000000..07f1b10 --- /dev/null +++ b/test/testCaseVmtx1.ml @@ -0,0 +1,81 @@ + +let number_of_glyphs = 500 + +let number_of_long_ver_metrics = 1 + +let marshaled = + TestUtil.make_string_even [ + (* `ipaexm.ttf` (offset: 0x773078, length: 1002) (truncated) *) + 0x0800; 0x0029; 0x070a; 0x070a; 0x070a; 0x0144; 0x0127; 0x013b; + 0x00fe; 0x0129; 0x013b; 0x0127; 0x00e5; 0x00e5; 0x0152; 0x026d; + 0x0621; 0x047b; 0x0623; 0x0116; 0x011f; 0x0116; 0x011f; 0x0121; + 0x013b; 0x0152; 0x011f; 0x0152; 0x0114; 0x011f; 0x030e; 0x030e; + 0x025a; 0x0393; 0x025a; 0x0146; 0x0133; 0x013b; 0x0152; 0x011f; + 0x0152; 0x0152; 0x0152; 0x011f; 0x0152; 0x0152; 0x0152; 0x0152; + 0x0152; 0x0152; 0x0152; 0x011f; 0x0152; 0x011f; 0x0152; 0x012f; + 0x0152; 0x0152; 0x0152; 0x0152; 0x0152; 0x0152; 0x0152; 0x00ef; + 0x0152; 0x00ef; 0x021a; 0x079e; 0x0129; 0x02f7; 0x0133; 0x02f7; + 0x0133; 0x02f7; 0x012f; 0x02a4; 0x0133; 0x014c; 0x014c; 0x0133; + 0x0133; 0x02f9; 0x02f9; 0x02f3; 0x02f9; 0x02fc; 0x02f9; 0x02f7; + 0x0203; 0x02f9; 0x031a; 0x031a; 0x031a; 0x031a; 0x031a; 0x00ef; + 0x0129; 0x00ef; 0x0173; 0x0070; 0x0116; 0x004e; 0x0129; 0x0171; + 0x0144; 0x0102; 0x00e5; 0x02be; 0x004e; 0x0308; 0x0485; 0x0442; + 0x03a5; 0x0070; 0x0308; 0x0148; 0x0129; 0x016b; 0x01b4; 0x0162; + 0x016d; 0x06f6; 0x0127; 0x014a; 0x0154; 0x012f; 0x0154; 0x011f; + 0x0154; 0x012b; 0x02f9; 0x0135; 0x02ed; 0x02f9; 0x012f; 0x044e; + 0x00d9; 0x03e9; 0x00d9; 0x020b; 0x0142; 0x014c; 0x013b; 0x012b; + 0x012b; 0x012b; 0xffb8; 0xffb8; 0xffd1; 0x0002; 0x0001; 0xffb8; + 0x011f; 0xffb8; 0xffb8; 0xffd1; 0xffff; 0xffb8; 0xffb8; 0xffd1; + 0xffff; 0x0154; 0x0002; 0xffb8; 0xffb8; 0xffd1; 0x0002; 0xffff; + 0x02d3; 0xffb8; 0xffb8; 0xffd1; 0x0001; 0xffb8; 0x0154; 0x0129; + 0x0129; 0x016b; 0x0173; 0x016f; 0x0131; 0x02f9; 0x0129; 0x0129; + 0x016b; 0x016f; 0x0129; 0x012b; 0x016b; 0x016f; 0x012d; 0x0173; + 0x0129; 0x0129; 0x016b; 0x0173; 0x016d; 0x02ac; 0x0129; 0x0129; + 0x016b; 0x016d; 0x0129; 0x0133; 0x016f; 0xffd9; 0xffd9; 0x01b4; + 0x014a; 0x014a; 0x070a; 0x009d; 0x0070; 0x00a3; 0x0068; 0x006a; + 0x00e5; 0x0070; 0x0062; 0x0062; 0x023d; 0x01cf; 0x05b8; 0x03d1; + 0x05e5; 0x006c; 0x00e5; 0x00d8; 0x00e5; 0x00e5; 0x010a; 0x010a; + 0x00e5; 0x010a; 0x00e5; 0x00e5; 0x02d9; 0x02d9; 0x017e; 0x0323; + 0x017e; 0x009d; 0x00d1; 0x00e5; 0x010a; 0x00e5; 0x010a; 0x010a; + 0x010a; 0x00e5; 0x010a; 0x010a; 0x010a; 0x010a; 0x010a; 0x010a; + 0x010a; 0x00e5; 0x010a; 0x00e5; 0x010a; 0x00e5; 0x010a; 0x010a; + 0x010a; 0x010a; 0x010a; 0x010a; 0x010a; 0x006e; 0x010a; 0x006e; + 0x00f1; 0x07cd; 0x02b4; 0x00db; 0x02b4; 0x00db; 0x02b4; 0x00e5; + 0x02b4; 0x00db; 0x00e5; 0x00e5; 0x00dd; 0x00da; 0x02aa; 0x02aa; + 0x02b4; 0x02aa; 0x02b4; 0x02aa; 0x02b4; 0x01a8; 0x02b0; 0x02d9; + 0x02d9; 0x02d9; 0x02d9; 0x02d9; 0x004e; 0x0000; 0x004e; 0x013f; + 0x05ac; 0x006e; 0x01cf; 0x05c0; 0x036c; 0x014e; 0x0229; 0x01ed; + 0x01b0; 0x02d6; 0x01d4; 0x01e7; 0x02c8; 0x0282; 0x0245; 0x037b; + 0x012f; 0x00e7; 0x00a6; 0x01e7; 0x00a5; 0x00af; 0x00ca; 0x00f1; + 0x00cf; 0x01c8; 0x00b2; 0x0114; 0x0162; 0x00e9; 0x0114; 0x00bc; + 0x00ba; 0x0142; 0x011f; 0x00d3; 0x00ab; 0x0229; 0x014e; 0x0083; + 0x0140; 0x01cb; 0x011e; 0x017b; 0x01f6; 0x00ba; 0x01d7; 0x0150; + 0x0112; 0x00ed; 0x0152; 0x00c8; 0x01dd; 0x018f; 0x0117; 0x00cd; + 0x0102; 0x011a; 0x018f; 0x0152; 0x014e; 0x0074; 0x005e; 0x00f1; + 0x0314; 0x0314; 0x0019; 0x018d; 0x018d; 0x018d; 0x00e5; 0x02b4; + 0x00e5; 0x0025; 0x01a0; 0x018d; 0x018d; 0x009d; 0x009d; 0x0019; + 0x0019; 0x018d; 0x018d; 0x018d; 0x018d; 0x018d; 0x018d; 0x018d; + 0x018d; 0x018d; 0x018d; 0x018d; 0x018d; 0x018d; 0x0102; 0x00a0; + 0x070a; 0x05c0; 0x05ac; 0x05b8; 0x05e5; 0x036c; 0x02d9; 0x02d9; + 0x009d; 0x009d; 0x0048; 0x004e; 0x00f1; 0x00f1; 0x00f2; 0x00f1; + 0x0000; 0x07cd; 0x0252; 0x0168; 0x01b0; 0x0177; 0x0204; 0x006e; + 0x0148; 0x010a; 0x00c8; 0x037d; 0x03d1; 0x03d1; 0x006c; 0x006c; + 0x0320; 0x0000; 0x0000; 0x0381; 0x037b; 0x004e; 0x0070; 0x004e; + 0x0070; 0x0062; 0x0062; 0x004e; 0x004e; 0x006e; 0x006e; 0x004e; + 0x004e; 0x0062; 0x0062; 0x0062; 0x0062; 0x006e; 0x01cf; 0x006e; + 0x01cf; 0x0062; 0x0062; 0x0142; 0x03d1; 0x0116; 0x019d; 0x01f1; + 0x0306; 0x0137; 0x007b; 0x007b; 0x00ba; 0x00ba; 0x026c; 0x017d; + 0x00e5; 0x006a; 0x005c; 0x006e; 0x006e; 0x005c; 0x010a; 0x0068; + 0x0101; 0x00e5; 0x006a; 0x00a3; 0x00e5; 0x0160; 0x00e1; 0x0068; + 0x0051; 0x0051; 0x00a8; 0x00a8; 0x00a0; 0x0062; 0x0062; 0x0104; + 0x0104; 0x00c3; 0x00c3; 0x0104; 0x0104; + ] + + +let test_cases = [ + (-1, None); + (0, Some((2048, 41))); + (400, Some((2048, 1472))); + (499, Some((2048, 260))); + (500, None); +] From dca490f61a2cde9ea92b4f28ca6b18dda1f339b9 Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Sun, 4 Jun 2023 20:14:34 +0900 Subject: [PATCH 08/26] add 'EncodeVmtx' and its test (which fails currently) --- src/encodeTable.ml | 1 + src/encodeVmtx.ml | 36 ++++++++++++++++++++++++++++++++++++ test/otfedTest.ml | 18 ++++++++++++++++++ test/testCaseVmtx1.ml | 6 ++++++ 4 files changed, 61 insertions(+) create mode 100644 src/encodeVmtx.ml diff --git a/src/encodeTable.ml b/src/encodeTable.ml index a0db529..c794cab 100644 --- a/src/encodeTable.ml +++ b/src/encodeTable.ml @@ -7,5 +7,6 @@ module Name = EncodeName module Hmtx = EncodeHmtx module Cmap = EncodeCmap module Vhea = EncodeVhea +module Vmtx = EncodeVmtx module Ttf = EncodeTtf module Cff = EncodeCff diff --git a/src/encodeVmtx.ml b/src/encodeVmtx.ml new file mode 100644 index 0000000..2c2a5f8 --- /dev/null +++ b/src/encodeVmtx.ml @@ -0,0 +1,36 @@ + +open Basic +open Value +open EncodeBasic + + +let e_long_ver_metric (advanceHeight, topSideBearing) = + let open EncodeOperation in + e_uint16 advanceHeight >>= fun () -> + e_int16 topSideBearing >>= fun () -> + return () + + +let e_top_side_bearing topSideBearing = + let open EncodeOperation in + e_int16 topSideBearing + + +let make_exact (long_ver_metrics : (design_units * design_units) list) (top_side_bearings : design_units list) : table ok = + let enc = + let open EncodeOperation in + e_list e_long_ver_metric long_ver_metrics >>= fun () -> + e_list e_top_side_bearing top_side_bearings >>= fun () -> + return () + in + let open ResultMonad in + enc |> EncodeOperation.run >>= fun (contents, ()) -> + return { + tag = Value.Tag.table_vmtx; + contents; + } + + +let make (metrics : (design_units * design_units) list) : table ok = + make_exact metrics [] + (* TODO: optimize the resulting size by using leftSideBearing-only entries *) diff --git a/test/otfedTest.ml b/test/otfedTest.ml index c7f61e3..fcf5356 100644 --- a/test/otfedTest.ml +++ b/test/otfedTest.ml @@ -27,6 +27,7 @@ module EncodePost = Otfed__EncodePost module DecodeVhea = Otfed__DecodeVhea module EncodeVhea = Otfed__EncodeVhea module DecodeVmtx = Otfed__DecodeVmtx +module EncodeVmtx = Otfed__EncodeVmtx module DecodeTtf = Otfed__DecodeTtf module EncodeTtf = Otfed__EncodeTtf module DecodeCff = Otfed__DecodeCff @@ -111,6 +112,7 @@ let e_vhea_tests () = let expected = Ok(TestCaseVhea1.marshaled) in Alcotest.(check encoding) "e_vhea" expected got + (** Tests for `DecodeVmtx.access` *) let access_vmtx_tests () = let vmtx = @@ -130,6 +132,19 @@ let access_vmtx_tests () = Alcotest.(check (decoding (option (pair int int)))) title expected got ) + +(** Tests for `Encodevmtx.make_exact` *) +let make_vmtx_tests () = + let got = + EncodeVmtx.make_exact + TestCaseVmtx1.unmarshaled_long_ver_metrics + TestCaseVmtx1.unmarshaled_top_side_bearings + |> Result.map Encode.get_contents + in + let expected = Ok(TestCaseVmtx1.marshaled) in + Alcotest.(check encoding) "make_vmtx" expected got + + (** Tests for `DecodeTtfMaxp.d_maxp` *) let d_ttf_maxp_tests () = let got = DecodeTtfMaxp.d_maxp |> run_decoder TestCaseTtfMaxp1.marshaled in @@ -481,6 +496,9 @@ let () = ("DecodeVmtx", [ test_case "access_vmtx" `Quick access_vmtx_tests; ]); + ("EncodeVmtx", [ + test_case "make_vmtx" `Quick make_vmtx_tests; + ]); ("DecodeTtf", [ test_case "d_glyph" `Quick d_glyph_tests; test_case "d_loca" `Quick d_loca_tests; diff --git a/test/testCaseVmtx1.ml b/test/testCaseVmtx1.ml index 07f1b10..3250181 100644 --- a/test/testCaseVmtx1.ml +++ b/test/testCaseVmtx1.ml @@ -72,6 +72,12 @@ let marshaled = ] +let unmarshaled_long_ver_metrics = [] + + +let unmarshaled_top_side_bearings = [] + + let test_cases = [ (-1, None); (0, Some((2048, 41))); From 0905bfb683ad71b573d4c69fafb5abdcfc42ed74 Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Sun, 4 Jun 2023 20:41:42 +0900 Subject: [PATCH 09/26] develop a test case for testing 'EncodeVmtx' --- src/decodeVmtx.ml | 16 ++++++++ test/otfedTest.ml | 16 ++++++++ test/testCaseVmtx1.ml | 86 +++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 115 insertions(+), 3 deletions(-) diff --git a/src/decodeVmtx.ml b/src/decodeVmtx.ml index edc4501..c38d46a 100644 --- a/src/decodeVmtx.ml +++ b/src/decodeVmtx.ml @@ -57,3 +57,19 @@ let access (vmtx : t) (gid : glyph_id) : ((design_units * design_units) option) let reloffset2 = 4 * num_long_ver_metrics + 2 * (gid - num_long_ver_metrics) in d_top_side_bearing |> DecodeOperation.run vmtx.core (vmtx.offset + reloffset2) >>= fun tsb -> return (Some((ah_last, tsb))) + + +let dump (vmtx : t) (gid_max : glyph_id) = + let open ResultMonad in + let rec iter acc gid = + if gid >= gid_max then + return @@ Alist.to_list acc + else + access vmtx gid >>= function + | None -> + assert false + + | Some((_, tsb)) -> + iter (Alist.extend acc tsb) (gid + 1) + in + iter Alist.empty 0 diff --git a/test/otfedTest.ml b/test/otfedTest.ml index fcf5356..f918f1e 100644 --- a/test/otfedTest.ml +++ b/test/otfedTest.ml @@ -513,4 +513,20 @@ let () = ("DecodeMath", [ test_case "math_decoder" `Quick math_decoder_tests; ]); + ("temp", [ + test_case "temp" `Quick (fun () -> + let vmtx = + let data = TestCaseVmtx1.marshaled in + let length = String.length data in + let core = DecodeBasic.{ data = data; max = length; } in + DecodeVmtx.make_scheme core 0 length + { + num_glyphs = TestCaseVmtx1.number_of_glyphs; + num_long_ver_metrics = TestCaseVmtx1.number_of_long_ver_metrics; + } + in + let got = DecodeVmtx.dump vmtx 500 in + Alcotest.(check (decoding (list int))) "temp" (Ok []) got + ) + ]); ] diff --git a/test/testCaseVmtx1.ml b/test/testCaseVmtx1.ml index 3250181..2249942 100644 --- a/test/testCaseVmtx1.ml +++ b/test/testCaseVmtx1.ml @@ -72,14 +72,94 @@ let marshaled = ] -let unmarshaled_long_ver_metrics = [] +let unmarshaled_long_ver_metrics = [ + (* 0: *) + (2048, 41); +] + + +let unmarshaled_top_side_bearings = [ + (* 1-79: *) + 1802; 1802; 1802; 324; 295; 315; 254; + 297; 315; 295; 229; 229; 338; 621; 1569; + 1147; 1571; 278; 287; 278; 287; 289; 315; + 338; 287; 338; 276; 287; 782; 782; 602; + 915; 602; 326; 307; 315; 338; 287; 338; + 338; 338; 287; 338; 338; 338; 338; 338; + 338; 338; 287; 338; 287; 338; 303; 338; + 338; 338; 338; 338; 338; 338; 239; 338; + 239; 538; 1950; 297; 759; 307; 759; 307; + 759; 303; 676; 307; 332; 332; 307; 307; + + (* 80-159 *) + 761; 761; 755; 761; 764; 761; 759; 515; + 761; 794; 794; 794; 794; 794; 239; 297; + 239; 371; 112; 278; 78; 297; 369; 324; + 258; 229; 702; 78; 776; 1157; 1090; 933; + 112; 776; 328; 297; 363; 436; 354; 365; + 1782; 295; 330; 340; 303; 340; 287; 340; + 299; 761; 309; 749; 761; 303; 1102; 217; + 1001; 217; 523; 322; 332; 315; 299; 299; + 299; -72; -72; -47; 2; 1; -72; 287; + -72; -72; -47; -1; -72; -72; -47; -1; + (* 160-239: *) + 340; 2; -72; -72; -47; 2; -1; 723; + -72; -72; -47; 1; -72; 340; 297; 297; + 363; 371; 367; 305; 761; 297; 297; 363; + 367; 297; 299; 363; 367; 301; 371; 297; + 297; 363; 371; 365; 684; 297; 297; 363; + 365; 297; 307; 367; -39; -39; 436; 330; + 330; 1802; 157; 112; 163; 104; 106; 229; + 112; 98; 98; 573; 463; 1464; 977; 1509; + 108; 229; 216; 229; 229; 266; 266; 229; + 266; 229; 229; 729; 729; 382; 803; 382; -let unmarshaled_top_side_bearings = [] + (* 240-319: *) + 157; 209; 229; 266; 229; 266; 266; 266; + 229; 266; 266; 266; 266; 266; 266; 266; + 229; 266; 229; 266; 229; 266; 266; 266; + 266; 266; 266; 266; 110; 266; 110; 241; + 1997; 692; 219; 692; 219; 692; 229; 692; + 219; 229; 229; 221; 218; 682; 682; 692; + 682; 692; 682; 692; 424; 688; 729; 729; + 729; 729; 729; 78; 0; 78; 319; 1452; + 110; 463; 1472; 876; 334; 553; 493; 432; + 726; 468; 487; 712; 642; 581; 891; 303; + + (* 320-399: *) + 231; 166; 487; 165; 175; 202; 241; 207; + 456; 178; 276; 354; 233; 276; 188; 186; + 322; 287; 211; 171; 553; 334; 131; 320; + 459; 286; 379; 502; 186; 471; 336; 274; + 237; 338; 200; 477; 399; 279; 205; 258; + 282; 399; 338; 334; 116; 94; 241; 788; + 788; 25; 397; 397; 397; 229; 692; 229; + 37; 416; 397; 397; 157; 157; 25; 25; + 397; 397; 397; 397; 397; 397; 397; 397; + 397; 397; 397; 397; 397; 258; 160; 1802; + + (* 400-479: *) + 1472; 1452; 1464; 1509; 876; 729; 729; 157; + 157; 72; 78; 241; 241; 242; 241; 0; + 1997; 594; 360; 432; 375; 516; 110; 328; + 266; 200; 893; 977; 977; 108; 108; 800; + 0; 0; 897; 891; 78; 112; 78; 112; + 98; 98; 78; 78; 110; 110; 78; 78; + 98; 98; 98; 98; 110; 463; 110; 463; + 98; 98; 322; 977; 278; 413; 497; 774; + 311; 123; 123; 186; 186; 620; 381; 229; + 106; 92; 110; 110; 92; 266; 104; 257; + + (* 480-499: *) + 229; 106; 163; 229; 352; 225; 104; 81; + 81; 168; 168; 160; 98; 98; 260; 260; + 195; 195; 260; 260; +] let test_cases = [ - (-1, None); + (-1, None) ; (0, Some((2048, 41))); (400, Some((2048, 1472))); (499, Some((2048, 260))); From 65d603f56d1f7885401a280f7a99bcb762192d02 Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Sun, 4 Jun 2023 20:47:12 +0900 Subject: [PATCH 10/26] update README and interface, and remove unnecessary temporary functions --- README.md | 2 +- src/decodeVmtx.ml | 16 ---------------- src/otfed.mli | 6 ++++++ test/otfedTest.ml | 16 ---------------- 4 files changed, 7 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index b6c9dc6..157c3f2 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ $ dune exec otfedcli input/Junicode.ttf subset 0,113,302 output/Junicode-subset. DSIG---- kern--v (Format 0 only)o vheavv (Version 1.0 only)vv (Version 1.0 only) - vmtx---- + vmtxvvvv Advanced BASE---- diff --git a/src/decodeVmtx.ml b/src/decodeVmtx.ml index c38d46a..edc4501 100644 --- a/src/decodeVmtx.ml +++ b/src/decodeVmtx.ml @@ -57,19 +57,3 @@ let access (vmtx : t) (gid : glyph_id) : ((design_units * design_units) option) let reloffset2 = 4 * num_long_ver_metrics + 2 * (gid - num_long_ver_metrics) in d_top_side_bearing |> DecodeOperation.run vmtx.core (vmtx.offset + reloffset2) >>= fun tsb -> return (Some((ah_last, tsb))) - - -let dump (vmtx : t) (gid_max : glyph_id) = - let open ResultMonad in - let rec iter acc gid = - if gid >= gid_max then - return @@ Alist.to_list acc - else - access vmtx gid >>= function - | None -> - assert false - - | Some((_, tsb)) -> - iter (Alist.extend acc tsb) (gid + 1) - in - iter Alist.empty 0 diff --git a/src/otfed.mli b/src/otfed.mli index 0735327..e3ab0bb 100644 --- a/src/otfed.mli +++ b/src/otfed.mli @@ -1509,6 +1509,12 @@ module Encode : sig val make : number_of_long_ver_metrics:int -> Intermediate.Vhea.t -> table ok end + (** Defines encoding operations for [vmtx] tables. *) + module Vmtx : sig + val make_exact : (Value.design_units * Value.design_units) list -> Value.design_units list -> table ok + val make : (Value.design_units * Value.design_units) list -> table ok + end + (** Defines encoding operations for tables specific to TrueType-based fonts. *) module Ttf : sig (** Defines encoding operations for [maxp] tables of TrueType-based fonts. *) diff --git a/test/otfedTest.ml b/test/otfedTest.ml index f918f1e..fcf5356 100644 --- a/test/otfedTest.ml +++ b/test/otfedTest.ml @@ -513,20 +513,4 @@ let () = ("DecodeMath", [ test_case "math_decoder" `Quick math_decoder_tests; ]); - ("temp", [ - test_case "temp" `Quick (fun () -> - let vmtx = - let data = TestCaseVmtx1.marshaled in - let length = String.length data in - let core = DecodeBasic.{ data = data; max = length; } in - DecodeVmtx.make_scheme core 0 length - { - num_glyphs = TestCaseVmtx1.number_of_glyphs; - num_long_ver_metrics = TestCaseVmtx1.number_of_long_ver_metrics; - } - in - let got = DecodeVmtx.dump vmtx 500 in - Alcotest.(check (decoding (list int))) "temp" (Ok []) got - ) - ]); ] From 8358da80fe0023713c3e1c824a7bdd4231d23eef Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Sun, 4 Jun 2023 21:12:13 +0900 Subject: [PATCH 11/26] update 'otfedcli' and fix 'DecodeVmtx.get' --- bin/main.ml | 58 +++++++++++++++++++++++++++++++++++++++++++++++ src/decodeVmtx.ml | 3 ++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/bin/main.ml b/bin/main.ml index 2672663..0aa3981 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -17,7 +17,9 @@ type config = { post : bool; name : bool; cff_top : bool; + vhea : bool; hmtx : V.glyph_id Alist.t; + vmtx : V.glyph_id Alist.t; glyf : (V.glyph_id * string) Alist.t; cff : (V.glyph_id * string) Alist.t; cff_lex : V.glyph_id Alist.t; @@ -84,6 +86,29 @@ let print_hmtx (source : D.source) (gid : V.glyph_id) = res |> inj +let print_vmtx (source : D.source) (gid : V.glyph_id) = + Format.printf "vmtx (gid: %d):@," gid; + let res = + let open ResultMonad in + D.Vmtx.get source >>= function + | None -> + Format.printf "vmtx table not found@,"; + return () + + | Some(ivmtx) -> + D.Vmtx.access ivmtx gid >>= function + | None -> + Format.printf "- none@,"; + return () + + | Some((ah, tsb)) -> + Format.printf "- ah: %d, tsb: %d@," + ah tsb; + return () + in + res |> inj + + let print_head (source : D.source) = let res = let open ResultMonad in @@ -199,6 +224,23 @@ let print_name (source : D.source) = res |> inj +let print_vhea (source : D.source) = + let res = + let open ResultMonad in + D.Vhea.get source >>= function + | None -> + Format.printf "vhea table not found@,"; + return () + + | Some(vhea) -> + Format.printf "vhea:@,"; + Format.printf "%a@," I.Vhea.pp vhea; + return () + in + res |> inj + + + let write_file path ~data = let open ResultMonad in try @@ -622,10 +664,16 @@ let parse_args () = | "cff_top" -> aux n { acc with cff_top = true } (i + 1) + | "vhea" -> aux n { acc with vhea = true } (i + 1) + | "hmtx" -> let gid = int_of_string (Sys.argv.(i + 1)) in aux n { acc with hmtx = Alist.extend acc.hmtx gid } (i + 2) + | "vmtx" -> + let gid = int_of_string (Sys.argv.(i + 1)) in + aux n { acc with vmtx = Alist.extend acc.vmtx gid } (i + 2) + | "glyf" -> let gid = int_of_string (Sys.argv.(i + 1)) in let path = Sys.argv.(i + 2) in @@ -684,8 +732,10 @@ let parse_args () = post = false; name = false; cff_top = false; + vhea = false; charset = Alist.empty; hmtx = Alist.empty; + vmtx = Alist.empty; glyf = Alist.empty; cff = Alist.empty; cff_lex = Alist.empty; @@ -731,7 +781,9 @@ let _ = post; name; cff_top; + vhea; hmtx; + vmtx; cmap; glyf; cff; @@ -770,9 +822,15 @@ let _ = begin if cff_top then print_cff_top source else return () end >>= fun () -> + begin + if vhea then print_vhea source else return () + end >>= fun () -> hmtx |> Alist.to_list |> mapM (fun gid -> print_hmtx source gid ) >>= fun _ -> + vmtx |> Alist.to_list |> mapM (fun gid -> + print_vmtx source gid + ) >>= fun _ -> begin if cmap then print_cmap source else return () end >>= fun () -> diff --git a/src/decodeVmtx.ml b/src/decodeVmtx.ml index edc4501..12a94fa 100644 --- a/src/decodeVmtx.ml +++ b/src/decodeVmtx.ml @@ -15,7 +15,7 @@ include GeneralTable(struct type t = info end) let get (src : source) : (t option) ok = let open ResultMonad in let common = get_common_source src in - match DecodeOperation.seek_table common.table_directory Tag.table_hmtx with + match DecodeOperation.seek_table common.table_directory Tag.table_vmtx with | None -> return None @@ -49,6 +49,7 @@ let access (vmtx : t) (gid : glyph_id) : ((design_units * design_units) option) return None else if gid < num_long_ver_metrics then let reloffset = 4 * gid in + let () = Printf.printf "*** GID: %d, OFFSET: %d\n" gid vmtx.offset in (* TODO: remove this *) d_long_ver_metric |> DecodeOperation.run vmtx.core (vmtx.offset + reloffset) >>= fun (ah, tsb) -> return (Some((ah, tsb))) else From 727e1ff130138da4a0fa50fdf80c233c8fdb2d26 Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Mon, 5 Jun 2023 06:19:43 +0900 Subject: [PATCH 12/26] begin to support cmap subtables of Format 14 --- bin/main.ml | 4 +- src/decodeCmap.ml | 92 +++++++++++++++++++++++++++++++++++------ src/decodeOperation.ml | 12 ++++++ src/decodeOperation.mli | 3 ++ src/otfed.mli | 12 +++++- src/subset.ml | 2 +- 6 files changed, 108 insertions(+), 17 deletions(-) diff --git a/bin/main.ml b/bin/main.ml index 0aa3981..546bab1 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -44,7 +44,7 @@ let print_cmap (source : D.source) = let res = let open ResultMonad in D.Cmap.get source >>= fun icmap -> - D.Cmap.get_subtables icmap >>= fun subtables -> + D.Cmap.get_subtables icmap >>= fun (subtables, _) -> foldM (fun () subtable -> let ids = D.Cmap.get_subtable_ids subtable in let format = D.Cmap.get_format_number subtable in @@ -436,7 +436,7 @@ let print_glyph_ids_for_string (source : D.source) (s : string) = let open ResultMonad in Utf8Handler.to_uchar_list s >>= fun uchs -> inj @@ D.Cmap.get source >>= fun icmap -> - inj @@ D.Cmap.get_subtables icmap >>= fun isubtables -> + inj @@ D.Cmap.get_subtables icmap >>= fun (isubtables, _) -> isubtables |> mapM D.Cmap.unmarshal_subtable |> inj >>= fun subtables -> subtables |> mapM (fun V.Cmap.{ mapping; subtable_ids; } -> Format.printf "* subtable (platform: %d, encoding: %d)@," diff --git a/src/decodeCmap.ml b/src/decodeCmap.ml index 1a7054e..b96a702 100644 --- a/src/decodeCmap.ml +++ b/src/decodeCmap.ml @@ -31,6 +31,8 @@ type format = int type subtable = t * offset * Value.Cmap.subtable_ids * format +type variation_subtable = subtable + open DecodeOperation @@ -48,7 +50,7 @@ let d_encoding_record (cmap : t) : subtable decoder = return @@ (cmap, offset, ids, format) -let get_subtables (cmap : t) : (subtable set) ok = +let get_subtables (cmap : t) : (subtable set * variation_subtable set) ok = let open DecodeOperation in let dec = d_uint16 >>= fun version -> @@ -56,27 +58,33 @@ let get_subtables (cmap : t) : (subtable set) ok = err @@ UnknownTableVersion(!% version) else d_list (d_encoding_record cmap) >>= fun raw_subtables -> - let subtables = - raw_subtables |> List.filter (fun (_, _, ids, format) -> + let (ordinary_acc, variation_acc) = + raw_subtables |> List.fold_left (fun (ordinary_acc, variation_acc) raw_subtable -> let open Value.Cmap in + let (_, _, ids, format) = raw_subtable in match format with | 4 | 12 | 13 -> begin match (ids.platform_id, ids.encoding_id) with - | (0, _) (* Unicode *) - | (3, 1) (* Windows, UCS-2 *) - | (3, 10) (* Windows, UCS-4 *) - | (1, _) (* Macintosh *) - -> true - - | _ -> false + | (0, _) (* Unicode *) + | (3, 1) (* Windows, UCS-2 *) + | (3, 10) (* Windows, UCS-4 *) + | (1, _) -> (* Macintosh *) + (Alist.extend ordinary_acc raw_subtable, variation_acc) + + | _ -> + (ordinary_acc, variation_acc) end + | 14 -> + (ordinary_acc, Alist.extend variation_acc raw_subtable) + | _ -> - false - ) + (ordinary_acc, variation_acc) + + ) (Alist.empty, Alist.empty) in - return subtables + return (Alist.to_list ordinary_acc, Alist.to_list variation_acc) in run cmap.core cmap.offset dec @@ -283,3 +291,61 @@ let unmarshal_subtable (subtable : subtable) : Value.Cmap.subtable ok = subtable_ids = get_subtable_ids subtable; mapping; } + + +type 'a folding_variation_entry = { + folding_default : Uchar.t -> 'a -> 'a; + folding_non_default : Uchar.t -> Value.glyph_id -> 'a -> 'a; +} + + +let d_default_uvs_table _folding_default _acc = + failwith "TODO: d_default_uvs_table" + + +let d_non_default_uvs_table _folding_non_default _acc = + failwith "TODO: d_non_default_uvs_table" + + +type variation_selector_record = { + var_selector : Uchar.t; + default_uvs_offset : offset; + non_default_uvs_offset : offset; +} + + +let d_variation_selector_record (offset_varsubtable : offset) : variation_selector_record decoder = + let open DecodeOperation in + d_uint24 >>= fun varSelector -> + d_long_offset offset_varsubtable >>= fun default_uvs_offset -> + d_long_offset offset_varsubtable >>= fun non_default_uvs_offset -> + return { var_selector = Uchar.of_int varSelector; default_uvs_offset; non_default_uvs_offset } + + +let d_cmap_14 (offset_varsubtable : offset) (f : Uchar.t -> 'a folding_variation_entry) (acc : 'a) : 'a decoder = + let open DecodeOperation in + (* Position: immediately AFTER the format number entry of a cmap subtable *) + d_uint32 >>= fun _length -> + d_uint32_int >>= fun numVarSelectorRecords -> + d_repeat numVarSelectorRecords (d_variation_selector_record offset_varsubtable) >>= fun records -> + foldM (fun acc record -> + let { var_selector; default_uvs_offset; non_default_uvs_offset } = record in + let { folding_default; folding_non_default } = f var_selector in + pick default_uvs_offset (d_default_uvs_table folding_default acc) >>= fun acc -> + pick non_default_uvs_offset (d_non_default_uvs_table folding_non_default acc) >>= fun acc -> + return acc + ) records acc + + +let d_cmap_variation_subtable f acc = + let open DecodeOperation in + current >>= fun offset_varsubtable -> + d_uint16 >>= fun format -> + match format with + | 14 -> d_cmap_14 offset_varsubtable f acc + | _ -> err @@ UnsupportedCmapFormat(format) + + +let fold_variation_subtable (varsubtable : variation_subtable) f acc = + let (cmap, offset, _, _) = varsubtable in + run cmap.core offset (d_cmap_variation_subtable f acc) diff --git a/src/decodeOperation.ml b/src/decodeOperation.ml index 7f2a26f..47c8481 100644 --- a/src/decodeOperation.ml +++ b/src/decodeOperation.ml @@ -109,6 +109,18 @@ fun count dec -> aux Alist.empty count +let d_fold : 'a 'b. int -> 'a decoder -> ('a -> 'b -> 'b) -> 'b -> 'b decoder = +fun count dec f acc -> + let rec aux acc i = + if i <= 0 then + return acc + else + dec >>= fun v -> + aux (f v acc) (i - 1) + in + aux acc count + + let d_list dec = d_uint16 >>= fun count -> d_repeat count dec diff --git a/src/decodeOperation.mli b/src/decodeOperation.mli index a2159d3..d85348e 100644 --- a/src/decodeOperation.mli +++ b/src/decodeOperation.mli @@ -52,6 +52,9 @@ val d_fetch_long : offset -> 'a decoder -> (offset * 'a) decoder (** [d_repeat count dec] decodes a sequence of values of length [count] by using [dec]. *) val d_repeat : int -> 'a decoder -> ('a list) decoder +(** Same as [d_repeat count dec >>= fun vs -> return (List.fold_left (flip f) acc vs)], but more efficient. *) +val d_fold : int -> 'a decoder -> ('a -> 'b -> 'b) -> 'b -> 'b decoder + (** [d_list dec] reads a 2-byte unsigned integer [count] and then behaves the same way as [d_repeat count dec]. *) val d_list : 'a decoder -> ('a list) decoder diff --git a/src/otfed.mli b/src/otfed.mli index e3ab0bb..b6f00c0 100644 --- a/src/otfed.mli +++ b/src/otfed.mli @@ -1054,8 +1054,11 @@ module Decode : sig (** The type for representing Unicode-aware [cmap] subtables. *) type subtable + (** The type for representing Unicode Variation Sequence subtables in [cmap]. *) + type variation_subtable + (** Gets all the Unicode-aware subtables in the given [cmap] table. *) - val get_subtables : t -> (subtable set) ok + val get_subtables : t -> (subtable set * variation_subtable set) ok val get_subtable_ids : subtable -> Value.Cmap.subtable_ids @@ -1073,6 +1076,13 @@ module Decode : sig (** Folds a [cmap] subtable in ascending order. *) val fold_subtable : subtable -> ('a -> segment -> 'a) -> 'a -> 'a ok + type 'a folding_variation_entry = { + folding_default : Uchar.t -> 'a -> 'a; + folding_non_default : Uchar.t -> Value.glyph_id -> 'a -> 'a; + } + + val fold_variation_subtable : variation_subtable -> (Uchar.t -> 'a folding_variation_entry) -> 'a -> 'a ok + (** Converts a [cmap] subtable to an in-memory mapping. *) val unmarshal_subtable : subtable -> Value.Cmap.subtable ok end diff --git a/src/subset.ml b/src/subset.ml index 6f92426..a6f1f24 100644 --- a/src/subset.ml +++ b/src/subset.ml @@ -336,7 +336,7 @@ let make_cmap (src : Decode.source) (gids : glyph_id list) : (Encode.table * Uch let open ResultMonad in let res_dec = Decode.Cmap.get src >>= fun icmap -> - Decode.Cmap.get_subtables icmap >>= fun isubtables -> + Decode.Cmap.get_subtables icmap >>= fun (isubtables, _) -> isubtables |> mapM Decode.Cmap.unmarshal_subtable in let (old_to_new_map, _) = From eb06a1f3b409544971519318fee35cded8456a7d Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Mon, 5 Jun 2023 07:06:12 +0900 Subject: [PATCH 13/26] develop 'fold_variation_subtable' --- src/decodeCmap.ml | 33 ++++++++++++++++++++++++++++----- src/otfed.mli | 7 ++++++- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/decodeCmap.ml b/src/decodeCmap.ml index b96a702..5a73a43 100644 --- a/src/decodeCmap.ml +++ b/src/decodeCmap.ml @@ -293,18 +293,41 @@ let unmarshal_subtable (subtable : subtable) : Value.Cmap.subtable ok = } +type unicode_value_range = { + start_unicode_value : Uchar.t; + additional_count : int; +} + type 'a folding_variation_entry = { - folding_default : Uchar.t -> 'a -> 'a; + folding_default : unicode_value_range -> 'a -> 'a; folding_non_default : Uchar.t -> Value.glyph_id -> 'a -> 'a; } -let d_default_uvs_table _folding_default _acc = - failwith "TODO: d_default_uvs_table" +let d_unicode_value_range : unicode_value_range decoder = + let open DecodeOperation in + d_uint24 >>= fun startUnicodeValue -> + d_uint8 >>= fun additional_count -> + return { start_unicode_value = Uchar.of_int startUnicodeValue; additional_count } + + +let d_default_uvs_table folding_default acc = + let open DecodeOperation in + d_uint32_int >>= fun numUnicodeValueRanges -> + d_fold numUnicodeValueRanges d_unicode_value_range folding_default acc + +let d_uvs_mapping : (Uchar.t * Value.glyph_id) decoder = + let open DecodeOperation in + d_uint24 >>= fun unicodeValue -> + d_uint16 >>= fun gid -> + return (Uchar.of_int unicodeValue, gid) -let d_non_default_uvs_table _folding_non_default _acc = - failwith "TODO: d_non_default_uvs_table" + +let d_non_default_uvs_table folding_non_default acc = + let open DecodeOperation in + d_uint32_int >>= fun numUVSMappings -> + d_fold numUVSMappings d_uvs_mapping (fun (uch, gid) -> folding_non_default uch gid) acc type variation_selector_record = { diff --git a/src/otfed.mli b/src/otfed.mli index b6f00c0..f095727 100644 --- a/src/otfed.mli +++ b/src/otfed.mli @@ -1076,8 +1076,13 @@ module Decode : sig (** Folds a [cmap] subtable in ascending order. *) val fold_subtable : subtable -> ('a -> segment -> 'a) -> 'a -> 'a ok + type unicode_value_range = { + start_unicode_value : Uchar.t; + additional_count : int; + } + type 'a folding_variation_entry = { - folding_default : Uchar.t -> 'a -> 'a; + folding_default : unicode_value_range -> 'a -> 'a; folding_non_default : Uchar.t -> Value.glyph_id -> 'a -> 'a; } From 114ccb3c39703e80907cb4b49ba17e9a5f024e17 Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Mon, 5 Jun 2023 08:12:45 +0900 Subject: [PATCH 14/26] fix 'd_variation_selector_record' etc. --- README.md | 2 +- bin/main.ml | 22 +++++++++++++++++++--- src/decodeCmap.ml | 23 ++++++++++++++++------- src/decodeOperation.ml | 8 ++++++++ src/decodeOperation.mli | 3 +++ 5 files changed, 47 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 157c3f2..b57a4ba 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ $ dune exec otfedcli input/Junicode.ttf subset 0,113,302 output/Junicode-subset. SupportedTestedSupportedTested Required - cmapv (Format 12 only)v (Format 12 only)v (Format 4, 12, and 13 only)v (Format 4 and 12 only) + cmapv (Format 12 only)v (Format 12 only)v (Format 4, 12, 13, and 14 only)v (Format 4 and 12 only) headvvvv hheavvvv hmtxvvvv diff --git a/bin/main.ml b/bin/main.ml index 546bab1..40b98b7 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -44,7 +44,7 @@ let print_cmap (source : D.source) = let res = let open ResultMonad in D.Cmap.get source >>= fun icmap -> - D.Cmap.get_subtables icmap >>= fun (subtables, _) -> + D.Cmap.get_subtables icmap >>= fun (subtables, varsubtables) -> foldM (fun () subtable -> let ids = D.Cmap.get_subtable_ids subtable in let format = D.Cmap.get_format_number subtable in @@ -63,7 +63,23 @@ let print_cmap (source : D.source) = | D.Cmap.Constant(uch1, uch2, gid) -> Format.printf " - C %a, %a --> %d@," pp_uchar uch1 pp_uchar uch2 gid ) () - ) subtables () + ) subtables () >>= fun () -> + foldM (fun () varsubtable -> + Format.printf "* variation subtable (format: 14)@,"; + D.Cmap.fold_variation_subtable varsubtable (fun uch_varselect -> + Format.printf " - V %a@," pp_uchar uch_varselect; + D.Cmap.{ + folding_default = + (fun { start_unicode_value; additional_count } () -> + Format.printf " * D %a, %d@," pp_uchar start_unicode_value additional_count + ); + folding_non_default = + (fun uch gid () -> + Format.printf " * N %a --> %d@," pp_uchar uch gid + ); + } + ) () + ) varsubtables () in res |> inj @@ -436,7 +452,7 @@ let print_glyph_ids_for_string (source : D.source) (s : string) = let open ResultMonad in Utf8Handler.to_uchar_list s >>= fun uchs -> inj @@ D.Cmap.get source >>= fun icmap -> - inj @@ D.Cmap.get_subtables icmap >>= fun (isubtables, _) -> + inj @@ D.Cmap.get_subtables icmap >>= fun (isubtables, _varsubtables) -> isubtables |> mapM D.Cmap.unmarshal_subtable |> inj >>= fun subtables -> subtables |> mapM (fun V.Cmap.{ mapping; subtable_ids; } -> Format.printf "* subtable (platform: %d, encoding: %d)@," diff --git a/src/decodeCmap.ml b/src/decodeCmap.ml index 5a73a43..127e532 100644 --- a/src/decodeCmap.ml +++ b/src/decodeCmap.ml @@ -332,16 +332,16 @@ let d_non_default_uvs_table folding_non_default acc = type variation_selector_record = { var_selector : Uchar.t; - default_uvs_offset : offset; - non_default_uvs_offset : offset; + default_uvs_offset : offset option; + non_default_uvs_offset : offset option; } let d_variation_selector_record (offset_varsubtable : offset) : variation_selector_record decoder = let open DecodeOperation in d_uint24 >>= fun varSelector -> - d_long_offset offset_varsubtable >>= fun default_uvs_offset -> - d_long_offset offset_varsubtable >>= fun non_default_uvs_offset -> + d_long_offset_opt offset_varsubtable >>= fun default_uvs_offset -> + d_long_offset_opt offset_varsubtable >>= fun non_default_uvs_offset -> return { var_selector = Uchar.of_int varSelector; default_uvs_offset; non_default_uvs_offset } @@ -354,10 +354,19 @@ let d_cmap_14 (offset_varsubtable : offset) (f : Uchar.t -> 'a folding_variation foldM (fun acc record -> let { var_selector; default_uvs_offset; non_default_uvs_offset } = record in let { folding_default; folding_non_default } = f var_selector in - pick default_uvs_offset (d_default_uvs_table folding_default acc) >>= fun acc -> - pick non_default_uvs_offset (d_non_default_uvs_table folding_non_default acc) >>= fun acc -> + begin + match default_uvs_offset with + | None -> return acc + | Some(offset) -> pick offset (d_default_uvs_table folding_default acc) + end >>= fun acc -> + begin + match non_default_uvs_offset with + | None -> return acc + | Some(offset) -> pick offset (d_non_default_uvs_table folding_non_default acc) + end >>= fun acc -> return acc - ) records acc + ) records acc >>= fun acc -> + return acc let d_cmap_variation_subtable f acc = diff --git a/src/decodeOperation.ml b/src/decodeOperation.ml index 47c8481..e48c5ae 100644 --- a/src/decodeOperation.ml +++ b/src/decodeOperation.ml @@ -73,6 +73,14 @@ let d_offset_opt (offset_origin : int) : (offset option) decoder = return @@ Some(offset_origin + reloffset) +let d_long_offset_opt (offset_origin : int) : (offset option) decoder = + d_uint32_int >>= fun reloffset -> + if reloffset = 0 then + return None + else + return @@ Some(offset_origin + reloffset) + + let d_fetch (offset_origin : offset) (dec : 'a decoder) : 'a decoder = d_offset offset_origin >>= fun offset -> pick offset dec diff --git a/src/decodeOperation.mli b/src/decodeOperation.mli index d85348e..4d84d5c 100644 --- a/src/decodeOperation.mli +++ b/src/decodeOperation.mli @@ -36,6 +36,9 @@ val d_long_offset : offset -> offset decoder (** Basically the same as [d_offset] except that [d_offset_opt] returns [None] when it has read [0]. *) val d_offset_opt : offset -> (offset option) decoder +(** Basically the same as [d_long_offset] except that [d_long_offset_opt] returns [None] when it has read [0]. *) +val d_long_offset_opt : offset -> (offset option) decoder + (** [d_fetch origin dec] reads 2 bytes as a relative offset [rel] and then reads data at [origin + rel] by using [dec]. Here, the position advances by 2 bytes. *) From cd8fc935e52ef1445dc9f8d59ae86f9e90c7ccc6 Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Mon, 5 Jun 2023 17:01:25 +0900 Subject: [PATCH 15/26] change interface of 'd_cmap_variation_subtable' and add a test for it --- README.md | 2 +- bin/main.ml | 3 + src/decodeCmap.ml | 50 +++-- src/otfed.mli | 19 +- test/otfedTest.ml | 38 ++++ test/testCaseCmap3.ml | 455 ++++++++++++++++++++++++++++++++++++++++++ test/testUtil.ml | 8 + 7 files changed, 555 insertions(+), 20 deletions(-) create mode 100644 test/testCaseCmap3.ml diff --git a/README.md b/README.md index b57a4ba..b1543e3 100644 --- a/README.md +++ b/README.md @@ -125,7 +125,7 @@ $ dune exec otfedcli input/Junicode.ttf subset 0,113,302 output/Junicode-subset. SupportedTestedSupportedTested Required - cmapv (Format 12 only)v (Format 12 only)v (Format 4, 12, 13, and 14 only)v (Format 4 and 12 only) + cmapv (Format 12 only)v (Format 12 only)v (Format 4, 12, 13, and 14 only)v (Format 4, 12, and 14 only) headvvvv hheavvvv hmtxvvvv diff --git a/bin/main.ml b/bin/main.ml index 40b98b7..c1e7668 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -69,14 +69,17 @@ let print_cmap (source : D.source) = D.Cmap.fold_variation_subtable varsubtable (fun uch_varselect -> Format.printf " - V %a@," pp_uchar uch_varselect; D.Cmap.{ + init_default = (); folding_default = (fun { start_unicode_value; additional_count } () -> Format.printf " * D %a, %d@," pp_uchar start_unicode_value additional_count ); + init_non_default = (fun () -> ()); folding_non_default = (fun uch gid () -> Format.printf " * N %a --> %d@," pp_uchar uch gid ); + unifier = (fun () () () -> ()); } ) () ) varsubtables () diff --git a/src/decodeCmap.ml b/src/decodeCmap.ml index 127e532..7e57010 100644 --- a/src/decodeCmap.ml +++ b/src/decodeCmap.ml @@ -294,14 +294,25 @@ let unmarshal_subtable (subtable : subtable) : Value.Cmap.subtable ok = type unicode_value_range = { - start_unicode_value : Uchar.t; + start_unicode_value : Uchar.t; [@printer pp_uchar] additional_count : int; } +[@@deriving show { with_path = false }] -type 'a folding_variation_entry = { - folding_default : unicode_value_range -> 'a -> 'a; - folding_non_default : Uchar.t -> Value.glyph_id -> 'a -> 'a; +type 'a1 folding_default = unicode_value_range -> 'a1 -> 'a1 +[@@deriving show { with_path = false }] + +type 'a2 folding_non_default = Uchar.t -> Value.glyph_id -> 'a2 -> 'a2 +[@@deriving show { with_path = false }] + +type ('a, 'a1, 'a2) folding_variation_entry = { + init_default : 'a1; + folding_default : 'a1 folding_default; + init_non_default : 'a1 -> 'a2; + folding_non_default : 'a2 folding_non_default; + unifier : 'a -> 'a1 -> 'a2 -> 'a; } +[@@deriving show { with_path = false }] let d_unicode_value_range : unicode_value_range decoder = @@ -311,7 +322,7 @@ let d_unicode_value_range : unicode_value_range decoder = return { start_unicode_value = Uchar.of_int startUnicodeValue; additional_count } -let d_default_uvs_table folding_default acc = +let d_default_uvs_table (folding_default : 'a1 folding_default) (acc : 'a1) : 'a1 decoder = let open DecodeOperation in d_uint32_int >>= fun numUnicodeValueRanges -> d_fold numUnicodeValueRanges d_unicode_value_range folding_default acc @@ -324,7 +335,7 @@ let d_uvs_mapping : (Uchar.t * Value.glyph_id) decoder = return (Uchar.of_int unicodeValue, gid) -let d_non_default_uvs_table folding_non_default acc = +let d_non_default_uvs_table (folding_non_default : 'a2 folding_non_default) (acc : 'a2) : 'a2 decoder = let open DecodeOperation in d_uint32_int >>= fun numUVSMappings -> d_fold numUVSMappings d_uvs_mapping (fun (uch, gid) -> folding_non_default uch gid) acc @@ -345,7 +356,7 @@ let d_variation_selector_record (offset_varsubtable : offset) : variation_select return { var_selector = Uchar.of_int varSelector; default_uvs_offset; non_default_uvs_offset } -let d_cmap_14 (offset_varsubtable : offset) (f : Uchar.t -> 'a folding_variation_entry) (acc : 'a) : 'a decoder = +let d_cmap_14 (offset_varsubtable : offset) (f : Uchar.t -> ('a, 'a1, 'a2) folding_variation_entry) (acc : 'a) : 'a decoder = let open DecodeOperation in (* Position: immediately AFTER the format number entry of a cmap subtable *) d_uint32 >>= fun _length -> @@ -353,18 +364,27 @@ let d_cmap_14 (offset_varsubtable : offset) (f : Uchar.t -> 'a folding_variation d_repeat numVarSelectorRecords (d_variation_selector_record offset_varsubtable) >>= fun records -> foldM (fun acc record -> let { var_selector; default_uvs_offset; non_default_uvs_offset } = record in - let { folding_default; folding_non_default } = f var_selector in + let + { + init_default = acc1; + folding_default; + init_non_default; + folding_non_default; + unifier; + } = f var_selector + in begin match default_uvs_offset with - | None -> return acc - | Some(offset) -> pick offset (d_default_uvs_table folding_default acc) - end >>= fun acc -> + | None -> return acc1 + | Some(offset) -> pick offset (d_default_uvs_table folding_default acc1) + end >>= fun acc1 -> + let acc2 = init_non_default acc1 in begin match non_default_uvs_offset with - | None -> return acc - | Some(offset) -> pick offset (d_non_default_uvs_table folding_non_default acc) - end >>= fun acc -> - return acc + | None -> return acc2 + | Some(offset) -> pick offset (d_non_default_uvs_table folding_non_default acc2) + end >>= fun acc2 -> + return (unifier acc acc1 acc2) ) records acc >>= fun acc -> return acc diff --git a/src/otfed.mli b/src/otfed.mli index f095727..ad51b9e 100644 --- a/src/otfed.mli +++ b/src/otfed.mli @@ -1080,13 +1080,24 @@ module Decode : sig start_unicode_value : Uchar.t; additional_count : int; } + [@@deriving show] + + type 'a1 folding_default = unicode_value_range -> 'a1 -> 'a1 + [@@deriving show] - type 'a folding_variation_entry = { - folding_default : unicode_value_range -> 'a -> 'a; - folding_non_default : Uchar.t -> Value.glyph_id -> 'a -> 'a; + type 'a2 folding_non_default = Uchar.t -> Value.glyph_id -> 'a2 -> 'a2 + [@@deriving show] + + type ('a, 'a1, 'a2) folding_variation_entry = { + init_default : 'a1; + folding_default : 'a1 folding_default; + init_non_default : 'a1 -> 'a2; + folding_non_default : 'a2 folding_non_default; + unifier : 'a -> 'a1 -> 'a2 -> 'a; } + [@@deriving show] - val fold_variation_subtable : variation_subtable -> (Uchar.t -> 'a folding_variation_entry) -> 'a -> 'a ok + val fold_variation_subtable : variation_subtable -> (Uchar.t -> ('a, 'a1, 'a2) folding_variation_entry) -> 'a -> 'a ok (** Converts a [cmap] subtable to an in-memory mapping. *) val unmarshal_subtable : subtable -> Value.Cmap.subtable ok diff --git a/test/otfedTest.ml b/test/otfedTest.ml index fcf5356..b2a1da3 100644 --- a/test/otfedTest.ml +++ b/test/otfedTest.ml @@ -225,6 +225,43 @@ let d_cmap_subtable_tests () = end +let d_cmap_variation_subtable_to_list = + let open DecodeOperation in + DecodeCmap.d_cmap_variation_subtable (fun uch_varselect -> { + init_default = Alist.empty; + folding_default = + (fun uvrange acc1 -> + Alist.extend acc1 uvrange + ); + init_non_default = (fun _acc1 -> Alist.empty); + folding_non_default = + (fun uch gid acc2 -> + Alist.extend acc2 (uch, gid) + ); + unifier = + (fun acc acc1 acc2 -> + Alist.extend acc (uch_varselect, (Alist.to_list acc1, Alist.to_list acc2)) + ); + }) Alist.empty >>= fun acc -> + return (Alist.to_list acc) + + +let default_list = + Alcotest.(list (of_pp DecodeCmap.pp_unicode_value_range)) + + +let non_default_list = + Alcotest.(list (pair uchar glyph_id)) + + +(** Tests for `DecodeCmap.d_cmap_variation_subtable` *) +let d_cmap_variation_subtable_tests () = + let got = d_cmap_variation_subtable_to_list |> run_decoder TestCaseCmap3.marshaled in + let expected = Ok(TestCaseCmap3.unmarshaled) in + Alcotest.(check (decoding (list (pair uchar (pair default_list non_default_list))))) + "d_cmap_variation_subtable (Format 14)" expected got + + (** Tests for `EncodeCmap.e_cmap_mapping` *) let e_cmap_mapping_tests () = let input = TestCaseCmap2.unmarshaled in @@ -471,6 +508,7 @@ let () = ]); ("DecodeCmap", [ test_case "d_cmap_subtable" `Quick d_cmap_subtable_tests; + test_case "d_cmap_variation_subtable" `Quick d_cmap_variation_subtable_tests; ]); ("EncodeCmap", [ test_case "e_cmap_mapping" `Quick e_cmap_mapping_tests; diff --git a/test/testCaseCmap3.ml b/test/testCaseCmap3.ml new file mode 100644 index 0000000..19f8938 --- /dev/null +++ b/test/testCaseCmap3.ml @@ -0,0 +1,455 @@ + +module DecodeCmap = Otfed__DecodeCmap + + +let marshaled = + TestUtil.make_string_odd [ + (* `ipaexm.ttf` (offset: 79672, length: 2179); a cmap subtable of Format 14 *) + 0x000e; 0x0000; 0x0883; 0x0000; 0x0007; 0x00fe; 0x0000; 0x0000; + 0x5700; 0x0000; 0x7700; 0xfe01; 0x0000; 0x0000; 0x0000; 0x021a; + 0x0e01; 0x0000; 0x0002; 0x2d00; 0x0002; 0x590e; 0x0101; 0x0000; + 0x0587; 0x0000; 0x07e7; 0x0e01; 0x0200; 0x0008; 0x2700; 0x0008; + 0x530e; 0x0103; 0x0000; 0x0866; 0x0000; 0x0872; 0x0e01; 0x0500; + 0x0008; 0x7b00; 0x0000; 0x0000; 0x0000; 0x0700; 0x51a4; 0x0000; + 0x5c60; 0x0000; 0x6753; 0x0000; 0x6adb; 0x0000; 0x6eba; 0x0000; + 0x6f23; 0x0000; 0x7149; 0x0000; 0x0000; 0x5300; 0x4fae; 0x212b; + 0x0050; 0xe721; 0x1700; 0x514d; 0x2131; 0x0051; 0xde2f; 0x9e00; + 0x52c9; 0x212d; 0x0052; 0xe421; 0x0300; 0x5351; 0x2127; 0x0055; + 0x9d1c; 0xb900; 0x5606; 0x211d; 0x0056; 0x6820; 0xfe00; 0x5840; + 0x212c; 0x0058; 0x5a1c; 0xf900; 0x58a8; 0x212f; 0x005c; 0x6421; + 0x1800; 0x5c6e; 0x267b; 0x005e; 0xca2f; 0x9a00; 0x6094; 0x20f7; + 0x0061; 0x6820; 0xf900; 0x618e; 0x211a; 0x0061; 0xf22f; 0xaa00; + 0x654f; 0x212a; 0x0065; 0xe220; 0xff00; 0x6674; 0x1eb0; 0x0066; + 0x9121; 0x1100; 0x6717; 0x2f9c; 0x0068; 0x8521; 0x2400; 0x6b04; + 0x2134; 0x006b; 0xba21; 0x0900; 0x6d77; 0x20f8; 0x006e; 0x1a1c; + 0xdb00; 0x6f22; 0x20fd; 0x0071; 0x6e21; 0x0c00; 0x722b; 0x24ec; + 0x0073; 0x2a1e; 0xf100; 0x7422; 0x1cef; 0x0076; 0xca1f; 0x0700; + 0x7891; 0x2128; 0x0079; 0x3c1f; 0x0f00; 0x793e; 0x210d; 0x0079; + 0x4821; 0x0000; 0x7949; 0x210a; 0x0079; 0x5021; 0x3300; 0x7956; + 0x2116; 0x0079; 0x5d21; 0x1000; 0x795e; 0x1f10; 0x0079; 0x651f; + 0x1100; 0x798d; 0x20f6; 0x0079; 0x8e21; 0x2100; 0x798f; 0x1f13; + 0x007a; 0x4021; 0x0800; 0x7a81; 0x2122; 0x007b; 0xc021; 0x1500; + 0x7cbe; 0x1f1a; 0x007e; 0x092c; 0x7400; 0x7e41; 0x2125; 0x007f; + 0x7221; 0x1200; 0x7fbd; 0x1f22; 0x0080; 0x0521; 0x0e00; 0x81ed; + 0x210f; 0x0082; 0x7921; 0x7b00; 0x8457; 0x211e; 0x0086; 0x122f; + 0xab00; 0x865c; 0x2136; 0x0089; 0x1020; 0xfc00; 0x8996; 0x210b; + 0x008a; 0xf81f; 0x3900; 0x8b01; 0x20f2; 0x008b; 0x3921; 0x0400; + 0x8cd3; 0x2129; 0x008d; 0x0821; 0x1b00; 0x8fb6; 0x24ed; 0x0090; + 0x381f; 0x4300; 0x90fd; 0x1f46; 0x0096; 0x8621; 0x3500; 0x96e3; + 0x2123; 0x0097; 0x561f; 0x1700; 0x97ff; 0x2102; 0x0098; 0x3b1d; + 0x1900; 0x985e; 0x2138; 0x0098; 0xef1f; 0x8300; 0x98fc; 0x1f84; + 0x0099; 0x281f; 0x8600; 0x9db4; 0x1f93; 0x0000; 0x0003; 0x007d; + 0xf421; 0x3b00; 0x8279; 0x217a; 0x0090; 0x3820; 0xf100; 0x0000; + 0x0a00; 0x5307; 0x0000; 0x55a9; 0x0000; 0x5bdb; 0x0000; 0x61f2; + 0x0000; 0x646f; 0x0000; 0x6adb; 0x0000; 0x6f23; 0x0000; 0x6ff9; + 0x0000; 0x7149; 0x0000; 0x8612; 0x0000; 0x0000; 0xa200; 0x5026; + 0x065d; 0x0050; 0xc505; 0xdd00; 0x5132; 0x0dfb; 0x0051; 0x4e0b; + 0x5600; 0x51a4; 0x0f9a; 0x0053; 0xa903; 0xf100; 0x53c9; 0x073b; + 0x0053; 0xdb0c; 0x6a00; 0x53df; 0x1002; 0x0054; 0xac10; 0x2600; + 0x54e8; 0x08a3; 0x0055; 0xb006; 0x0200; 0x5632; 0x1067; 0x0056; + 0x4203; 0xf500; 0x564c; 0x09d1; 0x0056; 0xc010; 0x7c00; 0x5835; + 0x0b58; 0x0059; 0x0628; 0xf300; 0x5a29; 0x0d40; 0x005c; 0x5106; + 0x0b00; 0x5c60; 0x0b5b; 0x005d; 0xf706; 0xd300; 0x5e96; 0x0d5f; + 0x005e; 0xdf0c; 0xc800; 0x5efb; 0x048c; 0x005f; 0x9811; 0xd200; + 0x5fbd; 0x055b; 0x0060; 0x6204; 0x9000; 0x609e; 0x223e; 0x0061; + 0x080e; 0x1e00; 0x6241; 0x1265; 0x0063; 0x3a0b; 0x2400; 0x633d; + 0x0c7e; 0x0063; 0x570a; 0xef00; 0x6372; 0x066a; 0x0063; 0xc30a; + 0x2d00; 0x647a; 0x095c; 0x0064; 0xb009; 0xab00; 0x64e2; 0x0b37; + 0x0065; 0xa70c; 0xe800; 0x6666; 0x0496; 0x0066; 0xb52a; 0x1100; + 0x6753; 0x081f; 0x0067; 0x5608; 0xf100; 0x6897; 0x06e4; 0x0069; + 0x620b; 0xd800; 0x696f; 0x087d; 0x0069; 0x8a07; 0x6d00; 0x6994; + 0x0eed; 0x0069; 0xcc0a; 0xfa00; 0x6a0b; 0x0c9f; 0x006a; 0x3d0a; + 0x8200; 0x6b4e; 0x0a8b; 0x006c; 0x7205; 0x9200; 0x6deb; 0x03d6; + 0x006e; 0xa203; 0xc800; 0x6eba; 0x0b3e; 0x0070; 0x150c; 0xd600; + 0x701e; 0x0bc2; 0x0070; 0x260a; 0xc900; 0x7058; 0x0bd5; 0x0070; + 0x7805; 0x9400; 0x707c; 0x0820; 0x0071; 0x4e09; 0xb300; 0x7152; + 0x22f1; 0x0071; 0x7d09; 0xb400; 0x723a; 0x0e0e; 0x0072; 0x4c0c; + 0x2300; 0x7259; 0x047d; 0x0072; 0xe115; 0x1300; 0x7337; 0x0e32; + 0x0075; 0x1107; 0x2100; 0x7515; 0x155e; 0x0075; 0x2615; 0x6100; + 0x75bc; 0x1583; 0x0077; 0xa50d; 0x3300; 0x7941; 0x0623; 0x0079; + 0x4705; 0x7000; 0x79b0; 0x0bf5; 0x0079; 0xe40c; 0x3500; 0x7a17; + 0x0cab; 0x007a; 0x7f09; 0xb600; 0x7ac8; 0x1647; 0x007b; 0x0805; + 0x9800; 0x7b75; 0x166e; 0x007b; 0xad09; 0xb700; 0x7bb8; 0x0c4e; + 0x007b; 0xc70d; 0x3900; 0x7bdd; 0x1687; 0x007c; 0x3e0e; 0xda00; + 0x7c7e; 0x0e04; 0x007c; 0x8206; 0x1500; 0x7feb; 0x0537; 0x007f; + 0xf005; 0x1f00; 0x8171; 0x1788; 0x0081; 0x7f0a; 0x5200; 0x8258; + 0x17bc; 0x0082; 0x9217; 0xcb00; 0x82a6; 0x038c; 0x0083; 0x2803; + 0xcb00; 0x845b; 0x04df; 0x0084; 0xec0d; 0x6d00; 0x8511; 0x0d34; + 0x0085; 0x3d0d; 0x2900; 0x85a9; 0x078b; 0x0085; 0xaf08; 0x9200; + 0x85f7; 0x0893; 0x0086; 0x5418; 0x6700; 0x86f8; 0x0a73; 0x0087; + 0x0318; 0x8400; 0x8755; 0x0906; 0x0088; 0x0518; 0x9f00; 0x8956; + 0x043e; 0x008a; 0x0a09; 0x3200; 0x8a1d; 0x191c; 0x008a; 0x3b0a; + 0xc400; 0x8a6e; 0x09bf; 0x008a; 0xb90c; 0x9a00; 0x8afa; 0x068b; + 0x008b; 0x0e0b; 0xd400; 0x8b2c; 0x0cbd; 0x008b; 0x7f1f; 0x3c00; + 0x8c79; 0x0cc7; 0x008c; 0xed0b; 0x6200; 0x8fbb; 0x0b06; 0x008f; + 0xbf0a; 0x7d00; 0x8fc2; 0x03e2; 0x008f; 0xc40d; 0xbc00; 0x8fe6; + 0x0476; 0x0090; 0x1709; 0x3c00; 0x9019; 0x0c33; 0x0090; 0x2203; + 0x8300; 0x903c; 0x0cb7; 0x0090; 0x410b; 0xc900; 0x905c; 0x0a33; + 0x0090; 0x6109; 0xe400; 0x912d; 0x0b32; 0x0091; 0x4b08; 0x5600; + 0x91dc; 0x04ec; 0x0093; 0x0607; 0x9000; 0x9375; 0x067a; 0x0093; + 0x9a0a; 0xfc00; 0x9453; 0x0e1c; 0x0096; 0x9906; 0x5000; 0x9771; + 0x1b06; 0x0097; 0x8404; 0xe700; 0x9798; 0x08e2; 0x0097; 0xad0d; + 0x4200; 0x98f4; 0x0395; 0x0099; 0x050e; 0x0100; 0x990c; 0x03fa; + 0x0099; 0x1007; 0xa500; 0x9957; 0x05d0; 0x0099; 0xc10c; 0x4900; + 0x9a19; 0x1b66; 0x009a; 0x4a24; 0xaf00; 0x9bab; 0x0791; 0x009b; + 0xd607; 0x8e00; 0x9c2f; 0x03cd; 0x009c; 0x520d; 0xb400; 0x9d09; + 0x1bd8; 0x009d; 0x6007; 0x1c00; 0x0000; 0x9700; 0x5026; 0x0000; + 0x50c5; 0x0000; 0x5132; 0x0000; 0x514e; 0x0000; 0x51a4; 0x0000; + 0x53c9; 0x0000; 0x53db; 0x0000; 0x53df; 0x0000; 0x54ac; 0x0000; + 0x54e8; 0x0000; 0x55b0; 0x0000; 0x5632; 0x0000; 0x5642; 0x0000; + 0x564c; 0x0000; 0x56c0; 0x0000; 0x5835; 0x0000; 0x5906; 0x0000; + 0x5a29; 0x0000; 0x5c51; 0x0000; 0x5c60; 0x0000; 0x5df7; 0x0000; + 0x5e96; 0x0000; 0x5edf; 0x0000; 0x5efb; 0x0000; 0x5f98; 0x0000; + 0x5fbd; 0x0000; 0x609e; 0x0000; 0x6108; 0x0000; 0x6241; 0x0000; + 0x633a; 0x0000; 0x633d; 0x0000; 0x6357; 0x0000; 0x6372; 0x0000; + 0x63c3; 0x0000; 0x647a; 0x0000; 0x64b0; 0x0000; 0x64e2; 0x0000; + 0x65a7; 0x0000; 0x6666; 0x0000; 0x66b5; 0x0000; 0x6717; 0x0000; + 0x6753; 0x0000; 0x6756; 0x0000; 0x6897; 0x0000; 0x6962; 0x0000; + 0x696f; 0x0000; 0x698a; 0x0000; 0x6994; 0x0000; 0x69cc; 0x0000; + 0x6a0b; 0x0000; 0x6a3d; 0x0000; 0x6b4e; 0x0000; 0x6c72; 0x0000; + 0x6deb; 0x0000; 0x6ea2; 0x0000; 0x6eba; 0x0000; 0x7015; 0x0000; + 0x701e; 0x0000; 0x7026; 0x0000; 0x7058; 0x0000; 0x7078; 0x0000; + 0x707c; 0x0000; 0x714e; 0x0000; 0x7152; 0x0000; 0x723a; 0x0000; + 0x724c; 0x0000; 0x7259; 0x0000; 0x72e1; 0x0000; 0x7526; 0x0000; + 0x75bc; 0x0000; 0x77a5; 0x0000; 0x7941; 0x0000; 0x7947; 0x0000; + 0x79b0; 0x0000; 0x79e4; 0x0000; 0x7a17; 0x0000; 0x7a7f; 0x0000; + 0x7ac8; 0x0000; 0x7b75; 0x0000; 0x7bad; 0x0000; 0x7bb8; 0x0000; + 0x7bc7; 0x0000; 0x7bdd; 0x0000; 0x7c82; 0x0000; 0x7feb; 0x0000; + 0x7ff0; 0x0000; 0x8171; 0x0000; 0x817f; 0x0000; 0x8258; 0x0000; + 0x8292; 0x0000; 0x82a6; 0x0000; 0x8328; 0x0000; 0x845b; 0x0000; + 0x84ec; 0x0000; 0x8511; 0x0000; 0x853d; 0x0000; 0x85a9; 0x0000; + 0x85af; 0x0000; 0x85f7; 0x0000; 0x8654; 0x0000; 0x86f8; 0x0000; + 0x8703; 0x0000; 0x8755; 0x0000; 0x8805; 0x0000; 0x8956; 0x0000; + 0x8a0a; 0x0000; 0x8a3b; 0x0000; 0x8a6e; 0x0000; 0x8ab9; 0x0000; + 0x8afa; 0x0000; 0x8b0e; 0x0000; 0x8b2c; 0x0000; 0x8b7f; 0x0000; + 0x8c79; 0x0000; 0x8ced; 0x0000; 0x8fbb; 0x0000; 0x8fbf; 0x0000; + 0x8fc2; 0x0000; 0x8fc4; 0x0000; 0x8fe6; 0x0000; 0x9017; 0x0000; + 0x9019; 0x0000; 0x9022; 0x0000; 0x903c; 0x0000; 0x905c; 0x0000; + 0x9061; 0x0000; 0x912d; 0x0000; 0x914b; 0x0000; 0x91dc; 0x0000; + 0x9306; 0x0000; 0x9375; 0x0000; 0x939a; 0x0000; 0x9453; 0x0000; + 0x9699; 0x0000; 0x9771; 0x0000; 0x9784; 0x0000; 0x9798; 0x0000; + 0x97ad; 0x0000; 0x98f4; 0x0000; 0x9905; 0x0000; 0x9910; 0x0000; + 0x9957; 0x0000; 0x99c1; 0x0000; 0x9a19; 0x0000; 0x9a4a; 0x0000; + 0x9bab; 0x0000; 0x9bd6; 0x0000; 0x9c2f; 0x0000; 0x9c52; 0x0000; + 0x9d09; 0x0000; 0x9d60; 0x0000; 0x0000; 0x0c00; 0x5307; 0x1e66; + 0x0053; 0x7f05; 0xb800; 0x5bdb; 0x1e85; 0x0060; 0x622f; 0x7a00; + 0x61f2; 0x2120; 0x0064; 0x6f2f; 0x7500; 0x6adb; 0x0609; 0x006f; + 0x230e; 0xd800; 0x6ff9; 0x2fcc; 0x0071; 0x490e; 0xd900; 0x7337; + 0x2fc9; 0x0086; 0x121f; 0x2f00; 0x0000; 0x0a00; 0x537f; 0x0000; + 0x6062; 0x0000; 0x717d; 0x0000; 0x7337; 0x0000; 0x7515; 0x0000; + 0x7b08; 0x0000; 0x7c3e; 0x0000; 0x7c7e; 0x0000; 0x8a1d; 0x0000; + 0x9041; 0x0000; 0x0000; 0x0300; 0x55a9; 0x2fcb; 0x0067; 0x171e; + 0xb800; 0x7511; 0x2f89; 0x0000; 0x0002; 0x0075; 0x1100; 0x0099; + 0x0c00; 0x0000; 0x0001; 0x0053; 0xa92f; 0xc700; 0x0000; 0x0100; + 0x53a9; + ] 0x00 + + +let unmarshaled = + let u = Uchar.of_int in + let ( --> ) x y = (x, y) in + DecodeCmap.[ + u 0xFE00 --> ( + [ + { start_unicode_value = u 0x51A4; additional_count = 0 }; + { start_unicode_value = u 0x5C60; additional_count = 0 }; + { start_unicode_value = u 0x6753; additional_count = 0 }; + { start_unicode_value = u 0x6ADB; additional_count = 0 }; + { start_unicode_value = u 0x6EBA; additional_count = 0 }; + { start_unicode_value = u 0x6F23; additional_count = 0 }; + { start_unicode_value = u 0x7149; additional_count = 0 }; + ], + [ + (u 0x4FAE, 8491); (u 0x50E7, 8471); (u 0x514D, 8497); (u 0x51DE, 12190); + (u 0x52C9, 8493); (u 0x52E4, 8451); (u 0x5351, 8487); (u 0x559D, 7353); + (u 0x5606, 8477); (u 0x5668, 8446); (u 0x5840, 8492); (u 0x585A, 7417); + (u 0x58A8, 8495); (u 0x5C64, 8472); (u 0x5C6E, 9851); (u 0x5ECA, 12186); + (u 0x6094, 8439); (u 0x6168, 8441); (u 0x618E, 8474); (u 0x61F2, 12202); + (u 0x654F, 8490); (u 0x65E2, 8447); (u 0x6674, 7856); (u 0x6691, 8465); + (u 0x6717, 12188); (u 0x6885, 8484); (u 0x6B04, 8500); (u 0x6BBA, 8457); + (u 0x6D77, 8440); (u 0x6E1A, 7387); (u 0x6F22, 8445); (u 0x716E, 8460); + (u 0x722B, 9452); (u 0x732A, 7921); (u 0x7422, 7407); (u 0x76CA, 7943); + (u 0x7891, 8488); (u 0x793C, 7951); (u 0x793E, 8461); (u 0x7948, 8448); + + (u 0x7949, 8458); (u 0x7950, 8499); (u 0x7956, 8470); (u 0x795D, 8464); + (u 0x795E, 7952); (u 0x7965, 7953); (u 0x798D, 8438); (u 0x798E, 8481); + (u 0x798F, 7955); (u 0x7A40, 8456); (u 0x7A81, 8482); (u 0x7BC0, 8469); + (u 0x7CBE, 7962); (u 0x7E09, 11380); (u 0x7E41, 8485); (u 0x7F72, 8466); + (u 0x7FBD, 7970); (u 0x8005, 8462); (u 0x81ED, 8463); (u 0x8279, 8571); + (u 0x8457, 8478); (u 0x8612, 12203); (u 0x865C, 8502); (u 0x8910, 8444); + (u 0x8996, 8459); (u 0x8AF8, 7993); (u 0x8B01, 8434); (u 0x8B39, 8452); + (u 0x8CD3, 8489); (u 0x8D08, 8475); (u 0x8FB6, 9453); (u 0x9038, 8003); + (u 0x90FD, 8006); (u 0x9686, 8501); (u 0x96E3, 8483); (u 0x9756, 7959); + (u 0x97FF, 8450); (u 0x983B, 7449); (u 0x985E, 8504); (u 0x98EF, 8067); + + (u 0x98FC, 8068); (u 0x9928, 8070); (u 0x9DB4, 8083); + ]); + u 0xFE01 --> ( + [], + [ + (u 0x7DF4, 8507); (u 0x8279, 8570); (u 0x9038, 8433); + ]); + u 0xE0100 --> ( + [ + { start_unicode_value = u 0x5307; additional_count = 0 }; + { start_unicode_value = u 0x55A9; additional_count = 0 }; + { start_unicode_value = u 0x5BDB; additional_count = 0 }; + { start_unicode_value = u 0x61F2; additional_count = 0 }; + { start_unicode_value = u 0x646F; additional_count = 0 }; + { start_unicode_value = u 0x6ADB; additional_count = 0 }; + { start_unicode_value = u 0x6F23; additional_count = 0 }; + { start_unicode_value = u 0x6FF9; additional_count = 0 }; + { start_unicode_value = u 0x7149; additional_count = 0 }; + { start_unicode_value = u 0x8612; additional_count = 0 }; + ], + [ + (u 0x5026, 1629); (u 0x50C5, 1501); (u 0x5132, 3579); (u 0x514E, 2902); + (u 0x51A4, 3994); (u 0x53A9, 1009); (u 0x53C9, 1851); (u 0x53DB, 3178); + (u 0x53DF, 4098); (u 0x54AC, 4134); (u 0x54E8, 2211); (u 0x55B0, 1538); + (u 0x5632, 4199); (u 0x5642, 1013); (u 0x564C, 2513); (u 0x56C0, 4220); + (u 0x5835, 2904); (u 0x5906, 10483); (u 0x5A29, 3392); (u 0x5C51, 1547); + (u 0x5C60, 2907); (u 0x5DF7, 1747); (u 0x5E96, 3423); (u 0x5EDF, 3272); + (u 0x5EFB, 1164); (u 0x5F98, 4562); (u 0x5FBD, 1371); (u 0x6062, 1168); + (u 0x609E, 8766); (u 0x6108, 3614); (u 0x6241, 4709); (u 0x633A, 2852); + (u 0x633D, 3198); (u 0x6357, 2799); (u 0x6372, 1642); (u 0x63C3, 2605); + (u 0x647A, 2396); (u 0x64B0, 2475); (u 0x64E2, 2871); (u 0x65A7, 3304); + + (u 0x6666, 1174); (u 0x66B5, 10769); (u 0x6753, 2079); (u 0x6756, 2289); + (u 0x6897, 1764); (u 0x6962, 3032); (u 0x696F, 2173); (u 0x698A, 1901); + (u 0x6994, 3821); (u 0x69CC, 2810); (u 0x6A0B, 3231); (u 0x6A3D, 2690); + (u 0x6B4E, 2699); (u 0x6C72, 1426); (u 0x6DEB, 982); (u 0x6EA2, 968); + (u 0x6EBA, 2878); (u 0x7015, 3286); (u 0x701E, 3010); (u 0x7026, 2761); + (u 0x7058, 3029); (u 0x7078, 1428); (u 0x707C, 2080); (u 0x714E, 2483); + (u 0x7152, 8945); (u 0x717D, 2484); (u 0x723A, 3598); (u 0x724C, 3107); + (u 0x7259, 1149); (u 0x72E1, 5395); (u 0x7337, 3634); (u 0x7511, 1825); + (u 0x7515, 5470); (u 0x7526, 5473); (u 0x75BC, 5507); (u 0x77A5, 3379); + (u 0x7941, 1571); (u 0x7947, 1392); (u 0x79B0, 3061); (u 0x79E4, 3125); + + (u 0x7A17, 3243); (u 0x7A7F, 2486); (u 0x7AC8, 5703); (u 0x7B08, 1432); + (u 0x7B75, 5742); (u 0x7BAD, 2487); (u 0x7BB8, 3150); (u 0x7BC7, 3385); + (u 0x7BDD, 5767); (u 0x7C3E, 3802); (u 0x7C7E, 3588); (u 0x7C82, 1557); + (u 0x7FEB, 1335); (u 0x7FF0, 1311); (u 0x8171, 6024); (u 0x817F, 2642); + (u 0x8258, 6076); (u 0x8292, 6091); (u 0x82A6, 908); (u 0x8328, 971); + (u 0x845B, 1247); (u 0x84EC, 3437); (u 0x8511, 3380); (u 0x853D, 3369); + (u 0x85A9, 1931); (u 0x85AF, 2194); (u 0x85F7, 2195); (u 0x8654, 6247); + (u 0x86F8, 2675); (u 0x8703, 6276); (u 0x8755, 2310); (u 0x8805, 6303); + (u 0x8956, 1086); (u 0x8A0A, 2354); (u 0x8A1D, 6428); (u 0x8A3B, 2756); + (u 0x8A6E, 2495); (u 0x8AB9, 3226); (u 0x8AFA, 1675); (u 0x8B0E, 3028); + + (u 0x8B2C, 3261); (u 0x8B7F, 7996); (u 0x8C79, 3271); (u 0x8CED, 2914); + (u 0x8FBB, 2822); (u 0x8FBF, 2685); (u 0x8FC2, 994); (u 0x8FC4, 3516); + (u 0x8FE6, 1142); (u 0x9017, 2364); (u 0x9019, 3123); (u 0x9022, 899); + (u 0x903C, 3255); (u 0x9041, 3017); (u 0x905C, 2611); (u 0x9061, 2532); + (u 0x912D, 2866); (u 0x914B, 2134); (u 0x91DC, 1260); (u 0x9306, 1936); + (u 0x9375, 1658); (u 0x939A, 2812); (u 0x9453, 3612); (u 0x9699, 1616); + (u 0x9771, 6918); (u 0x9784, 1255); (u 0x9798, 2274); (u 0x97AD, 3394); + (u 0x98F4, 917); (u 0x9905, 3585); (u 0x990C, 1018); (u 0x9910, 1957); + (u 0x9957, 1488); (u 0x99C1, 3145); (u 0x9A19, 7014); (u 0x9A4A, 9391); + (u 0x9BAB, 1937); (u 0x9BD6, 1934); (u 0x9C2F, 973); (u 0x9C52, 3508); + + (u 0x9D09, 7128); (u 0x9D60, 1820); + ]); + u 0xE0101 --> ( + [ + { start_unicode_value = u 0x5026; additional_count = 0 }; + { start_unicode_value = u 0x50C5; additional_count = 0 }; + { start_unicode_value = u 0x5132; additional_count = 0 }; + { start_unicode_value = u 0x514E; additional_count = 0 }; + { start_unicode_value = u 0x51A4; additional_count = 0 }; + { start_unicode_value = u 0x53C9; additional_count = 0 }; + { start_unicode_value = u 0x53DB; additional_count = 0 }; + { start_unicode_value = u 0x53DF; additional_count = 0 }; + { start_unicode_value = u 0x54AC; additional_count = 0 }; + { start_unicode_value = u 0x54E8; additional_count = 0 }; + + { start_unicode_value = u 0x55B0; additional_count = 0 }; + { start_unicode_value = u 0x5632; additional_count = 0 }; + { start_unicode_value = u 0x5642; additional_count = 0 }; + { start_unicode_value = u 0x564C; additional_count = 0 }; + { start_unicode_value = u 0x56C0; additional_count = 0 }; + { start_unicode_value = u 0x5835; additional_count = 0 }; + { start_unicode_value = u 0x5906; additional_count = 0 }; + { start_unicode_value = u 0x5A29; additional_count = 0 }; + { start_unicode_value = u 0x5C51; additional_count = 0 }; + { start_unicode_value = u 0x5C60; additional_count = 0 }; + + { start_unicode_value = u 0x5DF7; additional_count = 0 }; + { start_unicode_value = u 0x5E96; additional_count = 0 }; + { start_unicode_value = u 0x5EDF; additional_count = 0 }; + { start_unicode_value = u 0x5EFB; additional_count = 0 }; + { start_unicode_value = u 0x5F98; additional_count = 0 }; + { start_unicode_value = u 0x5FBD; additional_count = 0 }; + { start_unicode_value = u 0x609E; additional_count = 0 }; + { start_unicode_value = u 0x6108; additional_count = 0 }; + { start_unicode_value = u 0x6241; additional_count = 0 }; + { start_unicode_value = u 0x633A; additional_count = 0 }; + + { start_unicode_value = u 0x633D; additional_count = 0 }; + { start_unicode_value = u 0x6357; additional_count = 0 }; + { start_unicode_value = u 0x6372; additional_count = 0 }; + { start_unicode_value = u 0x63C3; additional_count = 0 }; + { start_unicode_value = u 0x647A; additional_count = 0 }; + { start_unicode_value = u 0x64B0; additional_count = 0 }; + { start_unicode_value = u 0x64E2; additional_count = 0 }; + { start_unicode_value = u 0x65A7; additional_count = 0 }; + { start_unicode_value = u 0x6666; additional_count = 0 }; + { start_unicode_value = u 0x66B5; additional_count = 0 }; + + { start_unicode_value = u 0x6717; additional_count = 0 }; + { start_unicode_value = u 0x6753; additional_count = 0 }; + { start_unicode_value = u 0x6756; additional_count = 0 }; + { start_unicode_value = u 0x6897; additional_count = 0 }; + { start_unicode_value = u 0x6962; additional_count = 0 }; + { start_unicode_value = u 0x696F; additional_count = 0 }; + { start_unicode_value = u 0x698A; additional_count = 0 }; + { start_unicode_value = u 0x6994; additional_count = 0 }; + { start_unicode_value = u 0x69CC; additional_count = 0 }; + { start_unicode_value = u 0x6A0B; additional_count = 0 }; + + { start_unicode_value = u 0x6A3D; additional_count = 0 }; + { start_unicode_value = u 0x6B4E; additional_count = 0 }; + { start_unicode_value = u 0x6C72; additional_count = 0 }; + { start_unicode_value = u 0x6DEB; additional_count = 0 }; + { start_unicode_value = u 0x6EA2; additional_count = 0 }; + { start_unicode_value = u 0x6EBA; additional_count = 0 }; + { start_unicode_value = u 0x7015; additional_count = 0 }; + { start_unicode_value = u 0x701E; additional_count = 0 }; + { start_unicode_value = u 0x7026; additional_count = 0 }; + { start_unicode_value = u 0x7058; additional_count = 0 }; + + { start_unicode_value = u 0x7078; additional_count = 0 }; + { start_unicode_value = u 0x707C; additional_count = 0 }; + { start_unicode_value = u 0x714E; additional_count = 0 }; + { start_unicode_value = u 0x7152; additional_count = 0 }; + { start_unicode_value = u 0x723A; additional_count = 0 }; + { start_unicode_value = u 0x724C; additional_count = 0 }; + { start_unicode_value = u 0x7259; additional_count = 0 }; + { start_unicode_value = u 0x72E1; additional_count = 0 }; + { start_unicode_value = u 0x7526; additional_count = 0 }; + { start_unicode_value = u 0x75BC; additional_count = 0 }; + + { start_unicode_value = u 0x77A5; additional_count = 0 }; + { start_unicode_value = u 0x7941; additional_count = 0 }; + { start_unicode_value = u 0x7947; additional_count = 0 }; + { start_unicode_value = u 0x79B0; additional_count = 0 }; + { start_unicode_value = u 0x79E4; additional_count = 0 }; + { start_unicode_value = u 0x7A17; additional_count = 0 }; + { start_unicode_value = u 0x7A7F; additional_count = 0 }; + { start_unicode_value = u 0x7AC8; additional_count = 0 }; + { start_unicode_value = u 0x7B75; additional_count = 0 }; + { start_unicode_value = u 0x7BAD; additional_count = 0 }; + + { start_unicode_value = u 0x7BB8; additional_count = 0 }; + { start_unicode_value = u 0x7BC7; additional_count = 0 }; + { start_unicode_value = u 0x7BDD; additional_count = 0 }; + { start_unicode_value = u 0x7C82; additional_count = 0 }; + { start_unicode_value = u 0x7FEB; additional_count = 0 }; + { start_unicode_value = u 0x7FF0; additional_count = 0 }; + { start_unicode_value = u 0x8171; additional_count = 0 }; + { start_unicode_value = u 0x817F; additional_count = 0 }; + { start_unicode_value = u 0x8258; additional_count = 0 }; + { start_unicode_value = u 0x8292; additional_count = 0 }; + + { start_unicode_value = u 0x82A6; additional_count = 0 }; + { start_unicode_value = u 0x8328; additional_count = 0 }; + { start_unicode_value = u 0x845B; additional_count = 0 }; + { start_unicode_value = u 0x84EC; additional_count = 0 }; + { start_unicode_value = u 0x8511; additional_count = 0 }; + { start_unicode_value = u 0x853D; additional_count = 0 }; + { start_unicode_value = u 0x85A9; additional_count = 0 }; + { start_unicode_value = u 0x85AF; additional_count = 0 }; + { start_unicode_value = u 0x85F7; additional_count = 0 }; + { start_unicode_value = u 0x8654; additional_count = 0 }; + + { start_unicode_value = u 0x86F8; additional_count = 0 }; + { start_unicode_value = u 0x8703; additional_count = 0 }; + { start_unicode_value = u 0x8755; additional_count = 0 }; + { start_unicode_value = u 0x8805; additional_count = 0 }; + { start_unicode_value = u 0x8956; additional_count = 0 }; + { start_unicode_value = u 0x8A0A; additional_count = 0 }; + { start_unicode_value = u 0x8A3B; additional_count = 0 }; + { start_unicode_value = u 0x8A6E; additional_count = 0 }; + { start_unicode_value = u 0x8AB9; additional_count = 0 }; + { start_unicode_value = u 0x8AFA; additional_count = 0 }; + + { start_unicode_value = u 0x8B0E; additional_count = 0 }; + { start_unicode_value = u 0x8B2C; additional_count = 0 }; + { start_unicode_value = u 0x8B7F; additional_count = 0 }; + { start_unicode_value = u 0x8C79; additional_count = 0 }; + { start_unicode_value = u 0x8CED; additional_count = 0 }; + { start_unicode_value = u 0x8FBB; additional_count = 0 }; + { start_unicode_value = u 0x8FBF; additional_count = 0 }; + { start_unicode_value = u 0x8FC2; additional_count = 0 }; + { start_unicode_value = u 0x8FC4; additional_count = 0 }; + { start_unicode_value = u 0x8FE6; additional_count = 0 }; + + { start_unicode_value = u 0x9017; additional_count = 0 }; + { start_unicode_value = u 0x9019; additional_count = 0 }; + { start_unicode_value = u 0x9022; additional_count = 0 }; + { start_unicode_value = u 0x903C; additional_count = 0 }; + { start_unicode_value = u 0x905C; additional_count = 0 }; + { start_unicode_value = u 0x9061; additional_count = 0 }; + { start_unicode_value = u 0x912D; additional_count = 0 }; + { start_unicode_value = u 0x914B; additional_count = 0 }; + { start_unicode_value = u 0x91DC; additional_count = 0 }; + { start_unicode_value = u 0x9306; additional_count = 0 }; + + { start_unicode_value = u 0x9375; additional_count = 0 }; + { start_unicode_value = u 0x939A; additional_count = 0 }; + { start_unicode_value = u 0x9453; additional_count = 0 }; + { start_unicode_value = u 0x9699; additional_count = 0 }; + { start_unicode_value = u 0x9771; additional_count = 0 }; + { start_unicode_value = u 0x9784; additional_count = 0 }; + { start_unicode_value = u 0x9798; additional_count = 0 }; + { start_unicode_value = u 0x97AD; additional_count = 0 }; + { start_unicode_value = u 0x98F4; additional_count = 0 }; + { start_unicode_value = u 0x9905; additional_count = 0 }; + + { start_unicode_value = u 0x9910; additional_count = 0 }; + { start_unicode_value = u 0x9957; additional_count = 0 }; + { start_unicode_value = u 0x99C1; additional_count = 0 }; + { start_unicode_value = u 0x9A19; additional_count = 0 }; + { start_unicode_value = u 0x9A4A; additional_count = 0 }; + { start_unicode_value = u 0x9BAB; additional_count = 0 }; + { start_unicode_value = u 0x9BD6; additional_count = 0 }; + { start_unicode_value = u 0x9C2F; additional_count = 0 }; + { start_unicode_value = u 0x9C52; additional_count = 0 }; + { start_unicode_value = u 0x9D09; additional_count = 0 }; + + { start_unicode_value = u 0x9D60; additional_count = 0 }; + ], + [ + (u 0x5307, 7782); (u 0x537F, 1464); (u 0x5BDB, 7813); (u 0x6062, 12154); + (u 0x61F2, 8480); (u 0x646F, 12149); (u 0x6ADB, 1545); (u 0x6F23, 3800); + (u 0x6FF9, 12236); (u 0x7149, 3801); (u 0x7337, 12233); (u 0x8612, 7983); + ]); + u 0xE0102 --> ( + [ + { start_unicode_value = u 0x537F; additional_count = 0 }; + { start_unicode_value = u 0x6062; additional_count = 0 }; + { start_unicode_value = u 0x717D; additional_count = 0 }; + { start_unicode_value = u 0x7337; additional_count = 0 }; + { start_unicode_value = u 0x7515; additional_count = 0 }; + { start_unicode_value = u 0x7B08; additional_count = 0 }; + { start_unicode_value = u 0x7C3E; additional_count = 0 }; + { start_unicode_value = u 0x7C7E; additional_count = 0 }; + { start_unicode_value = u 0x8A1D; additional_count = 0 }; + { start_unicode_value = u 0x9041; additional_count = 0 }; + ], + [ + (u 0x55A9, 12235); (u 0x6717, 7864); (u 0x7511, 12169); + ]); + u 0xE0103 --> ( + [ + { start_unicode_value = u 0x7511; additional_count = 0 }; + { start_unicode_value = u 0x990C; additional_count = 0 }; + ], + [ + (u 0x53A9, 12231); + ]); + u 0xE0105 --> ( + [ + { start_unicode_value = u 0x53A9; additional_count = 0 }; + ], + []); + ] diff --git a/test/testUtil.ml b/test/testUtil.ml index 0384e7a..0dae2ae 100644 --- a/test/testUtil.ml +++ b/test/testUtil.ml @@ -66,6 +66,14 @@ let encoding = Alcotest.(result (of_pp pp_xxd) (of_pp EncodeError.pp)) +let uchar = + Alcotest.(of_pp pp_uchar) + + +let glyph_id = + Alcotest.int + + let utf8_to_utf16be s_utf8 = let buffer = Buffer.create (String.length s_utf8 * 4) in let decoder = Uutf.decoder ~encoding:`UTF_8 (`String(s_utf8)) in From 2d9066c6a379e188ba481649da90dbf17835d029 Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Mon, 5 Jun 2023 23:45:14 +0900 Subject: [PATCH 16/26] update README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b1543e3..409e524 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ ## Table of Contents -- [Supported features](#supported-features) - [How to install](#how-to-install) - [Usage of an example CLI `otfedcli`](#usage-of-an-example-cli-otfedcli) - [Development status](#development-status) @@ -44,8 +43,10 @@ $ dune exec otfedcli | cmap_word "" # Consults `cmap` subtables for each character in the given text. | head # Prints the contents of `head` table. | hhea # Prints the contents of `hhea` table. + | vhea # Prints the contents of `vhea` table. | maxp # Prints the contents of `maxp` table. | hmtx # Consults the `hmtx` table by the glyph of ID . + | vmtx # Consults the `vmtx` table by the glyph of ID . | glyf # Outputs the glyph of ID that has TrueType outlines. | cff # Outputs the glyph of ID that has CFF outlines. | cff_lex # Prints the tokenized CharString of the glyph of ID . From 7f3b08f1cef05b9af1736f273c68591b612b1ec4 Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Tue, 6 Jun 2023 00:52:20 +0900 Subject: [PATCH 17/26] develop the table in README --- README.md | 126 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 73 insertions(+), 53 deletions(-) diff --git a/README.md b/README.md index 409e524..96dc773 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,7 @@ $ opam install otfed ## Usage of an example CLI `otfedcli` ```console -$ dune exec otfedcli - - ::= []* +$ dune exec otfedcli ... ::= | tables # Prints all the tags of tables contained in the font. @@ -112,58 +110,80 @@ $ dune exec otfedcli input/Junicode.ttf subset 0,113,302 output/Junicode-subset. ## Development status -* v: done -* o: no automated test has been given, but seems to be working fine for many inputs -* \-: not supported yet +* Support: + - V = supported + - \- = not supported yet +* Test: + - V = having automated tests + - \- = not supported + - o = no automated test has been given, but seems to be working fine for many inputs + - x = not well-tested - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TablesEncoding operationsDecoding operations
SupportedTestedSupportedTested
Requiredcmapv (Format 12 only)v (Format 12 only)v (Format 4, 12, 13, and 14 only)v (Format 4, 12, and 14 only)
headvvvv
hheavvvv
hmtxvvvv
maxpvvvv
namevvvv
OS/2vv (version 2 only)vv (version 2 only)
postvvvv
TTFcvt␣----
fpgm----
glyfvvvv
locavv (short only)vv (long only)
prep----
gasp----
CFFCFF␣vovv
CFF2----
VORG----
SVGSVG␣----
OptionalDSIG----
kern--v (Format 0 only)o
vheavv (Version 1.0 only)vv (Version 1.0 only)
vmtxvvvv
AdvancedBASE----
GDEF----
GPOS--v (LookupType 1, 2, 4, 5, 6, and 9 only)o
GSUB--v (LookupType 1, 2, and 4 only)o
JSTF----
MATH--vv
Tables Encoding operationsDecoding operations
SupportedTestedSupportedTested
RequiredcmapFormat 0 ----
Format 2 ----
Format 4 --VV
Format 6 ----
Format 8 ----
Format 10 ----
Format 12 VVVV
Format 13 --VV
Format 14 VVVV
head VVVV
hhea VVVV
hmtx VVVV
maxp VVVV
name VVVV
OS/2ver. 0 VoVo
ver. 1 VoVo
ver. 2 VVVV
ver. 3, 4, & 5VoVo
post VVVV
TTF cvt␣ ----
fpgm ----
glyf VVVV
locashort VVVo
long VoVV
prep ----
gasp ----
CFF CFF␣ VoVV
CFF2 ----
VORG ----
SVG SVG␣ ----
Optional DSIG ----
kernFormat 0 --Vo
other ----
vheaver. 1.0 VVVV
ver. 1.1 VxVx
vmtx VVVV
AdvancedBASE ----
GDEF ----
GPOSLookupType 1 --Vo
LookupType 2 --Vo
LookupType 3 ----
LookupType 4 --Vo
LookupType 5 --Vo
LookupType 6 --Vo
LookupType 7 ----
LookupType 8 ----
LookupType 9 --Vo
GSUBLookupType 1 --Vo
LookupType 2 --Vo
LookupType 3 ----
LookupType 4 --Vo
LookupType 5 ----
LookupType 6 ----
LookupType 7 ----
LookupType 8 ----
JSTF ----
MATH --VV
From 9b61316a1c1bf4e97baadb5e03514e0edc8f2968 Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Tue, 6 Jun 2023 02:13:19 +0900 Subject: [PATCH 18/26] support GPOS Lookup Type 3 --- README.md | 2 +- bin/main.ml | 3 +++ src/decodeGpos.ml | 32 +++++++++++++++++++++++++++++++- src/otfed.mli | 15 ++++++++++++++- src/value.ml | 5 +++++ 5 files changed, 54 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 96dc773..ad90f07 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ $ dune exec otfedcli input/Junicode.ttf subset 0,113,302 output/Junicode-subset. GDEF ---- GPOSLookupType 1 --Vo LookupType 2 --Vo - LookupType 3 ---- + LookupType 3 --Vx LookupType 4 --Vo LookupType 5 --Vo LookupType 6 --Vo diff --git a/bin/main.ml b/bin/main.ml index c1e7668..0386aed 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -577,6 +577,9 @@ let print_gpos_feature (feature : D.Gpos.feature) = (pp_list Format.pp_print_int) (tos |> List.map (fun (cv2, _, _) -> cv2)) ) ) + ~cursive1:(fun () (gid, _entry_exit_record) -> + Format.printf " - cursive1: gid = %d@," gid + ) ~markbase1:(fun _class_count () mark_assoc base_assoc -> Format.printf " - markbase1: mark = {%a}, base = {%a}@," (pp_list Format.pp_print_int) (mark_assoc |> List.map (fun (gid, _) -> gid)) diff --git a/src/decodeGpos.ml b/src/decodeGpos.ml index 0e2da1d..c844f62 100644 --- a/src/decodeGpos.ml +++ b/src/decodeGpos.ml @@ -79,6 +79,7 @@ type subtable = | SinglePosAdjustment2 of (glyph_id * value_record) list | PairPosAdjustment1 of (glyph_id * (glyph_id * value_record * value_record) list) list | PairPosAdjustment2 of class_definition list * class_definition list * (class_value * (class_value * value_record * value_record) list) list + | CursivePos1 of (glyph_id * entry_exit_record) list | MarkBasePos1 of int * (glyph_id * mark_record) list * (glyph_id * base_record) list | MarkLigPos1 of int * (glyph_id * mark_record) list * (glyph_id * ligature_attach) list | MarkMarkPos1 of int * (glyph_id * mark_record) list * (glyph_id * mark2_record) list @@ -226,6 +227,29 @@ let d_anchor : anchor decoder = err @@ Error.UnknownFormatNumber(anchorFormat) +let d_entry_exit_record (offset_CursivePos : offset) : entry_exit_record decoder = + let open DecodeOperation in + d_fetch offset_CursivePos d_anchor >>= fun entry_anchor -> + d_fetch offset_CursivePos d_anchor >>= fun exit_anchor -> + return { entry_anchor; exit_anchor } + + +let d_cursive_attachment_subtable = + let open DecodeOperation in + (* The position is supposed to be set to the beginning of a CursivePos subtable [page 197]. *) + current >>= fun offset_CursivePos -> + d_uint16 >>= fun posFormat -> + match posFormat with + | 1 -> + d_fetch offset_CursivePos d_coverage >>= fun coverage -> + d_list (d_entry_exit_record offset_CursivePos) >>= fun entryExitRecords -> + combine_coverage coverage entryExitRecords >>= fun cursive_assoc -> + return (CursivePos1(cursive_assoc)) + + | _ -> + err @@ Error.UnknownFormatNumber(posFormat) + + let d_mark_record offset_MarkArray classCount : mark_record decoder = let open DecodeOperation in d_uint16 >>= fun classId -> @@ -354,7 +378,7 @@ let lookup_exact offsets lookupType : (subtable list) decoder = | 3 -> (* Cursive attachment positioning [page 197] *) - return [] (* TODO *) + pick_each offsets d_cursive_attachment_subtable | 4 -> (* MarkToBase attachment positioning [page 198] *) @@ -424,6 +448,8 @@ type 'a folding_pair1 = 'a -> glyph_id * (glyph_id * value_record * value_record type 'a folding_pair2 = class_definition list -> class_definition list -> 'a -> (class_value * (class_value * value_record * value_record) list) list -> 'a +type 'a folding_cursive1 = 'a -> glyph_id * entry_exit_record -> 'a + type 'a folding_markbase1 = int -> 'a -> (glyph_id * mark_record) list -> (glyph_id * base_record) list -> 'a type 'a folding_marklig1 = int -> 'a -> (glyph_id * mark_record) list -> (glyph_id * ligature_attach) list -> 'a @@ -436,6 +462,7 @@ let fold_subtables ?single2:(f_single2 = (fun acc _ -> acc)) ?pair1:(f_pair1 = (fun acc _ -> acc)) ?pair2:(f_pair2 = (fun _ _ acc _ -> acc)) + ?cursive1:(f_cursive1 = (fun acc _ -> acc)) ?markbase1:(f_markbase1 = (fun _ acc _ _ -> acc)) ?marklig1:(f_marklig1 = (fun _ acc _ _ -> acc)) ?markmark1:(f_markmark1 = (fun _ acc _ _ -> acc)) @@ -458,6 +485,9 @@ let fold_subtables | PairPosAdjustment2(clsdefs1, clsdefs2, assoc) -> f_pair2 clsdefs1 clsdefs2 acc assoc + | CursivePos1(assoc) -> + List.fold_left f_cursive1 acc assoc + | MarkBasePos1(classCount, mark_assoc, base_assoc) -> f_markbase1 classCount acc mark_assoc base_assoc diff --git a/src/otfed.mli b/src/otfed.mli index ad51b9e..c661545 100644 --- a/src/otfed.mli +++ b/src/otfed.mli @@ -124,6 +124,12 @@ module Value : sig type mark_class = int + (** The type for EntryExitRecord tables (page 198). *) + type entry_exit_record = { + entry_anchor : anchor; + exit_anchor : anchor; + } + (** The type for MarkRecord (page 217). *) type mark_record = mark_class * anchor @@ -1279,6 +1285,12 @@ module Decode : sig class_definition list -> class_definition list -> 'a -> (Value.class_value * (Value.class_value * Value.value_record * Value.value_record) list) list -> 'a + (** The type for functions that handle + Cursive attachment positioning (Lookup Type 3) subtables of Format 1 (OpenType p.197). + See [fold_subtables]. *) + type 'a folding_cursive1 = + 'a -> Value.glyph_id * Value.entry_exit_record -> 'a + (** The type for functions that handle MarkToBase attachment positioning (Lookup Type 4) subtables of Format 1 (OpenType p.198). See [fold_subtables]. *) @@ -1304,6 +1316,7 @@ module Decode : sig {ul {- Lookup Type 1: Single adjustment positioning (OpenType p.192),} {- Lookup Type 2: Pair adjustment positioning (OpenType p.194),} + {- Lookup Type 3: Cursive attachment positioning (OpenType p.197),} {- Lookup Type 4: MarkToBase attachment positioning (OpenType p.198),} {- Lookup Type 5: MarkToLigature attachment positioning (OpenType p.199),} {- Lookup Type 6: MarkToMark attachment positioning (OpenType p.201), and} @@ -1312,7 +1325,6 @@ module Decode : sig Subtables of the following types are ignored: {ul - {- Lookup Type 3: Cursive attachment positioning (OpenType p.197),} {- Lookup Type 7: Contextual positioning (OpenType p.203), and} {- Lookup Type 8: Chaining contextual positioning (OpenType p.209).}} *) val fold_subtables : @@ -1320,6 +1332,7 @@ module Decode : sig ?single2:('a folding_single2) -> ?pair1:('a folding_pair1) -> ?pair2:('a folding_pair2) -> + ?cursive1:('a folding_cursive1) -> ?markbase1:('a folding_markbase1) -> ?marklig1:('a folding_marklig1) -> ?markmark1:('a folding_markmark1) -> diff --git a/src/value.ml b/src/value.ml index a5aab02..0fcc9be 100644 --- a/src/value.ml +++ b/src/value.ml @@ -303,6 +303,11 @@ type anchor = design_units * design_units * anchor_adjustment type mark_class = int +type entry_exit_record = { + entry_anchor : anchor; + exit_anchor : anchor; +} + type mark_record = mark_class * anchor type base_record = (anchor option) array From 68f1db3fdb95d82847e0790ae3a56644b68d2fce Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Tue, 6 Jun 2023 02:22:19 +0900 Subject: [PATCH 19/26] make Entry/ExitAnchor optional (according to the spec) --- src/decodeGpos.ml | 4 ++-- src/otfed.mli | 4 ++-- src/value.ml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/decodeGpos.ml b/src/decodeGpos.ml index c844f62..897b30b 100644 --- a/src/decodeGpos.ml +++ b/src/decodeGpos.ml @@ -229,8 +229,8 @@ let d_anchor : anchor decoder = let d_entry_exit_record (offset_CursivePos : offset) : entry_exit_record decoder = let open DecodeOperation in - d_fetch offset_CursivePos d_anchor >>= fun entry_anchor -> - d_fetch offset_CursivePos d_anchor >>= fun exit_anchor -> + d_fetch_opt offset_CursivePos d_anchor >>= fun entry_anchor -> + d_fetch_opt offset_CursivePos d_anchor >>= fun exit_anchor -> return { entry_anchor; exit_anchor } diff --git a/src/otfed.mli b/src/otfed.mli index c661545..9935b2e 100644 --- a/src/otfed.mli +++ b/src/otfed.mli @@ -126,8 +126,8 @@ module Value : sig (** The type for EntryExitRecord tables (page 198). *) type entry_exit_record = { - entry_anchor : anchor; - exit_anchor : anchor; + entry_anchor : anchor option; + exit_anchor : anchor option; } (** The type for MarkRecord (page 217). *) diff --git a/src/value.ml b/src/value.ml index 0fcc9be..a86a2e5 100644 --- a/src/value.ml +++ b/src/value.ml @@ -304,8 +304,8 @@ type anchor = design_units * design_units * anchor_adjustment type mark_class = int type entry_exit_record = { - entry_anchor : anchor; - exit_anchor : anchor; + entry_anchor : anchor option; + exit_anchor : anchor option; } type mark_record = mark_class * anchor From 39ff5926ba8c13c484cd0d39a14f28d726a336d2 Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Tue, 27 Jun 2023 15:15:57 +0900 Subject: [PATCH 20/26] release 0.2.0 --- CHANGELOG.md | 13 ++++++++++++- dune-project | 2 +- otfed.opam | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0eafa18..d3dba54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/), ## [Unreleased] +## [0.2.0] - 2023-06-27 +### Changed +- Rename `Hhea.t`'s `xmax_extent` to `x_max_extent` ([PR\#45](https://github.com/gfngfn/otfed/pull/45); breaking change). +- Change the interface of `Otfed.Decode.Cmap.get_subtables` ([PR\#47](https://github.com/gfngfn/otfed/pull/47); breaking change). + +### Added +- Support `vhea` tables ([PR\#45](https://github.com/gfngfn/otfed/pull/45)). +- Support `vmtx` tables ([PR\#46](https://github.com/gfngfn/otfed/pull/46)). +- Support decoding of `GPOS` Lookup Type 3, i.e., subtables for cursive attachment ([PR\#49](https://github.com/gfngfn/otfed/pull/49)). + ## [0.1.0] - 2023-05-26 ### Changed - Make `Otfed.Decode.Ttf.loca` distinguish "having an empty glyph" from "undefined" (breaking change). @@ -19,5 +29,6 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/), - Initial version of Otfed - [Unreleased]: https://github.com/gfngfn/otfed/compare/0.1.0...HEAD + [Unreleased]: https://github.com/gfngfn/otfed/compare/0.2.0...HEAD + [0.2.0]: https://github.com/gfngfn/otfed/compare/0.1.0...0.2.0 [0.1.0]: https://github.com/gfngfn/otfed/compare/0.0.1...0.1.0 diff --git a/dune-project b/dune-project index e852f74..f435131 100644 --- a/dune-project +++ b/dune-project @@ -1,6 +1,6 @@ (lang dune 2.7) (name otfed) -(version 0.1.0) +(version 0.2.0) (authors "Takashi Suwa") (maintainers "Takashi Suwa") (license MIT) diff --git a/otfed.opam b/otfed.opam index 1e18925..4481715 100644 --- a/otfed.opam +++ b/otfed.opam @@ -1,6 +1,6 @@ # This file is generated by dune, edit dune-project instead opam-version: "2.0" -version: "0.1.0" +version: "0.2.0" synopsis: "An OpenType encoder/decoder for OCaml" description: "" maintainer: ["Takashi Suwa"] From 15433f18804edf9d55b710a3bb0152ed592ebdcb Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Fri, 29 Sep 2023 23:20:26 +0900 Subject: [PATCH 21/26] fix how to handle `usWeightClass` --- src/decodeError.ml | 2 +- src/decodeOs2.ml | 18 ++++++------------ src/encodeError.ml | 1 + src/encodeOs2.ml | 19 +++++-------------- src/otfed.mli | 17 +++-------------- src/value.ml | 14 +------------- test/testCaseOs21.ml | 2 +- 7 files changed, 18 insertions(+), 55 deletions(-) diff --git a/src/decodeError.ml b/src/decodeError.ml index 93a927d..c159d45 100644 --- a/src/decodeError.ml +++ b/src/decodeError.ml @@ -65,7 +65,7 @@ type t = | UnexpectedMacStyle of int | UnknownCharstringToken of int | NegativeLengthForBytes of int - | UnknownWeightClass of int + | InvalidWeightClass of int | UnknownWidthClass of int | InvalidFsType of int | InvalidFsSelection of int diff --git a/src/decodeOs2.ml b/src/decodeOs2.ml index d301bde..ded2149 100644 --- a/src/decodeOs2.ml +++ b/src/decodeOs2.ml @@ -4,19 +4,13 @@ open DecodeBasic open DecodeOperation.Open -let d_weight_class : Value.Os2.weight_class decoder = +let d_weight_class : int decoder = let open DecodeOperation in - d_uint16 >>= function - | 100 -> return Value.Os2.WeightThin - | 200 -> return Value.Os2.WeightExtraLight - | 300 -> return Value.Os2.WeightLight - | 400 -> return Value.Os2.WeightNormal - | 500 -> return Value.Os2.WeightMedium - | 600 -> return Value.Os2.WeightSemiBold - | 700 -> return Value.Os2.WeightBold - | 800 -> return Value.Os2.WeightExtraBold - | 900 -> return Value.Os2.WeightBlack - | n -> err @@ UnknownWeightClass(n) + d_uint16 >>= fun n -> + if (1 <= n && n <= 1000) then + return n + else + err @@ InvalidWeightClass(n) let d_width_class : Value.Os2.width_class decoder = diff --git a/src/encodeError.ml b/src/encodeError.ml index 0c7cff7..e80ef48 100644 --- a/src/encodeError.ml +++ b/src/encodeError.ml @@ -14,6 +14,7 @@ type t = | NotEncodableAsUint32 of wint | NotEncodableAsInt32 of wint | NotEncodableAsTimestamp of wint + | InvalidWeightClass of int | NotA10BytePanose of string | NotA4ByteAchVendId of string | Version4FsSelection of Value.Os2.fs_selection diff --git a/src/encodeOs2.ml b/src/encodeOs2.ml index 1aca194..c4a547b 100644 --- a/src/encodeOs2.ml +++ b/src/encodeOs2.ml @@ -131,21 +131,12 @@ let e_extension1 ((ext1, ext2_option) : extension1) = return () -let e_weight_class (weight_class : Value.Os2.weight_class) = +let e_weight_class (weight_class : int) = let open EncodeOperation in - let u = - match weight_class with - | Value.Os2.WeightThin -> 100 - | Value.Os2.WeightExtraLight -> 200 - | Value.Os2.WeightLight -> 300 - | Value.Os2.WeightNormal -> 400 - | Value.Os2.WeightMedium -> 500 - | Value.Os2.WeightSemiBold -> 600 - | Value.Os2.WeightBold -> 700 - | Value.Os2.WeightExtraBold -> 800 - | Value.Os2.WeightBlack -> 900 - in - e_uint16 u + if (1 <= weight_class && weight_class <= 1000) then + e_uint16 weight_class + else + err @@ InvalidWeightClass(weight_class) let e_width_class (width_class : Value.Os2.width_class) = diff --git a/src/otfed.mli b/src/otfed.mli index 0591158..1ddd0f6 100644 --- a/src/otfed.mli +++ b/src/otfed.mli @@ -229,18 +229,6 @@ module Value : sig (** Defines types for master data in [OS/2] tables. *) module Os2 : sig - type weight_class = - | WeightThin - | WeightExtraLight - | WeightLight - | WeightNormal - | WeightMedium - | WeightSemiBold - | WeightBold - | WeightExtraBold - | WeightBlack - [@@deriving show] - type width_class = | WidthUltraCondensed | WidthExtraCondensed @@ -277,7 +265,7 @@ module Value : sig [@@deriving show] type t = { - us_weight_class : weight_class; + us_weight_class : int; (* values from 1 to 1000. *) us_width_class : width_class; fs_type : fs_type; y_subscript_x_size : design_units; @@ -929,7 +917,7 @@ module Decode : sig | UnexpectedMacStyle of int | UnknownCharstringToken of int | NegativeLengthForBytes of int - | UnknownWeightClass of int + | InvalidWeightClass of int | UnknownWidthClass of int | InvalidFsType of int | InvalidFsSelection of int @@ -1370,6 +1358,7 @@ module Encode : sig | NotEncodableAsUint32 of wint | NotEncodableAsInt32 of wint | NotEncodableAsTimestamp of wint + | InvalidWeightClass of int | NotA10BytePanose of string | NotA4ByteAchVendId of string | Version4FsSelection of Value.Os2.fs_selection diff --git a/src/value.ml b/src/value.ml index ad1a5de..5fba275 100644 --- a/src/value.ml +++ b/src/value.ml @@ -442,18 +442,6 @@ module Hhea = struct end module Os2 = struct - type weight_class = - | WeightThin - | WeightExtraLight - | WeightLight - | WeightNormal - | WeightMedium - | WeightSemiBold - | WeightBold - | WeightExtraBold - | WeightBlack - [@@deriving show { with_path = false }] - type width_class = | WidthUltraCondensed | WidthExtraCondensed @@ -490,7 +478,7 @@ module Os2 = struct [@@deriving show { with_path = false }] type t = { - us_weight_class : weight_class; + us_weight_class : int; (* values from 1 to 1000. *) us_width_class : width_class; fs_type : fs_type; y_subscript_x_size : design_units; diff --git a/test/testCaseOs21.ml b/test/testCaseOs21.ml index 8b944a2..24f20f5 100644 --- a/test/testCaseOs21.ml +++ b/test/testCaseOs21.ml @@ -19,7 +19,7 @@ let marshaled = let unmarshaled = Intermediate.Os2.{ value = Value.Os2.{ - us_weight_class = WeightNormal; + us_weight_class = 400; us_width_class = WidthMedium; fs_type = Value.Os2.{ From 2bd7aed6b8c312c0e59c97dc57226f8a7aa32691 Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Sat, 30 Sep 2023 01:56:43 +0900 Subject: [PATCH 22/26] fix how to decode Format 0 subtables of `kern` tables --- src/decodeKern.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/decodeKern.ml b/src/decodeKern.ml index a13b783..7b6dedf 100644 --- a/src/decodeKern.ml +++ b/src/decodeKern.ml @@ -72,6 +72,8 @@ let rec d_kerning_tables i t p acc = let (do_fold, acc) = t acc kern_info in if do_fold then d_uint16 >>= fun nPairs -> + d_skip 6 >>= fun () -> + (* Skips `searchRange`, `entrySelector`, and `rangeShift` *) d_kerning_pairs nPairs p acc >>= fun acc -> d_kerning_tables (i - 1) t p acc else @@ -90,7 +92,7 @@ let fold t p acc ikern = let dec = let open DecodeOperation in (* Only the Windows version of `kern` Table is supported; - the Apple version, which has a 32-bit version number, *) + the Apple version, which has a 32-bit version number, is not. *) d_uint16 >>= fun version -> match version with | 0 -> From 66dfdbcffe8ef95124100a172cef46095f47f088 Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Sat, 30 Sep 2023 06:07:31 +0900 Subject: [PATCH 23/26] add a unit test for `kern` tables --- src/decodeKern.ml | 29 ++++++++-------- test/otfedTest.ml | 28 +++++++++++++++ test/testCaseKern1.ml | 81 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 14 deletions(-) create mode 100644 test/testCaseKern1.ml diff --git a/src/decodeKern.ml b/src/decodeKern.ml index 7b6dedf..d5c1d06 100644 --- a/src/decodeKern.ml +++ b/src/decodeKern.ml @@ -88,18 +88,19 @@ let rec d_kerning_tables i t p acc = err @@ UnknownTableVersion(!% version) -let fold t p acc ikern = - let dec = - let open DecodeOperation in - (* Only the Windows version of `kern` Table is supported; - the Apple version, which has a 32-bit version number, is not. *) - d_uint16 >>= fun version -> - match version with - | 0 -> - d_uint16 >>= fun nTables -> - d_kerning_tables nTables t p acc +let d_kern t p acc = + let open DecodeOperation in + (* Only the Windows version of `kern` Table is supported; + the Apple version, which has a 32-bit version number, is not. *) + d_uint16 >>= fun version -> + match version with + | 0 -> + d_uint16 >>= fun nTables -> + d_kerning_tables nTables t p acc - | _ -> - err @@ UnknownTableVersion(!% version) - in - dec |> DecodeOperation.run ikern.core ikern.offset + | _ -> + err @@ UnknownTableVersion(!% version) + + +let fold t p acc ikern = + d_kern t p acc |> DecodeOperation.run ikern.core ikern.offset diff --git a/test/otfedTest.ml b/test/otfedTest.ml index b2a1da3..832d4e9 100644 --- a/test/otfedTest.ml +++ b/test/otfedTest.ml @@ -32,6 +32,7 @@ module DecodeTtf = Otfed__DecodeTtf module EncodeTtf = Otfed__EncodeTtf module DecodeCff = Otfed__DecodeCff module DecodeMath = Otfed__DecodeMath +module DecodeKern = Otfed__DecodeKern module DecodeError = Otfed__DecodeError module EncodeError = Otfed__EncodeError module Value = Otfed__Value @@ -464,6 +465,30 @@ let math_decoder_tests () = end +(** Tests for `DecodeKern` *) +let d_kern_tests () = + let dec = + DecodeKern.d_kern + (fun subtable_acc kern_info -> (true, (kern_info, Alist.empty) :: subtable_acc)) + (fun subtable_acc gid1 gid2 value -> + match subtable_acc with + | [] -> assert false + | (kern_info, acc) :: tail -> (kern_info, Alist.extend acc (gid1, gid2, value)) :: tail + ) + [] + in + let got = + dec |> run_decoder TestCaseKern1.marshaled |> Result.map (fun subtable_acc -> + subtable_acc |> List.rev_map (fun (kern_info, acc) -> + (kern_info, Alist.to_list acc) + ) + ) + in + let expected = Ok(TestCaseKern1.unmarshaled) in + Alcotest.(check (decoding (list (pair (of_pp DecodeKern.pp_kern_info) (list (triple int int int)))))) + "d_kern" expected got + + let () = let open Alcotest in run "Otfed" [ @@ -551,4 +576,7 @@ let () = ("DecodeMath", [ test_case "math_decoder" `Quick math_decoder_tests; ]); + ("DecodeKern", [ + test_case "d_kern" `Quick d_kern_tests; + ]); ] diff --git a/test/testCaseKern1.ml b/test/testCaseKern1.ml new file mode 100644 index 0000000..963aed7 --- /dev/null +++ b/test/testCaseKern1.ml @@ -0,0 +1,81 @@ + +open! Otfed__Basic +module DecodeKern = Otfed__DecodeKern + + +let marshaled = + TestUtil.make_string_even [ + (* `kern` header: *) + 0x0000; 0x0003; + (* First subtable's header: *) + 0x0000; 0x0086; 0x0001; 0x0014; 0x0040; 0x0004; + 0x0018; + (* First subtable's contents: *) + 0x0010; 0x0024; 0xffd3; 0x0010; 0x0025; 0xffb7; 0x0010; + 0x002a; 0x004b; 0x0010; 0x002d; 0x0072; 0x0010; 0x0032; 0x0039; + 0x0010; 0x0034; 0x004b; 0x0010; 0x0037; 0xff44; 0x0010; 0x0039; + 0xff88; 0x0010; 0x003a; 0xffad; 0x0010; 0x003b; 0xff9a; 0x0010; + 0x003c; 0xff0d; 0x0010; 0x0052; 0x0026; 0x0010; 0x0059; 0xffc9; + 0x0010; 0x005c; 0xffdc; 0x0010; 0x0082; 0xffd3; 0x0010; 0x0083; + 0xffd3; 0x0010; 0x0084; 0xffd3; 0x0010; 0x0085; 0xffd3; 0x0010; + 0x0086; 0xffd3; 0x0010; 0x0087; 0xffd3; + (* Second subtable's header: *) + 0x0000; 0x0086; 0x0001; + 0x0014; 0x0040; 0x0004; 0x0018; + (* Second subtable's contents: *) + 0x0141; 0x0011; 0xff6b; 0x0141; + 0x001d; 0xffb7; 0x0141; 0x0057; 0xffdc; 0x0141; 0x005a; 0xffdc; + 0x0141; 0x005c; 0xffdc; 0x0141; 0x006d; 0xffb7; 0x0141; 0x007d; + 0xffdc; 0x0141; 0x00c1; 0xffdc; 0x0141; 0x0125; 0xffdc; 0x0141; + 0x0127; 0xffdc; 0x0141; 0x0129; 0xffdc; 0x0141; 0x0137; 0xffdc; + 0x0141; 0x0139; 0xffdc; 0x0141; 0x0165; 0xffdc; 0x0141; 0x0167; + 0xffdc; 0x0141; 0x016e; 0xffdc; 0x0141; 0x01c6; 0xffdc; 0x0141; + 0x01d6; 0xffdc; 0x0141; 0x03e5; 0xffdc; 0x0141; 0x03e7; 0xffdc; + (* Third subtable's header: *) + 0x0000; 0x0086; 0x0001; 0x0014; 0x0040; 0x0004; 0x0018; + (* Third subtable's contents: *) + 0x03d9; + 0x0010; 0xff7d; 0x03d9; 0x0011; 0xff44; 0x03d9; 0x001d; 0xffdc; + 0x03d9; 0x0046; 0xffd3; 0x03d9; 0x0047; 0xffdc; 0x03d9; 0x0048; + 0xffd3; 0x03d9; 0x004a; 0xffdc; 0x03d9; 0x004b; 0xffdc; 0x03d9; + 0x0050; 0xffdc; 0x03d9; 0x0051; 0xffdc; 0x03d9; 0x0052; 0xffd3; + 0x03d9; 0x0054; 0xffdc; 0x03d9; 0x005b; 0xffc9; 0x03d9; 0x006d; + 0xffb7; 0x03d9; 0x00a9; 0xffd3; 0x03d9; 0x00aa; 0xffd3; 0x03d9; + 0x00ab; 0xffd3; 0x03d9; 0x00ac; 0xffd3; 0x03d9; 0x00ad; 0xffd3; + 0x03d9; 0x00b3; 0xffdc; + ] + + +let unmarshaled = + [ + ( + DecodeKern.{ horizontal = true; minimum = false; cross_stream = false }, + [ + (16, 36, -45); (16, 37, -73); (16, 42, 75); (16, 45, 114); + (16, 50, 57); (16, 52, 75); (16, 55, -188); (16, 57, -120); + (16, 58, -83); (16, 59, -102); (16, 60, -243); (16, 82, 38); + (16, 89, -55); (16, 92, -36); (16, 130, -45); (16, 131, -45); + (16, 132, -45); (16, 133, -45); (16, 134, -45); (16, 135, -45); + ] + ); + ( + DecodeKern.{ horizontal = true; minimum = false; cross_stream = false }, + [ + (321, 17, -149); (321, 29, -73); (321, 87, -36); (321, 90, -36); + (321, 92, -36); (321, 109, -73); (321, 125, -36); (321, 193, -36); + (321, 293, -36); (321, 295, -36); (321, 297, -36); (321, 311, -36); + (321, 313, -36); (321, 357, -36); (321, 359, -36); (321, 366, -36); + (321, 454, -36); (321, 470, -36); (321, 997, -36); (321, 999, -36); + ] + ); + ( + DecodeKern.{ horizontal = true; minimum = false; cross_stream = false }, + [ + (985, 16, -131); (985, 17, -188); (985, 29, -36); (985, 70, -45); + (985, 71, -36); (985, 72, -45); (985, 74, -36); (985, 75, -36); + (985, 80, -36); (985, 81, -36); (985, 82, -45); (985, 84, -36); + (985, 91, -55); (985, 109, -73); (985, 169, -45); (985, 170, -45); + (985, 171, -45); (985, 172, -45); (985, 173, -45); (985, 179, -36); + ] + ); + ] From dff1cbf4a506b2ec596bba4e3a91953ea2fce15c Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Sat, 30 Sep 2023 06:09:56 +0900 Subject: [PATCH 24/26] update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ad90f07..78e5509 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ $ dune exec otfedcli input/Junicode.ttf subset 0,113,302 output/Junicode-subset. SVG SVG␣ ---- Optional DSIG ---- - kernFormat 0 --Vo + kernFormat 0 --VV other ---- vheaver. 1.0 VVVV ver. 1.1 VxVx From 3ccec84cda23244021804db9deb6e397c16de0d4 Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Sat, 30 Sep 2023 06:19:34 +0900 Subject: [PATCH 25/26] add comments about fonts used for unit tests --- README.md | 2 ++ test/testCaseKern1.ml | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 78e5509..946e08b 100644 --- a/README.md +++ b/README.md @@ -228,3 +228,5 @@ Some unit tests use data extracted from the following fonts: - See the license [here](https://www.latex-project.org/lppl/) * [Junicode](https://junicode.sourceforge.io/) - See the license [here](https://github.com/psb1558/Junicode-font/blob/master/OFL.txt) +* [DejaVu Sans](https://dejavu-fonts.github.io/): `DejaVuSans-ExtraLight.ttf` + - See the license [here](https://dejavu-fonts.github.io/License.html) diff --git a/test/testCaseKern1.ml b/test/testCaseKern1.ml index 963aed7..a7a599d 100644 --- a/test/testCaseKern1.ml +++ b/test/testCaseKern1.ml @@ -10,7 +10,8 @@ let marshaled = (* First subtable's header: *) 0x0000; 0x0086; 0x0001; 0x0014; 0x0040; 0x0004; 0x0018; - (* First subtable's contents: *) + (* First subtable's contents; + `DejaVuSans-ExtraLight.ttf` (ottset: 93214, length: 120): *) 0x0010; 0x0024; 0xffd3; 0x0010; 0x0025; 0xffb7; 0x0010; 0x002a; 0x004b; 0x0010; 0x002d; 0x0072; 0x0010; 0x0032; 0x0039; 0x0010; 0x0034; 0x004b; 0x0010; 0x0037; 0xff44; 0x0010; 0x0039; @@ -22,7 +23,8 @@ let marshaled = (* Second subtable's header: *) 0x0000; 0x0086; 0x0001; 0x0014; 0x0040; 0x0004; 0x0018; - (* Second subtable's contents: *) + (* Second subtable's contents; + `DejaVuSans-ExtraLight.ttf` (offset: 157164, length: 120): *) 0x0141; 0x0011; 0xff6b; 0x0141; 0x001d; 0xffb7; 0x0141; 0x0057; 0xffdc; 0x0141; 0x005a; 0xffdc; 0x0141; 0x005c; 0xffdc; 0x0141; 0x006d; 0xffb7; 0x0141; 0x007d; @@ -33,7 +35,8 @@ let marshaled = 0x01d6; 0xffdc; 0x0141; 0x03e5; 0xffdc; 0x0141; 0x03e7; 0xffdc; (* Third subtable's header: *) 0x0000; 0x0086; 0x0001; 0x0014; 0x0040; 0x0004; 0x0018; - (* Third subtable's contents: *) + (* Third subtable's contents; + `DejaVuSans-ExtraLight.ttf` (offset: 220736, length: 120): *) 0x03d9; 0x0010; 0xff7d; 0x03d9; 0x0011; 0xff44; 0x03d9; 0x001d; 0xffdc; 0x03d9; 0x0046; 0xffd3; 0x03d9; 0x0047; 0xffdc; 0x03d9; 0x0048; From 769ceb0bc8658aed962c169ee63dc40e49fc8764 Mon Sep 17 00:00:00 2001 From: Takashi Suwa Date: Sat, 30 Sep 2023 14:23:49 +0900 Subject: [PATCH 26/26] fix `Value.Cmap.Mapping.add_{incremental,constant}_range` --- src/value.ml | 39 +++++++++++++++++++++++-------- test/otfedTest.ml | 59 ++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 85 insertions(+), 13 deletions(-) diff --git a/src/value.ml b/src/value.ml index fe142c1..0dbcef7 100644 --- a/src/value.ml +++ b/src/value.ml @@ -360,24 +360,43 @@ module Cmap = struct MapImpl.add - let add_incremental_range ~start ~last ~gid map = - let rec aux map uch gid = - if uch > last then + let uchar_opt_of_int (scalar : int) : Uchar.t option = + if Uchar.is_valid scalar then + Some(Uchar.of_int scalar) + else + None + + + let add_incremental_range ~start:uch_start ~last:uch_last ~gid map = + let start = Uchar.to_int uch_start in + let last = Uchar.to_int uch_last in + let rec aux map scalar gid = + if scalar > last then map else - let map = map |> add_single uch gid in - aux map (Uchar.succ uch) (gid + 1) + let map = + match uchar_opt_of_int scalar with + | Some(uch) -> map |> add_single uch gid + | None -> map + in + aux map (scalar + 1) (gid + 1) in aux map start gid - let add_constant_range ~start ~last ~gid map = - let rec aux map uch = - if uch > last then + let add_constant_range ~start:uch_start ~last:uch_last ~gid map = + let start = Uchar.to_int uch_start in + let last = Uchar.to_int uch_last in + let rec aux map scalar = + if scalar > last then map else - let map = map |> add_single uch gid in - aux map (Uchar.succ uch) + let map = + match uchar_opt_of_int scalar with + | Some(uch) -> map |> add_single uch gid + | None -> map + in + aux map (scalar + 1) in aux map start diff --git a/test/otfedTest.ml b/test/otfedTest.ml index 832d4e9..07be866 100644 --- a/test/otfedTest.ml +++ b/test/otfedTest.ml @@ -37,6 +37,7 @@ module DecodeError = Otfed__DecodeError module EncodeError = Otfed__EncodeError module Value = Otfed__Value module Intermediate = Otfed__Intermediate +module CmapMapping = Value.Cmap.Mapping (** Tests for `DecodeOperation.d_int16` and `EncodeOperation.e_int16` *) @@ -247,6 +248,57 @@ let d_cmap_variation_subtable_to_list = return (Alist.to_list acc) +let cmap_entry_list = + Alcotest.(list (pair uchar int)) + + +(** Tests for `Value.Cmap.Mapping` *) +let cmap_mapping_tests () = + let list_of_mapping mapping = + CmapMapping.fold (fun uch gid acc -> + Alist.extend acc (uch, gid) + ) mapping Alist.empty |> Alist.to_list + in + begin + let mapping = + CmapMapping.empty |> CmapMapping.add_incremental_range + ~start:(Uchar.of_int 0x10FFF0) + ~last:(Uchar.of_int 0x10FFFF) (* The maximum unicode codepoint *) + ~gid:42 + in + let got = list_of_mapping mapping in + let expected = + let f = Uchar.of_int in + [ + (f 0x10FFF0, 42); (f 0x10FFF1, 43); (f 0x10FFF2, 44); (f 0x10FFF3, 45); (f 0x10FFF4, 46); + (f 0x10FFF5, 47); (f 0x10FFF6, 48); (f 0x10FFF7, 49); (f 0x10FFF8, 50); (f 0x10FFF9, 51); + (f 0x10FFFA, 52); (f 0x10FFFB, 53); (f 0x10FFFC, 54); (f 0x10FFFD, 55); (f 0x10FFFE, 56); + (f 0x10FFFF, 57); + ] + in + Alcotest.(check cmap_entry_list) "add_incremental_range can handle the maximum codepoint" expected got + end; + begin + let mapping = + CmapMapping.empty |> CmapMapping.add_constant_range + ~start:(Uchar.of_int 0x10FFF0) + ~last:(Uchar.of_int 0x10FFFF) (* The maximum unicode codepoint *) + ~gid:42 + in + let got = list_of_mapping mapping in + let expected = + let f = Uchar.of_int in + [ + (f 0x10FFF0, 42); (f 0x10FFF1, 42); (f 0x10FFF2, 42); (f 0x10FFF3, 42); (f 0x10FFF4, 42); + (f 0x10FFF5, 42); (f 0x10FFF6, 42); (f 0x10FFF7, 42); (f 0x10FFF8, 42); (f 0x10FFF9, 42); + (f 0x10FFFA, 42); (f 0x10FFFB, 42); (f 0x10FFFC, 42); (f 0x10FFFD, 42); (f 0x10FFFE, 42); + (f 0x10FFFF, 42); + ] + in + Alcotest.(check cmap_entry_list) "add_constant_range can handle the maximum codepoint" expected got + end + + let default_list = Alcotest.(list (of_pp DecodeCmap.pp_unicode_value_range)) @@ -270,11 +322,11 @@ let e_cmap_mapping_tests () = input |> List.fold_left (fun cmap_mapping segment -> match segment with | DecodeCmap.Incremental(start, last, gid) -> - cmap_mapping |> Value.Cmap.Mapping.add_incremental_range ~start ~last ~gid + cmap_mapping |> CmapMapping.add_incremental_range ~start ~last ~gid | DecodeCmap.Constant(start, last, gid) -> - cmap_mapping |> Value.Cmap.Mapping.add_constant_range ~start ~last ~gid - ) Value.Cmap.Mapping.empty + cmap_mapping |> CmapMapping.add_constant_range ~start ~last ~gid + ) CmapMapping.empty in let got = EncodeCmap.e_cmap_mapping cmap_mapping |> run_encoder in let expected = Ok(TestCaseCmap2.marshaled) in @@ -534,6 +586,7 @@ let () = ("DecodeCmap", [ test_case "d_cmap_subtable" `Quick d_cmap_subtable_tests; test_case "d_cmap_variation_subtable" `Quick d_cmap_variation_subtable_tests; + test_case "cmap_mapping" `Quick cmap_mapping_tests; ]); ("EncodeCmap", [ test_case "e_cmap_mapping" `Quick e_cmap_mapping_tests;