fontisan 0.2.16 → 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 (318) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +14 -90
  3. data/Gemfile +6 -3
  4. data/README.adoc +257 -1
  5. data/docs/.vitepress/config.ts +68 -8
  6. data/docs/.vitepress/theme/style.css +570 -272
  7. data/docs/CONVERSION_GUIDE.adoc +31 -8
  8. data/docs/EXTRACT_TTC_MIGRATION.md +1 -1
  9. data/docs/WOFF_WOFF2_FORMATS.adoc +53 -0
  10. data/docs/api/conversion-options.md +37 -14
  11. data/docs/api/font-loader.md +21 -15
  12. data/docs/cli/audit.md +337 -0
  13. data/docs/cli/convert.md +20 -1
  14. data/docs/cli/index.md +31 -0
  15. data/docs/guide/color.md +1 -1
  16. data/docs/guide/conversion/options.md +32 -3
  17. data/docs/guide/conversion/ttf-otf.md +1 -1
  18. data/docs/guide/conversion/type1.md +1 -1
  19. data/docs/guide/conversion/web.md +91 -32
  20. data/docs/guide/conversion.md +6 -5
  21. data/docs/guide/formats/woff.md +35 -11
  22. data/docs/guide/index.md +2 -2
  23. data/docs/guide/migrations/extract-ttc.md +1 -1
  24. data/docs/guide/quick-start.md +4 -4
  25. data/docs/guide/type1.md +4 -4
  26. data/docs/guide/woff.md +19 -17
  27. data/docs/index.md +2 -0
  28. data/docs/lychee.toml +5 -1
  29. data/docs/package.json +1 -1
  30. data/docs/public/robots.txt +4 -0
  31. data/docs/scripts/post-build.mjs +81 -0
  32. data/lib/fontisan/audit/codepoint_range_coalescer.rb +41 -0
  33. data/lib/fontisan/audit/context.rb +122 -0
  34. data/lib/fontisan/audit/differ.rb +124 -0
  35. data/lib/fontisan/audit/extractors/aggregations.rb +54 -0
  36. data/lib/fontisan/audit/extractors/base.rb +26 -0
  37. data/lib/fontisan/audit/extractors/color_capabilities.rb +141 -0
  38. data/lib/fontisan/audit/extractors/coverage.rb +48 -0
  39. data/lib/fontisan/audit/extractors/hinting.rb +197 -0
  40. data/lib/fontisan/audit/extractors/identity.rb +52 -0
  41. data/lib/fontisan/audit/extractors/language_coverage.rb +37 -0
  42. data/lib/fontisan/audit/extractors/licensing.rb +79 -0
  43. data/lib/fontisan/audit/extractors/metrics.rb +103 -0
  44. data/lib/fontisan/audit/extractors/opentype_layout.rb +69 -0
  45. data/lib/fontisan/audit/extractors/provenance.rb +29 -0
  46. data/lib/fontisan/audit/extractors/style.rb +32 -0
  47. data/lib/fontisan/audit/extractors/variation_detail.rb +99 -0
  48. data/lib/fontisan/audit/extractors.rb +27 -0
  49. data/lib/fontisan/audit/library_aggregator.rb +83 -0
  50. data/lib/fontisan/audit/library_auditor.rb +90 -0
  51. data/lib/fontisan/audit/registry.rb +60 -0
  52. data/lib/fontisan/audit/style_extractor.rb +80 -0
  53. data/lib/fontisan/audit.rb +20 -0
  54. data/lib/fontisan/base_collection.rb +23 -9
  55. data/lib/fontisan/binary/structures.rb +0 -2
  56. data/lib/fontisan/binary.rb +11 -0
  57. data/lib/fontisan/cldr/aggregator.rb +33 -0
  58. data/lib/fontisan/cldr/cache_manager.rb +110 -0
  59. data/lib/fontisan/cldr/config.rb +59 -0
  60. data/lib/fontisan/cldr/download_error.rb +9 -0
  61. data/lib/fontisan/cldr/downloader.rb +79 -0
  62. data/lib/fontisan/cldr/error.rb +8 -0
  63. data/lib/fontisan/cldr/index.rb +64 -0
  64. data/lib/fontisan/cldr/index_builder.rb +72 -0
  65. data/lib/fontisan/cldr/unicode_set_parser.rb +172 -0
  66. data/lib/fontisan/cldr/unknown_version_error.rb +9 -0
  67. data/lib/fontisan/cldr/version_resolver.rb +91 -0
  68. data/lib/fontisan/cldr.rb +23 -0
  69. data/lib/fontisan/cli/cldr_cli.rb +85 -0
  70. data/lib/fontisan/cli/ucd_cli.rb +97 -0
  71. data/lib/fontisan/cli.rb +201 -2
  72. data/lib/fontisan/collection/builder.rb +0 -4
  73. data/lib/fontisan/collection/dfont_builder.rb +0 -4
  74. data/lib/fontisan/collection/shared_logic.rb +0 -2
  75. data/lib/fontisan/collection/writer.rb +0 -3
  76. data/lib/fontisan/collection.rb +15 -0
  77. data/lib/fontisan/commands/audit_command.rb +123 -0
  78. data/lib/fontisan/commands/audit_compare_command.rb +66 -0
  79. data/lib/fontisan/commands/audit_library_command.rb +46 -0
  80. data/lib/fontisan/commands/base_command.rb +0 -3
  81. data/lib/fontisan/commands/convert_command.rb +25 -20
  82. data/lib/fontisan/commands/dump_table_command.rb +0 -3
  83. data/lib/fontisan/commands/export_command.rb +0 -4
  84. data/lib/fontisan/commands/features_command.rb +0 -3
  85. data/lib/fontisan/commands/instance_command.rb +0 -5
  86. data/lib/fontisan/commands/ls_command.rb +0 -6
  87. data/lib/fontisan/commands/optical_size_command.rb +0 -3
  88. data/lib/fontisan/commands/pack_command.rb +0 -5
  89. data/lib/fontisan/commands/scripts_command.rb +0 -2
  90. data/lib/fontisan/commands/subset_command.rb +0 -3
  91. data/lib/fontisan/commands/unicode_command.rb +0 -3
  92. data/lib/fontisan/commands/unpack_command.rb +0 -7
  93. data/lib/fontisan/commands/validate_command.rb +0 -8
  94. data/lib/fontisan/commands/variable_command.rb +0 -3
  95. data/lib/fontisan/commands.rb +29 -0
  96. data/lib/fontisan/config/cldr.yml +22 -0
  97. data/lib/fontisan/config/conversion_matrix.yml +38 -0
  98. data/lib/fontisan/config/ucd.yml +23 -0
  99. data/lib/fontisan/constants.rb +48 -6
  100. data/lib/fontisan/conversion_options.rb +30 -19
  101. data/lib/fontisan/converters/cff_table_builder.rb +0 -3
  102. data/lib/fontisan/converters/collection_converter.rb +0 -8
  103. data/lib/fontisan/converters/conversion_strategy.rb +161 -46
  104. data/lib/fontisan/converters/format_converter.rb +143 -32
  105. data/lib/fontisan/converters/glyf_table_builder.rb +0 -2
  106. data/lib/fontisan/converters/outline_converter.rb +0 -19
  107. data/lib/fontisan/converters/outline_extraction.rb +0 -5
  108. data/lib/fontisan/converters/outline_optimizer.rb +0 -5
  109. data/lib/fontisan/converters/svg_generator.rb +0 -4
  110. data/lib/fontisan/converters/table_copier.rb +0 -2
  111. data/lib/fontisan/converters/type1_converter.rb +0 -11
  112. data/lib/fontisan/converters/woff2_encoder.rb +49 -20
  113. data/lib/fontisan/converters/woff_writer.rb +211 -282
  114. data/lib/fontisan/converters.rb +21 -0
  115. data/lib/fontisan/dfont_collection.rb +29 -10
  116. data/lib/fontisan/export/exporter.rb +0 -6
  117. data/lib/fontisan/export/transformers/font_to_ttx.rb +0 -9
  118. data/lib/fontisan/export/transformers/head_transformer.rb +0 -2
  119. data/lib/fontisan/export/transformers/hhea_transformer.rb +0 -2
  120. data/lib/fontisan/export/transformers/maxp_transformer.rb +0 -2
  121. data/lib/fontisan/export/transformers/name_transformer.rb +0 -2
  122. data/lib/fontisan/export/transformers/os2_transformer.rb +0 -2
  123. data/lib/fontisan/export/transformers/post_transformer.rb +0 -2
  124. data/lib/fontisan/export/transformers.rb +17 -0
  125. data/lib/fontisan/export.rb +13 -0
  126. data/lib/fontisan/font_loader.rb +189 -328
  127. data/lib/fontisan/font_writer.rb +0 -1
  128. data/lib/fontisan/formatters/audit_diff_text_renderer.rb +122 -0
  129. data/lib/fontisan/formatters/audit_text_renderer.rb +324 -0
  130. data/lib/fontisan/formatters/library_summary_text_renderer.rb +99 -0
  131. data/lib/fontisan/formatters/text_formatter.rb +6 -0
  132. data/lib/fontisan/formatters.rb +12 -0
  133. data/lib/fontisan/hints/hint_converter.rb +0 -1
  134. data/lib/fontisan/hints/postscript_hint_applier.rb +0 -9
  135. data/lib/fontisan/hints/postscript_hint_extractor.rb +0 -2
  136. data/lib/fontisan/hints/truetype_hint_extractor.rb +0 -2
  137. data/lib/fontisan/hints.rb +16 -0
  138. data/lib/fontisan/metrics_calculator.rb +0 -2
  139. data/lib/fontisan/models/all_scripts_features_info.rb +0 -1
  140. data/lib/fontisan/models/audit/audit_axis.rb +30 -0
  141. data/lib/fontisan/models/audit/audit_block.rb +32 -0
  142. data/lib/fontisan/models/audit/audit_diff.rb +77 -0
  143. data/lib/fontisan/models/audit/audit_report.rb +153 -0
  144. data/lib/fontisan/models/audit/codepoint_range.rb +40 -0
  145. data/lib/fontisan/models/audit/codepoint_set_diff.rb +34 -0
  146. data/lib/fontisan/models/audit/color_capabilities.rb +93 -0
  147. data/lib/fontisan/models/audit/duplicate_group.rb +23 -0
  148. data/lib/fontisan/models/audit/embedding_type.rb +76 -0
  149. data/lib/fontisan/models/audit/field_change.rb +28 -0
  150. data/lib/fontisan/models/audit/fs_selection_flags.rb +61 -0
  151. data/lib/fontisan/models/audit/gasp_range.rb +63 -0
  152. data/lib/fontisan/models/audit/hinting.rb +93 -0
  153. data/lib/fontisan/models/audit/library_summary.rb +40 -0
  154. data/lib/fontisan/models/audit/licensing.rb +48 -0
  155. data/lib/fontisan/models/audit/metrics.rb +111 -0
  156. data/lib/fontisan/models/audit/named_instance.rb +41 -0
  157. data/lib/fontisan/models/audit/opentype_layout.rb +40 -0
  158. data/lib/fontisan/models/audit/script_coverage_row.rb +26 -0
  159. data/lib/fontisan/models/audit/script_features.rb +28 -0
  160. data/lib/fontisan/models/audit/variation_detail.rb +44 -0
  161. data/lib/fontisan/models/audit.rb +33 -0
  162. data/lib/fontisan/models/cldr/language_coverage.rb +31 -0
  163. data/lib/fontisan/models/cldr.rb +12 -0
  164. data/lib/fontisan/models/collection_brief_info.rb +0 -1
  165. data/lib/fontisan/models/collection_info.rb +0 -2
  166. data/lib/fontisan/models/collection_list_info.rb +0 -1
  167. data/lib/fontisan/models/collection_validation_report.rb +0 -2
  168. data/lib/fontisan/models/color_glyph.rb +0 -1
  169. data/lib/fontisan/models/font_report.rb +0 -1
  170. data/lib/fontisan/models/ttx/tables.rb +21 -0
  171. data/lib/fontisan/models/ttx/ttfont.rb +0 -8
  172. data/lib/fontisan/models/ttx.rb +14 -0
  173. data/lib/fontisan/models/ucd/ucd.rb +38 -0
  174. data/lib/fontisan/models/ucd/ucd_char.rb +67 -0
  175. data/lib/fontisan/models/ucd.rb +19 -0
  176. data/lib/fontisan/models.rb +47 -0
  177. data/lib/fontisan/open_type_collection.rb +6 -5
  178. data/lib/fontisan/open_type_font.rb +8 -2
  179. data/lib/fontisan/open_type_font_extensions.rb +9 -9
  180. data/lib/fontisan/optimizers/pattern_analyzer.rb +0 -1
  181. data/lib/fontisan/optimizers.rb +14 -0
  182. data/lib/fontisan/outline_extractor.rb +0 -2
  183. data/lib/fontisan/parsers/dfont_parser.rb +0 -1
  184. data/lib/fontisan/parsers.rb +10 -0
  185. data/lib/fontisan/pipeline/format_detector.rb +29 -102
  186. data/lib/fontisan/pipeline/output_writer.rb +11 -9
  187. data/lib/fontisan/pipeline/strategies/instance_strategy.rb +0 -4
  188. data/lib/fontisan/pipeline/strategies/named_strategy.rb +0 -4
  189. data/lib/fontisan/pipeline/strategies/preserve_strategy.rb +0 -2
  190. data/lib/fontisan/pipeline/strategies.rb +14 -0
  191. data/lib/fontisan/pipeline/transformation_pipeline.rb +0 -7
  192. data/lib/fontisan/pipeline/variation_resolver.rb +0 -7
  193. data/lib/fontisan/pipeline.rb +13 -0
  194. data/lib/fontisan/sfnt_font.rb +29 -14
  195. data/lib/fontisan/sfnt_table.rb +0 -4
  196. data/lib/fontisan/subset/builder.rb +0 -6
  197. data/lib/fontisan/subset.rb +13 -0
  198. data/lib/fontisan/svg/font_generator.rb +0 -4
  199. data/lib/fontisan/svg/glyph_generator.rb +0 -2
  200. data/lib/fontisan/svg.rb +12 -0
  201. data/lib/fontisan/tables/cbdt.rb +0 -1
  202. data/lib/fontisan/tables/cblc.rb +0 -1
  203. data/lib/fontisan/tables/cff/charset.rb +0 -1
  204. data/lib/fontisan/tables/cff/charstring.rb +0 -1
  205. data/lib/fontisan/tables/cff/charstring_rebuilder.rb +0 -4
  206. data/lib/fontisan/tables/cff/charstrings_index.rb +0 -3
  207. data/lib/fontisan/tables/cff/dict.rb +0 -1
  208. data/lib/fontisan/tables/cff/encoding.rb +0 -1
  209. data/lib/fontisan/tables/cff/header.rb +0 -2
  210. data/lib/fontisan/tables/cff/hint_operation_injector.rb +0 -2
  211. data/lib/fontisan/tables/cff/index.rb +0 -1
  212. data/lib/fontisan/tables/cff/private_dict.rb +0 -2
  213. data/lib/fontisan/tables/cff/private_dict_writer.rb +0 -2
  214. data/lib/fontisan/tables/cff/table_builder.rb +0 -6
  215. data/lib/fontisan/tables/cff/top_dict.rb +0 -2
  216. data/lib/fontisan/tables/cff.rb +22 -15
  217. data/lib/fontisan/tables/cff2/charstring_parser.rb +0 -2
  218. data/lib/fontisan/tables/cff2/table_builder.rb +0 -11
  219. data/lib/fontisan/tables/cff2/table_reader.rb +0 -2
  220. data/lib/fontisan/tables/cff2.rb +13 -14
  221. data/lib/fontisan/tables/cmap.rb +24 -2
  222. data/lib/fontisan/tables/cmap_table.rb +0 -3
  223. data/lib/fontisan/tables/colr.rb +0 -1
  224. data/lib/fontisan/tables/cpal.rb +0 -1
  225. data/lib/fontisan/tables/cvar.rb +0 -2
  226. data/lib/fontisan/tables/fvar.rb +0 -1
  227. data/lib/fontisan/tables/glyf/compound_glyph_resolver.rb +0 -2
  228. data/lib/fontisan/tables/glyf/glyph_builder.rb +0 -3
  229. data/lib/fontisan/tables/glyf.rb +0 -6
  230. data/lib/fontisan/tables/glyf_table.rb +0 -3
  231. data/lib/fontisan/tables/gpos.rb +0 -2
  232. data/lib/fontisan/tables/gsub.rb +0 -2
  233. data/lib/fontisan/tables/gvar.rb +0 -2
  234. data/lib/fontisan/tables/head.rb +0 -2
  235. data/lib/fontisan/tables/head_table.rb +0 -3
  236. data/lib/fontisan/tables/hhea.rb +0 -2
  237. data/lib/fontisan/tables/hhea_table.rb +0 -3
  238. data/lib/fontisan/tables/hmtx.rb +0 -2
  239. data/lib/fontisan/tables/hmtx_table.rb +0 -3
  240. data/lib/fontisan/tables/hvar.rb +0 -3
  241. data/lib/fontisan/tables/loca.rb +0 -2
  242. data/lib/fontisan/tables/loca_table.rb +0 -3
  243. data/lib/fontisan/tables/maxp.rb +0 -2
  244. data/lib/fontisan/tables/maxp_table.rb +0 -3
  245. data/lib/fontisan/tables/mvar.rb +0 -3
  246. data/lib/fontisan/tables/name.rb +0 -2
  247. data/lib/fontisan/tables/name_table.rb +0 -3
  248. data/lib/fontisan/tables/os2_table.rb +0 -3
  249. data/lib/fontisan/tables/post_table.rb +0 -3
  250. data/lib/fontisan/tables/sbix.rb +0 -1
  251. data/lib/fontisan/tables/svg.rb +0 -1
  252. data/lib/fontisan/tables/variation_common.rb +0 -1
  253. data/lib/fontisan/tables/vvar.rb +0 -3
  254. data/lib/fontisan/tables.rb +54 -0
  255. data/lib/fontisan/true_type_collection.rb +6 -14
  256. data/lib/fontisan/true_type_font.rb +8 -2
  257. data/lib/fontisan/true_type_font_extensions.rb +9 -9
  258. data/lib/fontisan/type1/afm_generator.rb +0 -4
  259. data/lib/fontisan/type1/conversion_options.rb +0 -2
  260. data/lib/fontisan/type1/encodings.rb +0 -2
  261. data/lib/fontisan/type1/generator.rb +0 -8
  262. data/lib/fontisan/type1/pfa_generator.rb +0 -3
  263. data/lib/fontisan/type1/pfb_generator.rb +0 -5
  264. data/lib/fontisan/type1/pfm_generator.rb +0 -4
  265. data/lib/fontisan/type1.rb +42 -69
  266. data/lib/fontisan/type1_font.rb +40 -11
  267. data/lib/fontisan/ucd/aggregator.rb +73 -0
  268. data/lib/fontisan/ucd/cache_manager.rb +111 -0
  269. data/lib/fontisan/ucd/config.rb +59 -0
  270. data/lib/fontisan/ucd/download_error.rb +9 -0
  271. data/lib/fontisan/ucd/downloader.rb +88 -0
  272. data/lib/fontisan/ucd/error.rb +8 -0
  273. data/lib/fontisan/ucd/index.rb +103 -0
  274. data/lib/fontisan/ucd/index_builder.rb +107 -0
  275. data/lib/fontisan/ucd/range_entry.rb +56 -0
  276. data/lib/fontisan/ucd/unknown_version_error.rb +9 -0
  277. data/lib/fontisan/ucd/version_resolver.rb +79 -0
  278. data/lib/fontisan/ucd.rb +23 -0
  279. data/lib/fontisan/utilities/checksum_calculator.rb +0 -1
  280. data/lib/fontisan/utilities.rb +10 -0
  281. data/lib/fontisan/utils.rb +10 -0
  282. data/lib/fontisan/validation/collection_validator.rb +0 -2
  283. data/lib/fontisan/validation.rb +9 -0
  284. data/lib/fontisan/validators/basic_validator.rb +0 -2
  285. data/lib/fontisan/validators/font_book_validator.rb +0 -2
  286. data/lib/fontisan/validators/opentype_validator.rb +0 -2
  287. data/lib/fontisan/validators/profile_loader.rb +0 -5
  288. data/lib/fontisan/validators/validator.rb +0 -2
  289. data/lib/fontisan/validators/web_font_validator.rb +0 -2
  290. data/lib/fontisan/validators.rb +14 -0
  291. data/lib/fontisan/variable/delta_applicator.rb +0 -4
  292. data/lib/fontisan/variable/instancer.rb +0 -3
  293. data/lib/fontisan/variable/static_font_builder.rb +0 -3
  294. data/lib/fontisan/variable.rb +16 -0
  295. data/lib/fontisan/variation/blend_applier.rb +0 -2
  296. data/lib/fontisan/variation/cache.rb +0 -2
  297. data/lib/fontisan/variation/converter.rb +0 -3
  298. data/lib/fontisan/variation/data_extractor.rb +0 -2
  299. data/lib/fontisan/variation/delta_applier.rb +0 -5
  300. data/lib/fontisan/variation/inspector.rb +0 -1
  301. data/lib/fontisan/variation/instance_generator.rb +0 -6
  302. data/lib/fontisan/variation/instance_writer.rb +0 -5
  303. data/lib/fontisan/variation/metrics_adjuster.rb +0 -4
  304. data/lib/fontisan/variation/optimizer.rb +0 -3
  305. data/lib/fontisan/variation/parallel_generator.rb +0 -3
  306. data/lib/fontisan/variation/subsetter.rb +0 -4
  307. data/lib/fontisan/variation/tuple_variation_header.rb +0 -2
  308. data/lib/fontisan/variation/variable_svg_generator.rb +0 -3
  309. data/lib/fontisan/variation/variation_context.rb +0 -3
  310. data/lib/fontisan/variation/variation_preserver.rb +0 -3
  311. data/lib/fontisan/variation.rb +31 -0
  312. data/lib/fontisan/version.rb +1 -1
  313. data/lib/fontisan/woff2.rb +13 -0
  314. data/lib/fontisan/woff2_font.rb +31 -9
  315. data/lib/fontisan/woff_font.rb +31 -2
  316. data/lib/fontisan.rb +124 -196
  317. metadata +128 -7
  318. data/fontisan.gemspec +0 -47
@@ -113,12 +113,21 @@ Generating options control how the output font is written.
113
113
  | `optimize_tables` | Boolean | Enable table optimization | Reduce file size |
114
114
  | `reencode_first_256` | Boolean | Reencode first 256 glyphs | Type 1 output |
115
115
  | `encoding_vector` | String | Custom encoding vector | Type 1 output |
116
- | `compression` | String | Compression: zlib, brotli, none | Web font output |
117
- | `transform_tables` | Boolean | Transform tables for output | Format-specific transformations |
116
+ | `zlib_level` | Integer (0–9) | zlib compression level | WOFF output (default 6) |
117
+ | `uncompressed` | Boolean | Store tables uncompressed | WOFF only (legal per WOFF 1.0 §5.1) |
118
+ | `compression_threshold` | Integer | Skip compression for tables < N bytes | WOFF only (default 100) |
119
+ | `brotli_quality` | Integer (0–11) | Brotli quality | WOFF2 output (default 11) |
120
+ | `transform_tables` | Boolean | Apply glyf/loca + hmtx transformations | WOFF2 only |
121
+ | `metadata_xml` | String | Optional WOFF metadata XML | WOFF only |
122
+ | `private_data` | String | Optional WOFF private data | WOFF only |
118
123
  | `preserve_metadata` | Boolean | Preserve copyright/license metadata | Maintain font metadata |
119
124
  | `strip_metadata` | Boolean | Remove metadata | Reduce file size |
120
125
  | `target_format` | String | Collection target format | Collection conversions |
121
126
 
127
+ Compression knobs are declared by each strategy (`WoffWriter`, `Woff2Encoder`)
128
+ via the `ConversionStrategy` DSL. `FormatConverter.validate_options_for_target!`
129
+ rejects knobs that don't apply to the requested target format.
130
+
122
131
  === CLI Option Mapping
123
132
 
124
133
  ==== Opening Options
@@ -388,7 +397,7 @@ Notes: dfont supports both TrueType and OpenType/CFF, or mixed formats.
388
397
  Fontisan::ConversionOptions.from_preset(:web_optimized)
389
398
  # From: :otf, To: :woff2
390
399
  # opening: {}
391
- # generating: { compression: "brotli", transform_tables: true,
400
+ # generating: { brotli_quality: 11, transform_tables: true,
392
401
  # optimize_tables: true, preserve_metadata: true }
393
402
  ----
394
403
 
@@ -398,8 +407,10 @@ Benefits: 30-50% smaller than TTF/OTF
398
407
 
399
408
  [source,ruby]
400
409
  ----
401
- # generating: { compression: "zlib", preserve_metadata: true,
402
- # add_private_data: false }
410
+ Fontisan::ConversionOptions.from_preset(:legacy_web)
411
+ # From: :otf, To: :woff
412
+ # generating: { zlib_level: 9, optimize_tables: true,
413
+ # preserve_metadata: true }
403
414
  ----
404
415
 
405
416
  ==== Type 1 → WOFF/WOFF2
@@ -410,8 +421,8 @@ Workflow: Type 1 → OTF → WOFF2
410
421
  ----
411
422
  # Via OTF intermediate
412
423
  # opening: { decompose_composites: false, generate_unicode: true }
413
- # generating: { compression: "brotli" } # WOFF2
414
- # OR compression: "zlib" # WOFF
424
+ # generating: { brotli_quality: 11 } # WOFF2
425
+ # OR generating: { zlib_level: 9 } # WOFF
415
426
  ----
416
427
 
417
428
  === SVG Font Generation
@@ -481,7 +492,7 @@ Optimize fonts for web delivery:
481
492
  Fontisan::ConversionOptions.from_preset(:web_optimized)
482
493
  # From: :otf, To: :woff2
483
494
  # opening: {}
484
- # generating: { compression: "brotli", transform_tables: true,
495
+ # generating: { brotli_quality: 11, transform_tables: true,
485
496
  # optimize_tables: true, preserve_metadata: true }
486
497
  ----
487
498
 
@@ -491,6 +502,18 @@ Use cases:
491
502
  * Reducing page load time
492
503
  * Bandwidth optimization
493
504
 
505
+ === legacy_web
506
+
507
+ WOFF with maximum zlib compression for legacy browser reach (IE 9+):
508
+
509
+ [source,ruby]
510
+ ----
511
+ Fontisan::ConversionOptions.from_preset(:legacy_web)
512
+ # From: :otf, To: :woff
513
+ # generating: { zlib_level: 9, optimize_tables: true,
514
+ # preserve_metadata: true }
515
+ ----
516
+
494
517
  === archive_to_modern
495
518
 
496
519
  Convert font archives to modern format:
@@ -246,7 +246,7 @@ fontisan maintains 100% backward compatibility:
246
246
 
247
247
  = ExtractTTC to Fontisan Migration Guide
248
248
 
249
- This guide helps users migrate from https://github.com/fontist/extract_ttc[ExtractTTC] to Fontisan.
249
+ This guide helps users migrate from [ExtractTTC](https://github.com/fontist/extract_ttc) to Fontisan.
250
250
 
251
251
  Fontisan provides complete compatibility with all ExtractTTC functionality while adding comprehensive font analysis, subsetting, validation, and format conversion capabilities.
252
252
 
@@ -74,6 +74,59 @@ $ fontisan convert INPUT.ttf --to woff --output output.woff
74
74
  $ fontisan convert INPUT.ttf --to woff2 --output output.woff2
75
75
  ----
76
76
 
77
+ === Choosing between WOFF and WOFF2
78
+
79
+ WOFF and WOFF2 use different compression algorithms, fixed by their specs.
80
+ The format you choose **is** the choice of algorithm — there is no separate
81
+ `--compression` flag.
82
+
83
+ [options="header"]
84
+ |====
85
+ | Format | Algorithm | Compatibility | Typical size
86
+ | WOFF | zlib | All browsers (legacy-safe) | baseline
87
+ | WOFF2 | Brotli | Modern browsers only | ~30% smaller than WOFF
88
+ |====
89
+
90
+ If you need to support old browsers that lack Brotli decoders, use WOFF:
91
+
92
+ [source,shell]
93
+ ----
94
+ $ fontisan convert INPUT.ttf --to woff --output out.woff --zlib-level 9
95
+ ----
96
+
97
+ For modern-only delivery, WOFF2 is preferred:
98
+
99
+ [source,shell]
100
+ ----
101
+ $ fontisan convert INPUT.ttf --to woff2 --output out.woff2
102
+ ----
103
+
104
+ Note: WOFF2 mandates Brotli, WOFF mandates zlib. The container format is the
105
+ algorithm selector — you cannot mix and match.
106
+
107
+ ==== Per-format compression knobs
108
+
109
+ Each container exposes only the knobs that apply to it. Options declared for
110
+ the wrong target format raise a clear `ArgumentError` at convert time.
111
+
112
+ [options="header"]
113
+ |====
114
+ | Format | Knob | Range / values | Default
115
+ | WOFF | `--zlib-level` | 0–9 | 6
116
+ | WOFF | `--uncompressed` | (flag) | off
117
+ | WOFF | `--compression-threshold` | bytes | 100
118
+ | WOFF2 | `--brotli-quality` | 0–11 | 11
119
+ | WOFF2 | `--[no-]transform-tables` | (flag) | off
120
+ |====
121
+
122
+ The same knobs are available on the Ruby API via keyword arguments:
123
+
124
+ [source,ruby]
125
+ ----
126
+ Fontisan.convert("f.ttf", to: :woff, output: "f.woff", zlib_level: 9)
127
+ Fontisan.convert("f.ttf", to: :woff2, output: "f.woff2", brotli_quality: 11)
128
+ ----
129
+
77
130
  .Conversion with optimization
78
131
  [example]
79
132
  ====
@@ -51,6 +51,7 @@ options = Fontisan::ConversionOptions.from_preset(:type1_to_modern)
51
51
  | `type1_to_modern` | Type 1 | OTF |
52
52
  | `modern_to_type1` | OTF | Type 1 |
53
53
  | `web_optimized` | OTF | WOFF2 |
54
+ | `legacy_web` | OTF | WOFF |
54
55
  | `archive_to_modern` | TTC | OTF |
55
56
 
56
57
  ### new(**kwargs)
@@ -96,19 +97,28 @@ Opening options control source font processing.
96
97
 
97
98
  Generating options control output font writing.
98
99
 
99
- | Option | Type | Default |
100
- |--------|------|---------|
101
- | `write_pfm` | Boolean | false |
102
- | `write_afm` | Boolean | false |
103
- | `write_inf` | Boolean | false |
104
- | `select_encoding_automatically` | Boolean | false |
105
- | `hinting_mode` | String | 'preserve' |
106
- | `decompose_on_output` | Boolean | false |
107
- | `write_custom_tables` | Boolean | true |
108
- | `optimize_tables` | Boolean | false |
109
- | `compression` | String | nil |
110
- | `transform_tables` | Boolean | false |
111
- | `preserve_metadata` | Boolean | true |
100
+ | Option | Type | Default | Applies to |
101
+ |--------|------|---------|------------|
102
+ | `write_pfm` | Boolean | false | Type 1 output |
103
+ | `write_afm` | Boolean | false | Type 1 output |
104
+ | `write_inf` | Boolean | false | Type 1 output |
105
+ | `select_encoding_automatically` | Boolean | false | Type 1 output |
106
+ | `hinting_mode` | String | 'preserve' | All outline conversions |
107
+ | `decompose_on_output` | Boolean | false | All outline conversions |
108
+ | `write_custom_tables` | Boolean | true | All |
109
+ | `optimize_tables` | Boolean | false | All |
110
+ | `zlib_level` | Integer (0–9) | 6 | WOFF only |
111
+ | `uncompressed` | Boolean | false | WOFF only |
112
+ | `compression_threshold` | Integer (bytes) | 100 | WOFF only |
113
+ | `brotli_quality` | Integer (0–11) | 11 | WOFF2 only |
114
+ | `transform_tables` | Boolean | false | WOFF2 only |
115
+ | `metadata_xml` | String | nil | WOFF only |
116
+ | `private_data` | String | nil | WOFF only |
117
+ | `preserve_metadata` | Boolean | true | All |
118
+
119
+ Compression knobs are declared by each strategy (`WoffWriter`, `Woff2Encoder`)
120
+ via the `ConversionStrategy` DSL. `FormatConverter.validate_options_for_target!`
121
+ rejects knobs that don't apply to the requested target format.
112
122
 
113
123
  ## Examples
114
124
 
@@ -131,11 +141,24 @@ Fontisan::FontWriter.write(font, 'output.woff2', options: options)
131
141
 
132
142
  ```ruby
133
143
  options = Fontisan::ConversionOptions.new(
144
+ from: :ttf,
145
+ to: :woff2,
134
146
  opening: { autohint: true },
135
147
  generating: {
136
148
  hinting_mode: 'auto',
137
149
  optimize_tables: true,
138
- compression: 'brotli'
150
+ brotli_quality: 11,
151
+ transform_tables: true
139
152
  }
140
153
  )
141
154
  ```
155
+
156
+ ### WOFF with Max zlib (Legacy Browser Reach)
157
+
158
+ ```ruby
159
+ options = Fontisan::ConversionOptions.new(
160
+ from: :ttf,
161
+ to: :woff,
162
+ generating: { zlib_level: 9, preserve_metadata: true }
163
+ )
164
+ ```
@@ -40,9 +40,11 @@ font = Fontisan::FontLoader.load(File.read('font.ttf', mode: 'rb'))
40
40
 
41
41
  **Raises:** Fontisan::FormatError if format is unsupported
42
42
 
43
- ### detect_format(source)
43
+ ### detect_format(path)
44
44
 
45
- Detect font format without loading.
45
+ Detect a font's on-disk format from its content (magic bytes). The file
46
+ extension is ignored — a `.ttc` that actually contains a single OpenType-CFF
47
+ font is reported as `:otf`.
46
48
 
47
49
  ```ruby
48
50
  format = Fontisan::FontLoader.detect_format('font.ttf')
@@ -52,24 +54,28 @@ format = Fontisan::FontLoader.detect_format('font.otf')
52
54
  # => :otf
53
55
 
54
56
  format = Fontisan::FontLoader.detect_format('font.pfb')
55
- # => :type1
57
+ # => :pfb
58
+
59
+ format = Fontisan::FontLoader.detect_format('font.pfa')
60
+ # => :pfa
56
61
  ```
57
62
 
58
- **Returns:** Symbol or nil
63
+ **Returns:** Symbol (`:ttf`, `:otf`, `:ttc`, `:otc`, `:woff`, `:woff2`,
64
+ `:dfont`, `:pfa`, `:pfb`) or `nil` if the format is not recognised.
59
65
 
60
66
  ## Supported Formats
61
67
 
62
- | Format | Detection | Notes |
63
- |--------|-----------|-------|
64
- | TTF | Magic number | TrueType |
65
- | OTF | Magic number | OpenType/CFF |
66
- | TTC | Magic number | TrueType Collection |
67
- | OTC | Magic number | OpenType Collection |
68
- | WOFF | Magic number | Web Open Font Format |
69
- | WOFF2 | Magic number | Web Open Font Format 2 |
70
- | PFB | Marker byte | Adobe Type 1 Binary |
71
- | PFA | Text | Adobe Type 1 ASCII |
72
- | dfont | Magic number | Apple Data Fork |
68
+ | Symbol | Detection | Notes |
69
+ |---------|-------------|--------------------------------|
70
+ | `:ttf` | Magic bytes | TrueType |
71
+ | `:otf` | Magic bytes | OpenType / CFF |
72
+ | `:ttc` | Magic bytes | TrueType Collection |
73
+ | `:otc` | Magic bytes | OpenType Collection |
74
+ | `:woff` | Magic bytes | Web Open Font Format |
75
+ | `:woff2` | Magic bytes | Web Open Font Format 2 |
76
+ | `:pfb` | Marker byte | Adobe Type 1 Binary |
77
+ | `:pfa` | Text header | Adobe Type 1 ASCII |
78
+ | `:dfont` | Magic bytes | Apple Data-Fork resource fork |
73
79
 
74
80
  ## Examples
75
81
 
data/docs/cli/audit.md ADDED
@@ -0,0 +1,337 @@
1
+ ---
2
+ title: audit
3
+ ---
4
+
5
+ # audit
6
+
7
+ Produce a complete per-face font audit report — identity, style, metrics,
8
+ coverage, licensing, hinting, color capabilities, variable-font detail,
9
+ OpenType layout features, and Unicode/CLDR aggregation.
10
+
11
+ `audit` is the successor to `otfinfo`: it covers everything `otfinfo`
12
+ reports plus a great deal more (coverage, hinting, color, variation,
13
+ layout, language coverage), and supports collections, compare mode, and
14
+ whole-library summaries.
15
+
16
+ ## Quick Reference
17
+
18
+ ```bash
19
+ # One face
20
+ fontisan audit FONT.ttf
21
+
22
+ # Collection (one report per face)
23
+ fontisan audit COLLECTION.ttc
24
+
25
+ # Whole library
26
+ fontisan audit DIR/ --recursive --summary
27
+
28
+ # Compare two fonts or saved reports
29
+ fontisan audit --compare A.ttf B.ttf
30
+ ```
31
+
32
+ ## Variants
33
+
34
+ | Variant | What it does | Output |
35
+ |---------|-------------|--------|
36
+ | `audit FONT.ttf` | One AuditReport for the single face | `AuditReport` |
37
+ | `audit COLLECTION.ttc` | One AuditReport per face (in source order) | `Array<AuditReport>` |
38
+ | `audit DIR/ --recursive --summary` | Walk the directory, summarize the library | `LibrarySummary` |
39
+ | `audit --compare A B` | Diff two faces or two saved reports | `AuditDiff` |
40
+
41
+ ## Options
42
+
43
+ | Option | Description |
44
+ |--------|-------------|
45
+ | `--format FORMAT` | Output format: `text` (default), `yaml`, `json` |
46
+ | `--output PATH`, `-o` | Write to a directory (collection/library), a file (single/compare), or stdout |
47
+ | `--font-index N` | Audit only face N of a collection (default: all) |
48
+ | `--brief` | Fast inventory — skip metrics/hinting/color/layout/UCD/CLDR |
49
+ | `--all-codepoints` | Include the full per-codepoint list (defaults to a compact range view) |
50
+ | `--ucd-version VER` | Aggregate against this UCD version (`latest` to probe) |
51
+ | `--with-language-coverage` | Compute CLDR language coverage % (auto-downloads CLDR on first use) |
52
+ | `--cldr-version VER` | CLDR version to use (`latest` to probe) |
53
+ | `--compare` | Diff two inputs (requires exactly two paths) |
54
+ | `--recursive` | Library mode: walk into subdirectories |
55
+ | `--summary` | Library mode: produce a `LibrarySummary` over a directory |
56
+
57
+ ## Brief Mode
58
+
59
+ `--brief` runs only the cheap name-table extractors (provenance, identity,
60
+ style, licensing, coverage) and skips metrics, hinting, color,
61
+ variation, OpenType layout, UCD block/script aggregation, and CLDR
62
+ language coverage. Useful for taking a fast inventory of large libraries.
63
+
64
+ ```bash
65
+ fontisan audit FONT.ttf --brief
66
+ fontisan audit DIR/ --recursive --summary --brief
67
+ ```
68
+
69
+ Note: `audit --brief` is distinct from `info --brief`. `info --brief`
70
+ loads only 6 tables; `audit --brief` still loads the full font (Coverage
71
+ reads `cmap`) but selects a cheaper extractor subset.
72
+
73
+ ## Single-Face Audit
74
+
75
+ ```bash
76
+ # Text formatter (default)
77
+ fontisan audit FONT.ttf
78
+
79
+ # YAML (machine-readable)
80
+ fontisan audit FONT.ttf --format yaml
81
+
82
+ # JSON
83
+ fontisan audit FONT.ttf --format json | jq '.licensing'
84
+
85
+ # Write to disk
86
+ fontisan audit FONT.ttf -o report.yaml
87
+ ```
88
+
89
+ Sample text output (truncated):
90
+
91
+ ```
92
+ NotoSans-Regular
93
+ ================================================================================
94
+ generated_at: 2026-06-24T18:11:39Z fontisan: 0.2.20
95
+ source_sha256: f5f552c8c5edb61fe6efb824baf4d4de47b1a8689ab4925ff43f7bd6a4ebece5
96
+ source_file: /path/to/NotoSans-Regular.ttf
97
+ source_format: ttf layout: single face (1/1)
98
+
99
+ IDENTITY
100
+ Family: Noto Sans
101
+ Subfamily: Regular
102
+ Full name: Noto Sans Regular
103
+ PostScript: NotoSans-Regular
104
+ Version: Version 2.015; ttfautohint (v1.8.4.7-5d5b)
105
+ Revision: 2.0149993896484375
106
+
107
+ STYLE
108
+ Weight class: 400 (Regular)
109
+ Width class: 5 (Medium)
110
+ Bold: no
111
+ Italic: no
112
+ PANOSE: 2 11 5 2 4 5 4 2 2 4
113
+
114
+ COVERAGE
115
+ Codepoints: 3094
116
+ Glyphs: 4515
117
+ cmap subtables: 4, 12
118
+ Ranges (top 10): U+0000-U+0000, U+000D-U+000D, U+0020-U+007E, ...
119
+ Unicode scripts: Latin, ...
120
+
121
+ LICENSING
122
+ License URL: https://scripts.sil.org/OFL
123
+ Embedding: 0x0000 (Editable embedding)
124
+
125
+ METRICS
126
+ Units per em: 1000
127
+ Ascender: 1160
128
+ Descender: -288
129
+ ...
130
+
131
+ UNICODE BLOCKS
132
+ Basic Latin 95 / 128 74.2%
133
+ Latin-1 Supplement 96 / 128 75.0%
134
+ ...
135
+
136
+ VARIABLE FONT
137
+ Axes: wght (100–900, default 400)
138
+ Named instances: 9
139
+
140
+ OPENTYPE LAYOUT
141
+ Scripts: latn, cyrl, grek, ...
142
+ Features: c2sc, calt, case, dlig, dnom, frac, kern, liga, ...
143
+
144
+ LANGUAGE COVERAGE
145
+ en: 99.2%
146
+ fr: 97.4%
147
+ ...
148
+ ```
149
+
150
+ ## Collections
151
+
152
+ For TTC/OTC/dfont, one report is produced per face in source order.
153
+
154
+ ```bash
155
+ # Audit every face
156
+ fontisan audit COLLECTION.ttc
157
+
158
+ # Audit only face 2
159
+ fontisan audit COLLECTION.ttc --font-index 2
160
+
161
+ # Write one file per face into a directory
162
+ fontisan audit COLLECTION.ttc -o reports/
163
+
164
+ # Resulting files use the postscript name with a 2-digit index prefix:
165
+ # 00-NotoSans-Regular.yaml
166
+ # 01-NotoSans-Bold.yaml
167
+ # 02-NotoSerif-Italic.yaml
168
+ ```
169
+
170
+ ## Compare Mode
171
+
172
+ `--compare` diffs two inputs. Each input is one of:
173
+
174
+ - A previously saved audit report (`.yaml` / `.yml` / `.json`)
175
+ - A font file (audited on-the-fly)
176
+
177
+ Mixed inputs are allowed — useful for tracking a font's evolution
178
+ against a checked-in baseline.
179
+
180
+ ```bash
181
+ # Two live fonts
182
+ fontisan audit --compare a.ttf b.ttf
183
+
184
+ # Saved baseline vs. live
185
+ fontisan audit --compare baseline.yaml new.ttf
186
+
187
+ # Two saved reports
188
+ fontisan audit --compare v1.yaml v2.yaml -o diff.yaml
189
+ ```
190
+
191
+ The output is an `AuditDiff` containing:
192
+
193
+ - `field_changes` — scalar field-level changes (e.g. weight_class 400 → 700)
194
+ - `codepoints` — added/removed/unchanged codepoint counts
195
+ - `added_features` / `removed_features`
196
+ - `added_scripts` / `removed_scripts`
197
+ - `added_blocks` / `removed_blocks`
198
+ - `added_languages` / `removed_languages`
199
+
200
+ ## Library Mode
201
+
202
+ Point `audit` at a directory with `--recursive` and/or `--summary` to
203
+ scan a whole library of fonts.
204
+
205
+ ```bash
206
+ # Flat directory
207
+ fontisan audit lib/ --summary
208
+
209
+ # Walk subdirectories
210
+ fontisan audit lib/ --recursive --summary
211
+
212
+ # YAML for downstream processing
213
+ fontisan audit lib/ --recursive --summary --format yaml -o library.yaml
214
+ ```
215
+
216
+ The output is a `LibrarySummary` containing:
217
+
218
+ - `root_path`, `total_files`, `total_faces`, `scanned_extensions`
219
+ - `aggregate_metrics` — total codepoints/glyphs/bytes summed across faces
220
+ - `script_coverage` — per-script face counts and lists
221
+ - `duplicate_groups` — files grouped by `source_sha256` (size > 1)
222
+ - `license_distribution` — face counts per `license_url`
223
+ - `per_face_reports` — the full per-face reports used to aggregate
224
+
225
+ Files that fail to load (corrupt, unsupported) are listed on stderr as
226
+ `skipped <path>` and excluded from the summary.
227
+
228
+ ## UCD Aggregation
229
+
230
+ By default, audit aggregates codepoints against the configured-default
231
+ UCD version, producing:
232
+
233
+ - `blocks` — per-Unicode-block coverage rows (name, range, total, covered, fill_ratio, complete)
234
+ - `unicode_scripts` — distinct scripts present in the font
235
+
236
+ Override with `--ucd-version`:
237
+
238
+ ```bash
239
+ # Use a specific UCD version
240
+ fontisan audit FONT.ttf --ucd-version 16.0.0
241
+
242
+ # Probe and use the latest
243
+ fontisan audit FONT.ttf --ucd-version latest
244
+ ```
245
+
246
+ Manage the local UCD cache with `fontisan ucd`:
247
+
248
+ ```bash
249
+ fontisan ucd status
250
+ fontisan ucd list
251
+ fontisan ucd download 17.0.0
252
+ ```
253
+
254
+ ## CLDR Language Coverage
255
+
256
+ `--with-language-coverage` computes per-language coverage % using CLDR
257
+ exemplar sets. The first invocation downloads the CLDR data
258
+ (~MBs); subsequent invocations use the cache.
259
+
260
+ ```bash
261
+ fontisan audit FONT.ttf --with-language-coverage
262
+
263
+ # Use a specific CLDR version
264
+ fontisan audit FONT.ttf --with-language-coverage --cldr-version 45
265
+ ```
266
+
267
+ Manage the CLDR cache with `fontisan cldr`:
268
+
269
+ ```bash
270
+ fontisan cldr status
271
+ fontisan cldr list
272
+ fontisan cldr download 45
273
+ ```
274
+
275
+ ## Ruby API
276
+
277
+ ### Single face
278
+
279
+ ```ruby
280
+ require "fontisan"
281
+
282
+ # Returns an AuditReport for a single font, or an Array<AuditReport>
283
+ # for a collection (one per face).
284
+ report = Fontisan::Commands::AuditCommand.new("font.ttf",
285
+ ucd_version: "17.0.0").run
286
+
287
+ puts report.family_name
288
+ puts report.total_codepoints
289
+ puts report.licensing.license_url
290
+ puts report.blocks.first.fill_ratio
291
+ ```
292
+
293
+ ### Compare
294
+
295
+ ```ruby
296
+ diff = Fontisan::Commands::AuditCompareCommand.new(
297
+ "baseline.yaml", "new.ttf", ucd_version: "17.0.0"
298
+ ).run
299
+
300
+ diff.field_changes.each { |c| puts "#{c.field}: #{c.left} → #{c.right}" }
301
+ puts "added codepoints: #{diff.codepoints.added_count}"
302
+ ```
303
+
304
+ ### Library summary
305
+
306
+ ```ruby
307
+ cmd = Fontisan::Commands::AuditLibraryCommand.new(
308
+ "lib/", recursive: true, options: { ucd_version: "17.0.0" }
309
+ )
310
+ summary = cmd.run
311
+ cmd.skipped.each { |path| warn "skipped #{path}" }
312
+
313
+ summary.script_coverage.each do |row|
314
+ puts "#{row.script}: #{row.face_count} faces"
315
+ end
316
+ summary.duplicate_groups.each do |group|
317
+ puts "duplicate #{group.source_sha256[0,8]}: #{group.files.join(', ')}"
318
+ end
319
+ ```
320
+
321
+ ### Brief mode
322
+
323
+ ```ruby
324
+ # Use the audit_brief: key (NOT brief:, which would trigger metadata-only
325
+ # font loading via BaseCommand).
326
+ report = Fontisan::Commands::AuditCommand.new(
327
+ "font.ttf", audit_brief: true, ucd_version: "17.0.0"
328
+ ).run
329
+ ```
330
+
331
+ ## Related Commands
332
+
333
+ - [info](/cli/info) — Lighter-weight font metadata (replaces `otfinfo -i`)
334
+ - [tables](/cli/tables) — Raw OpenType table listing
335
+ - [features](/cli/features) — Just GSUB/GPOS features
336
+ - [scripts](/cli/scripts) — Just supported scripts
337
+ - [validate](/cli/validate) — Pass/fail quality checks (audit is descriptive; validate is prescriptive)
data/docs/cli/convert.md CHANGED
@@ -36,6 +36,15 @@ These are individual font formats that can be converted:
36
36
  | `--output FILE` | Output file path |
37
37
  | `--optimize` | Enable outline optimization |
38
38
  | `--flatten` | Flatten composite glyphs |
39
+ | `--zlib-level=N` | WOFF only: zlib compression level (0–9, default 6) |
40
+ | `--uncompressed` | WOFF only: store tables uncompressed (legal per WOFF 1.0 §5.1) |
41
+ | `--compression-threshold=N` | WOFF only: skip compression for tables smaller than N bytes (default 100) |
42
+ | `--brotli-quality=N` | WOFF2 only: Brotli quality (0–11, default 11) |
43
+ | `--transform-tables` / `--no-transform-tables` | WOFF2 only: apply glyf/loca + hmtx transformations |
44
+
45
+ The format you pick (`--to woff` vs `--to woff2`) **is** the algorithm
46
+ choice — WOFF mandates zlib, WOFF2 mandates Brotli. Passing a WOFF knob
47
+ to a WOFF2 target (or vice versa) exits 1 with a clear error.
39
48
 
40
49
  ## Common Workflows
41
50
 
@@ -45,8 +54,18 @@ These are individual font formats that can be converted:
45
54
  # TTF to WOFF2 (recommended for modern browsers)
46
55
  fontisan convert font.ttf --to woff2 --output font.woff2
47
56
 
48
- # OTF to WOFF (broader compatibility)
57
+ # OTF to WOFF (broader compatibility — works on IE 9+)
49
58
  fontisan convert font.otf --to woff --output font.woff
59
+
60
+ # Smallest possible WOFF2 (max Brotli + table transforms)
61
+ fontisan convert font.ttf --to woff2 --output font.woff2 \
62
+ --brotli-quality 11 --transform-tables
63
+
64
+ # WOFF with max zlib compression
65
+ fontisan convert font.ttf --to woff --output font.woff --zlib-level 9
66
+
67
+ # WOFF stored uncompressed (legal per WOFF 1.0 §5.1; useful for tooling)
68
+ fontisan convert font.ttf --to woff --output font.woff --uncompressed
50
69
  ```
51
70
 
52
71
  ### Convert Between Outline Formats