Class: Fontisan::Optimizers::SubroutineGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/fontisan/optimizers/subroutine_generator.rb

Overview

Main orchestrator for CFF subroutine generation pipeline. Coordinates PatternAnalyzer, SubroutineOptimizer, SubroutineBuilder, and CharstringRewriter to generate optimized subroutines for fonts.

The generator processes CharStrings from a CFF font table by:

  1. Analyzing patterns across all glyphs

  2. Selecting optimal patterns (avoiding conflicts, within limits)

  3. Ordering patterns by frequency for efficient encoding

  4. Building actual subroutine CharStrings

  5. Rewriting original CharStrings with subroutine calls

Examples:

Basic usage

generator = SubroutineGenerator.new(min_pattern_length: 10)
result = generator.generate(font)
puts "Generated #{result[:selected_count]} subroutines"
puts "Total savings: #{result[:savings]} bytes"

See Also:

  • docs/SUBROUTINE_ARCHITECTUREdocs/SUBROUTINE_ARCHITECTURE.md

Constant Summary collapse

DEFAULT_MIN_PATTERN_LENGTH =

Default minimum pattern length in bytes

10
DEFAULT_MAX_SUBROUTINES =

Default maximum number of subroutines (CFF limit)

65_535

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ SubroutineGenerator

Initialize generator with options

Parameters:

  • options (Hash) (defaults to: {})

    configuration options

Options Hash (options):

  • :min_pattern_length (Integer) — default: 10

    minimum pattern size

  • :max_subroutines (Integer) — default: 65535

    max subroutine count

  • :optimize_ordering (Boolean) — default: true

    enable frequency ordering



36
37
38
39
40
41
# File 'lib/fontisan/optimizers/subroutine_generator.rb', line 36

def initialize(options = {})
  @min_pattern_length = options[:min_pattern_length] ||
    DEFAULT_MIN_PATTERN_LENGTH
  @max_subroutines = options[:max_subroutines] || DEFAULT_MAX_SUBROUTINES
  @optimize_ordering = options[:optimize_ordering] != false
end

Instance Method Details

#generate(font) ⇒ Hash

Generate subroutines for a font

Main entry point for the subroutine generation pipeline. Processes a font’s CFF table to create optimized subroutines and rewrite CharStrings.

Parameters:

Returns:

  • (Hash)

    result containing:

    • :local_subrs [Array<String>] subroutine CharStrings

    • :charstrings [Hash<Integer, String>] rewritten CharStrings

    • :bias [Integer] CFF bias value for subroutines

    • :savings [Integer] total bytes saved

    • :pattern_count [Integer] total patterns found

    • :selected_count [Integer] patterns selected as subroutines

Raises:

  • (ArgumentError)

    if font has no CFF table



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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
# File 'lib/fontisan/optimizers/subroutine_generator.rb', line 58

def generate(font)
  # 1. Extract CharStrings from CFF table
  charstrings = extract_charstrings(font)

  # Handle empty font gracefully
  if charstrings.empty?
    return {
      local_subrs: [],
      charstrings: {},
      bias: 0,
      savings: 0,
      pattern_count: 0,
      selected_count: 0,
    }
  end

  # 2. Analyze patterns
  analyzer = PatternAnalyzer.new(
    min_length: @min_pattern_length,
    stack_aware: true,
  )
  patterns = analyzer.analyze(charstrings)

  # 3. Optimize selection
  optimizer = SubroutineOptimizer.new(patterns,
                                      max_subrs: @max_subroutines)
  selected_patterns = optimizer.optimize_selection

  # 4. Optimize ordering (if enabled)
  if @optimize_ordering
    selected_patterns = optimizer.optimize_ordering(selected_patterns)
  end

  # 5. Build subroutines
  builder = SubroutineBuilder.new(selected_patterns, type: :local)
  subroutines = builder.build

  # 6. Build subroutine map
  subroutine_map = build_subroutine_map(selected_patterns)

  # 7. Rewrite CharStrings
  rewriter = CharstringRewriter.new(subroutine_map, builder)
  rewritten_charstrings = rewrite_charstrings(
    charstrings,
    selected_patterns,
    rewriter,
  )

  # 8. Return complete result
  {
    local_subrs: subroutines,
    charstrings: rewritten_charstrings,
    bias: builder.bias,
    savings: calculate_total_savings(selected_patterns),
    pattern_count: patterns.length,
    selected_count: selected_patterns.length,
  }
end