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,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../font_loader"
4
-
5
3
  module Fontisan
6
4
  module Pipeline
7
5
  # Detects font format and capabilities
@@ -50,107 +48,62 @@ module Fontisan
50
48
 
51
49
  # Detect font format
52
50
  #
53
- # @return [Symbol] One of :ttf, :otf, :ttc, :otc, :woff, :woff2, :svg
51
+ # Delegates to the loaded font object's own `#format` method.
52
+ # Each font class (TrueTypeFont, OpenTypeFont, WoffFont, Woff2Font,
53
+ # Type1Font, *Collection) is the single source of truth for its
54
+ # format identifier. This keeps FormatDetector closed for modification
55
+ # when new font classes are added (OCP): no case statement to edit.
56
+ #
57
+ # @return [Symbol] One of :ttf, :otf, :ttc, :otc, :woff, :woff2,
58
+ # :type1, :dfont, or :unknown
54
59
  def detect_format
55
- # Check for SVG first (from file extension even if font failed to load)
60
+ # SVG fonts are an export-only target; no font object exists yet.
56
61
  return :svg if @file_path.end_with?(".svg")
57
62
 
58
63
  return :unknown unless @font
59
64
 
60
- # Use is_a? for proper class checking
61
- case @font
62
- when Fontisan::TrueTypeCollection
63
- :ttc
64
- when Fontisan::OpenTypeCollection
65
- :otc
66
- when Fontisan::TrueTypeFont
67
- if @file_path.end_with?(".woff")
68
- :woff
69
- elsif @file_path.end_with?(".woff2")
70
- :woff2
71
- else
72
- :ttf
73
- end
74
- when Fontisan::OpenTypeFont
75
- if @file_path.end_with?(".woff")
76
- :woff
77
- elsif @file_path.end_with?(".woff2")
78
- :woff2
79
- else
80
- :otf
81
- end
82
- else
83
- :unknown
84
- end
65
+ @font.format
85
66
  end
86
67
 
87
68
  # Detect variation type
88
69
  #
89
- # @return [Symbol] One of :static, :gvar, :cff2
70
+ # Delegates to the loaded font object's own `#variation_type` method.
71
+ # Each font class is the single source of truth for its variation
72
+ # profile, so this method needs no class-specific branching.
73
+ #
74
+ # @return [Symbol] :static, :gvar (TrueType variable), or :cff2
90
75
  def detect_variation
91
76
  return :static unless @font
92
77
 
93
- # Collections don't have has_table? method
94
- # Return :static for collections (variation detection would need to load first font)
95
- return :static if collection?
96
-
97
- # Check for variable font tables
98
- if @font.has_table?("fvar")
99
- # Variable font detected - check variation type
100
- if @font.has_table?("gvar")
101
- :gvar # TrueType variable font
102
- elsif @font.has_table?("CFF2")
103
- :cff2 # OpenType variable font (CFF2)
104
- else
105
- :static # Has fvar but no variation data (shouldn't happen)
106
- end
107
- else
108
- :static
109
- end
78
+ @font.variation_type
110
79
  end
111
80
 
112
81
  # Detect font capabilities
113
82
  #
83
+ # Aggregates four pieces of self-knowledge owned by the font class:
84
+ # outline representation, variation support, collection status, and
85
+ # the table directory. Each is exposed as a method on the font object
86
+ # so this method stays free of class-specific branching.
87
+ #
114
88
  # @return [Hash] Capabilities hash
115
89
  def detect_capabilities
116
90
  return default_capabilities unless @font
117
91
 
118
- # Check if this is a collection
119
- is_collection = collection?
120
-
121
- font_to_check = if is_collection
122
- # Collections don't have fonts method, need to load first font
123
- nil # Will handle in API usage
124
- else
125
- @font
126
- end
127
-
128
- # For collections, return basic capabilities
129
- if is_collection
130
- return {
131
- outline: :unknown, # Would need to load first font to know
132
- variation: false, # Would need to load first font to know
133
- collection: true,
134
- tables: [],
135
- }
136
- end
137
-
138
- return default_capabilities unless font_to_check
139
-
140
92
  {
141
- outline: detect_outline_type(font_to_check),
142
- variation: detect_variation != :static,
143
- collection: false,
144
- tables: available_tables(font_to_check),
93
+ outline: @font.outline_type,
94
+ variation: @font.variation_type != :static,
95
+ collection: @font.collection?,
96
+ tables: @font.table_names,
145
97
  }
146
98
  end
147
99
 
148
100
  # Check if font is a collection
149
101
  #
150
- # @return [Boolean] True if collection (TTC/OTC)
102
+ # @return [Boolean] True if the loaded object is a font collection
151
103
  def collection?
152
- @font.is_a?(Fontisan::TrueTypeCollection) ||
153
- @font.is_a?(Fontisan::OpenTypeCollection)
104
+ return false unless @font
105
+
106
+ @font.collection?
154
107
  end
155
108
 
156
109
  # Check if font is variable
@@ -207,32 +160,6 @@ module Fontisan
207
160
  @font = nil
208
161
  end
209
162
 
210
- # Detect outline type
211
- #
212
- # @param font [Font] Font object
213
- # @return [Symbol] :truetype or :cff
214
- def detect_outline_type(font)
215
- if font.has_table?("glyf") || font.has_table?("gvar")
216
- :truetype
217
- elsif font.has_table?("CFF ") || font.has_table?("CFF2")
218
- :cff
219
- else
220
- :unknown
221
- end
222
- end
223
-
224
- # Get available tables
225
- #
226
- # @param font [Font] Font object
227
- # @return [Array<String>] List of table tags
228
- def available_tables(font)
229
- return [] unless font.respond_to?(:table_names)
230
-
231
- font.table_names
232
- rescue StandardError
233
- []
234
- end
235
-
236
163
  # Default capabilities when font cannot be loaded
237
164
  #
238
165
  # @return [Hash] Default capabilities
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../font_writer"
4
-
5
3
  module Fontisan
6
4
  module Pipeline
7
5
  # Handles writing font tables to various output formats
@@ -33,8 +31,9 @@ module Fontisan
33
31
  # Initialize output writer
34
32
  #
35
33
  # @param output_path [String] Path to write output
36
- # @param format [Symbol] Target format (:ttf, :otf, :woff, :woff2)
37
- # @param options [Hash] Writing options
34
+ # @param format [Symbol] Target format (:ttf, :otf, :woff, :woff2, :svg)
35
+ # @param options [Hash] Writing options. Format-specific compression
36
+ # knobs (zlib_level, brotli_quality, etc.) pass through to the strategy.
38
37
  def initialize(output_path, format, options = {})
39
38
  @output_path = output_path
40
39
  @format = format
@@ -45,8 +44,15 @@ module Fontisan
45
44
  #
46
45
  # @param tables [Hash<String, String>, Hash] Font tables (tag => binary data) or special format result
47
46
  # @return [Integer] Number of bytes written
48
- # @raise [ArgumentError] If format is unsupported
47
+ # @raise [ArgumentError] If format is unsupported or options don't apply
49
48
  def write(tables)
49
+ # Catch cross-format misuse before delegating, so the error message
50
+ # is consistent regardless of whether the format goes through
51
+ # FormatConverter (outline conversions) or this writer (packaging
52
+ # changes for WOFF/WOFF2).
53
+ Converters::FormatConverter.validate_options_for_target!(@format,
54
+ @options)
55
+
50
56
  case @format
51
57
  when :ttf, :otf
52
58
  write_sfnt(tables)
@@ -92,8 +98,6 @@ module Fontisan
92
98
  # @param tables [Hash<String, String>] Font tables
93
99
  # @return [Integer] Number of bytes written
94
100
  def write_woff(tables)
95
- require_relative "../converters/woff_writer"
96
-
97
101
  writer = Converters::WoffWriter.new
98
102
  font = build_font_from_tables(tables)
99
103
  woff_data = writer.convert(font, @options)
@@ -106,8 +110,6 @@ module Fontisan
106
110
  # @param tables [Hash<String, String>] Font tables
107
111
  # @return [Integer] Number of bytes written
108
112
  def write_woff2(tables)
109
- require_relative "../converters/woff2_encoder"
110
-
111
113
  encoder = Converters::Woff2Encoder.new
112
114
  font = build_font_from_tables(tables)
113
115
  result = encoder.convert(font, @options)
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_strategy"
4
- require_relative "../../variation/instance_generator"
5
- require_relative "../../variation/variation_context"
6
-
7
3
  module Fontisan
8
4
  module Pipeline
9
5
  module Strategies
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_strategy"
4
- require_relative "instance_strategy"
5
- require_relative "../../variation/variation_context"
6
-
7
3
  module Fontisan
8
4
  module Pipeline
9
5
  module Strategies
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "base_strategy"
4
-
5
3
  module Fontisan
6
4
  module Pipeline
7
5
  module Strategies
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Autoload hub for the Fontisan::Pipeline::Strategies namespace.
4
+
5
+ module Fontisan
6
+ module Pipeline
7
+ module Strategies
8
+ autoload :BaseStrategy, "fontisan/pipeline/strategies/base_strategy"
9
+ autoload :InstanceStrategy, "fontisan/pipeline/strategies/instance_strategy"
10
+ autoload :NamedStrategy, "fontisan/pipeline/strategies/named_strategy"
11
+ autoload :PreserveStrategy, "fontisan/pipeline/strategies/preserve_strategy"
12
+ end
13
+ end
14
+ end
@@ -1,12 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "format_detector"
4
- require_relative "variation_resolver"
5
- require_relative "../converters/format_converter"
6
- require_relative "../font_loader"
7
- require_relative "../font_writer"
8
- require_relative "output_writer"
9
-
10
3
  module Fontisan
11
4
  module Pipeline
12
5
  # Orchestrates universal font transformation pipeline
@@ -1,10 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "strategies/base_strategy"
4
- require_relative "strategies/preserve_strategy"
5
- require_relative "strategies/instance_strategy"
6
- require_relative "strategies/named_strategy"
7
-
8
3
  module Fontisan
9
4
  module Pipeline
10
5
  # Resolves variation data using strategy pattern
@@ -136,7 +131,6 @@ module Fontisan
136
131
  def validate_instance_coordinates(coordinates)
137
132
  return if coordinates.empty?
138
133
 
139
- require_relative "../variation/variation_context"
140
134
  context = Variation::VariationContext.new(@font)
141
135
  context.validate_coordinates(coordinates)
142
136
  end
@@ -146,7 +140,6 @@ module Fontisan
146
140
  # @param instance_index [Integer] Instance index to validate
147
141
  # @raise [ArgumentError] If index invalid
148
142
  def validate_named_instance_index(instance_index)
149
- require_relative "../variation/variation_context"
150
143
  context = Variation::VariationContext.new(@font)
151
144
 
152
145
  unless context.fvar
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Autoload hub for the Fontisan::Pipeline namespace.
4
+
5
+ module Fontisan
6
+ module Pipeline
7
+ autoload :FormatDetector, "fontisan/pipeline/format_detector"
8
+ autoload :OutputWriter, "fontisan/pipeline/output_writer"
9
+ autoload :Strategies, "fontisan/pipeline/strategies/base_strategy"
10
+ autoload :TransformationPipeline, "fontisan/pipeline/transformation_pipeline"
11
+ autoload :VariationResolver, "fontisan/pipeline/variation_resolver"
12
+ end
13
+ end
@@ -1,20 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bindata"
4
- require_relative "constants"
5
- require_relative "loading_modes"
6
- require_relative "utilities/checksum_calculator"
7
- require_relative "sfnt_table"
8
- require_relative "tables/head_table"
9
- require_relative "tables/name_table"
10
- require_relative "tables/os2_table"
11
- require_relative "tables/cmap_table"
12
- require_relative "tables/glyf_table"
13
- require_relative "tables/hhea_table"
14
- require_relative "tables/maxp_table"
15
- require_relative "tables/post_table"
16
- require_relative "tables/hmtx_table"
17
- require_relative "tables/loca_table"
18
4
 
19
5
  module Fontisan
20
6
  # SFNT Offset Table structure
@@ -377,6 +363,35 @@ module Fontisan
377
363
  !find_table_entry(tag).nil?
378
364
  end
379
365
 
366
+ # Whether this object represents a font collection rather than a single
367
+ # font. Each font class is the authority on this question.
368
+ #
369
+ # @return [Boolean]
370
+ def collection? = false
371
+
372
+ # Variation profile of the font, derived from its table set. Used by the
373
+ # conversion pipeline to choose between preserving variation tables and
374
+ # generating a static instance.
375
+ #
376
+ # @return [Symbol] :static, :gvar (TrueType variable), or :cff2 (CFF2 variable)
377
+ def variation_type
378
+ return :static unless has_table?("fvar")
379
+ return :gvar if has_table?("gvar")
380
+ return :cff2 if has_table?("CFF2")
381
+
382
+ :static
383
+ end
384
+
385
+ # Outline representation used by the font. Derived from the table set.
386
+ #
387
+ # @return [Symbol] :truetype (glyf/gvar) or :cff (CFF/CFF2)
388
+ def outline_type
389
+ return :truetype if has_table?("glyf") || has_table?("gvar")
390
+ return :cff if has_table?("CFF ") || has_table?("CFF2")
391
+
392
+ :unknown
393
+ end
394
+
380
395
  # Check if a table is available in the current loading mode
381
396
  #
382
397
  # @param tag [String] The table tag to check
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "constants"
4
- require_relative "loading_modes"
5
-
6
3
  module Fontisan
7
4
  # Base class for SFNT font tables
8
5
  #
@@ -198,7 +195,6 @@ module Fontisan
198
195
  def calculate_checksum
199
196
  load_data! unless data_loaded?
200
197
 
201
- require_relative "utilities/checksum_calculator"
202
198
  Utilities::ChecksumCalculator.calculate_table_checksum(@data)
203
199
  end
204
200
 
@@ -1,11 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "options"
4
- require_relative "profile"
5
- require_relative "glyph_mapping"
6
- require_relative "table_subsetter"
7
- require_relative "../font_writer"
8
-
9
3
  module Fontisan
10
4
  module Subset
11
5
  # Main font subsetting engine
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Autoload hub for the Fontisan::Subset namespace.
4
+
5
+ module Fontisan
6
+ module Subset
7
+ autoload :Builder, "fontisan/subset/builder"
8
+ autoload :GlyphMapping, "fontisan/subset/glyph_mapping"
9
+ autoload :Options, "fontisan/subset/options"
10
+ autoload :Profile, "fontisan/subset/profile"
11
+ autoload :TableSubsetter, "fontisan/subset/table_subsetter"
12
+ end
13
+ end
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "font_face_generator"
4
- require_relative "glyph_generator"
5
- require_relative "view_box_calculator"
6
-
7
3
  module Fontisan
8
4
  module Svg
9
5
  # Generates complete SVG font XML structure
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "view_box_calculator"
4
-
5
3
  module Fontisan
6
4
  module Svg
7
5
  # Generates SVG glyph elements from glyph outlines
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Autoload hub for the Fontisan::Svg namespace.
4
+
5
+ module Fontisan
6
+ module Svg
7
+ autoload :FontFaceGenerator, "fontisan/svg/font_face_generator"
8
+ autoload :FontGenerator, "fontisan/svg/font_generator"
9
+ autoload :GlyphGenerator, "fontisan/svg/glyph_generator"
10
+ autoload :ViewBoxCalculator, "fontisan/svg/view_box_calculator"
11
+ end
12
+ end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "stringio"
4
- require_relative "../binary/base_record"
5
4
 
6
5
  module Fontisan
7
6
  module Tables
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "stringio"
4
- require_relative "../binary/base_record"
5
4
 
6
5
  module Fontisan
7
6
  module Tables
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "stringio"
4
- require_relative "../../binary/base_record"
5
4
 
6
5
  module Fontisan
7
6
  module Tables
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "stringio"
4
- require_relative "../../binary/base_record"
5
4
 
6
5
  module Fontisan
7
6
  module Tables
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "charstring_parser"
4
- require_relative "charstring_builder"
5
- require_relative "index_builder"
6
-
7
3
  module Fontisan
8
4
  module Tables
9
5
  class Cff
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "index"
4
- require_relative "charstring"
5
-
6
3
  module Fontisan
7
4
  module Tables
8
5
  class Cff
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "stringio"
4
- require_relative "../../binary/base_record"
5
4
 
6
5
  module Fontisan
7
6
  module Tables
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "stringio"
4
- require_relative "../../binary/base_record"
5
4
 
6
5
  module Fontisan
7
6
  module Tables
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../binary/base_record"
4
-
5
3
  module Fontisan
6
4
  module Tables
7
5
  class Cff
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../models/hint"
4
-
5
3
  module Fontisan
6
4
  module Tables
7
5
  class Cff
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "stringio"
4
- require_relative "../../binary/base_record"
5
4
 
6
5
  module Fontisan
7
6
  module Tables
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "dict"
4
-
5
3
  module Fontisan
6
4
  module Tables
7
5
  class Cff
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "dict_builder"
4
-
5
3
  module Fontisan
6
4
  module Tables
7
5
  class Cff
@@ -1,11 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "private_dict_writer"
4
- require_relative "offset_recalculator"
5
- require_relative "index_builder"
6
- require_relative "dict_builder"
7
- require_relative "charstring_rebuilder"
8
- require_relative "hint_operation_injector"
9
3
  require "stringio"
10
4
 
11
5
  module Fontisan
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "dict"
4
-
5
3
  module Fontisan
6
4
  module Tables
7
5
  class Cff
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "stringio"
4
- require_relative "../binary/base_record"
5
4
 
6
5
  module Fontisan
7
6
  module Tables
@@ -42,6 +41,28 @@ module Fontisan
42
41
  # puts cff.font_count # => 1
43
42
  # puts cff.header.version # => "1.0"
44
43
  class Cff < Binary::BaseRecord
44
+ # Inner namespace autoloads — declared here so Cff::* constants
45
+ # resolve on first reference without require_relative.
46
+ autoload :CFFGlyph, "fontisan/tables/cff/cff_glyph"
47
+ autoload :Charset, "fontisan/tables/cff/charset"
48
+ autoload :CharString, "fontisan/tables/cff/charstring"
49
+ autoload :CharStringBuilder, "fontisan/tables/cff/charstring_builder"
50
+ autoload :CharStringParser, "fontisan/tables/cff/charstring_parser"
51
+ autoload :CharStringRebuilder, "fontisan/tables/cff/charstring_rebuilder"
52
+ autoload :CharstringsIndex, "fontisan/tables/cff/charstrings_index"
53
+ autoload :Dict, "fontisan/tables/cff/dict"
54
+ autoload :DictBuilder, "fontisan/tables/cff/dict_builder"
55
+ autoload :Encoding, "fontisan/tables/cff/encoding"
56
+ autoload :Header, "fontisan/tables/cff/header"
57
+ autoload :HintOperationInjector, "fontisan/tables/cff/hint_operation_injector"
58
+ autoload :Index, "fontisan/tables/cff/index"
59
+ autoload :IndexBuilder, "fontisan/tables/cff/index_builder"
60
+ autoload :OffsetRecalculator, "fontisan/tables/cff/offset_recalculator"
61
+ autoload :PrivateDict, "fontisan/tables/cff/private_dict"
62
+ autoload :PrivateDictWriter, "fontisan/tables/cff/private_dict_writer"
63
+ autoload :TableBuilder, "fontisan/tables/cff/table_builder"
64
+ autoload :TopDict, "fontisan/tables/cff/top_dict"
65
+
45
66
  # OpenType table tag for CFF
46
67
  TAG = "CFF "
47
68
 
@@ -468,19 +489,5 @@ module Fontisan
468
489
  raise CorruptedTableError, "Failed to parse Encoding: #{e.message}"
469
490
  end
470
491
  end
471
-
472
- # Load nested class definitions after the main class is defined
473
- require_relative "cff/header"
474
- require_relative "cff/index"
475
- require_relative "cff/dict"
476
- require_relative "cff/top_dict"
477
- require_relative "cff/private_dict"
478
- require_relative "cff/charstring"
479
- require_relative "cff/charstring_parser"
480
- require_relative "cff/charstring_rebuilder"
481
- require_relative "cff/charstrings_index"
482
- require_relative "cff/charset"
483
- require_relative "cff/encoding"
484
- require_relative "cff/cff_glyph"
485
492
  end
486
493
  end