fontisan 0.2.17 → 0.2.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +14 -90
  3. data/README.adoc +257 -1
  4. data/docs/.vitepress/config.ts +68 -8
  5. data/docs/.vitepress/theme/style.css +570 -272
  6. data/docs/CONVERSION_GUIDE.adoc +31 -8
  7. data/docs/EXTRACT_TTC_MIGRATION.md +1 -1
  8. data/docs/WOFF_WOFF2_FORMATS.adoc +53 -0
  9. data/docs/api/conversion-options.md +37 -14
  10. data/docs/cli/audit.md +337 -0
  11. data/docs/cli/convert.md +20 -1
  12. data/docs/cli/index.md +31 -0
  13. data/docs/guide/color.md +1 -1
  14. data/docs/guide/conversion/options.md +32 -3
  15. data/docs/guide/conversion/ttf-otf.md +1 -1
  16. data/docs/guide/conversion/type1.md +1 -1
  17. data/docs/guide/conversion/web.md +91 -32
  18. data/docs/guide/conversion.md +6 -5
  19. data/docs/guide/formats/woff.md +35 -11
  20. data/docs/guide/index.md +2 -2
  21. data/docs/guide/migrations/extract-ttc.md +1 -1
  22. data/docs/guide/quick-start.md +4 -4
  23. data/docs/guide/type1.md +4 -4
  24. data/docs/guide/woff.md +19 -17
  25. data/docs/index.md +2 -0
  26. data/docs/lychee.toml +5 -1
  27. data/docs/package.json +1 -1
  28. data/docs/public/robots.txt +4 -0
  29. data/docs/scripts/post-build.mjs +81 -0
  30. data/lib/fontisan/audit/codepoint_range_coalescer.rb +41 -0
  31. data/lib/fontisan/audit/context.rb +122 -0
  32. data/lib/fontisan/audit/differ.rb +124 -0
  33. data/lib/fontisan/audit/extractors/aggregations.rb +54 -0
  34. data/lib/fontisan/audit/extractors/base.rb +26 -0
  35. data/lib/fontisan/audit/extractors/color_capabilities.rb +141 -0
  36. data/lib/fontisan/audit/extractors/coverage.rb +48 -0
  37. data/lib/fontisan/audit/extractors/hinting.rb +197 -0
  38. data/lib/fontisan/audit/extractors/identity.rb +52 -0
  39. data/lib/fontisan/audit/extractors/language_coverage.rb +37 -0
  40. data/lib/fontisan/audit/extractors/licensing.rb +79 -0
  41. data/lib/fontisan/audit/extractors/metrics.rb +103 -0
  42. data/lib/fontisan/audit/extractors/opentype_layout.rb +69 -0
  43. data/lib/fontisan/audit/extractors/provenance.rb +29 -0
  44. data/lib/fontisan/audit/extractors/style.rb +32 -0
  45. data/lib/fontisan/audit/extractors/variation_detail.rb +99 -0
  46. data/lib/fontisan/audit/extractors.rb +27 -0
  47. data/lib/fontisan/audit/library_aggregator.rb +83 -0
  48. data/lib/fontisan/audit/library_auditor.rb +90 -0
  49. data/lib/fontisan/audit/registry.rb +60 -0
  50. data/lib/fontisan/audit/style_extractor.rb +80 -0
  51. data/lib/fontisan/audit.rb +20 -0
  52. data/lib/fontisan/base_collection.rb +23 -9
  53. data/lib/fontisan/binary/structures.rb +0 -2
  54. data/lib/fontisan/binary.rb +11 -0
  55. data/lib/fontisan/cldr/aggregator.rb +33 -0
  56. data/lib/fontisan/cldr/cache_manager.rb +110 -0
  57. data/lib/fontisan/cldr/config.rb +59 -0
  58. data/lib/fontisan/cldr/download_error.rb +9 -0
  59. data/lib/fontisan/cldr/downloader.rb +79 -0
  60. data/lib/fontisan/cldr/error.rb +8 -0
  61. data/lib/fontisan/cldr/index.rb +64 -0
  62. data/lib/fontisan/cldr/index_builder.rb +72 -0
  63. data/lib/fontisan/cldr/unicode_set_parser.rb +172 -0
  64. data/lib/fontisan/cldr/unknown_version_error.rb +9 -0
  65. data/lib/fontisan/cldr/version_resolver.rb +91 -0
  66. data/lib/fontisan/cldr.rb +23 -0
  67. data/lib/fontisan/cli/cldr_cli.rb +85 -0
  68. data/lib/fontisan/cli/ucd_cli.rb +97 -0
  69. data/lib/fontisan/cli.rb +201 -2
  70. data/lib/fontisan/collection/builder.rb +0 -4
  71. data/lib/fontisan/collection/dfont_builder.rb +0 -4
  72. data/lib/fontisan/collection/shared_logic.rb +0 -2
  73. data/lib/fontisan/collection/writer.rb +0 -3
  74. data/lib/fontisan/collection.rb +15 -0
  75. data/lib/fontisan/commands/audit_command.rb +123 -0
  76. data/lib/fontisan/commands/audit_compare_command.rb +66 -0
  77. data/lib/fontisan/commands/audit_library_command.rb +46 -0
  78. data/lib/fontisan/commands/base_command.rb +0 -3
  79. data/lib/fontisan/commands/convert_command.rb +25 -20
  80. data/lib/fontisan/commands/dump_table_command.rb +0 -3
  81. data/lib/fontisan/commands/export_command.rb +0 -4
  82. data/lib/fontisan/commands/features_command.rb +0 -3
  83. data/lib/fontisan/commands/instance_command.rb +0 -5
  84. data/lib/fontisan/commands/ls_command.rb +0 -6
  85. data/lib/fontisan/commands/optical_size_command.rb +0 -3
  86. data/lib/fontisan/commands/pack_command.rb +0 -5
  87. data/lib/fontisan/commands/scripts_command.rb +0 -2
  88. data/lib/fontisan/commands/subset_command.rb +0 -3
  89. data/lib/fontisan/commands/unicode_command.rb +0 -3
  90. data/lib/fontisan/commands/unpack_command.rb +0 -7
  91. data/lib/fontisan/commands/validate_command.rb +0 -8
  92. data/lib/fontisan/commands/variable_command.rb +0 -3
  93. data/lib/fontisan/commands.rb +29 -0
  94. data/lib/fontisan/config/cldr.yml +22 -0
  95. data/lib/fontisan/config/conversion_matrix.yml +38 -0
  96. data/lib/fontisan/config/ucd.yml +23 -0
  97. data/lib/fontisan/constants.rb +19 -0
  98. data/lib/fontisan/conversion_options.rb +30 -19
  99. data/lib/fontisan/converters/cff_table_builder.rb +0 -3
  100. data/lib/fontisan/converters/collection_converter.rb +0 -8
  101. data/lib/fontisan/converters/conversion_strategy.rb +161 -46
  102. data/lib/fontisan/converters/format_converter.rb +143 -32
  103. data/lib/fontisan/converters/glyf_table_builder.rb +0 -2
  104. data/lib/fontisan/converters/outline_converter.rb +0 -19
  105. data/lib/fontisan/converters/outline_extraction.rb +0 -5
  106. data/lib/fontisan/converters/outline_optimizer.rb +0 -5
  107. data/lib/fontisan/converters/svg_generator.rb +0 -4
  108. data/lib/fontisan/converters/table_copier.rb +0 -2
  109. data/lib/fontisan/converters/type1_converter.rb +0 -11
  110. data/lib/fontisan/converters/woff2_encoder.rb +49 -20
  111. data/lib/fontisan/converters/woff_writer.rb +211 -282
  112. data/lib/fontisan/converters.rb +21 -0
  113. data/lib/fontisan/dfont_collection.rb +29 -10
  114. data/lib/fontisan/export/exporter.rb +0 -6
  115. data/lib/fontisan/export/transformers/font_to_ttx.rb +0 -9
  116. data/lib/fontisan/export/transformers/head_transformer.rb +0 -2
  117. data/lib/fontisan/export/transformers/hhea_transformer.rb +0 -2
  118. data/lib/fontisan/export/transformers/maxp_transformer.rb +0 -2
  119. data/lib/fontisan/export/transformers/name_transformer.rb +0 -2
  120. data/lib/fontisan/export/transformers/os2_transformer.rb +0 -2
  121. data/lib/fontisan/export/transformers/post_transformer.rb +0 -2
  122. data/lib/fontisan/export/transformers.rb +17 -0
  123. data/lib/fontisan/export.rb +13 -0
  124. data/lib/fontisan/font_loader.rb +14 -19
  125. data/lib/fontisan/font_writer.rb +0 -1
  126. data/lib/fontisan/formatters/audit_diff_text_renderer.rb +122 -0
  127. data/lib/fontisan/formatters/audit_text_renderer.rb +324 -0
  128. data/lib/fontisan/formatters/library_summary_text_renderer.rb +99 -0
  129. data/lib/fontisan/formatters/text_formatter.rb +6 -0
  130. data/lib/fontisan/formatters.rb +12 -0
  131. data/lib/fontisan/hints/hint_converter.rb +0 -1
  132. data/lib/fontisan/hints/postscript_hint_applier.rb +0 -9
  133. data/lib/fontisan/hints/postscript_hint_extractor.rb +0 -2
  134. data/lib/fontisan/hints/truetype_hint_extractor.rb +0 -2
  135. data/lib/fontisan/hints.rb +16 -0
  136. data/lib/fontisan/metrics_calculator.rb +0 -2
  137. data/lib/fontisan/models/all_scripts_features_info.rb +0 -1
  138. data/lib/fontisan/models/audit/audit_axis.rb +30 -0
  139. data/lib/fontisan/models/audit/audit_block.rb +32 -0
  140. data/lib/fontisan/models/audit/audit_diff.rb +77 -0
  141. data/lib/fontisan/models/audit/audit_report.rb +153 -0
  142. data/lib/fontisan/models/audit/codepoint_range.rb +40 -0
  143. data/lib/fontisan/models/audit/codepoint_set_diff.rb +34 -0
  144. data/lib/fontisan/models/audit/color_capabilities.rb +93 -0
  145. data/lib/fontisan/models/audit/duplicate_group.rb +23 -0
  146. data/lib/fontisan/models/audit/embedding_type.rb +76 -0
  147. data/lib/fontisan/models/audit/field_change.rb +28 -0
  148. data/lib/fontisan/models/audit/fs_selection_flags.rb +61 -0
  149. data/lib/fontisan/models/audit/gasp_range.rb +63 -0
  150. data/lib/fontisan/models/audit/hinting.rb +93 -0
  151. data/lib/fontisan/models/audit/library_summary.rb +40 -0
  152. data/lib/fontisan/models/audit/licensing.rb +48 -0
  153. data/lib/fontisan/models/audit/metrics.rb +111 -0
  154. data/lib/fontisan/models/audit/named_instance.rb +41 -0
  155. data/lib/fontisan/models/audit/opentype_layout.rb +40 -0
  156. data/lib/fontisan/models/audit/script_coverage_row.rb +26 -0
  157. data/lib/fontisan/models/audit/script_features.rb +28 -0
  158. data/lib/fontisan/models/audit/variation_detail.rb +44 -0
  159. data/lib/fontisan/models/audit.rb +33 -0
  160. data/lib/fontisan/models/cldr/language_coverage.rb +31 -0
  161. data/lib/fontisan/models/cldr.rb +12 -0
  162. data/lib/fontisan/models/collection_brief_info.rb +0 -1
  163. data/lib/fontisan/models/collection_info.rb +0 -2
  164. data/lib/fontisan/models/collection_list_info.rb +0 -1
  165. data/lib/fontisan/models/collection_validation_report.rb +0 -2
  166. data/lib/fontisan/models/color_glyph.rb +0 -1
  167. data/lib/fontisan/models/font_report.rb +0 -1
  168. data/lib/fontisan/models/ttx/tables.rb +21 -0
  169. data/lib/fontisan/models/ttx/ttfont.rb +0 -8
  170. data/lib/fontisan/models/ttx.rb +14 -0
  171. data/lib/fontisan/models/ucd/ucd.rb +38 -0
  172. data/lib/fontisan/models/ucd/ucd_char.rb +67 -0
  173. data/lib/fontisan/models/ucd.rb +19 -0
  174. data/lib/fontisan/models.rb +47 -0
  175. data/lib/fontisan/open_type_collection.rb +6 -5
  176. data/lib/fontisan/open_type_font.rb +8 -2
  177. data/lib/fontisan/open_type_font_extensions.rb +9 -9
  178. data/lib/fontisan/optimizers/pattern_analyzer.rb +0 -1
  179. data/lib/fontisan/optimizers.rb +14 -0
  180. data/lib/fontisan/outline_extractor.rb +0 -2
  181. data/lib/fontisan/parsers/dfont_parser.rb +0 -1
  182. data/lib/fontisan/parsers.rb +10 -0
  183. data/lib/fontisan/pipeline/format_detector.rb +29 -102
  184. data/lib/fontisan/pipeline/output_writer.rb +11 -9
  185. data/lib/fontisan/pipeline/strategies/instance_strategy.rb +0 -4
  186. data/lib/fontisan/pipeline/strategies/named_strategy.rb +0 -4
  187. data/lib/fontisan/pipeline/strategies/preserve_strategy.rb +0 -2
  188. data/lib/fontisan/pipeline/strategies.rb +14 -0
  189. data/lib/fontisan/pipeline/transformation_pipeline.rb +0 -7
  190. data/lib/fontisan/pipeline/variation_resolver.rb +0 -7
  191. data/lib/fontisan/pipeline.rb +13 -0
  192. data/lib/fontisan/sfnt_font.rb +29 -14
  193. data/lib/fontisan/sfnt_table.rb +0 -4
  194. data/lib/fontisan/subset/builder.rb +0 -6
  195. data/lib/fontisan/subset.rb +13 -0
  196. data/lib/fontisan/svg/font_generator.rb +0 -4
  197. data/lib/fontisan/svg/glyph_generator.rb +0 -2
  198. data/lib/fontisan/svg.rb +12 -0
  199. data/lib/fontisan/tables/cbdt.rb +0 -1
  200. data/lib/fontisan/tables/cblc.rb +0 -1
  201. data/lib/fontisan/tables/cff/charset.rb +0 -1
  202. data/lib/fontisan/tables/cff/charstring.rb +0 -1
  203. data/lib/fontisan/tables/cff/charstring_rebuilder.rb +0 -4
  204. data/lib/fontisan/tables/cff/charstrings_index.rb +0 -3
  205. data/lib/fontisan/tables/cff/dict.rb +0 -1
  206. data/lib/fontisan/tables/cff/encoding.rb +0 -1
  207. data/lib/fontisan/tables/cff/header.rb +0 -2
  208. data/lib/fontisan/tables/cff/hint_operation_injector.rb +0 -2
  209. data/lib/fontisan/tables/cff/index.rb +0 -1
  210. data/lib/fontisan/tables/cff/private_dict.rb +0 -2
  211. data/lib/fontisan/tables/cff/private_dict_writer.rb +0 -2
  212. data/lib/fontisan/tables/cff/table_builder.rb +0 -6
  213. data/lib/fontisan/tables/cff/top_dict.rb +0 -2
  214. data/lib/fontisan/tables/cff.rb +22 -15
  215. data/lib/fontisan/tables/cff2/charstring_parser.rb +0 -2
  216. data/lib/fontisan/tables/cff2/table_builder.rb +0 -11
  217. data/lib/fontisan/tables/cff2/table_reader.rb +0 -2
  218. data/lib/fontisan/tables/cff2.rb +13 -14
  219. data/lib/fontisan/tables/cmap.rb +24 -2
  220. data/lib/fontisan/tables/cmap_table.rb +0 -3
  221. data/lib/fontisan/tables/colr.rb +0 -1
  222. data/lib/fontisan/tables/cpal.rb +0 -1
  223. data/lib/fontisan/tables/cvar.rb +0 -2
  224. data/lib/fontisan/tables/fvar.rb +0 -1
  225. data/lib/fontisan/tables/glyf/compound_glyph_resolver.rb +0 -2
  226. data/lib/fontisan/tables/glyf/glyph_builder.rb +0 -3
  227. data/lib/fontisan/tables/glyf.rb +0 -6
  228. data/lib/fontisan/tables/glyf_table.rb +0 -3
  229. data/lib/fontisan/tables/gpos.rb +0 -2
  230. data/lib/fontisan/tables/gsub.rb +0 -2
  231. data/lib/fontisan/tables/gvar.rb +0 -2
  232. data/lib/fontisan/tables/head.rb +0 -2
  233. data/lib/fontisan/tables/head_table.rb +0 -3
  234. data/lib/fontisan/tables/hhea.rb +0 -2
  235. data/lib/fontisan/tables/hhea_table.rb +0 -3
  236. data/lib/fontisan/tables/hmtx.rb +0 -2
  237. data/lib/fontisan/tables/hmtx_table.rb +0 -3
  238. data/lib/fontisan/tables/hvar.rb +0 -3
  239. data/lib/fontisan/tables/loca.rb +0 -2
  240. data/lib/fontisan/tables/loca_table.rb +0 -3
  241. data/lib/fontisan/tables/maxp.rb +0 -2
  242. data/lib/fontisan/tables/maxp_table.rb +0 -3
  243. data/lib/fontisan/tables/mvar.rb +0 -3
  244. data/lib/fontisan/tables/name.rb +0 -2
  245. data/lib/fontisan/tables/name_table.rb +0 -3
  246. data/lib/fontisan/tables/os2_table.rb +0 -3
  247. data/lib/fontisan/tables/post_table.rb +0 -3
  248. data/lib/fontisan/tables/sbix.rb +0 -1
  249. data/lib/fontisan/tables/svg.rb +0 -1
  250. data/lib/fontisan/tables/variation_common.rb +0 -1
  251. data/lib/fontisan/tables/vvar.rb +0 -3
  252. data/lib/fontisan/tables.rb +54 -0
  253. data/lib/fontisan/true_type_collection.rb +6 -14
  254. data/lib/fontisan/true_type_font.rb +8 -2
  255. data/lib/fontisan/true_type_font_extensions.rb +9 -9
  256. data/lib/fontisan/type1/afm_generator.rb +0 -4
  257. data/lib/fontisan/type1/conversion_options.rb +0 -2
  258. data/lib/fontisan/type1/encodings.rb +0 -2
  259. data/lib/fontisan/type1/generator.rb +0 -8
  260. data/lib/fontisan/type1/pfa_generator.rb +0 -3
  261. data/lib/fontisan/type1/pfb_generator.rb +0 -5
  262. data/lib/fontisan/type1/pfm_generator.rb +0 -4
  263. data/lib/fontisan/type1.rb +42 -69
  264. data/lib/fontisan/type1_font.rb +40 -11
  265. data/lib/fontisan/ucd/aggregator.rb +73 -0
  266. data/lib/fontisan/ucd/cache_manager.rb +111 -0
  267. data/lib/fontisan/ucd/config.rb +59 -0
  268. data/lib/fontisan/ucd/download_error.rb +9 -0
  269. data/lib/fontisan/ucd/downloader.rb +88 -0
  270. data/lib/fontisan/ucd/error.rb +8 -0
  271. data/lib/fontisan/ucd/index.rb +103 -0
  272. data/lib/fontisan/ucd/index_builder.rb +107 -0
  273. data/lib/fontisan/ucd/range_entry.rb +56 -0
  274. data/lib/fontisan/ucd/unknown_version_error.rb +9 -0
  275. data/lib/fontisan/ucd/version_resolver.rb +79 -0
  276. data/lib/fontisan/ucd.rb +23 -0
  277. data/lib/fontisan/utilities/checksum_calculator.rb +0 -1
  278. data/lib/fontisan/utilities.rb +10 -0
  279. data/lib/fontisan/utils.rb +10 -0
  280. data/lib/fontisan/validation/collection_validator.rb +0 -2
  281. data/lib/fontisan/validation.rb +9 -0
  282. data/lib/fontisan/validators/basic_validator.rb +0 -2
  283. data/lib/fontisan/validators/font_book_validator.rb +0 -2
  284. data/lib/fontisan/validators/opentype_validator.rb +0 -2
  285. data/lib/fontisan/validators/profile_loader.rb +0 -5
  286. data/lib/fontisan/validators/validator.rb +0 -2
  287. data/lib/fontisan/validators/web_font_validator.rb +0 -2
  288. data/lib/fontisan/validators.rb +14 -0
  289. data/lib/fontisan/variable/delta_applicator.rb +0 -4
  290. data/lib/fontisan/variable/instancer.rb +0 -3
  291. data/lib/fontisan/variable/static_font_builder.rb +0 -3
  292. data/lib/fontisan/variable.rb +16 -0
  293. data/lib/fontisan/variation/blend_applier.rb +0 -2
  294. data/lib/fontisan/variation/cache.rb +0 -2
  295. data/lib/fontisan/variation/converter.rb +0 -3
  296. data/lib/fontisan/variation/data_extractor.rb +0 -2
  297. data/lib/fontisan/variation/delta_applier.rb +0 -5
  298. data/lib/fontisan/variation/inspector.rb +0 -1
  299. data/lib/fontisan/variation/instance_generator.rb +0 -6
  300. data/lib/fontisan/variation/instance_writer.rb +0 -5
  301. data/lib/fontisan/variation/metrics_adjuster.rb +0 -4
  302. data/lib/fontisan/variation/optimizer.rb +0 -3
  303. data/lib/fontisan/variation/parallel_generator.rb +0 -3
  304. data/lib/fontisan/variation/subsetter.rb +0 -4
  305. data/lib/fontisan/variation/tuple_variation_header.rb +0 -2
  306. data/lib/fontisan/variation/variable_svg_generator.rb +0 -3
  307. data/lib/fontisan/variation/variation_context.rb +0 -3
  308. data/lib/fontisan/variation/variation_preserver.rb +0 -3
  309. data/lib/fontisan/variation.rb +31 -0
  310. data/lib/fontisan/version.rb +1 -1
  311. data/lib/fontisan/woff2.rb +13 -0
  312. data/lib/fontisan/woff2_font.rb +31 -9
  313. data/lib/fontisan/woff_font.rb +31 -2
  314. data/lib/fontisan.rb +124 -196
  315. metadata +114 -7
  316. data/fontisan.gemspec +0 -48
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Fontisan
6
+ module Models
7
+ module Audit
8
+ # Per-script breakdown of OpenType features.
9
+ #
10
+ # Pairs a script tag (e.g. "latn", "kana ") with the GSUB features
11
+ # and GPOS features that apply to it. The two collections are
12
+ # kept separate because substitution and positioning have different
13
+ # semantics — consumers answering "does this font support kerning
14
+ # for Latin?" want to look at GPOS only.
15
+ class ScriptFeatures < Lutaml::Model::Serializable
16
+ attribute :script, :string
17
+ attribute :gsub_features, :string, collection: true
18
+ attribute :gpos_features, :string, collection: true
19
+
20
+ key_value do
21
+ map "script", to: :script
22
+ map "gsub_features", to: :gsub_features
23
+ map "gpos_features", to: :gpos_features
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Fontisan
6
+ module Models
7
+ module Audit
8
+ # Variable-font detail for one face.
9
+ #
10
+ # Bundles everything fvar-derived (axes + named instances) with the
11
+ # presence flags for every variation side-table (avar/cvar/HVAR/VVAR/
12
+ # MVAR/gvar). Replaces the previous flat `axes` + `is_variable` pair
13
+ # on AuditReport for MECE cleanliness — a face is variable iff this
14
+ # object is non-nil.
15
+ #
16
+ # `axes` reuses the existing AuditAxis shape; `named_instances` is a
17
+ # parallel NamedInstance collection. The has_* booleans are presence
18
+ # checks only — they don't validate the table contents.
19
+ class VariationDetail < Lutaml::Model::Serializable
20
+ attribute :axes, AuditAxis, collection: true
21
+ attribute :named_instances, NamedInstance, collection: true
22
+
23
+ # Variation side-table presence flags.
24
+ attribute :has_avar, Lutaml::Model::Type::Boolean # axis variation
25
+ attribute :has_cvar, Lutaml::Model::Type::Boolean # CVT variation
26
+ attribute :has_hvar, Lutaml::Model::Type::Boolean # horizontal metrics
27
+ attribute :has_vvar, Lutaml::Model::Type::Boolean # vertical metrics
28
+ attribute :has_mvar, Lutaml::Model::Type::Boolean # metrics variation
29
+ attribute :has_gvar, Lutaml::Model::Type::Boolean # glyph variation (TT)
30
+
31
+ key_value do
32
+ map "axes", to: :axes
33
+ map "named_instances", to: :named_instances
34
+ map "has_avar", to: :has_avar
35
+ map "has_cvar", to: :has_cvar
36
+ map "has_hvar", to: :has_hvar
37
+ map "has_vvar", to: :has_vvar
38
+ map "has_mvar", to: :has_mvar
39
+ map "has_gvar", to: :has_gvar
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Namespace hub for audit-related models.
4
+ #
5
+ # All Models::Audit::* constants are autoloaded from here.
6
+
7
+ module Fontisan
8
+ module Models
9
+ module Audit
10
+ autoload :AuditBlock, "fontisan/models/audit/audit_block"
11
+ autoload :AuditAxis, "fontisan/models/audit/audit_axis"
12
+ autoload :AuditDiff, "fontisan/models/audit/audit_diff"
13
+ autoload :AuditReport, "fontisan/models/audit/audit_report"
14
+ autoload :CodepointRange, "fontisan/models/audit/codepoint_range"
15
+ autoload :CodepointSetDiff, "fontisan/models/audit/codepoint_set_diff"
16
+ autoload :ColorCapabilities, "fontisan/models/audit/color_capabilities"
17
+ autoload :DuplicateGroup, "fontisan/models/audit/duplicate_group"
18
+ autoload :EmbeddingType, "fontisan/models/audit/embedding_type"
19
+ autoload :FieldChange, "fontisan/models/audit/field_change"
20
+ autoload :FsSelectionFlags, "fontisan/models/audit/fs_selection_flags"
21
+ autoload :GaspRange, "fontisan/models/audit/gasp_range"
22
+ autoload :Hinting, "fontisan/models/audit/hinting"
23
+ autoload :LibrarySummary, "fontisan/models/audit/library_summary"
24
+ autoload :Licensing, "fontisan/models/audit/licensing"
25
+ autoload :Metrics, "fontisan/models/audit/metrics"
26
+ autoload :NamedInstance, "fontisan/models/audit/named_instance"
27
+ autoload :OpenTypeLayout, "fontisan/models/audit/opentype_layout"
28
+ autoload :ScriptCoverageRow, "fontisan/models/audit/script_coverage_row"
29
+ autoload :ScriptFeatures, "fontisan/models/audit/script_features"
30
+ autoload :VariationDetail, "fontisan/models/audit/variation_detail"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Fontisan
6
+ module Models
7
+ module Cldr
8
+ # Per-language coverage for one face against a CLDR exemplar set.
9
+ #
10
+ # `coverage_ratio` is in [0.0, 1.0] rounded to 4 decimal places;
11
+ # `fully_supported` is true only when every required codepoint
12
+ # (total > 0) is covered. A language with total == 0 (empty exemplar
13
+ # set in the index) is reported as ratio 0.0, fully_supported false.
14
+ class LanguageCoverage < Lutaml::Model::Serializable
15
+ attribute :language, :string
16
+ attribute :covered, :integer
17
+ attribute :total, :integer
18
+ attribute :coverage_ratio, :float
19
+ attribute :fully_supported, Lutaml::Model::Type::Boolean
20
+
21
+ key_value do
22
+ map "language", to: :language
23
+ map "covered", to: :covered
24
+ map "total", to: :total
25
+ map "coverage_ratio", to: :coverage_ratio
26
+ map "fully_supported", to: :fully_supported
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Fontisan
6
+ module Models
7
+ # Namespace for CLDR-derived audit models.
8
+ module Cldr
9
+ autoload :LanguageCoverage, "fontisan/models/cldr/language_coverage"
10
+ end
11
+ end
12
+ end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/model"
4
- require_relative "font_info"
5
4
 
6
5
  module Fontisan
7
6
  module Models
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/model"
4
- require_relative "table_sharing_info"
5
- require_relative "font_info"
6
4
 
7
5
  module Fontisan
8
6
  module Models
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/model"
4
- require_relative "collection_font_summary"
5
4
 
6
5
  module Fontisan
7
6
  module Models
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "font_report"
4
- require_relative "validation_report"
5
3
  require "lutaml/model"
6
4
 
7
5
  module Fontisan
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/model"
4
- require_relative "color_layer"
5
4
 
6
5
  module Fontisan
7
6
  module Models
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "validation_report"
4
3
  require "lutaml/model"
5
4
 
6
5
  module Fontisan
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Autoload hub for the Fontisan::Models::Ttx::Tables namespace.
4
+
5
+ module Fontisan
6
+ module Models
7
+ module Ttx
8
+ module Tables
9
+ autoload :BinaryTable, "fontisan/models/ttx/tables/binary_table"
10
+ autoload :HeadTable, "fontisan/models/ttx/tables/head_table"
11
+ autoload :HheaTable, "fontisan/models/ttx/tables/hhea_table"
12
+ autoload :MaxpTable, "fontisan/models/ttx/tables/maxp_table"
13
+ autoload :NameRecord, "fontisan/models/ttx/tables/name_table"
14
+ autoload :NameTable, "fontisan/models/ttx/tables/name_table"
15
+ autoload :Os2Table, "fontisan/models/ttx/tables/os2_table"
16
+ autoload :Panose, "fontisan/models/ttx/tables/os2_table"
17
+ autoload :PostTable, "fontisan/models/ttx/tables/post_table"
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,14 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "lutaml/model"
4
- require_relative "glyph_order"
5
- require_relative "tables/head_table"
6
- require_relative "tables/name_table"
7
- require_relative "tables/maxp_table"
8
- require_relative "tables/hhea_table"
9
- require_relative "tables/os2_table"
10
- require_relative "tables/post_table"
11
- require_relative "tables/binary_table"
12
4
 
13
5
  module Fontisan
14
6
  module Models
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Autoload hub for the Fontisan::Models::Ttx namespace.
4
+
5
+ module Fontisan
6
+ module Models
7
+ module Ttx
8
+ autoload :GlyphId, "fontisan/models/ttx/glyph_order"
9
+ autoload :GlyphOrder, "fontisan/models/ttx/glyph_order"
10
+ autoload :Tables, "fontisan/models/ttx/tables/os2_table"
11
+ autoload :TtFont, "fontisan/models/ttx/ttfont"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Fontisan
6
+ module Models
7
+ module Ucd
8
+ # Root <ucd> element of the UCDXML flat file.
9
+ #
10
+ # The flat UCDXML file has the structure:
11
+ #
12
+ # <ucd>
13
+ # <description>...</description>
14
+ # <last_revision date="2025-..." version="17.0.0" />
15
+ # <char cp="0000" .../>
16
+ # <char cp="0001" .../>
17
+ # ...
18
+ # <char first-cp="3400" last-cp="4DBF" .../>
19
+ # ...
20
+ # </ucd>
21
+ #
22
+ # The flat variant merges all per-category UCD files (Blocks.txt,
23
+ # Scripts.txt, UnicodeData.txt, etc.) into one stream of <char>
24
+ # elements. Roughly 340,000 entries for Unicode 17.0.0.
25
+ class Ucd < Lutaml::Model::Serializable
26
+ attribute :last_revision, :string
27
+ attribute :chars, UcdChar, collection: true
28
+
29
+ xml do
30
+ element "ucd"
31
+
32
+ map_element "last_revision", to: :last_revision
33
+ map_element "char", to: :chars
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Fontisan
6
+ module Models
7
+ module Ucd
8
+ # Single <char> element from the UCDXML flat file.
9
+ #
10
+ # UCDXML uses two forms:
11
+ # <char cp="0041" name="..." script="Latin" block="Basic Latin" .../>
12
+ # <char first-cp="3400" last-cp="4DBF" name="..." script="Han" .../>
13
+ #
14
+ # The first form describes one codepoint. The second form describes a
15
+ # closed range of codepoints that share the same properties (used for
16
+ # CJK ideograph ranges where each codepoint would otherwise need its
17
+ # own <char> entry).
18
+ #
19
+ # Both forms can appear in the same document; `cp` is mutually
20
+ # exclusive with `first-cp`/`last-cp`.
21
+ class UcdChar < Lutaml::Model::Serializable
22
+ attribute :cp, :string
23
+ attribute :first_cp, :string
24
+ attribute :last_cp, :string
25
+ attribute :name, :string
26
+ attribute :general_category, :string
27
+ attribute :script, :string
28
+ attribute :block, :string
29
+ attribute :age, :string
30
+
31
+ xml do
32
+ element "char"
33
+
34
+ map_attribute "cp", to: :cp
35
+ map_attribute "first-cp", to: :first_cp
36
+ map_attribute "last-cp", to: :last_cp
37
+ map_attribute "name", to: :name
38
+ map_attribute "general-category", to: :general_category
39
+ map_attribute "script", to: :script
40
+ map_attribute "block", to: :block
41
+ map_attribute "age", to: :age
42
+ end
43
+
44
+ # True if this entry describes a codepoint range rather than a
45
+ # single codepoint.
46
+ def range?
47
+ !first_cp.nil? && !last_cp.nil?
48
+ end
49
+
50
+ # The codepoints covered by this entry, as Integers.
51
+ # For a single-codepoint entry, returns a one-element array.
52
+ # For a range entry, returns the inclusive range as an array
53
+ # (caller should treat this lazily if the range is huge — CJK
54
+ # ranges can have tens of thousands of codepoints).
55
+ def codepoints
56
+ if range?
57
+ (first_cp.to_i(16)..last_cp.to_i(16)).to_a
58
+ elsif cp
59
+ [cp.to_i(16)]
60
+ else
61
+ []
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lutaml/model"
4
+
5
+ module Fontisan
6
+ module Models
7
+ # Namespace for UCDXML deserialization models.
8
+ #
9
+ # These classes deserialize the upstream UCDXML flat file
10
+ # (https://www.unicode.org/Public/<version>/ucdxml/ucd.all.flat.zip)
11
+ # into Ruby objects. They are used by Fontisan::Ucd::IndexBuilder to
12
+ # derive compact run-length-encoded indices for Unicode block and
13
+ # script lookup.
14
+ module Ucd
15
+ autoload :UcdChar, "fontisan/models/ucd/ucd_char"
16
+ autoload :Ucd, "fontisan/models/ucd/ucd"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Autoload hub for the Fontisan::Models namespace.
4
+
5
+ module Fontisan
6
+ module Models
7
+ autoload :AllScriptsFeaturesInfo, "fontisan/models/all_scripts_features_info"
8
+ autoload :Audit, "fontisan/models/audit"
9
+ autoload :AxisInfo, "fontisan/models/variable_font_info"
10
+ autoload :BitmapGlyph, "fontisan/models/bitmap_glyph"
11
+ autoload :BitmapStrike, "fontisan/models/bitmap_strike"
12
+ autoload :CollectionBriefInfo, "fontisan/models/collection_brief_info"
13
+ autoload :CollectionFontSummary, "fontisan/models/collection_font_summary"
14
+ autoload :CollectionInfo, "fontisan/models/collection_info"
15
+ autoload :CollectionListInfo, "fontisan/models/collection_list_info"
16
+ autoload :CollectionValidationReport, "fontisan/models/collection_validation_report"
17
+ autoload :Cldr, "fontisan/models/cldr"
18
+ autoload :ColorGlyph, "fontisan/models/color_glyph"
19
+ autoload :ColorLayer, "fontisan/models/color_layer"
20
+ autoload :ColorPalette, "fontisan/models/color_palette"
21
+ autoload :FeatureRecord, "fontisan/models/features_info"
22
+ autoload :FeaturesInfo, "fontisan/models/features_info"
23
+ autoload :FontExport, "fontisan/models/font_export"
24
+ autoload :FontInfo, "fontisan/models/font_info"
25
+ autoload :FontReport, "fontisan/models/font_report"
26
+ autoload :FontSummary, "fontisan/models/font_summary"
27
+ autoload :GlyphInfo, "fontisan/models/glyph_info"
28
+ autoload :GlyphOutline, "fontisan/models/glyph_outline"
29
+ autoload :Hint, "fontisan/models/hint"
30
+ autoload :HintSet, "fontisan/models/hint"
31
+ autoload :InstanceInfo, "fontisan/models/variable_font_info"
32
+ autoload :OpticalSizeInfo, "fontisan/models/optical_size_info"
33
+ autoload :Outline, "fontisan/models/outline"
34
+ autoload :ScriptRecord, "fontisan/models/scripts_info"
35
+ autoload :ScriptsInfo, "fontisan/models/scripts_info"
36
+ autoload :SvgGlyph, "fontisan/models/svg_glyph"
37
+ autoload :TableEntry, "fontisan/models/table_info"
38
+ autoload :TableInfo, "fontisan/models/table_info"
39
+ autoload :TableSharingInfo, "fontisan/models/table_sharing_info"
40
+ autoload :Ttx, "fontisan/models/ttx/ttfont"
41
+ autoload :Ucd, "fontisan/models/ucd"
42
+ autoload :UnicodeMapping, "fontisan/models/unicode_mappings"
43
+ autoload :UnicodeMappings, "fontisan/models/unicode_mappings"
44
+ autoload :ValidationReport, "fontisan/models/validation_report"
45
+ autoload :VariableFontInfo, "fontisan/models/variable_font_info"
46
+ end
47
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_collection"
4
-
5
3
  module Fontisan
6
4
  # OpenType Collection domain object
7
5
  #
@@ -15,11 +13,16 @@ module Fontisan
15
13
  # fonts = otc.extract_fonts(io) # => [OpenTypeFont, OpenTypeFont, ...]
16
14
  # end
17
15
  class OpenTypeCollection < BaseCollection
16
+ # High-level pipeline format identifier. Owned by the collection class
17
+ # so the conversion pipeline can dispatch without case statements (OCP).
18
+ #
19
+ # @return [Symbol] :otc
20
+ def format = :otc
21
+
18
22
  # Get the font class for OpenType collections
19
23
  #
20
24
  # @return [Class] OpenTypeFont class
21
25
  def self.font_class
22
- require_relative "open_type_font"
23
26
  OpenTypeFont
24
27
  end
25
28
 
@@ -38,8 +41,6 @@ module Fontisan
38
41
  # @param io [IO] Open file handle to read fonts from
39
42
  # @return [Array<OpenTypeFont>] Array of font objects
40
43
  def extract_fonts(io)
41
- require_relative "open_type_font"
42
-
43
44
  font_offsets.map do |offset|
44
45
  OpenTypeFont.from_collection(io, offset)
45
46
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "sfnt_font"
4
-
5
3
  module Fontisan
6
4
  # OpenType Font domain object
7
5
  #
@@ -26,6 +24,14 @@ module Fontisan
26
24
  # @example Reading from TTC collection
27
25
  # otf = OpenTypeFont.from_collection(io, offset)
28
26
  class OpenTypeFont < SfntFont
27
+ extend OpenTypeFontExtensions
28
+
29
+ # High-level pipeline format identifier. Owned by the font class so the
30
+ # conversion pipeline can dispatch without case statements (OCP).
31
+ #
32
+ # @return [Symbol] :otf
33
+ def format = :otf
34
+
29
35
  # Page cache for lazy loading (maps page_start_offset => page_data)
30
36
  attr_accessor :page_cache
31
37
 
@@ -1,24 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fontisan
4
- # Extensions to OpenTypeFont for table-based construction
5
- class OpenTypeFont
4
+ # Extension module for {OpenTypeFont} providing table-based construction.
5
+ #
6
+ # Extended into OpenTypeFont from +open_type_font.rb+ so that
7
+ # +OpenTypeFont.from_tables(...)+ is available whenever the class
8
+ # itself is loaded.
9
+ module OpenTypeFontExtensions
6
10
  # Create font from hash of tables
7
11
  #
8
12
  # This is used during font conversion when we have tables but not a file.
9
13
  #
10
14
  # @param tables [Hash<String, String>] Map of table tag to binary data
11
15
  # @return [OpenTypeFont] New font instance
12
- def self.from_tables(tables)
13
- # Create minimal header structure
16
+ def from_tables(tables)
14
17
  font = new
15
18
  font.initialize_storage
16
19
  font.loading_mode = LoadingModes::FULL
17
20
 
18
- # Store table data
19
21
  font.table_data = tables
20
22
 
21
- # Build header from tables
22
23
  num_tables = tables.size
23
24
  max_power = 0
24
25
  n = num_tables
@@ -37,13 +38,12 @@ module Fontisan
37
38
  font.header.entry_selector = entry_selector
38
39
  font.header.range_shift = range_shift
39
40
 
40
- # Build table directory
41
41
  font.tables.clear
42
42
  tables.each_key do |tag|
43
43
  entry = TableDirectory.new
44
44
  entry.tag = tag
45
- entry.checksum = 0 # Will be calculated on write
46
- entry.offset = 0 # Will be calculated on write
45
+ entry.checksum = 0
46
+ entry.offset = 0
47
47
  entry.table_length = tables[tag].bytesize
48
48
  font.tables << entry
49
49
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "stringio"
4
- require_relative "stack_tracker"
5
4
 
6
5
  module Fontisan
7
6
  module Optimizers
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Autoload hub for the Fontisan::Optimizers namespace.
4
+
5
+ module Fontisan
6
+ module Optimizers
7
+ autoload :CharstringRewriter, "fontisan/optimizers/charstring_rewriter"
8
+ autoload :PatternAnalyzer, "fontisan/optimizers/pattern_analyzer"
9
+ autoload :StackTracker, "fontisan/optimizers/stack_tracker"
10
+ autoload :SubroutineBuilder, "fontisan/optimizers/subroutine_builder"
11
+ autoload :SubroutineGenerator, "fontisan/optimizers/subroutine_generator"
12
+ autoload :SubroutineOptimizer, "fontisan/optimizers/subroutine_optimizer"
13
+ end
14
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "models/glyph_outline"
4
-
5
3
  module Fontisan
6
4
  # Extracts glyph outlines from font tables
7
5
  #
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bindata"
4
- require_relative "../error"
5
4
 
6
5
  module Fontisan
7
6
  module Parsers
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Autoload hub for the Fontisan::Parsers namespace.
4
+
5
+ module Fontisan
6
+ module Parsers
7
+ autoload :DfontParser, "fontisan/parsers/dfont_parser"
8
+ autoload :Tag, "fontisan/parsers/tag"
9
+ end
10
+ end