Module: Fontisan::Converters::OutlineOptimizer

Included in:
OutlineConverter
Defined in:
lib/fontisan/converters/outline_optimizer.rb

Overview

Optimizes CFF CharStrings using subroutine extraction

This module analyzes CharStrings for repeated patterns, extracts them as subroutines, and rewrites the CharStrings to call the subroutines instead of repeating the code.

The optimization process:

  1. Analyze patterns across all CharStrings

  2. Select optimal set of patterns for subroutines

  3. Optimize subroutine ordering

  4. Build subroutines from selected patterns

  5. Rewrite CharStrings to call subroutines

Instance Method Summary collapse

Instance Method Details

#optimize_charstrings(charstrings) ⇒ Array<Array<String>, Array<String>>

Optimize CharStrings using subroutine extraction

Parameters:

  • charstrings (Array<String>)

    Original CharString bytes

Returns:

  • (Array<Array<String>, Array<String>>)
    optimized_charstrings, local_subrs


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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
# File 'lib/fontisan/converters/outline_optimizer.rb', line 27

def optimize_charstrings(charstrings)
  # Convert to hash format expected by PatternAnalyzer
  charstrings_hash = {}
  charstrings.each_with_index do |cs, index|
    charstrings_hash[index] = cs
  end

  # Analyze patterns
  analyzer = Optimizers::PatternAnalyzer.new(
    min_length: 10,
    stack_aware: true,
  )
  patterns = analyzer.analyze(charstrings_hash)

  # Return original if no patterns found
  return [charstrings, []] if patterns.empty?

  # Optimize selection
  optimizer = Optimizers::SubroutineOptimizer.new(patterns,
                                                  max_subrs: 65_535)
  selected_patterns = optimizer.optimize_selection

  # Optimize ordering
  selected_patterns = optimizer.optimize_ordering(selected_patterns)

  # Return original if no patterns selected
  return [charstrings, []] if selected_patterns.empty?

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

  # Build subroutine map
  subroutine_map = {}
  selected_patterns.each_with_index do |pattern, index|
    subroutine_map[pattern.bytes] = index
  end

  # Rewrite CharStrings
  rewriter = Optimizers::CharstringRewriter.new(subroutine_map, builder)
  optimized_charstrings = charstrings.map.with_index do |charstring, glyph_id|
    # Find patterns for this glyph
    glyph_patterns = selected_patterns.select do |p|
      p.glyphs.include?(glyph_id)
    end

    if glyph_patterns.empty?
      charstring
    else
      rewriter.rewrite(charstring, glyph_patterns, glyph_id)
    end
  end

  [optimized_charstrings, local_subrs]
rescue StandardError => e
  # If optimization fails for any reason, return original CharStrings
  warn "Optimization warning: #{e.message}"
  [charstrings, []]
end