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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 356bd0157b272462910d8822c6b77a6c768e12a45f4c56b2646d6bc683413239
4
- data.tar.gz: 981dca0c122d3d3e2dab683c6a7962bc2bcc1be35fb5c0818857d6c55ff7bbbf
3
+ metadata.gz: 215de38977d0c94ac54def7b0aef0cc200e326172fd71b30912f6484ae122320
4
+ data.tar.gz: da89a4c238f617179e0da73377b83ae088d292c9e337cbc5c18832308864d970
5
5
  SHA512:
6
- metadata.gz: '0852e6bc36559c125cab91cf4fe4d65256628df2e8c873bc52b4f291b2adc944126ed2a0e26d6daafbe7d568934a9788677cb47c8b70982097320addbc93365a'
7
- data.tar.gz: dc9d8fdacf95fc0761bb62cfec0f56e7a7da1a78986915db76243461cf2434d3f8142a0547e153d6746e242aa4a3a5dc33b5b505e8ac073e467e9b9c269d97bf
6
+ metadata.gz: 5e18eef08d7be3ddd02e465497971cc020c7c5b03f40e4d92f02cf1c696d6553c9aa75b75cbea5ce4a5e43f0a7d683e57d7f1efada42b8d02b0b55da91837221
7
+ data.tar.gz: '02830dec034211c50cad0d5ebef7c771276808f815e5487e0a012bf08cd0b820eac0570befc0eb5a1b0bc518f5f906b5bfa3012de5d13ebe20a67cd42fb79a7c'
data/.rubocop_todo.yml CHANGED
@@ -1,68 +1,27 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2026-05-02 04:07:39 UTC using RuboCop version 1.86.1.
3
+ # on 2026-06-24 11:12:20 UTC using RuboCop version 1.86.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 1
10
- # This cop supports safe autocorrection (--autocorrect).
11
- Gemspec/RequireMFA:
12
- Exclude:
13
- - 'docs/node_modules/speakingurl/speakingurl-rails.gemspec'
14
-
15
9
  # Offense count: 2
16
10
  Gemspec/RequiredRubyVersion:
17
11
  Exclude:
18
12
  - 'docs/node_modules/speakingurl/speakingurl-rails.gemspec'
19
13
  - 'fontisan.gemspec'
20
14
 
21
- # Offense count: 1
22
- # This cop supports safe autocorrection (--autocorrect).
23
- Layout/EmptyLines:
24
- Exclude:
25
- - 'lib/fontisan/loading_modes.rb'
26
-
27
- # Offense count: 4
28
- # This cop supports safe autocorrection (--autocorrect).
29
- # Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment.
30
- Layout/ExtraSpacing:
31
- Exclude:
32
- - 'docs/node_modules/speakingurl/lib/speakingurl-rails.rb'
33
- - 'docs/node_modules/speakingurl/speakingurl-rails.gemspec'
34
-
35
- # Offense count: 1
36
- # This cop supports safe autocorrection (--autocorrect).
37
- # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
38
- # SupportedHashRocketStyles: key, separator, table
39
- # SupportedColonStyles: key, separator, table
40
- # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
41
- Layout/HashAlignment:
42
- Exclude:
43
- - 'docs/node_modules/speakingurl/speakingurl-rails.gemspec'
44
-
45
- # Offense count: 1585
15
+ # Offense count: 1606
46
16
  # This cop supports safe autocorrection (--autocorrect).
47
17
  # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
48
18
  # URISchemes: http, https
49
19
  Layout/LineLength:
50
20
  Enabled: false
51
21
 
52
- # Offense count: 5
53
- # This cop supports safe autocorrection (--autocorrect).
54
- # Configuration parameters: AllowForAlignment, EnforcedStyleForExponentOperator, EnforcedStyleForRationalLiterals.
55
- # SupportedStylesForExponentOperator: space, no_space
56
- # SupportedStylesForRationalLiterals: space, no_space
57
- Layout/SpaceAroundOperators:
58
- Exclude:
59
- - 'docs/node_modules/speakingurl/lib/speakingurl-rails.rb'
60
- - 'docs/node_modules/speakingurl/speakingurl-rails.gemspec'
61
-
62
- # Offense count: 3
22
+ # Offense count: 2
63
23
  Lint/CopDirectiveSyntax:
64
24
  Exclude:
65
- - 'lib/fontisan/converters/woff_writer.rb'
66
25
  - 'lib/fontisan/woff2_font.rb'
67
26
  - 'spec/fontisan/variation/cache_spec.rb'
68
27
 
@@ -114,28 +73,18 @@ Lint/MissingSuper:
114
73
  - 'lib/fontisan/commands/validate_command.rb'
115
74
  - 'lib/fontisan/tables/cff2/table_builder.rb'
116
75
 
117
- # Offense count: 4
118
- # This cop supports safe autocorrection (--autocorrect).
119
- Lint/RedundantRequireStatement:
120
- Exclude:
121
- - 'lib/fontisan/commands/features_command.rb'
122
- - 'lib/fontisan/commands/scripts_command.rb'
123
- - 'lib/fontisan/loading_modes.rb'
124
- - 'lib/fontisan/variation/optimizer.rb'
125
-
126
76
  # Offense count: 1
127
77
  Lint/StructNewOverride:
128
78
  Exclude:
129
79
  - 'lib/fontisan/optimizers/pattern_analyzer.rb'
130
80
 
131
- # Offense count: 8
81
+ # Offense count: 7
132
82
  # This cop supports safe autocorrection (--autocorrect).
133
83
  # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions.
134
84
  # NotImplementedExceptions: NotImplementedError
135
85
  Lint/UnusedMethodArgument:
136
86
  Exclude:
137
87
  - 'lib/fontisan.rb'
138
- - 'lib/fontisan/font_loader.rb'
139
88
  - 'lib/fontisan/tables/cff2/private_dict_blend_handler.rb'
140
89
  - 'lib/fontisan/utilities/brotli_wrapper.rb'
141
90
  - 'lib/fontisan/variation/table_accessor.rb'
@@ -148,12 +97,12 @@ Lint/UselessConstantScoping:
148
97
  - 'lib/fontisan/conversion_options.rb'
149
98
  - 'lib/fontisan/type1/charstrings.rb'
150
99
 
151
- # Offense count: 549
100
+ # Offense count: 552
152
101
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
153
102
  Metrics/AbcSize:
154
103
  Enabled: false
155
104
 
156
- # Offense count: 33
105
+ # Offense count: 31
157
106
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
158
107
  # AllowedMethods: refine
159
108
  Metrics/BlockLength:
@@ -171,17 +120,17 @@ Metrics/CollectionLiteralLength:
171
120
  - 'lib/fontisan/type1/agl.rb'
172
121
  - 'lib/fontisan/type1/encodings.rb'
173
122
 
174
- # Offense count: 301
123
+ # Offense count: 304
175
124
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
176
125
  Metrics/CyclomaticComplexity:
177
126
  Enabled: false
178
127
 
179
- # Offense count: 842
128
+ # Offense count: 844
180
129
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
181
130
  Metrics/MethodLength:
182
131
  Max: 135
183
132
 
184
- # Offense count: 22
133
+ # Offense count: 24
185
134
  # Configuration parameters: CountKeywordArgs.
186
135
  Metrics/ParameterLists:
187
136
  Max: 39
@@ -241,7 +190,7 @@ Naming/VariableName:
241
190
  - 'lib/fontisan/type1/pfm_generator.rb'
242
191
  - 'lib/fontisan/type1/pfm_parser.rb'
243
192
 
244
- # Offense count: 113
193
+ # Offense count: 111
245
194
  # Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
246
195
  # SupportedStyles: snake_case, normalcase, non_integer
247
196
  # AllowedIdentifiers: TLS1_1, TLS1_2, capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64
@@ -264,7 +213,7 @@ RSpec/AnyInstance:
264
213
  RSpec/ContextWording:
265
214
  Enabled: false
266
215
 
267
- # Offense count: 27
216
+ # Offense count: 28
268
217
  # Configuration parameters: IgnoredMetadata.
269
218
  RSpec/DescribeClass:
270
219
  Enabled: false
@@ -280,7 +229,7 @@ RSpec/DescribeMethod:
280
229
  - 'spec/fontisan/type1/seac_expander_spec.rb'
281
230
  - 'spec/fontisan/type1_real_fonts_spec.rb'
282
231
 
283
- # Offense count: 1428
232
+ # Offense count: 1446
284
233
  # Configuration parameters: CountAsOne.
285
234
  RSpec/ExampleLength:
286
235
  Max: 66
@@ -357,11 +306,11 @@ RSpec/MultipleDescribes:
357
306
  - 'spec/fontisan/utils/thread_pool_spec.rb'
358
307
  - 'spec/fontisan/variation/cache_spec.rb'
359
308
 
360
- # Offense count: 1855
309
+ # Offense count: 1868
361
310
  RSpec/MultipleExpectations:
362
311
  Max: 19
363
312
 
364
- # Offense count: 152
313
+ # Offense count: 154
365
314
  # Configuration parameters: AllowSubject.
366
315
  RSpec/MultipleMemoizedHelpers:
367
316
  Max: 13
@@ -442,13 +391,6 @@ Style/EmptyElse:
442
391
  Exclude:
443
392
  - 'lib/fontisan/type1/seac_expander.rb'
444
393
 
445
- # Offense count: 2
446
- # This cop supports safe autocorrection (--autocorrect).
447
- Style/ExpandPathArguments:
448
- Exclude:
449
- - 'docs/node_modules/speakingurl/lib/speakingurl-rails.rb'
450
- - 'docs/node_modules/speakingurl/speakingurl-rails.gemspec'
451
-
452
394
  # Offense count: 11
453
395
  # This cop supports safe autocorrection (--autocorrect).
454
396
  # Configuration parameters: EnforcedStyle, MaxUnannotatedPlaceholdersAllowed, Mode, AllowedMethods, AllowedPatterns.
@@ -466,27 +408,9 @@ Style/HashLikeCase:
466
408
  - 'lib/fontisan/commands/unpack_command.rb'
467
409
  - 'lib/fontisan/models/validation_report.rb'
468
410
 
469
- # Offense count: 1
470
- # This cop supports safe autocorrection (--autocorrect).
471
- # Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
472
- # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
473
- # SupportedShorthandSyntax: always, never, either, consistent, either_consistent
474
- Style/HashSyntax:
475
- Exclude:
476
- - 'docs/node_modules/speakingurl/lib/speakingurl-rails.rb'
477
-
478
411
  # Offense count: 1
479
412
  # Configuration parameters: AllowedMethods.
480
413
  # AllowedMethods: respond_to_missing?
481
414
  Style/OptionalBooleanParameter:
482
415
  Exclude:
483
416
  - 'lib/fontisan/conversion_options.rb'
484
-
485
- # Offense count: 7
486
- # This cop supports safe autocorrection (--autocorrect).
487
- # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
488
- # SupportedStyles: single_quotes, double_quotes
489
- Style/StringLiterals:
490
- Exclude:
491
- - 'docs/node_modules/speakingurl/lib/speakingurl-rails.rb'
492
- - 'docs/node_modules/speakingurl/speakingurl-rails.gemspec'
data/Gemfile CHANGED
@@ -5,12 +5,12 @@ source "https://rubygems.org"
5
5
  # Specify your gem's dependencies in fontisan.gemspec
6
6
  gemspec
7
7
 
8
- gem "canon", "~> 0.1.3"
9
- gem "get_process_mem", "~> 0.2"
8
+ gem "benchmark"
10
9
  # bigdecimal is required by get_process_mem for Ruby 3.4+ compatibility
11
10
  gem "bigdecimal"
11
+ gem "canon", "~> 0.1.3"
12
+ gem "get_process_mem", "~> 0.2"
12
13
  gem "openssl", "~> 3.0"
13
- # sys-proctable is required by get_process_mem on Windows
14
14
  gem "rake"
15
15
  gem "rspec"
16
16
  gem "rubocop"
@@ -18,4 +18,7 @@ gem "rubocop-performance"
18
18
  gem "rubocop-rake"
19
19
  gem "rubocop-rspec"
20
20
  gem "rubyzip"
21
+ # sys-proctable is required by get_process_mem on Windows
21
22
  gem "sys-proctable", platforms: %i[mswin mingw mswin64]
23
+ # win32ole was a default gem until Ruby 4.0 — pin it for Windows runners
24
+ gem "win32ole", platforms: %i[mswin mingw mswin64]
data/README.adoc CHANGED
@@ -61,6 +61,7 @@ Color fonts and collections::
61
61
  * Font collection validation with per-font reporting (see link:docs/COLLECTION_VALIDATION.adoc[Collection Validation Guide])
62
62
 
63
63
  Font analysis and information::
64
+ * Comprehensive per-face font audit (replaces `otfinfo`) covering identity, style, metrics, codepoint coverage, licensing, hinting, color capabilities, variable-font detail, OpenType layout features, UCD block/script aggregation, and CLDR language coverage — plus `--compare` mode and whole-library `--recursive --summary` mode
64
65
  * Bidirectional font hint conversion (see link:docs/FONT_HINTING.adoc[Font Hinting Guide])
65
66
  * Extract comprehensive font metadata (name, version, designer, license, etc.)
66
67
  * Brief mode for fast font indexing (5x faster, metadata-only loading)
@@ -99,11 +100,224 @@ Font engineering::
99
100
 
100
101
  Export and interfaces::
101
102
  * TTX/YAML/JSON export (complete)
102
- * Command-line interface with 18 commands
103
+ * Command-line interface with 19 commands
103
104
  * Ruby library API for programmatic access
104
105
  * Structured output in YAML, JSON, and text formats
105
106
 
106
107
 
108
+ == Font audit
109
+
110
+ === General
111
+
112
+ The `audit` command produces a complete per-face font audit report —
113
+ the successor to `otfinfo`, covering everything `otfinfo` reports plus
114
+ codepoint coverage, licensing, hinting, color capabilities, variable-font
115
+ detail, OpenType layout, UCD block/script aggregation, and CLDR language
116
+ coverage. It also supports collections, `--compare` mode, and whole-
117
+ library `--recursive --summary` mode.
118
+
119
+ For each face, audit reports:
120
+
121
+ Identity & provenance:: `family_name`, `subfamily_name`, `full_name`,
122
+ `postscript_name`, `version`, `font_revision`, `source_sha256`,
123
+ `source_format`, `fontisan_version`, `generated_at`.
124
+ Style:: `weight_class` (+ human label), `width_class`, `bold`, `italic`,
125
+ `panose`, `fs_selection_flags`.
126
+ Coverage:: `total_codepoints`, `total_glyphs`, `cmap_subtables`,
127
+ `codepoint_ranges` (compact by default; `--all-codepoints` for the
128
+ flat list).
129
+ Licensing:: `license_url`, `license_description`, `embedding_type`
130
+ (decoded `fsType`).
131
+ Metrics:: `units_per_em`, ascender/descender/etc., x-height, cap height,
132
+ `metrics_consistent?` flag.
133
+ Hinting:: `has_fpgm`, `has_prep`, `gasp_ranges`, TrueType vs PostScript
134
+ hint presence.
135
+ Color capabilities:: COLR/CPAL, sbix, CBDT/CBLC, SVG table presence.
136
+ Variable-font detail:: axes (`fvar`), named instances.
137
+ OpenType layout:: scripts/features in GSUB and GPOS.
138
+ Aggregations (UCD):: per-block coverage rows, per-script roll-up.
139
+ Language coverage (opt-in via `--with-language-coverage`):: per-language
140
+ coverage % using CLDR exemplar sets.
141
+
142
+ [source,shell]
143
+ ----
144
+ $ fontisan audit PATH [--compare LEFT RIGHT] [--recursive] [--summary]
145
+ [--brief] [--all-codepoints]
146
+ [--ucd-version VERSION] [--with-language-coverage]
147
+ [--cldr-version VERSION]
148
+ [--font-index N] [--output PATH] [--format FORMAT]
149
+ ----
150
+
151
+ Where,
152
+
153
+ `PATH`:: Path to a font file (single-face audit), collection
154
+ (`TTC`/`OTC`/`dfont` — one report per face), or directory
155
+ (library mode with `--recursive`/`--summary`).
156
+ `--compare`:: Diff two inputs (each is a font file or a saved
157
+ `.yaml`/`.yml`/`.json` audit report). Requires exactly two paths.
158
+ `--recursive`:: Library mode: walk subdirectories.
159
+ `--summary`:: Library mode: produce a `LibrarySummary`.
160
+ `--brief`:: Fast inventory: skip metrics, hinting, color, variation,
161
+ OpenType layout, and UCD/CLDR aggregation. Note: distinct from
162
+ `info --brief` — `audit --brief` still loads the full font.
163
+ `--all-codepoints`:: Include the full per-codepoint list (default is a
164
+ compact range view).
165
+ `--ucd-version VERSION`:: Aggregate against this UCD version
166
+ (`latest` probes upstream).
167
+ `--with-language-coverage`:: Compute CLDR per-language coverage %
168
+ (auto-downloads CLDR on first use).
169
+ `--cldr-version VERSION`:: CLDR version (`latest` probes upstream).
170
+ `--font-index N`:: Audit only face N of a collection.
171
+ `--output PATH`:: Write to a directory (collection/library), file
172
+ (single/compare), or stdout.
173
+ `--format FORMAT`:: `text` (default), `yaml`, or `json`.
174
+
175
+ === Command-line usage
176
+
177
+ Single face:
178
+
179
+ [source,shell]
180
+ ----
181
+ $ fontisan audit NotoSans-Regular.ttf
182
+ $ fontisan audit NotoSans-Regular.ttf --format yaml
183
+ $ fontisan audit NotoSans-Regular.ttf --brief
184
+ $ fontisan audit NotoSans-Regular.ttf --with-language-coverage
185
+ ----
186
+
187
+ Collection (one report per face):
188
+
189
+ [source,shell]
190
+ ----
191
+ $ fontisan audit Family.ttc
192
+ $ fontisan audit Family.ttc --font-index 2
193
+ $ fontisan audit Family.ttc --output reports/
194
+ ----
195
+
196
+ Compare two fonts or saved reports:
197
+
198
+ [source,shell]
199
+ ----
200
+ $ fontisan audit --compare a.ttf b.ttf
201
+ $ fontisan audit --compare baseline.yaml new.ttf
202
+ $ fontisan audit --compare v1.yaml v2.yaml --output diff.yaml
203
+ ----
204
+
205
+ Library summary:
206
+
207
+ [source,shell]
208
+ ----
209
+ $ fontisan audit lib/ --summary
210
+ $ fontisan audit lib/ --recursive --summary
211
+ $ fontisan audit lib/ --recursive --summary --format yaml --output library.yaml
212
+ ----
213
+
214
+ The library summary includes:
215
+
216
+ * `root_path`, `total_files`, `total_faces`, `scanned_extensions`
217
+ * `aggregate_metrics` — summed codepoints/glyphs/bytes
218
+ * `script_coverage` — per-script face counts and face lists
219
+ * `duplicate_groups` — files grouped by `source_sha256` (size > 1)
220
+ * `license_distribution` — face counts per `license_url`
221
+ * `per_face_reports` — the full per-face reports
222
+
223
+ Files that fail to load (corrupt, unsupported) are reported on stderr
224
+ as `skipped <path>` and excluded from the summary.
225
+
226
+ UCD/CLDR cache management:
227
+
228
+ [source,shell]
229
+ ----
230
+ $ fontisan ucd status
231
+ $ fontisan ucd list
232
+ $ fontisan ucd download 17.0.0
233
+ $ fontisan cldr status
234
+ $ fontisan cldr list
235
+ $ fontisan cldr download 45
236
+ ----
237
+
238
+ === Ruby API usage
239
+
240
+ Single face:
241
+
242
+ [source,ruby]
243
+ ----
244
+ require "fontisan"
245
+
246
+ # Returns an AuditReport for a single font, or an Array<AuditReport>
247
+ # for a collection (one per face, in source order).
248
+ report = Fontisan::Commands::AuditCommand.new(
249
+ "NotoSans-Regular.ttf", ucd_version: "17.0.0"
250
+ ).run
251
+
252
+ report.family_name # => "Noto Sans"
253
+ report.licensing.license_url # => "https://scripts.sil.org/OFL"
254
+ report.blocks.first.fill_ratio # => 0.742
255
+ report.opentype_layout.features # => ["c2sc", "calt", "case", ...]
256
+ ----
257
+
258
+ Compare:
259
+
260
+ [source,ruby]
261
+ ----
262
+ diff = Fontisan::Commands::AuditCompareCommand.new(
263
+ "baseline.yaml", "new.ttf", ucd_version: "17.0.0"
264
+ ).run
265
+
266
+ diff.field_changes.each { |c| puts "#{c.field}: #{c.left} -> #{c.right}" }
267
+ puts "added codepoints: #{diff.codepoints.added_count}"
268
+ ----
269
+
270
+ Library summary:
271
+
272
+ [source,ruby]
273
+ ----
274
+ cmd = Fontisan::Commands::AuditLibraryCommand.new(
275
+ "lib/", recursive: true, options: { ucd_version: "17.0.0" }
276
+ )
277
+ summary = cmd.run
278
+ cmd.skipped.each { |path| warn "skipped #{path}" }
279
+
280
+ summary.script_coverage.each do |row|
281
+ puts "#{row.script}: #{row.face_count} faces"
282
+ end
283
+
284
+ summary.duplicate_groups.each do |group|
285
+ puts "duplicate #{group.source_sha256[0, 8]}: #{group.files.join(', ')}"
286
+ end
287
+ ----
288
+
289
+ Brief mode (note the `audit_brief:` key — `brief:` would trigger
290
+ metadata-only font loading via `BaseCommand`):
291
+
292
+ [source,ruby]
293
+ ----
294
+ report = Fontisan::Commands::AuditCommand.new(
295
+ "NotoSans-Regular.ttf", audit_brief: true, ucd_version: "17.0.0"
296
+ ).run
297
+ ----
298
+
299
+ === Architecture
300
+
301
+ Each audit concern is implemented as a small extractor class
302
+ registered in `Fontisan::Audit::Registry`. `AuditCommand` builds an
303
+ `Audit::Context` and iterates the registry, merging each extractor's
304
+ output into a single `Models::Audit::AuditReport`. Adding a new field
305
+ group means adding one extractor class and one line in the registry —
306
+ `AuditCommand` itself never changes (open/closed).
307
+
308
+ Two extractor sets are defined:
309
+
310
+ * `ORDERED_EXTRACTORS` — the full set (default).
311
+ * `BRIEF_EXTRACTORS` — a cheap subset for `--brief` (provenance,
312
+ identity, style, licensing, coverage).
313
+
314
+ Sub-models (`Licensing`, `Metrics`, `Hinting`, `ColorCapabilities`,
315
+ `VariationDetail`, `OpenTypeLayout`, `CodepointRange`, `AuditBlock`,
316
+ `NamedInstance`, `AuditAxis`, `ScriptFeatures`) group related fields
317
+ on `AuditReport` (MECE). Compare mode produces an `AuditDiff`; library
318
+ mode produces a `LibrarySummary`.
319
+
320
+
107
321
  == Font information
108
322
 
109
323
  === General
@@ -2133,11 +2347,33 @@ Generating options (control how the output font is written):
2133
2347
  * `--optimize-tables` - Enable table optimization
2134
2348
  * `--decompose-on-output` - Decompose composites in output
2135
2349
 
2350
+ WOFF compression knobs (apply to `--to woff` only):
2351
+
2352
+ * `--zlib-level=N` - zlib level (0–9, default 6)
2353
+ * `--uncompressed` - store tables uncompressed (legal per WOFF 1.0 §5.1)
2354
+ * `--compression-threshold=N` - skip compression for tables smaller than N bytes
2355
+
2356
+ WOFF2 compression knobs (apply to `--to woff2` only):
2357
+
2358
+ * `--brotli-quality=N` - Brotli quality (0–11, default 11)
2359
+ * `--transform-tables` / `--no-transform-tables` - apply glyf/loca + hmtx transformations
2360
+
2361
+ The format selection (`--to woff` vs `--to woff2`) **is** the algorithm
2362
+ choice: WOFF mandates zlib, WOFF2 mandates Brotli. Passing a knob that
2363
+ doesn't apply to the requested target exits 1 with a clear error.
2364
+
2136
2365
  [source,shell]
2137
2366
  ----
2138
2367
  # Convert with autohinting and optimization
2139
2368
  fontisan convert font.ttf --to otf --output font.otf \
2140
2369
  --autohint --hinting-mode auto --optimize-tables
2370
+
2371
+ # Smallest WOFF2 (max Brotli + table transforms)
2372
+ fontisan convert font.ttf --to woff2 --output font.woff2 \
2373
+ --brotli-quality 11 --transform-tables
2374
+
2375
+ # WOFF for IE 9+ legacy reach (max zlib)
2376
+ fontisan convert font.ttf --to woff --output font.woff --zlib-level 9
2141
2377
  ----
2142
2378
 
2143
2379
  For detailed information on all available options and conversion scenarios,
@@ -2206,6 +2442,26 @@ tables = converter.convert(font, options: options)
2206
2442
  ====
2207
2443
 
2208
2444
 
2445
+ .One-shot conversion via Fontisan.convert
2446
+ [example]
2447
+ ====
2448
+ [source,ruby]
2449
+ ----
2450
+ require 'fontisan'
2451
+
2452
+ # TTF → WOFF2 (modern browsers)
2453
+ Fontisan.convert('font.ttf', to: :woff2, output: 'font.woff2',
2454
+ brotli_quality: 11, transform_tables: true)
2455
+
2456
+ # TTF → WOFF (IE 9+ legacy reach, max zlib)
2457
+ Fontisan.convert('font.ttf', to: :woff, output: 'font.woff', zlib_level: 9)
2458
+
2459
+ # WOFF stored uncompressed (legal per WOFF 1.0 §5.1; useful for tooling)
2460
+ Fontisan.convert('font.ttf', to: :woff, output: 'font.woff', uncompressed: true)
2461
+ ----
2462
+ ====
2463
+
2464
+
2209
2465
  == Round-Trip validation
2210
2466
 
2211
2467
  === General
@@ -1,4 +1,19 @@
1
- import { defineConfig } from "vitepress";
1
+ import { defineConfig, type HeadConfig } from "vitepress";
2
+
3
+ const SITE_ORIGIN = "https://www.fontist.org";
4
+ const BASE_PATH = "/fontisan/";
5
+ const DEFAULT_DESCRIPTION =
6
+ "The most comprehensive font processing library for Ruby — 100% Pure Ruby, no Python, no C++, no C# dependencies.";
7
+ const DEFAULT_OG_IMAGE = `${SITE_ORIGIN}/og-image.png`;
8
+
9
+ // Map a page's source path to its canonical public URL (origin + base + clean path).
10
+ function pathToUrl(relativePath: string): string {
11
+ const bare = relativePath.replace(/\.md$/, "").replace(/\\/g, "/");
12
+ if (bare === "index") return `${SITE_ORIGIN}${BASE_PATH}`;
13
+ if (bare.endsWith("/index"))
14
+ return `${SITE_ORIGIN}${BASE_PATH}${bare.slice(0, -6)}/`;
15
+ return `${SITE_ORIGIN}${BASE_PATH}${bare}`;
16
+ }
2
17
 
3
18
  // https://vitepress.dev/reference/site-config
4
19
  export default defineConfig({
@@ -7,12 +22,29 @@ export default defineConfig({
7
22
  // https://vitepress.dev/guide/routing#generating-clean-url
8
23
  cleanUrls: true,
9
24
 
25
+ // Exclude standalone guide stubs that collide with directory routes.
26
+ // Both guide/<name>.md and guide/<name>/index.md exist for these three
27
+ // (hinting, validation, conversion); the directory version is the canonical
28
+ // one (sidebar-linked, richer content). Excluding the standalone stub from
29
+ // the build resolves the duplicate-route collision while keeping the source
30
+ // files in the repo.
31
+ srcExclude: [
32
+ "guide/hinting.md",
33
+ "guide/validation.md",
34
+ "guide/conversion.md",
35
+ ],
36
+
10
37
  title: "Fontisan",
11
38
  description:
12
39
  "The most comprehensive font processing library for Ruby — 100% Pure Ruby, no Python, no C++, no C# dependencies.",
13
40
 
14
41
  lastUpdated: true,
15
42
 
43
+ // https://vitepress.dev/reference/site-config#sitemap
44
+ sitemap: {
45
+ hostname: SITE_ORIGIN,
46
+ },
47
+
16
48
  // Base path for deployment (e.g., /fontisan/ for fontist.org/fontisan/)
17
49
  base: process.env.BASE_PATH || "/fontisan/",
18
50
 
@@ -29,19 +61,40 @@ export default defineConfig({
29
61
  ],
30
62
  ["link", { rel: "manifest", href: "/site.webmanifest" }],
31
63
  ["meta", { property: "og:type", content: "website" }],
32
- ["meta", { property: "og:title", content: "Fontisan" }],
64
+ ["link", { rel: "preconnect", href: "https://fonts.googleapis.com" }],
65
+ ["link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossorigin: "" }],
33
66
  [
34
- "meta",
67
+ "link",
35
68
  {
36
- property: "og:description",
37
- content:
38
- "The most comprehensive font processing library for Ruby — 100% Pure Ruby.",
69
+ rel: "stylesheet",
70
+ href: "https://fonts.googleapis.com/css2?family=Spectral:ital,opsz,wght@0,7..72,200..800;1,7..72,200..800&family=IBM+Plex+Mono:wght@400;500&family=IBM+Plex+Sans:wght@300;400;500;600&display=swap",
39
71
  },
40
72
  ],
41
- ["meta", { property: "og:image", content: "/logo-full.svg" }],
42
- ["meta", { name: "twitter:card", content: "summary_large_image" }],
43
73
  ],
44
74
 
75
+ // Per-page og:* and twitter:* tags are derived from each page's frontmatter.
76
+ // https://vitepress.dev/reference/site-config#transformhead
77
+ transformHead(ctx) {
78
+ const { pageData } = ctx;
79
+ const url = pathToUrl(pageData.relativePath);
80
+ const title = pageData.title || "Fontisan";
81
+ const description = pageData.description || DEFAULT_DESCRIPTION;
82
+ const image = pageData.frontmatter.image || DEFAULT_OG_IMAGE;
83
+
84
+ const tags: HeadConfig[] = [
85
+ ["meta", { property: "og:title", content: title }],
86
+ ["meta", { property: "og:description", content: description }],
87
+ ["meta", { property: "og:url", content: url }],
88
+ ["meta", { property: "og:image", content: image }],
89
+ ["meta", { name: "twitter:card", content: "summary_large_image" }],
90
+ ["meta", { name: "twitter:title", content: title }],
91
+ ["meta", { name: "twitter:description", content: description }],
92
+ ["meta", { name: "twitter:image", content: image }],
93
+ ];
94
+
95
+ return tags;
96
+ },
97
+
45
98
  // https://vitepress.dev/reference/default-theme-config
46
99
  themeConfig: {
47
100
  logo: "/logo-full.svg",
@@ -210,6 +263,13 @@ export default defineConfig({
210
263
  { text: "Overview", link: "/cli/" },
211
264
  ],
212
265
  },
266
+ {
267
+ text: "Font Audit",
268
+ collapsed: true,
269
+ items: [
270
+ { text: "audit", link: "/cli/audit" },
271
+ ],
272
+ },
213
273
  {
214
274
  text: "Font Information",
215
275
  collapsed: true,