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]



22
23
24
25
26
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
# File 'lib/fontisan/converters/outline_optimizer.rb', line 22

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