fontisan 0.2.23 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/lib/fontisan/cli.rb +6 -0
  3. data/lib/fontisan/stitcher/selector/codepoints.rb +29 -0
  4. data/lib/fontisan/stitcher/selector/gid.rb +25 -0
  5. data/lib/fontisan/stitcher/selector/range.rb +30 -0
  6. data/lib/fontisan/stitcher/selector.rb +26 -0
  7. data/lib/fontisan/stitcher/source.rb +97 -0
  8. data/lib/fontisan/stitcher.rb +182 -0
  9. data/lib/fontisan/stitcher_cli.rb +69 -0
  10. data/lib/fontisan/ufo/anchor.rb +17 -0
  11. data/lib/fontisan/ufo/cli.rb +85 -0
  12. data/lib/fontisan/ufo/compile/base_compiler.rb +81 -0
  13. data/lib/fontisan/ufo/compile/cff.rb +224 -0
  14. data/lib/fontisan/ufo/compile/cmap.rb +129 -0
  15. data/lib/fontisan/ufo/compile/filters/cubic_to_quadratic.rb +174 -0
  16. data/lib/fontisan/ufo/compile/filters/decompose_components.rb +33 -0
  17. data/lib/fontisan/ufo/compile/filters/flatten_components.rb +22 -0
  18. data/lib/fontisan/ufo/compile/filters/reverse_contour_direction.rb +27 -0
  19. data/lib/fontisan/ufo/compile/filters.rb +57 -0
  20. data/lib/fontisan/ufo/compile/glyf_loca.rb +145 -0
  21. data/lib/fontisan/ufo/compile/head.rb +98 -0
  22. data/lib/fontisan/ufo/compile/hhea.rb +36 -0
  23. data/lib/fontisan/ufo/compile/hmtx.rb +27 -0
  24. data/lib/fontisan/ufo/compile/maxp.rb +57 -0
  25. data/lib/fontisan/ufo/compile/name.rb +79 -0
  26. data/lib/fontisan/ufo/compile/os2.rb +81 -0
  27. data/lib/fontisan/ufo/compile/otf_compiler.rb +43 -0
  28. data/lib/fontisan/ufo/compile/post.rb +32 -0
  29. data/lib/fontisan/ufo/compile/ttf_compiler.rb +69 -0
  30. data/lib/fontisan/ufo/compile.rb +48 -0
  31. data/lib/fontisan/ufo/component.rb +18 -0
  32. data/lib/fontisan/ufo/contour.rb +29 -0
  33. data/lib/fontisan/ufo/convert/from_bin_data.rb +246 -0
  34. data/lib/fontisan/ufo/convert.rb +18 -0
  35. data/lib/fontisan/ufo/data_set.rb +21 -0
  36. data/lib/fontisan/ufo/features.rb +17 -0
  37. data/lib/fontisan/ufo/font.rb +61 -0
  38. data/lib/fontisan/ufo/glyph.rb +421 -0
  39. data/lib/fontisan/ufo/guideline.rb +19 -0
  40. data/lib/fontisan/ufo/image.rb +16 -0
  41. data/lib/fontisan/ufo/image_set.rb +19 -0
  42. data/lib/fontisan/ufo/info.rb +79 -0
  43. data/lib/fontisan/ufo/kerning.rb +32 -0
  44. data/lib/fontisan/ufo/layer.rb +38 -0
  45. data/lib/fontisan/ufo/layer_set.rb +37 -0
  46. data/lib/fontisan/ufo/lib.rb +24 -0
  47. data/lib/fontisan/ufo/plist.rb +118 -0
  48. data/lib/fontisan/ufo/point.rb +38 -0
  49. data/lib/fontisan/ufo/reader.rb +144 -0
  50. data/lib/fontisan/ufo/transformation.rb +39 -0
  51. data/lib/fontisan/ufo/writer.rb +115 -0
  52. data/lib/fontisan/ufo.rb +44 -0
  53. data/lib/fontisan/version.rb +1 -1
  54. data/lib/fontisan.rb +3 -0
  55. metadata +51 -1
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+
5
+ module Fontisan
6
+ module Ufo
7
+ # Minimal XML plist parser/serializer for the files that UFO v3
8
+ # uses. UFO uses XML plists exclusively; binary plists are not
9
+ # used in the spec.
10
+ #
11
+ # The parser is typed at the value level: strings stay as String,
12
+ # integers stay as Integer, booleans become true/false, dicts
13
+ # become Hash<String, Object>, arrays become Array<Object>.
14
+ # Dates, data blobs, and nested structures are supported.
15
+ module Plist
16
+ class ParseError < StandardError; end
17
+
18
+ PLIST_DTD = "-//Apple//DTD PLIST 1.0//EN"
19
+ PLIST_NS = "http://www.apple.com/DTDs/PropertyList-1.0.dtd"
20
+
21
+ # @param source [String, Nokogiri::XML::Document] the XML plist
22
+ # @return [Object] the deserialized value
23
+ def self.parse(source)
24
+ doc = source.is_a?(Nokogiri::XML::Document) ? source : Nokogiri::XML(source)
25
+ root = doc.root
26
+ raise ParseError, "no <plist> root element" unless root&.name == "plist"
27
+
28
+ parse_value(root.children.find { |c| c.element? || c.cdata? || c.text? && !c.text.strip.empty? })
29
+ end
30
+
31
+ # @param value [Object] the value to serialize
32
+ # @return [String] the XML plist text
33
+ def self.emit(value)
34
+ builder = Nokogiri::XML::Builder.new(encoding: "UTF-8") do |xml|
35
+ create_doc_internal_subset(xml)
36
+ xml.plist(version: "1.0") do
37
+ emit_value(xml, value)
38
+ end
39
+ end
40
+ builder.to_xml(
41
+ save_with:
42
+ Nokogiri::XML::Node::SaveOptions::AS_XML |
43
+ Nokogiri::XML::Node::SaveOptions::NO_DECLARATION,
44
+ )
45
+ end
46
+
47
+ # @return [Nokogiri::XML::Document] the typed root
48
+ def self.parse_document(source)
49
+ source.is_a?(Nokogiri::XML::Document) ? source : Nokogiri::XML(source)
50
+ end
51
+
52
+ # ---------- private-ish (called via module_function) ----------
53
+
54
+ def self.parse_value(node)
55
+ return nil if node.nil?
56
+
57
+ case node.name
58
+ when "string" then node.text
59
+ when "integer" then node.text.to_i
60
+ when "real" then node.text.to_f
61
+ when "true" then true
62
+ when "false" then false
63
+ when "dict" then parse_dict(node)
64
+ when "array" then parse_array(node)
65
+ else
66
+ raise ParseError, "unsupported plist element: <#{node.name}>"
67
+ end
68
+ end
69
+
70
+ def self.parse_dict(node)
71
+ result = {}
72
+ children = node.element_children.to_a
73
+ # dict children are key/value pairs (key first, then value)
74
+ while children.any?
75
+ key_node = children.shift
76
+ raise ParseError, "dict key is not <key>" unless key_node.name == "key"
77
+
78
+ value_node = children.shift
79
+ result[key_node.text] = parse_value(value_node)
80
+ end
81
+ result
82
+ end
83
+
84
+ def self.parse_array(node)
85
+ node.element_children.map { |child| parse_value(child) }
86
+ end
87
+
88
+ def self.create_doc_internal_subset(xml)
89
+ xml.doc.create_internal_subset("plist", PLIST_DTD, PLIST_NS)
90
+ end
91
+
92
+ def self.emit_value(xml, value)
93
+ case value
94
+ when nil
95
+ # No <nil/> in Apple's plist DTD; use a sentinel string.
96
+ xml.string("")
97
+ when true then xml.__send__(true)
98
+ when false then xml.__send__(false)
99
+ when Integer then xml.integer(value)
100
+ when Float then xml.real(value)
101
+ when String then xml.string(value)
102
+ when Symbol then xml.string(value.to_s)
103
+ when Array then xml.array { value.each { |v| emit_value(xml, v) } }
104
+ when Hash then xml.dict { emit_dict_body(xml, value) }
105
+ else
106
+ raise ArgumentError, "cannot plist-encode #{value.class}"
107
+ end
108
+ end
109
+
110
+ def self.emit_dict_body(xml, hash)
111
+ hash.each do |k, v|
112
+ xml.key(k.to_s)
113
+ emit_value(xml, v)
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fontisan
4
+ module Ufo
5
+ # A single outline point. UFO's `point` element has:
6
+ # - x, y (Float, font units)
7
+ # - type (one of "move", "line", "offcurve", "curve", "qcurve")
8
+ # - smooth (Bool, optional)
9
+ #
10
+ # "offcurve" is the UFO 1/2 name; UFO 3 uses "qcurve". Both are
11
+ # accepted on read.
12
+ class Point
13
+ attr_reader :x, :y, :type, :smooth
14
+
15
+ def initialize(x:, y:, type:, smooth: false)
16
+ @x = x
17
+ @y = y
18
+ @type = type.to_s
19
+ @smooth = smooth
20
+ end
21
+
22
+ def on_curve?
23
+ @type == "line" || @type == "move" || @type == "curve"
24
+ end
25
+
26
+ def off_curve?
27
+ @type == "offcurve" || @type == "qcurve"
28
+ end
29
+
30
+ # @return [Hash] suitable for `to_glif` output
31
+ def to_h
32
+ h = { x: @x, y: @y, type: @type }
33
+ h[:smooth] = true if @smooth
34
+ h
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fontisan
4
+ module Ufo
5
+ # Reads a UFO source directory into a typed Fontisan::Ufo::Font.
6
+ #
7
+ # Reads, in order:
8
+ # - `metainfo.plist` (UFO version stamp; informational)
9
+ # - `fontinfo.plist` (Info)
10
+ # - `layercontents.plist` (layer ordering; optional in UFO 3)
11
+ # - `glyphs/contents.plist` (default-layer glyph order)
12
+ # - `glyphs/<layer>/<name>.glif` (per-glyph XML)
13
+ # - `kerning.plist` (Kerning)
14
+ # - `features.fea` (Features)
15
+ # - `lib.plist` (Lib)
16
+ #
17
+ # Phase 1 reads `fontinfo.plist` + `glyphs/contents.plist` +
18
+ # minimal `.glif` (name, advance, unicodes). Full `.glif`
19
+ # decoding (contours, components, anchors) lands when the
20
+ # compiler layer is built.
21
+ class Reader
22
+ attr_reader :font
23
+
24
+ def initialize(font)
25
+ @font = font
26
+ end
27
+
28
+ # @return [Fontisan::Ufo::Font]
29
+ def read
30
+ read_metainfo
31
+ read_fontinfo
32
+ read_layercontents
33
+ read_glyphs_contents
34
+ read_kerning
35
+ read_features
36
+ read_lib
37
+ @font
38
+ end
39
+
40
+ private
41
+
42
+ def read_metainfo
43
+ path = join(@font.path, "metainfo.plist")
44
+ return unless File.exist?(path)
45
+
46
+ data = Plist.parse(File.read(path))
47
+ @font.ufo_version = data["formatVersion"]
48
+ end
49
+
50
+ def read_fontinfo
51
+ path = join(@font.path, "fontinfo.plist")
52
+ return unless File.exist?(path)
53
+
54
+ data = Plist.parse(File.read(path))
55
+ @font.info = Info.new(data)
56
+ end
57
+
58
+ def read_layercontents
59
+ # layercontents.plist is optional in UFO 3; default layer is
60
+ # always present via LayerSet#initialize.
61
+ path = join(@font.path, "layercontents.plist")
62
+ return unless File.exist?(path)
63
+
64
+ order = Plist.parse(File.read(path))
65
+ order.each do |layer_name|
66
+ @font.layers.add(layer_name)
67
+ end
68
+ end
69
+
70
+ def read_glyphs_contents
71
+ # Each layer's glyphs may live under `glyphs/<layer_name>/` (UFO 3)
72
+ # or directly under `glyphs/` (UFO 2, single-layer case).
73
+ # The latter applies when `glyphs/<layer_name>/` does not exist
74
+ # but `glyphs/contents.plist` does.
75
+ @font.layers.each do |layer|
76
+ subdir = join(@font.path, "glyphs", layer.name)
77
+ contents_path = if Dir.exist?(subdir)
78
+ join(subdir, "contents.plist")
79
+ else
80
+ join(@font.path, "glyphs", "contents.plist")
81
+ end
82
+ next unless File.exist?(contents_path)
83
+
84
+ order = Plist.parse(File.read(contents_path))
85
+
86
+ # UFO 2 contents.plist is Hash<glyph_name, glif_filename>.
87
+ # UFO 3 contents.plist can be Array<glif_filename> or
88
+ # Array<Hash<glif_filename, glyph_name>>. Normalize to
89
+ # Hash<glyph_name, glif_filename>.
90
+ entries =
91
+ case order
92
+ when Hash then order
93
+ when Array
94
+ if order.first.is_a?(Hash)
95
+ order.reduce({}) { |h, pair| h.merge(pair) }
96
+ else
97
+ order.to_h { |filename| [File.basename(filename, ".glif"), filename] }
98
+ end
99
+ else
100
+ raise "unsupported contents.plist format: #{order.class}"
101
+ end
102
+
103
+ entries.each_value do |glif_filename|
104
+ glif_path = if Dir.exist?(subdir)
105
+ join(subdir, glif_filename)
106
+ else
107
+ join(@font.path, "glyphs", glif_filename)
108
+ end
109
+ next unless File.exist?(glif_path)
110
+
111
+ layer.add(Glyph.from_glif(File.read(glif_path)))
112
+ end
113
+ end
114
+ end
115
+
116
+ def read_kerning
117
+ path = join(@font.path, "kerning.plist")
118
+ return unless File.exist?(path)
119
+
120
+ data = Plist.parse(File.read(path))
121
+ @font.kerning = Kerning.new(data)
122
+ end
123
+
124
+ def read_features
125
+ path = join(@font.path, "features.fea")
126
+ return unless File.exist?(path)
127
+
128
+ @font.features = Features.new(text: File.read(path))
129
+ end
130
+
131
+ def read_lib
132
+ path = join(@font.path, "lib.plist")
133
+ return unless File.exist?(path)
134
+
135
+ data = Plist.parse(File.read(path))
136
+ @font.lib = Lib.new(data)
137
+ end
138
+
139
+ def join(*parts)
140
+ File.join(*parts)
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fontisan
4
+ module Ufo
5
+ # 2×3 transformation matrix, as used in UFO 3 for component and
6
+ # image placement. The matrix is laid out in the standard affine
7
+ # row-major order: `[a b c d e f]` represents
8
+ #
9
+ # | a c e |
10
+ # | b d f |
11
+ # | 0 0 1 |
12
+ #
13
+ # (UFO and OpenType both use the row-major convention despite the
14
+ # geometrically column-major appearance — this is how the
15
+ # `transformation` element reads in .glif XML.)
16
+ class Transformation
17
+ IDENTITY_MATRIX = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0].freeze
18
+
19
+ attr_reader :a, :b, :c, :d, :e, :f
20
+
21
+ def initialize(a: 1.0, b: 0.0, c: 0.0, d: 1.0, e: 0.0, f: 0.0)
22
+ @a = a.to_f
23
+ @b = b.to_f
24
+ @c = c.to_f
25
+ @d = d.to_f
26
+ @e = e.to_f
27
+ @f = f.to_f
28
+ end
29
+
30
+ def identity?
31
+ [a, b, c, d, e, f] == IDENTITY_MATRIX
32
+ end
33
+
34
+ def to_a
35
+ [a, b, c, d, e, f]
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fontisan
4
+ module Ufo
5
+ # Writes a UFO source directory from a typed Fontisan::Ufo::Font.
6
+ #
7
+ # Mirror of Reader. Writes, in order:
8
+ # - `metainfo.plist` (UFO version stamp)
9
+ # - `fontinfo.plist` (Info)
10
+ # - `layercontents.plist` (UFO 3 only; layer ordering)
11
+ # - `glyphs/contents.plist` (default-layer glyph order)
12
+ # - `glyphs/<name>.glif` (per-glyph XML)
13
+ # - `glyphs/<layer>/...` (additional layers)
14
+ # - `kerning.plist`
15
+ # - `features.fea`
16
+ # - `lib.plist`
17
+ class Writer
18
+ UFO_VERSION_DEFAULT = 3
19
+
20
+ attr_reader :font
21
+
22
+ def initialize(font)
23
+ @font = font
24
+ end
25
+
26
+ # @param path [String] directory to write into; created if missing
27
+ # @param ufo_version [Integer] 2 or 3 (default 3)
28
+ def write(path, ufo_version: nil)
29
+ @ufo_version = ufo_version || @font.ufo_version || UFO_VERSION_DEFAULT
30
+
31
+ FileUtils.mkpath(path)
32
+ write_metainfo(path)
33
+ write_fontinfo(path)
34
+ write_layercontents(path)
35
+ write_glyphs(path)
36
+ write_kerning(path)
37
+ write_features(path)
38
+ write_lib(path)
39
+ path
40
+ end
41
+
42
+ private
43
+
44
+ def write_metainfo(path)
45
+ data = {
46
+ "creator" => "org.fontisan.ufo",
47
+ "formatVersion" => @ufo_version.to_i,
48
+ }
49
+ File.write(File.join(path, "metainfo.plist"), Plist.emit(data))
50
+ end
51
+
52
+ def write_fontinfo(path)
53
+ File.write(File.join(path, "fontinfo.plist"), Plist.emit(@font.info.to_plist))
54
+ end
55
+
56
+ def write_layercontents(path)
57
+ return unless @ufo_version >= 3
58
+ return if @font.layers.size <= 1
59
+
60
+ order = @font.layers.layers.keys
61
+ File.write(File.join(path, "layercontents.plist"), Plist.emit(order))
62
+ end
63
+
64
+ def write_glyphs(path)
65
+ # Default layer writes glyphs/contents.plist + glyphs/*.glif.
66
+ # Additional layers write glyphs/<layer>/contents.plist + .glif.
67
+ @font.layers.each do |layer|
68
+ layer_dir = if layer.name == Layer::DEFAULT_NAME && @ufo_version < 3
69
+ File.join(path, "glyphs")
70
+ elsif layer.name == Layer::DEFAULT_NAME
71
+ File.join(path, "glyphs")
72
+ else
73
+ File.join(path, "glyphs", layer.name)
74
+ end
75
+ FileUtils.mkpath(layer_dir)
76
+
77
+ contents =
78
+ layer.glyphs.transform_values { |g| "#{safe_filename(g.name)}.glif" }
79
+ File.write(File.join(layer_dir, "contents.plist"), Plist.emit(contents))
80
+
81
+ layer.glyphs.each_value do |glyph|
82
+ File.write(File.join(layer_dir, "#{safe_filename(glyph.name)}.glif"), glyph.to_glif)
83
+ end
84
+ end
85
+ end
86
+
87
+ # Map a glyph name to a filesystem-safe filename. UFO conventions
88
+ # use a base-prefixed name for names starting with non-letter.
89
+ def safe_filename(name)
90
+ return "_#{name}" if name.start_with?(".")
91
+ return "_#{name}" unless name.match?(/\A[A-Za-z_]/)
92
+
93
+ name
94
+ end
95
+
96
+ def write_kerning(path)
97
+ return if @font.kerning.empty?
98
+
99
+ File.write(File.join(path, "kerning.plist"), Plist.emit(@font.kerning.to_plist))
100
+ end
101
+
102
+ def write_features(path)
103
+ return if @font.features.text.nil? || @font.features.text.empty?
104
+
105
+ File.write(File.join(path, "features.fea"), @font.features.text)
106
+ end
107
+
108
+ def write_lib(path)
109
+ return if @font.lib.data.empty?
110
+
111
+ File.write(File.join(path, "lib.plist"), Plist.emit(@font.lib.data))
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fontisan
4
+ # Unified Font Object (UFO) v3 model + compile + convert pipeline.
5
+ #
6
+ # A UFO is a directory-based font source format. Each UFO is a
7
+ # human-readable, human-editable collection of plists + GLIF XML
8
+ # files. fontisan can:
9
+ #
10
+ # - Read any UFO source into a typed model (this namespace).
11
+ # - Edit the model programmatically.
12
+ # - Compile to a binary font (TTF, OTF, …) via
13
+ # Fontisan::Ufo::Compile::*
14
+ # - Convert any binary font fontisan can read INTO a UFO source.
15
+ # - Stitch glyphs from multiple sources into a new font via
16
+ # Fontisan::Stitcher.
17
+ #
18
+ # Reference: https://unifiedfontobject.org
19
+ module Ufo
20
+ autoload :Font, "fontisan/ufo/font"
21
+ autoload :Info, "fontisan/ufo/info"
22
+ autoload :Layer, "fontisan/ufo/layer"
23
+ autoload :LayerSet, "fontisan/ufo/layer_set"
24
+ autoload :Glyph, "fontisan/ufo/glyph"
25
+ autoload :Contour, "fontisan/ufo/contour"
26
+ autoload :Point, "fontisan/ufo/point"
27
+ autoload :Component, "fontisan/ufo/component"
28
+ autoload :Anchor, "fontisan/ufo/anchor"
29
+ autoload :Guideline, "fontisan/ufo/guideline"
30
+ autoload :Image, "fontisan/ufo/image"
31
+ autoload :Transformation, "fontisan/ufo/transformation"
32
+ autoload :Kerning, "fontisan/ufo/kerning"
33
+ autoload :Features, "fontisan/ufo/features"
34
+ autoload :Lib, "fontisan/ufo/lib"
35
+ autoload :DataSet, "fontisan/ufo/data_set"
36
+ autoload :ImageSet, "fontisan/ufo/image_set"
37
+ autoload :Plist, "fontisan/ufo/plist"
38
+ autoload :Reader, "fontisan/ufo/reader"
39
+ autoload :Writer, "fontisan/ufo/writer"
40
+ autoload :Compile, "fontisan/ufo/compile"
41
+ autoload :Convert, "fontisan/ufo/convert"
42
+ autoload :Cli, "fontisan/ufo/cli"
43
+ end
44
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fontisan
4
- VERSION = "0.2.23"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/fontisan.rb CHANGED
@@ -88,6 +88,7 @@ module Fontisan
88
88
  autoload :Svg, "fontisan/svg"
89
89
  autoload :Tables, "fontisan/tables"
90
90
  autoload :Type1, "fontisan/type1"
91
+ autoload :Ufo, "fontisan/ufo"
91
92
  autoload :Utilities, "fontisan/utilities"
92
93
  autoload :Utils, "fontisan/utils"
93
94
  autoload :Validation, "fontisan/validation"
@@ -110,6 +111,8 @@ module Fontisan
110
111
  autoload :OutlineExtractor, "fontisan/outline_extractor"
111
112
  autoload :SfntFont, "fontisan/sfnt_font"
112
113
  autoload :SfntTable, "fontisan/sfnt_table"
114
+ autoload :Stitcher, "fontisan/stitcher"
115
+ autoload :StitcherCli, "fontisan/stitcher_cli"
113
116
  autoload :TrueTypeCollection, "fontisan/true_type_collection"
114
117
  autoload :TrueTypeFont, "fontisan/true_type_font"
115
118
  autoload :TrueTypeFontExtensions, "fontisan/true_type_font_extensions"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fontisan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.23
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -435,6 +435,13 @@ files:
435
435
  - lib/fontisan/pipeline/variation_resolver.rb
436
436
  - lib/fontisan/sfnt_font.rb
437
437
  - lib/fontisan/sfnt_table.rb
438
+ - lib/fontisan/stitcher.rb
439
+ - lib/fontisan/stitcher/selector.rb
440
+ - lib/fontisan/stitcher/selector/codepoints.rb
441
+ - lib/fontisan/stitcher/selector/gid.rb
442
+ - lib/fontisan/stitcher/selector/range.rb
443
+ - lib/fontisan/stitcher/source.rb
444
+ - lib/fontisan/stitcher_cli.rb
438
445
  - lib/fontisan/subset.rb
439
446
  - lib/fontisan/subset/builder.rb
440
447
  - lib/fontisan/subset/glyph_mapping.rb
@@ -544,6 +551,49 @@ files:
544
551
  - lib/fontisan/type1/ttf_to_type1_converter.rb
545
552
  - lib/fontisan/type1/upm_scaler.rb
546
553
  - lib/fontisan/type1_font.rb
554
+ - lib/fontisan/ufo.rb
555
+ - lib/fontisan/ufo/anchor.rb
556
+ - lib/fontisan/ufo/cli.rb
557
+ - lib/fontisan/ufo/compile.rb
558
+ - lib/fontisan/ufo/compile/base_compiler.rb
559
+ - lib/fontisan/ufo/compile/cff.rb
560
+ - lib/fontisan/ufo/compile/cmap.rb
561
+ - lib/fontisan/ufo/compile/filters.rb
562
+ - lib/fontisan/ufo/compile/filters/cubic_to_quadratic.rb
563
+ - lib/fontisan/ufo/compile/filters/decompose_components.rb
564
+ - lib/fontisan/ufo/compile/filters/flatten_components.rb
565
+ - lib/fontisan/ufo/compile/filters/reverse_contour_direction.rb
566
+ - lib/fontisan/ufo/compile/glyf_loca.rb
567
+ - lib/fontisan/ufo/compile/head.rb
568
+ - lib/fontisan/ufo/compile/hhea.rb
569
+ - lib/fontisan/ufo/compile/hmtx.rb
570
+ - lib/fontisan/ufo/compile/maxp.rb
571
+ - lib/fontisan/ufo/compile/name.rb
572
+ - lib/fontisan/ufo/compile/os2.rb
573
+ - lib/fontisan/ufo/compile/otf_compiler.rb
574
+ - lib/fontisan/ufo/compile/post.rb
575
+ - lib/fontisan/ufo/compile/ttf_compiler.rb
576
+ - lib/fontisan/ufo/component.rb
577
+ - lib/fontisan/ufo/contour.rb
578
+ - lib/fontisan/ufo/convert.rb
579
+ - lib/fontisan/ufo/convert/from_bin_data.rb
580
+ - lib/fontisan/ufo/data_set.rb
581
+ - lib/fontisan/ufo/features.rb
582
+ - lib/fontisan/ufo/font.rb
583
+ - lib/fontisan/ufo/glyph.rb
584
+ - lib/fontisan/ufo/guideline.rb
585
+ - lib/fontisan/ufo/image.rb
586
+ - lib/fontisan/ufo/image_set.rb
587
+ - lib/fontisan/ufo/info.rb
588
+ - lib/fontisan/ufo/kerning.rb
589
+ - lib/fontisan/ufo/layer.rb
590
+ - lib/fontisan/ufo/layer_set.rb
591
+ - lib/fontisan/ufo/lib.rb
592
+ - lib/fontisan/ufo/plist.rb
593
+ - lib/fontisan/ufo/point.rb
594
+ - lib/fontisan/ufo/reader.rb
595
+ - lib/fontisan/ufo/transformation.rb
596
+ - lib/fontisan/ufo/writer.rb
547
597
  - lib/fontisan/utilities.rb
548
598
  - lib/fontisan/utilities/brotli_wrapper.rb
549
599
  - lib/fontisan/utilities/checksum_calculator.rb