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
@@ -1,11 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_command"
4
- require_relative "../pipeline/transformation_pipeline"
5
- require_relative "../converters/collection_converter"
6
- require_relative "../conversion_options"
7
- require_relative "../font_loader"
8
-
9
3
  module Fontisan
10
4
  module Commands
11
5
  # Command for converting fonts between formats
@@ -67,29 +61,33 @@ module Fontisan
67
61
  def initialize(font_path, options = {})
68
62
  super(font_path, options)
69
63
 
70
- # Convert string keys to symbols for Thor compatibility
71
- opts = options.transform_keys(&:to_sym)
64
+ # Convert string keys to symbols for Thor compatibility. All command
65
+ # code reads @options with symbol keys (e.g., @options[:quiet]);
66
+ # normalizing once at construction is cleaner than threading a
67
+ # second opts hash alongside @options.
68
+ @options = options.transform_keys(&:to_sym)
72
69
 
73
- @output_path = opts[:output]
70
+ @output_path = @options[:output]
74
71
 
75
72
  # Parse target format
76
- @target_format = parse_target_format(opts[:to])
73
+ @target_format = parse_target_format(@options[:to])
77
74
 
78
75
  # Extract ConversionOptions if provided
79
- @conv_options = extract_conversion_options(opts)
76
+ @conv_options = extract_conversion_options(@options)
80
77
 
81
78
  # Parse coordinates if string provided
82
- @coordinates = if opts[:coordinates]
83
- parse_coordinates(opts[:coordinates])
84
- elsif opts[:instance_coordinates]
85
- opts[:instance_coordinates]
79
+ @coordinates = if @options[:coordinates]
80
+ parse_coordinates(@options[:coordinates])
81
+ elsif @options[:instance_coordinates]
82
+ @options[:instance_coordinates]
86
83
  end
87
84
 
88
- @instance_index = opts[:instance_index]
89
- @preserve_variation = opts[:preserve_variation]
90
- @preserve_hints = opts.fetch(:preserve_hints, false)
91
- @collection_target_format = opts.fetch(:target_format, "preserve").to_s
92
- @validate = !opts[:no_validate]
85
+ @instance_index = @options[:instance_index]
86
+ @preserve_variation = @options[:preserve_variation]
87
+ @preserve_hints = @options.fetch(:preserve_hints, false)
88
+ @collection_target_format = @options.fetch(:target_format,
89
+ "preserve").to_s
90
+ @validate = !@options[:no_validate]
93
91
  end
94
92
 
95
93
  # Execute the conversion
@@ -152,6 +150,13 @@ module Fontisan
152
150
  # Add hint preservation option
153
151
  pipeline_options[:preserve_hints] = @preserve_hints if @preserve_hints
154
152
 
153
+ # Forward format-specific compression knobs declared by strategies
154
+ # (zlib_level, brotli_quality, etc.). Cross-format misuse is caught
155
+ # downstream by FormatConverter.validate_options_for_target!.
156
+ Converters::FormatConverter.all_strategy_option_names.each do |opt|
157
+ pipeline_options[opt] = @options[opt] if @options.key?(opt)
158
+ end
159
+
155
160
  # Use TransformationPipeline for universal conversion
156
161
  pipeline = Pipeline::TransformationPipeline.new(
157
162
  font_path,
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../font_loader"
4
- require_relative "../error"
5
-
6
3
  module Fontisan
7
4
  module Commands
8
5
  # Command to dump raw table data from fonts
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_command"
4
- require_relative "../export/exporter"
5
- require_relative "../font_loader"
6
-
7
3
  module Fontisan
8
4
  module Commands
9
5
  # ExportCommand provides CLI interface for font export to YAML/JSON
@@ -1,9 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "yaml"
4
- require_relative "base_command"
5
- require_relative "../models/features_info"
6
- require_relative "../models/all_scripts_features_info"
7
4
 
8
5
  module Fontisan
9
6
  module Commands
@@ -1,11 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "thor"
4
- require_relative "base_command"
5
- require_relative "../variation/instance_generator"
6
- require_relative "../variation/instance_writer"
7
- require_relative "../variation/validator"
8
- require_relative "../error"
9
4
 
10
5
  module Fontisan
11
6
  module Commands
@@ -1,11 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../font_loader"
4
- require_relative "../models/collection_list_info"
5
- require_relative "../models/font_summary"
6
- require_relative "../tables/name"
7
- require_relative "../error"
8
-
9
3
  module Fontisan
10
4
  module Commands
11
5
  # Command to list contents of font files (collections or individual fonts).
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_command"
4
- require_relative "../models/optical_size_info"
5
-
6
3
  module Fontisan
7
4
  module Commands
8
5
  # Command to extract optical size information from fonts
@@ -1,10 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_command"
4
- require_relative "../collection/builder"
5
- require_relative "../collection/dfont_builder"
6
- require_relative "../font_loader"
7
-
8
3
  module Fontisan
9
4
  module Commands
10
5
  # Command for packing multiple fonts into a TTC/OTC collection
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "yaml"
4
- require_relative "base_command"
5
- require_relative "../models/scripts_info"
6
4
 
7
5
  module Fontisan
8
6
  module Commands
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_command"
4
- require_relative "../subset/builder"
5
- require_relative "../subset/options"
6
3
  require "fileutils"
7
4
 
8
5
  module Fontisan
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_command"
4
- require_relative "../models/unicode_mappings"
5
-
6
3
  module Fontisan
7
4
  module Commands
8
5
  # Command to list Unicode to glyph index mappings from a font file
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_command"
4
- require_relative "../font_loader"
5
- require_relative "../font_writer"
6
3
  require "fileutils"
7
4
 
8
5
  module Fontisan
@@ -144,10 +141,8 @@ module Fontisan
144
141
  # Load as TTC or OTC based on extension hint
145
142
  # Both use same structure, main difference is expected font types
146
143
  if ext == ".otc"
147
- require_relative "../open_type_collection"
148
144
  OpenTypeCollection.read(io)
149
145
  else
150
- require_relative "../true_type_collection"
151
146
  TrueTypeCollection.read(io)
152
147
  end
153
148
  end
@@ -252,8 +247,6 @@ module Fontisan
252
247
  # @param output_path [String] Output file path
253
248
  # @return [void]
254
249
  def convert_and_write(font, output_path)
255
- require_relative "../converters/format_converter"
256
-
257
250
  converter = Converters::FormatConverter.new
258
251
  converter.convert(font, @format, output_path: output_path)
259
252
  rescue StandardError => e
@@ -1,10 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_command"
4
- require_relative "../validators/profile_loader"
5
- require_relative "../font_loader"
6
- require_relative "../tables/name"
7
-
8
3
  module Fontisan
9
4
  module Commands
10
5
  # ValidateCommand provides CLI interface for font validation
@@ -138,9 +133,6 @@ module Fontisan
138
133
  # @param mode [Symbol] Loading mode
139
134
  # @return [Integer] Exit code
140
135
  def validate_collection(mode)
141
- require_relative "../models/collection_validation_report"
142
- require_relative "../models/font_report"
143
-
144
136
  # Load collection metadata
145
137
  collection = FontLoader.load_collection(@input)
146
138
 
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_command"
4
- require_relative "../variation/inspector"
5
-
6
3
  module Fontisan
7
4
  module Commands
8
5
  # Command to extract variable font information.
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Autoload hub for the Fontisan::Commands namespace.
4
+
5
+ module Fontisan
6
+ module Commands
7
+ autoload :AuditCompareCommand, "fontisan/commands/audit_compare_command"
8
+ autoload :AuditCommand, "fontisan/commands/audit_command"
9
+ autoload :AuditLibraryCommand, "fontisan/commands/audit_library_command"
10
+ autoload :BaseCommand, "fontisan/commands/base_command"
11
+ autoload :ConvertCommand, "fontisan/commands/convert_command"
12
+ autoload :DumpTableCommand, "fontisan/commands/dump_table_command"
13
+ autoload :ExportCommand, "fontisan/commands/export_command"
14
+ autoload :FeaturesCommand, "fontisan/commands/features_command"
15
+ autoload :GlyphsCommand, "fontisan/commands/glyphs_command"
16
+ autoload :InfoCommand, "fontisan/commands/info_command"
17
+ autoload :InstanceCommand, "fontisan/commands/instance_command"
18
+ autoload :LsCommand, "fontisan/commands/ls_command"
19
+ autoload :OpticalSizeCommand, "fontisan/commands/optical_size_command"
20
+ autoload :PackCommand, "fontisan/commands/pack_command"
21
+ autoload :ScriptsCommand, "fontisan/commands/scripts_command"
22
+ autoload :SubsetCommand, "fontisan/commands/subset_command"
23
+ autoload :TablesCommand, "fontisan/commands/tables_command"
24
+ autoload :UnicodeCommand, "fontisan/commands/unicode_command"
25
+ autoload :UnpackCommand, "fontisan/commands/unpack_command"
26
+ autoload :ValidateCommand, "fontisan/commands/validate_command"
27
+ autoload :VariableCommand, "fontisan/commands/variable_command"
28
+ end
29
+ end
@@ -0,0 +1,22 @@
1
+ # UCD alternative: CLDR (Common Locale Data Repository) configuration.
2
+ #
3
+ # This file is the single source of truth for which CLDR version Fontisan
4
+ # uses by default, what versions are recognized, and where to fetch them.
5
+ #
6
+ # Update `default_version` and `known_versions` when a new CLDR release
7
+ # ships (typically 2× per year, alongside Unicode releases).
8
+ # Users override at runtime via `fontisan cldr download --version X.Y.Z`
9
+ # or `fontisan audit --cldr-version X.Y.Z`.
10
+
11
+ default_version: "46.0.0"
12
+
13
+ known_versions:
14
+ - "45.0.0"
15
+ - "46.0.0"
16
+
17
+ # CLDR JSON releases are published on GitHub.
18
+ # Full URL: <base_url>/<version>/cldr-<version>-json-full.zip
19
+ base_url: "https://github.com/unicode-org/cldr-json/releases/download"
20
+
21
+ # Releases listing URL for `--latest` probing. JSON API.
22
+ listing_url: "https://api.github.com/repos/unicode-org/cldr-json/releases"
@@ -174,6 +174,25 @@ conversions:
174
174
  WOFF decompression with zlib. Preserves all font data and metadata.
175
175
  Automatically detects font flavor from WOFF header.
176
176
 
177
+ - from: woff
178
+ to: woff2
179
+ strategy: woff_reader
180
+ description: "Transcode WOFF to WOFF2 (re-wrap with Brotli)"
181
+ status: implemented
182
+ notes: >
183
+ Web-format transcoding. The pipeline decodes WOFF (zlib) into its
184
+ underlying SFNT tables, then re-wraps them as WOFF2 with Brotli.
185
+ Useful for migrating legacy web fonts to the smaller WOFF2 format.
186
+
187
+ - from: woff
188
+ to: svg
189
+ strategy: svg_generator
190
+ description: "Generate SVG font from WOFF"
191
+ status: implemented
192
+ notes: >
193
+ Decodes WOFF (zlib) to SFNT tables, then renders glyph outlines as
194
+ SVG paths. Note: SVG fonts are deprecated in favor of WOFF2.
195
+
177
196
  # Phase 2 conversions (Milestone 2.2) - SVG
178
197
  - from: ttf
179
198
  to: svg
@@ -254,6 +273,25 @@ conversions:
254
273
  Same-format copy operation. Decompresses and re-compresses WOFF2 data
255
274
  with Brotli, useful for normalizing compression or updating metadata.
256
275
 
276
+ - from: woff2
277
+ to: woff
278
+ strategy: woff2_decoder
279
+ description: "Transcode WOFF2 to WOFF (re-wrap with zlib)"
280
+ status: implemented
281
+ notes: >
282
+ Web-format transcoding. The pipeline decodes WOFF2 (Brotli) into its
283
+ underlying SFNT tables, then re-wraps them as WOFF with zlib. Useful
284
+ for legacy browser compatibility where WOFF2 is unavailable.
285
+
286
+ - from: woff2
287
+ to: svg
288
+ strategy: svg_generator
289
+ description: "Generate SVG font from WOFF2"
290
+ status: implemented
291
+ notes: >
292
+ Decodes WOFF2 (Brotli) to SFNT tables, then renders glyph outlines as
293
+ SVG paths. Note: SVG fonts are deprecated in favor of WOFF2.
294
+
257
295
  # Collection format conversions (TTC/OTC/dfont)
258
296
  # Same-format collection operations
259
297
  - from: ttc
@@ -0,0 +1,23 @@
1
+ # UCD (Unicode Character Database) configuration.
2
+ #
3
+ # This file is the single source of truth for which UCD version Fontisan
4
+ # uses by default, what versions are recognized, and where to fetch them.
5
+ #
6
+ # Update `default_version` and `known_versions` when a new Unicode version
7
+ # is released (typically twice a year). Users may override at runtime via
8
+ # `fontisan ucd download --version X.Y.Z` or `fontisan audit --ucd-version X.Y.Z`.
9
+
10
+ default_version: "17.0.0"
11
+
12
+ known_versions:
13
+ - "15.0.0"
14
+ - "15.1.0"
15
+ - "16.0.0"
16
+ - "17.0.0"
17
+
18
+ # Base URL for fetching UCDXML artifacts.
19
+ # Full URL: <base_url>/<version>/ucdxml/ucd.all.flat.zip
20
+ base_url: "https://www.unicode.org/Public"
21
+
22
+ # Listing URL for `--latest` probing. HTML scraping, best-effort.
23
+ listing_url: "https://www.unicode.org/Public/ucdxml/"
@@ -89,6 +89,9 @@ module Fontisan
89
89
  # Fvar table tag identifier (Font Variations)
90
90
  FVAR_TAG = "fvar"
91
91
 
92
+ # Avar table tag identifier (Axis Variation)
93
+ AVAR_TAG = "avar"
94
+
92
95
  # Gvar table tag identifier (Glyph Variations for TrueType)
93
96
  GVAR_TAG = "gvar"
94
97
 
@@ -129,6 +132,22 @@ module Fontisan
129
132
  # Control Value Table (metrics used by TrueType hinting)
130
133
  CVT_TAG = "cvt "
131
134
 
135
+ # Grid-fitting And Scan-conversion Procedure table (per-ppem hinting policy)
136
+ GASP_TAG = "gasp"
137
+
138
+ # Color font tables.
139
+ # COLR: COLR vector color glyph table (v0 or v1).
140
+ # CPAL: Color Palette table.
141
+ # SVG: SVG-in-OpenType color glyph table (tag is "SVG " — 4 bytes incl. trailing space).
142
+ # CBDT/CBLC: Color Bitmap Data / Location (paired — EBLC/EBDT equivalents for color).
143
+ # sbix: Apple bitmap glyph table.
144
+ COLR_TAG = "COLR"
145
+ CPAL_TAG = "CPAL"
146
+ SVG_TAG = "SVG "
147
+ CBDT_TAG = "CBDT"
148
+ CBLC_TAG = "CBLC"
149
+ SBIX_TAG = "sbix"
150
+
132
151
  # Magic number used for font file checksum adjustment calculation.
133
152
  # This constant is used in conjunction with the file checksum to compute
134
153
  # the checksumAdjustment value stored in the 'head' table.
@@ -35,8 +35,12 @@ module Fontisan
35
35
  preserve_encoding
36
36
  ].freeze
37
37
 
38
- # Generating options (output processing)
39
- GENERATING_OPTIONS = %i[
38
+ # Generating options (output processing) declared statically by
39
+ # ConversionOptions itself. Format-specific compression knobs (zlib_level,
40
+ # brotli_quality, transform_tables, etc.) are declared by each strategy
41
+ # via the ConversionStrategy DSL and discovered via
42
+ # FormatConverter.all_strategy_option_names — see `.generating_options`.
43
+ STATIC_GENERATING_OPTIONS = %i[
40
44
  write_pfm
41
45
  write_afm
42
46
  write_inf
@@ -47,19 +51,28 @@ module Fontisan
47
51
  optimize_tables
48
52
  reencode_first_256
49
53
  encoding_vector
50
- compression
51
- transform_tables
52
54
  preserve_metadata
53
55
  strip_metadata
54
56
  target_format
55
57
  ].freeze
56
58
 
59
+ # Full list of recognized generating options, computed lazily because
60
+ # converters `require_relative` this file, creating a load cycle that
61
+ # prevents resolving `FormatConverter` at class-definition time. The first
62
+ # call resolves all strategies (which are loaded before any conversion
63
+ # actually runs) and caches the result.
64
+ #
65
+ # @return [Array<Symbol>]
66
+ def self.generating_options
67
+ @generating_options ||= (
68
+ STATIC_GENERATING_OPTIONS +
69
+ Converters::FormatConverter.all_strategy_option_names
70
+ ).uniq.freeze
71
+ end
72
+
57
73
  # Valid hinting modes
58
74
  HINTING_MODES = %w[preserve auto none full].freeze
59
75
 
60
- # Valid compression modes
61
- COMPRESSION_MODES = %w[zlib brotli none].freeze
62
-
63
76
  attr_reader :from, :to, :opening, :generating
64
77
 
65
78
  # Initialize conversion options
@@ -199,9 +212,9 @@ module Fontisan
199
212
  # Validate generating options
200
213
  def validate_generating_options!
201
214
  @generating.each_key do |key|
202
- unless GENERATING_OPTIONS.include?(key)
215
+ unless self.class.generating_options.include?(key)
203
216
  raise ArgumentError, "Unknown generating option: #{key}. " \
204
- "Available: #{GENERATING_OPTIONS.join(', ')}"
217
+ "Available: #{self.class.generating_options.join(', ')}"
205
218
  end
206
219
  end
207
220
 
@@ -214,15 +227,6 @@ module Fontisan
214
227
  end
215
228
  end
216
229
 
217
- # Validate compression mode
218
- if @generating[:compression]
219
- comp = @generating[:compression].to_s
220
- unless COMPRESSION_MODES.include?(comp)
221
- raise ArgumentError, "Invalid compression: #{comp}. " \
222
- "Available: #{COMPRESSION_MODES.join(', ')}"
223
- end
224
- end
225
-
226
230
  # Validate target_format for collection conversions
227
231
  if @generating[:target_format]
228
232
  target = @generating[:target_format].to_s
@@ -364,9 +368,16 @@ module Fontisan
364
368
  from: :otf,
365
369
  to: :woff2,
366
370
  opening: {},
367
- generating: { compression: "brotli", transform_tables: true,
371
+ generating: { brotli_quality: 11, transform_tables: true,
368
372
  optimize_tables: true, preserve_metadata: true },
369
373
  },
374
+ legacy_web: {
375
+ from: :otf,
376
+ to: :woff,
377
+ opening: {},
378
+ generating: { zlib_level: 9, optimize_tables: true,
379
+ preserve_metadata: true },
380
+ },
370
381
  archive_to_modern: {
371
382
  from: :ttc,
372
383
  to: :otf,
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../tables/cff/index_builder"
4
- require_relative "../tables/cff/dict_builder"
5
-
6
3
  module Fontisan
7
4
  module Converters
8
5
  # Builds CFF table data from glyph outlines
@@ -1,12 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../conversion_options"
4
- require_relative "format_converter"
5
- require_relative "../collection/builder"
6
- require_relative "../collection/dfont_builder"
7
- require_relative "../parsers/dfont_parser"
8
- require_relative "../font_loader"
9
-
10
3
  module Fontisan
11
4
  module Converters
12
5
  # CollectionConverter handles conversion between collection formats
@@ -343,7 +336,6 @@ conv_options = nil)
343
336
  # @return [Font] Font object
344
337
  def build_font_from_tables(tables, format)
345
338
  # Create temporary font from tables
346
- require_relative "../font_writer"
347
339
  require "stringio"
348
340
 
349
341
  sfnt_version = format == :otf ? 0x4F54544F : 0x00010000