Class: Fontisan::Converters::Woff2Encoder

Inherits:
Object
  • Object
show all
Includes:
ConversionStrategy, Woff2FontMemoryLoader
Defined in:
lib/fontisan/converters/woff2_encoder.rb

Overview

WOFF2 encoder conversion strategy

Woff2Encoder implements the ConversionStrategy interface to convert TTF or OTF fonts to WOFF2 format with Brotli compression.

WOFF2 encoding process:

  1. Load configuration settings
  2. Determine font flavor (TTF or CFF)
  3. Collect and order tables
  4. Transform tables (placeholder for glyf/loca/hmtx optimization)
  5. Compress all tables with single Brotli stream
  6. Build WOFF2 header and table directory
  7. Assemble complete WOFF2 binary
  8. (Optional) Validate encoded WOFF2

For Phase 2 Milestone 2.1:

  • Basic WOFF2 structure generation
  • Brotli compression of table data
  • Valid WOFF2 files for web font delivery
  • Table transformations are architectural placeholders

Examples:

Convert TTF to WOFF2

encoder = Woff2Encoder.new
result = encoder.convert(font)
File.binwrite('font.woff2', result[:woff2_binary])

Convert with validation

encoder = Woff2Encoder.new
result = encoder.convert(font, validate: true)
puts result[:validation_report].text_summary if result[:validation_report]

Defined Under Namespace

Modules: Woff2FontMemoryLoader

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from ConversionStrategy

included, #supports?

Methods included from Woff2FontMemoryLoader

from_file_io

Constructor Details

#initialize(config_path: nil) ⇒ Woff2Encoder

Initialize encoder with configuration.

The encoder is stateless per call; all conversion knobs come through the per-convert options hash. The config file supplies default values only for fields not expressed via the DSL.

Parameters:

  • config_path (String, nil) (defaults to: nil)

    Path to config file



54
55
56
# File 'lib/fontisan/converters/woff2_encoder.rb', line 54

def initialize(config_path: nil)
  @config = load_configuration(config_path)
end

Instance Attribute Details

#configHash (readonly)

Returns Configuration settings.

Returns:

  • (Hash)

    Configuration settings



45
46
47
# File 'lib/fontisan/converters/woff2_encoder.rb', line 45

def config
  @config
end

Instance Method Details

#convert(font, options = {}) ⇒ Hash{Symbol => String}

Convert font to WOFF2 format.

Returns a hash with :woff2_binary containing the complete WOFF2 file.

Parameters:

  • font (TrueTypeFont, OpenTypeFont)

    Source font

  • options (Hash{Symbol => Object}) (defaults to: {})

    Per-call options:

    • :brotli_quality (0–11, default from config or 11)
    • :transform_tables (bool, default false)
    • :quality — legacy alias for :brotli_quality (backward compat)

Returns:

  • (Hash{Symbol => String})

    { woff2_binary: <bytes> }

Raises:

  • (ArgumentError)

    if any option fails validation

  • (Fontisan::Error)

    if encoding fails



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/fontisan/converters/woff2_encoder.rb', line 77

def convert(font, options = {})
  validate(font, :woff2)

  resolved = normalize_legacy_quality(options)
  self.class.validate_options!(strategy_options(resolved))

  quality = resolved.fetch(:brotli_quality) do
    config["brotli"]["quality"]
  end

  # Detect font flavor
  flavor = detect_flavor(font)

  # Collect all tables
  table_data = collect_tables(font, options)

  # Transform tables (if enabled)
  transformer = Woff2::TableTransformer.new(font)
  transform_enabled = resolved.fetch(:transform_tables, false)

  # Build table directory entries
  entries = build_table_entries(table_data, transformer,
                                transform_enabled)

  # Compress all table data into single stream
  compressed_data = compress_tables(entries, table_data, quality)

  # Calculate sizes
  total_sfnt_size = calculate_sfnt_size(table_data)
  total_compressed_size = compressed_data.bytesize

  # Build WOFF2 header
  header = build_header(
    flavor: flavor,
    num_tables: entries.size,
    total_sfnt_size: total_sfnt_size,
    total_compressed_size: total_compressed_size,
  )

  # Assemble WOFF2 binary
  woff2_binary = assemble_woff2(header, entries, compressed_data)

  # Prepare result
  { woff2_binary: woff2_binary }

  # Optional validation
  # Temporarily disabled - will be reimplemented with new DSL framework
  # if options[:validate]
  #   validation_report = validate_encoding(woff2_binary, options)
  #   result[:validation_report] = validation_report
  # end
end

#supported_conversionsArray<Array<Symbol>>

Get list of supported conversions

Returns:

  • (Array<Array<Symbol>>)

    Supported conversion pairs



133
134
135
136
137
138
# File 'lib/fontisan/converters/woff2_encoder.rb', line 133

def supported_conversions
  [
    %i[ttf woff2],
    %i[otf woff2],
  ]
end

#validate(font, target_format) ⇒ Boolean

Validate that conversion is possible

Parameters:

Returns:

  • (Boolean)

    True if valid

Raises:

  • (Error)

    If conversion is not possible



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/fontisan/converters/woff2_encoder.rb', line 146

def validate(font, target_format)
  unless target_format == :woff2
    raise Fontisan::Error,
          "Woff2Encoder only supports conversion to woff2, " \
          "got: #{target_format}"
  end

  # Verify font has required tables
  required_tables = %w[head hhea maxp]
  required_tables.each do |tag|
    unless font.table(tag)
      raise Fontisan::Error,
            "Font is missing required table: #{tag}"
    end
  end

  # Verify font has either glyf or CFF table
  unless font.has_table?("glyf") || font.has_table?("CFF ") || font.has_table?("CFF2")
    raise Fontisan::Error,
          "Font must have either glyf or CFF/CFF2 table"
  end

  true
end