Class: Fontisan::Converters::FormatConverter
- Inherits:
-
Object
- Object
- Fontisan::Converters::FormatConverter
- Defined in:
- lib/fontisan/converters/format_converter.rb
Overview
Main orchestrator for font format conversions
FormatConverter is the
primary entry point for all format conversion operations. It:
- Selects appropriate conversion strategy based on source/target formats
- Validates conversions against the conversion matrix
- Validates user-supplied options against the selected strategy's schema
- Delegates actual conversion to strategy implementations
- Provides clean error messages for unsupported conversions
The converter uses a strategy pattern with pluggable strategies for
different conversion types. Each strategy declares its own options via
the ConversionStrategy.option DSL; the converter enforces the
format ↔ option mapping at runtime.
Constant Summary collapse
- STRATEGY_CLASSES =
Registry of all strategy classes. Single source of truth for option discovery and strategy lookup. Add a new format by appending its strategy class here.
[ TableCopier, OutlineConverter, Type1Converter, WoffWriter, Woff2Encoder, SvgGenerator, ].freeze
Instance Attribute Summary collapse
-
#conversion_matrix ⇒ Hash
readonly
Conversion matrix loaded from config.
-
#strategies ⇒ Array
readonly
Available conversion strategies.
Class Method Summary collapse
-
.all_strategy_option_names ⇒ Array<Symbol>
All option names declared by any strategy.
-
.strategies_for_target(target_format) ⇒ Array[Class]
Strategies whose supported_conversions include the given target.
-
.strategy_class_for(source_format, target_format) ⇒ Class?
Look up the strategy class that handles a given conversion.
-
.validate_options_for_target!(target_format, options) ⇒ void
Cross-format option check, independent of source format.
Instance Method Summary collapse
-
#all_conversions ⇒ Array<Hash>
Get all supported conversions.
-
#convert(font, target_format, options = {}) ⇒ Hash<String, String>
Convert font to target format.
-
#initialize(conversion_matrix_path: nil) ⇒ FormatConverter
constructor
Initialize converter with strategies.
-
#supported?(source_format, target_format) ⇒ Boolean
Check if a conversion is supported.
-
#supported_targets(source_format) ⇒ Array<Symbol>
Get list of supported target formats for a source format.
Constructor Details
#initialize(conversion_matrix_path: nil) ⇒ FormatConverter
Initialize converter with strategies
118 119 120 121 |
# File 'lib/fontisan/converters/format_converter.rb', line 118 def initialize(conversion_matrix_path: nil) @strategies = self.class::STRATEGY_CLASSES.map(&:new) load_conversion_matrix(conversion_matrix_path) end |
Instance Attribute Details
#conversion_matrix ⇒ Hash (readonly)
Returns Conversion matrix loaded from config.
109 110 111 |
# File 'lib/fontisan/converters/format_converter.rb', line 109 def conversion_matrix @conversion_matrix end |
#strategies ⇒ Array (readonly)
Returns Available conversion strategies.
112 113 114 |
# File 'lib/fontisan/converters/format_converter.rb', line 112 def strategies @strategies end |
Class Method Details
.all_strategy_option_names ⇒ Array<Symbol>
All option names declared by any strategy. Used by ConversionOptions to keep its generating-options list in sync without duplicating the schema.
51 52 53 |
# File 'lib/fontisan/converters/format_converter.rb', line 51 def all_strategy_option_names STRATEGY_CLASSES.flat_map { |k| k..map(&:name) }.uniq end |
.strategies_for_target(target_format) ⇒ Array[Class]
Strategies whose supported_conversions include the given target.
72 73 74 75 76 77 78 |
# File 'lib/fontisan/converters/format_converter.rb', line 72 def strategies_for_target(target_format) STRATEGY_CLASSES.select do |klass| klass.new.supported_conversions.any? { |(_, t)| t == target_format } rescue NotImplementedError false end end |
.strategy_class_for(source_format, target_format) ⇒ Class?
Look up the strategy class that handles a given conversion.
60 61 62 63 64 65 66 |
# File 'lib/fontisan/converters/format_converter.rb', line 60 def strategy_class_for(source_format, target_format) STRATEGY_CLASSES.find do |klass| klass.new.supports?(source_format, target_format) rescue NotImplementedError false end end |
.validate_options_for_target!(target_format, options) ⇒ void
This method returns an undefined value.
Cross-format option check, independent of source format. Used by
OutputWriter (which doesn't know the source format at write time)
to catch e.g. --brotli-quality passed with --to woff.
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/fontisan/converters/format_converter.rb', line 89 def (target_format, ) handlers = strategies_for_target(target_format) handler_names = handlers.flat_map do |k| k..map(&:name) end.to_set non_handler_names = (STRATEGY_CLASSES - handlers) .flat_map { |k| k..map(&:name) } conflicts = .keys.select do |k| non_handler_names.include?(k.to_sym) end return if conflicts.empty? accepted = handler_names.empty? ? "(none)" : handler_names.join(", ") raise ArgumentError, "Option(s) #{conflicts.map(&:inspect).join(', ')} do not apply " \ "to --to #{target_format}. Accepted for #{target_format}: #{accepted}" end |
Instance Method Details
#all_conversions ⇒ Array<Hash>
Get all supported conversions
228 229 230 231 232 233 234 235 236 237 |
# File 'lib/fontisan/converters/format_converter.rb', line 228 def all_conversions return [] unless conversion_matrix conversions = conversion_matrix["conversions"] return [] unless conversions conversions.map do |conv| { from: conv["from"].to_sym, to: conv["to"].to_sym } end end |
#convert(font, target_format, options = {}) ⇒ Hash<String, String>
Convert font to target format
This is the main entry point for format conversion. It:
- Detects source format from font
- Validates conversion is supported
- Selects appropriate strategy
- Delegates conversion to strategy
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/fontisan/converters/format_converter.rb', line 152 def convert(font, target_format, = {}) validate_parameters!(font, target_format) source_format = detect_format(font) validate_conversion_supported!(source_format, target_format) # Special case: Variable font to SVG if variable_font?(font) && target_format == :svg return convert_variable_to_svg(font, ) end strategy = select_strategy(source_format, target_format) # Enforce format ↔ option mapping at the orchestrator level so each # strategy only sees options it declared. Cross-format misuse # (e.g., `--zlib-level` on a WOFF2 conversion) raises here with a # clear message; the strategy itself never has to defensively # ignore unknown keys. (strategy, source_format, target_format, ) sliced = (, strategy) tables = strategy.convert( font, sliced.merge(target_format: target_format), ) # Preserve variation data if requested and font is variable if .fetch(:preserve_variation, true) && variable_font?(font) tables = preserve_variation_data( font, tables, source_format, target_format, , ) end tables end |
#supported?(source_format, target_format) ⇒ Boolean
Check if a conversion is supported
198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/fontisan/converters/format_converter.rb', line 198 def supported?(source_format, target_format) return false unless conversion_matrix conversions = conversion_matrix["conversions"] return false unless conversions conversions.any? do |conv| conv["from"] == source_format.to_s && conv["to"] == target_format.to_s end end |
#supported_targets(source_format) ⇒ Array<Symbol>
Get list of supported target formats for a source format
214 215 216 217 218 219 220 221 222 223 |
# File 'lib/fontisan/converters/format_converter.rb', line 214 def supported_targets(source_format) return [] unless conversion_matrix conversions = conversion_matrix["conversions"] return [] unless conversions conversions .select { |conv| conv["from"] == source_format.to_s } .map { |conv| conv["to"].to_sym } end |