Class: Fontisan::Svg::GlyphGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/fontisan/svg/glyph_generator.rb

Overview

Generates SVG glyph elements from glyph outlines

GlyphGenerator converts GlyphOutline objects to SVG <glyph> elements with proper path data and coordinate transformations.

Responsibilities:

  • Transform glyph outline to SVG path with Y-axis flip
  • Generate SVG glyph element with attributes
  • Handle unicode and glyph name mapping
  • Calculate horizontal advance width
  • Format path data with proper precision

This class uses ViewBoxCalculator for coordinate transformations and GlyphOutline's to_svg_path method for path generation.

Examples:

Generate SVG glyph element

generator = GlyphGenerator.new(calculator)
xml = generator.generate_glyph_xml(outline, codepoints: [0x41], advance_width: 600)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(calculator) ⇒ GlyphGenerator

Initialize generator with calculator

Parameters:

Raises:

  • (ArgumentError)

    If calculator is nil



32
33
34
35
36
# File 'lib/fontisan/svg/glyph_generator.rb', line 32

def initialize(calculator)
  raise ArgumentError, "Calculator cannot be nil" if calculator.nil?

  @calculator = calculator
end

Instance Attribute Details

#calculatorViewBoxCalculator (readonly)

Returns Coordinate calculator.

Returns:



26
27
28
# File 'lib/fontisan/svg/glyph_generator.rb', line 26

def calculator
  @calculator
end

Instance Method Details

#generate_glyph_xml(outline, codepoints: [], glyph_name: nil, advance_width: 0, indent: " ") ⇒ String

Generate SVG glyph element

Parameters:

  • outline (Models::GlyphOutline)

    Glyph outline

  • codepoints (Array<Integer>) (defaults to: [])

    Unicode codepoints that cmap maps to this glyph. Empty for unmapped glyphs (.notdef, raw ligatures, etc.). Each codepoint is rendered per SVG Fonts spec: printable ASCII as the character (XML-escaped), others as &#xHEX; entities. Multiple codepoints are concatenated — rare but valid (e.g. a glyph mapped from both space and non-breaking space).

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

    Glyph name from the post table

  • advance_width (Integer) (defaults to: 0)

    Horizontal advance width

  • indent (String) (defaults to: " ")

    Indentation string

Returns:

  • (String)

    XML glyph element



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/fontisan/svg/glyph_generator.rb', line 52

def generate_glyph_xml(outline, codepoints: [], glyph_name: nil,
advance_width: 0, indent: "      ")
  attr_parts = []

  unless codepoints.empty?
    attr_parts << %(unicode="#{format_codepoints(codepoints)}")
  end
  attr_parts << %(glyph-name="#{escape_xml(glyph_name)}") if glyph_name
  attr_parts << %(horiz-adv-x="#{advance_width}") if advance_width&.positive?

  # Generate SVG path with Y-axis transformation
  path_data = generate_svg_path(outline)
  attr_parts << %(d="#{path_data}") if path_data && !path_data.empty?

  "#{indent}<glyph #{attr_parts.join(' ')}/>"
end

#generate_missing_glyph(advance_width: 500, indent: " ") ⇒ String

Generate missing-glyph element

Parameters:

  • advance_width (Integer) (defaults to: 500)

    Default advance width

  • indent (String) (defaults to: " ")

    Indentation string

Returns:

  • (String)

    XML missing-glyph element



74
75
76
# File 'lib/fontisan/svg/glyph_generator.rb', line 74

def generate_missing_glyph(advance_width: 500, indent: "      ")
  "#{indent}<missing-glyph horiz-adv-x=\"#{advance_width}\"/>"
end

#generate_svg_path(outline) ⇒ String

Generate SVG path data with coordinate transformation

Transforms the glyph outline from font space to SVG space by flipping the Y-axis. Font coordinates use Y-up (ascender positive), while SVG uses Y-down (origin at top).

Parameters:

Returns:

  • (String)

    SVG path data



86
87
88
89
90
91
92
93
94
# File 'lib/fontisan/svg/glyph_generator.rb', line 86

def generate_svg_path(outline)
  return "" if outline.empty?

  path_parts = outline.contours.map do |contour|
    build_transformed_contour_path(contour)
  end

  path_parts.join(" ")
end