Class: Fontisan::Ufo::Glyph

Inherits:
Object
  • Object
show all
Defined in:
lib/fontisan/ufo/glyph.rb

Overview

A glyph in a UFO source. Holds contours, components, anchors, guidelines, images, unicode codepoints, advance width/height, and a custom-data bag (lib).

The .glif XML format has multiple revisions (1, 2, 3); the parser accepts all of them.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name:) ⇒ Glyph

Returns a new instance of Glyph.



18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/fontisan/ufo/glyph.rb', line 18

def initialize(name:)
  @name = name.to_s
  @unicodes = []
  @width = 0.0
  @height = 0.0
  @contours = []
  @components = []
  @anchors = []
  @guidelines = []
  @images = []
  @note = nil
  @lib = Lib.new
end

Instance Attribute Details

#anchorsObject (readonly)

Returns the value of attribute anchors.



15
16
17
# File 'lib/fontisan/ufo/glyph.rb', line 15

def anchors
  @anchors
end

#componentsObject (readonly)

Returns the value of attribute components.



15
16
17
# File 'lib/fontisan/ufo/glyph.rb', line 15

def components
  @components
end

#contoursObject (readonly)

Returns the value of attribute contours.



15
16
17
# File 'lib/fontisan/ufo/glyph.rb', line 15

def contours
  @contours
end

#guidelinesObject (readonly)

Returns the value of attribute guidelines.



15
16
17
# File 'lib/fontisan/ufo/glyph.rb', line 15

def guidelines
  @guidelines
end

#heightObject

Returns the value of attribute height.



14
15
16
# File 'lib/fontisan/ufo/glyph.rb', line 14

def height
  @height
end

#imagesObject (readonly)

Returns the value of attribute images.



15
16
17
# File 'lib/fontisan/ufo/glyph.rb', line 15

def images
  @images
end

#libObject

Returns the value of attribute lib.



14
15
16
# File 'lib/fontisan/ufo/glyph.rb', line 14

def lib
  @lib
end

#nameObject (readonly)

Returns the value of attribute name.



15
16
17
# File 'lib/fontisan/ufo/glyph.rb', line 15

def name
  @name
end

#noteObject

Returns the value of attribute note.



14
15
16
# File 'lib/fontisan/ufo/glyph.rb', line 14

def note
  @note
end

#unicodesObject (readonly)

Returns the value of attribute unicodes.



15
16
17
# File 'lib/fontisan/ufo/glyph.rb', line 15

def unicodes
  @unicodes
end

#widthObject

Returns the value of attribute width.



14
15
16
# File 'lib/fontisan/ufo/glyph.rb', line 14

def width
  @width
end

Class Method Details

.from_glif(xml) ⇒ Glyph

Returns parsed glyph.

Parameters:

  • xml (String)

    the .glif XML body

Returns:

  • (Glyph)

    parsed glyph



88
89
90
91
92
# File 'lib/fontisan/ufo/glyph.rb', line 88

def self.from_glif(xml)
  doc = Nokogiri::XML(xml)
  root = doc.root
  new(name: root["name"]).tap { |g| g.read_from(root) }
end

Instance Method Details

#add_anchor(anchor) ⇒ Object



46
47
48
49
# File 'lib/fontisan/ufo/glyph.rb', line 46

def add_anchor(anchor)
  @anchors << anchor
  anchor
end

#add_component(component) ⇒ Object



41
42
43
44
# File 'lib/fontisan/ufo/glyph.rb', line 41

def add_component(component)
  @components << component
  component
end

#add_contour(contour) ⇒ Object



36
37
38
39
# File 'lib/fontisan/ufo/glyph.rb', line 36

def add_contour(contour)
  @contours << contour
  contour
end

#add_guideline(guideline) ⇒ Object



51
52
53
54
# File 'lib/fontisan/ufo/glyph.rb', line 51

def add_guideline(guideline)
  @guidelines << guideline
  guideline
end

#add_image(image) ⇒ Object



56
57
58
59
# File 'lib/fontisan/ufo/glyph.rb', line 56

def add_image(image)
  @images << image
  image
end

#add_unicode(codepoint) ⇒ Object



32
33
34
# File 'lib/fontisan/ufo/glyph.rb', line 32

def add_unicode(codepoint)
  @unicodes << codepoint.to_i
end

#bboxBoundingBox?

Returns axis-aligned bbox of contours.

Returns:

  • (BoundingBox, nil)

    axis-aligned bbox of contours.



72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/fontisan/ufo/glyph.rb', line 72

def bbox
  return nil if @contours.empty?

  points = @contours.flat_map(&:points)
  return nil if points.empty?

  BoundingBox.new(
    x_min: points.map(&:x).min,
    y_min: points.map(&:y).min,
    x_max: points.map(&:x).max,
    y_max: points.map(&:y).max,
  )
end

#composite?Boolean

Composite glyphs reference other glyphs via components.

Returns:

  • (Boolean)


62
63
64
# File 'lib/fontisan/ufo/glyph.rb', line 62

def composite?
  !@components.empty?
end

#point_countObject

Total number of points across all contours.



67
68
69
# File 'lib/fontisan/ufo/glyph.rb', line 67

def point_count
  @contours.sum(&:point_count)
end

#read_from(root) ⇒ Object

Populate this glyph from a Nokogiri node.



110
111
112
113
114
115
116
117
118
119
# File 'lib/fontisan/ufo/glyph.rb', line 110

def read_from(root)
  read_advance(root)
  read_unicodes(root)
  read_outline(root)
  read_anchors(root)
  read_guidelines(root)
  read_image(root)
  read_lib(root)
  read_note(root)
end

#to_glifString

Render this glyph back to .glif XML.

Returns:

  • (String)

    XML text



96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/fontisan/ufo/glyph.rb', line 96

def to_glif
  builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
    xml.glyph(name: @name, format: "2") do
      emit_advance(xml)
      emit_unicodes(xml)
      emit_outline(xml)
      emit_lib(xml)
      emit_note(xml)
    end
  end
  builder.to_xml
end

#to_outlineFontisan::Models::Outline

Convert this glyph to a fontisan Models::Outline for use by the CFF charstring builder.

UFO contours use cubic Bezier curves ("curve" type with two off-curve controls). Single off-curves are interpreted as quadratic and degree-elevated to cubic.



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/fontisan/ufo/glyph.rb', line 129

def to_outline
  commands = []
  bbox_hash = { x_min: 0, y_min: 0, x_max: 0, y_max: 0 }

  @contours.each do |contour|
    next if contour.points.empty?

    points = contour.points
    commands << { type: :move_to, x: points.first.x, y: points.first.y }

    i = 1
    while i < points.size
      pt = points[i]
      if pt.on_curve?
        commands << { type: :line_to, x: pt.x, y: pt.y }
        i += 1
      elsif i + 2 < points.size &&
          !points[i + 1].on_curve? && points[i + 2].on_curve?
        commands << {
          type: :curve_to,
          cx1: points[i].x, cy1: points[i].y,
          cx2: points[i + 1].x, cy2: points[i + 1].y,
          x: points[i + 2].x, y: points[i + 2].y
        }
        i += 3
      elsif i + 1 < points.size && points[i + 1].on_curve?
        prev = points[i - 1]
        nxt = points[i + 1]
        cx1 = prev.x + (2.0 / 3.0) * (pt.x - prev.x)
        cy1 = prev.y + (2.0 / 3.0) * (pt.y - prev.y)
        cx2 = nxt.x + (2.0 / 3.0) * (pt.x - nxt.x)
        cy2 = nxt.y + (2.0 / 3.0) * (pt.y - nxt.y)
        commands << {
          type: :curve_to,
          cx1: cx1, cy1: cy1, cx2: cx2, cy2: cy2,
          x: nxt.x, y: nxt.y
        }
        i += 2
      else
        i += 1
      end
    end

    commands << { type: :close_path }
  end

  bb = bbox
  if bb
    bbox_hash = {
      x_min: bb.x_min.to_i, y_min: bb.y_min.to_i,
      x_max: bb.x_max.to_i, y_max: bb.y_max.to_i
    }
  end

  Fontisan::Models::Outline.new(
    glyph_id: 0,
    commands: commands,
    bbox: bbox_hash,
    width: @width.to_i,
  )
end