Class: Fontisan::Type1::SeacExpander

Inherits:
Object
  • Object
show all
Defined in:
lib/fontisan/type1/seac_expander.rb

Overview

Expands Type 1 seac composite glyphs into base + accent outlines

[‘SeacExpander`](lib/fontisan/type1/seac_expander.rb) handles the decomposition of Type 1 composite glyphs that use the `seac` operator. The seac operator combines two glyphs (a base character and an accent) with a positioning offset, which must be decomposed for CFF conversion.

The seac operator format is: “‘ seac asb adx ady bchar achar “`

Where:

  • ‘asb`: Accent side bearing (not used in decomposition)

  • ‘adx`: X offset for accent placement

  • ‘ady`: Y offset for accent placement

  • ‘bchar`: Character code of base glyph

  • ‘achar`: Character code of accent glyph

Examples:

Decompose a seac composite

expander = Fontisan::Type1::SeacExpander.new(charstrings, private_dict)
decomposed = expander.decompose("Agrave")
# => Returns merged outline of base 'A' + accent 'grave'

See Also:

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(charstrings, private_dict) ⇒ SeacExpander

Initialize a new SeacExpander

Parameters:

  • charstrings (CharStrings)

    Type 1 CharStrings dictionary

  • private_dict (PrivateDict)

    Private dictionary for context



41
42
43
44
# File 'lib/fontisan/type1/seac_expander.rb', line 41

def initialize(charstrings, private_dict)
  @charstrings = charstrings
  @private_dict = private_dict
end

Instance Attribute Details

#charstringsCharStrings (readonly)

Returns Type 1 CharStrings dictionary.

Returns:



32
33
34
# File 'lib/fontisan/type1/seac_expander.rb', line 32

def charstrings
  @charstrings
end

#private_dictPrivateDict (readonly)

Returns Private dictionary for hinting.

Returns:



35
36
37
# File 'lib/fontisan/type1/seac_expander.rb', line 35

def private_dict
  @private_dict
end

Instance Method Details

#composite?(glyph_name) ⇒ Boolean

Check if a glyph is a seac composite

Parameters:

  • glyph_name (String)

    Glyph name to check

Returns:

  • (Boolean)

    True if the glyph uses seac operator



113
114
115
# File 'lib/fontisan/type1/seac_expander.rb', line 113

def composite?(glyph_name)
  @charstrings.composite?(glyph_name)
end

#composite_glyphsArray<String>

Get all seac composite glyphs in the font

Returns:

  • (Array<String>)

    List of composite glyph names



120
121
122
# File 'lib/fontisan/type1/seac_expander.rb', line 120

def composite_glyphs
  @charstrings.glyph_names.select { |name| composite?(name) }
end

#decompose(glyph_name) ⇒ String?

Decompose a seac composite glyph into base + accent outlines

This method:

  1. Parses the seac operator to extract components

  2. Gets CharStrings for base and accent glyphs

  3. Parses both CharStrings into outline commands

  4. Transforms the accent by (adx, ady) offset

  5. Merges base and accent outlines into a single path

  6. Returns the decomposed CharString data

Examples:

Decompose “Agrave” glyph

expander.decompose("Agrave")

Parameters:

  • glyph_name (String)

    Name of the composite glyph to decompose

Returns:

  • (String, nil)

    Decomposed CharString bytecode, or nil if not a seac composite

Raises:



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
# File 'lib/fontisan/type1/seac_expander.rb', line 62

def decompose(glyph_name)
  components = @charstrings.components_for(glyph_name)
  return nil unless components

  # Use the encoding map to lookup glyph names from character codes
  base_glyph_name = @charstrings.encoding[components[:base]]
  accent_glyph_name = @charstrings.encoding[components[:accent]]

  if base_glyph_name.nil?
    raise Fontisan::Error,
          "Base glyph for char code #{components[:base]} not found"
  end

  if accent_glyph_name.nil?
    raise Fontisan::Error,
          "Accent glyph for char code #{components[:accent]} not found"
  end

  # Get CharStrings for base and accent
  base_charstring = @charstrings[base_glyph_name]
  accent_charstring = @charstrings[accent_glyph_name]

  if base_charstring.nil?
    raise Fontisan::Error,
          "CharString not found for base glyph #{base_glyph_name}"
  end

  if accent_charstring.nil?
    raise Fontisan::Error,
          "CharString not found for accent glyph #{accent_glyph_name}"
  end

  # Parse both CharStrings into command sequences
  base_commands = parse_charstring_to_commands(base_charstring)
  accent_commands = parse_charstring_to_commands(accent_charstring)

  # Transform accent by (adx, ady) offset
  accent_commands = transform_commands(accent_commands, components[:adx],
                                       components[:ady])

  # Merge base and accent commands
  merged_commands = merge_outline_commands(base_commands, accent_commands)

  # Convert merged commands back to CharString bytecode
  generate_charstring(merged_commands)
end