fontico 0.1.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.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +214 -0
- data/docs/expand-check.png +0 -0
- data/docs/icon-authoring.html +371 -0
- data/docs/lucide-glyphs-extracted.png +0 -0
- data/docs/lucide-ttf-glyph.png +0 -0
- data/docs/rails-preview.png +0 -0
- data/docs/sprite-sheet.png +0 -0
- data/docs/stroke-vs-fill.png +0 -0
- data/docs/ttf-glyphs.png +0 -0
- data/lib/fontico/builder.rb +115 -0
- data/lib/fontico/emitters/font.rb +62 -0
- data/lib/fontico/emitters/sprite.rb +30 -0
- data/lib/fontico/emitters/stylesheet.rb +45 -0
- data/lib/fontico/helper.rb +105 -0
- data/lib/fontico/icon.rb +15 -0
- data/lib/fontico/lockfile.rb +87 -0
- data/lib/fontico/manifest.rb +75 -0
- data/lib/fontico/node/build_font.mjs +52 -0
- data/lib/fontico/node/extract_glyphs.mjs +35 -0
- data/lib/fontico/node/package.json +11 -0
- data/lib/fontico/node_runner.rb +66 -0
- data/lib/fontico/outliner.rb +80 -0
- data/lib/fontico/prawn.rb +62 -0
- data/lib/fontico/preprocessor.rb +238 -0
- data/lib/fontico/provider_fonts.rb +66 -0
- data/lib/fontico/railtie.rb +54 -0
- data/lib/fontico/resolver.rb +148 -0
- data/lib/fontico/version.rb +5 -0
- data/lib/fontico.rb +100 -0
- data/lib/tasks/fontico.rake +69 -0
- metadata +132 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "uri"
|
|
5
|
+
require "json"
|
|
6
|
+
|
|
7
|
+
module Fontico
|
|
8
|
+
# Turns manifest entries into raw SVG. Vendor icons come from the Iconify
|
|
9
|
+
# API in one batched request per provider; first-party icons come off disk.
|
|
10
|
+
class Resolver
|
|
11
|
+
class Error < Fontico::Error; end
|
|
12
|
+
|
|
13
|
+
API = "https://api.iconify.design"
|
|
14
|
+
|
|
15
|
+
# Guards a malformed set; real chains are one or two hops.
|
|
16
|
+
MAX_ALIAS_DEPTH = 8
|
|
17
|
+
|
|
18
|
+
Source = Struct.new(:markup, :width, :height, keyword_init: true)
|
|
19
|
+
|
|
20
|
+
# { "save" => "lucide/save: not found in provider lucide" } — icons this
|
|
21
|
+
# run could not resolve. Filled by #call; see the comment there.
|
|
22
|
+
attr_reader :missing
|
|
23
|
+
|
|
24
|
+
def initialize(manifest, root: Dir.pwd, api: API)
|
|
25
|
+
@manifest = manifest
|
|
26
|
+
@root = root
|
|
27
|
+
@api = api
|
|
28
|
+
@missing = {}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# => { "save" => Source, ... } keyed by icon name.
|
|
32
|
+
#
|
|
33
|
+
# One bad name in a manifest of two hundred used to take the whole build
|
|
34
|
+
# down, which meant a typo in an icon nobody had shipped yet blocked
|
|
35
|
+
# everyone. A single icon failing is now recorded in #missing and left
|
|
36
|
+
# out of the result; the caller reports it and builds the rest. Failures
|
|
37
|
+
# that are not per-icon — an unreachable API, a provider that 404s — are
|
|
38
|
+
# still raised, because then nothing would be correct.
|
|
39
|
+
def call(only: nil)
|
|
40
|
+
icons = @manifest.icons
|
|
41
|
+
icons = icons.select { only.include?(_1.name) } if only
|
|
42
|
+
|
|
43
|
+
resolved = {}
|
|
44
|
+
icons.select(&:local?).each do |icon|
|
|
45
|
+
try(icon) { resolved[icon.name] = local(icon) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
icons.reject(&:local?).group_by(&:provider).each do |provider, group|
|
|
49
|
+
payload = fetch(provider, group.map(&:slug).uniq.sort)
|
|
50
|
+
group.each { |icon| try(icon) { resolved[icon.name] = remote(icon, payload) } }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
resolved
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def try(icon)
|
|
59
|
+
yield
|
|
60
|
+
rescue Error => e
|
|
61
|
+
@missing[icon.name] = e.message
|
|
62
|
+
nil
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def local(icon)
|
|
66
|
+
path = File.join(@root, @manifest.local_path, "#{icon.slug}.svg")
|
|
67
|
+
raise Error, "#{icon.source}: no such file #{path}" unless File.exist?(path)
|
|
68
|
+
|
|
69
|
+
Source.new(markup: File.read(path))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# One request per provider, not per icon. 30 icons across two providers
|
|
73
|
+
# measured at ~0.9s and 7KB total.
|
|
74
|
+
def fetch(provider, slugs)
|
|
75
|
+
uri = URI("#{@api}/#{provider}.json?icons=#{slugs.join(",")}")
|
|
76
|
+
body = Net::HTTP.get_response(uri).then do |res|
|
|
77
|
+
raise Error, "#{provider}: API returned #{res.code}" unless res.is_a?(Net::HTTPSuccess)
|
|
78
|
+
|
|
79
|
+
res.body
|
|
80
|
+
end
|
|
81
|
+
JSON.parse(body)
|
|
82
|
+
rescue JSON::ParserError, SocketError, Errno::ECONNREFUSED => e
|
|
83
|
+
raise Error, "#{provider}: could not reach #{@api} (#{e.class}). " \
|
|
84
|
+
"Run with a populated icons.lock to build offline."
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def remote(icon, payload)
|
|
88
|
+
data, transform = lookup(icon, payload)
|
|
89
|
+
raise Error, "#{icon.source}: not found in provider #{icon.provider}" if data.nil?
|
|
90
|
+
|
|
91
|
+
# Per-icon dimensions override the set default; fa6-solid ships 512
|
|
92
|
+
# sets with 576 icons inside them.
|
|
93
|
+
width = data["width"] || payload["width"]
|
|
94
|
+
height = data["height"] || payload["height"]
|
|
95
|
+
|
|
96
|
+
Source.new(
|
|
97
|
+
markup: apply(transform, data.fetch("body"), width, height),
|
|
98
|
+
width: width,
|
|
99
|
+
height: height
|
|
100
|
+
)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Iconify keeps renames and mirrored variants out of "icons" and in
|
|
104
|
+
# "aliases", pointing at a parent that may itself be an alias. Without
|
|
105
|
+
# this, `lucide/fingerprint` — an alias since the icon was renamed to
|
|
106
|
+
# fingerprint-pattern — resolves to nothing and the build dies.
|
|
107
|
+
def lookup(icon, payload)
|
|
108
|
+
slug = icon.slug
|
|
109
|
+
transform = {}
|
|
110
|
+
seen = []
|
|
111
|
+
|
|
112
|
+
MAX_ALIAS_DEPTH.times do
|
|
113
|
+
return [payload.dig("icons", slug), transform] if payload.dig("icons", slug)
|
|
114
|
+
|
|
115
|
+
entry = payload.dig("aliases", slug)
|
|
116
|
+
return [nil, transform] if entry.nil?
|
|
117
|
+
|
|
118
|
+
# A transform is expressed relative to the parent, so an alias chain
|
|
119
|
+
# composes outwards: rotation adds, each flip toggles.
|
|
120
|
+
transform[:rotate] = (transform[:rotate].to_i + entry["rotate"].to_i) % 4
|
|
121
|
+
transform[:h_flip] = transform[:h_flip] ^ true if entry["hFlip"]
|
|
122
|
+
transform[:v_flip] = transform[:v_flip] ^ true if entry["vFlip"]
|
|
123
|
+
|
|
124
|
+
seen << slug
|
|
125
|
+
slug = entry["parent"]
|
|
126
|
+
raise Error, "#{icon.source}: alias cycle #{(seen << slug).join(" -> ")}" if seen.include?(slug)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
raise Error, "#{icon.source}: alias chain deeper than #{MAX_ALIAS_DEPTH} in #{icon.provider}"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Flip about the box centre, then rotate about it in quarter turns —
|
|
133
|
+
# the order Iconify defines. Emitted as one wrapping <g> so the body
|
|
134
|
+
# itself is untouched and still normalises like any other.
|
|
135
|
+
def apply(transform, body, width, height)
|
|
136
|
+
return body if transform.empty? || transform.values.none? { _1 == true || _1.to_i.positive? }
|
|
137
|
+
|
|
138
|
+
w = (width || 24).to_f
|
|
139
|
+
h = (height || 24).to_f
|
|
140
|
+
ops = []
|
|
141
|
+
ops << "rotate(#{transform[:rotate] * 90} #{w / 2} #{h / 2})" if transform[:rotate].to_i.positive?
|
|
142
|
+
ops << "translate(#{w} 0) scale(-1 1)" if transform[:h_flip]
|
|
143
|
+
ops << "translate(0 #{h}) scale(1 -1)" if transform[:v_flip]
|
|
144
|
+
|
|
145
|
+
%(<g transform="#{ops.join(" ")}">#{body}</g>)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
data/lib/fontico.rb
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "erb"
|
|
4
|
+
require_relative "fontico/version"
|
|
5
|
+
|
|
6
|
+
module Fontico
|
|
7
|
+
class Error < StandardError; end
|
|
8
|
+
|
|
9
|
+
autoload :Icon, "fontico/icon"
|
|
10
|
+
autoload :Manifest, "fontico/manifest"
|
|
11
|
+
autoload :Preprocessor, "fontico/preprocessor"
|
|
12
|
+
autoload :Resolver, "fontico/resolver"
|
|
13
|
+
autoload :Lockfile, "fontico/lockfile"
|
|
14
|
+
autoload :Builder, "fontico/builder"
|
|
15
|
+
autoload :Helper, "fontico/helper"
|
|
16
|
+
autoload :NodeRunner, "fontico/node_runner"
|
|
17
|
+
autoload :ProviderFonts, "fontico/provider_fonts"
|
|
18
|
+
autoload :Outliner, "fontico/outliner"
|
|
19
|
+
# fontico/prawn is opt-in: `require "fontico/prawn"`, so the gem never
|
|
20
|
+
# loads Prawn for apps that only build a sprite.
|
|
21
|
+
|
|
22
|
+
module Emitters
|
|
23
|
+
autoload :Sprite, "fontico/emitters/sprite"
|
|
24
|
+
autoload :Font, "fontico/emitters/font"
|
|
25
|
+
autoload :Stylesheet, "fontico/emitters/stylesheet"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class << self
|
|
29
|
+
attr_writer :manifest_path, :root, :output_dir, :inline_sprite, :css_class
|
|
30
|
+
|
|
31
|
+
# Base class on every emitted <svg>; also the stylesheet's selector.
|
|
32
|
+
def css_class = @css_class ||= "ico"
|
|
33
|
+
|
|
34
|
+
def root = @root ||= Dir.pwd
|
|
35
|
+
def output_dir = @output_dir ||= "app/assets/builds"
|
|
36
|
+
def manifest_path = @manifest_path ||= File.join(root, "icons.yml")
|
|
37
|
+
def manifest = @manifest ||= Manifest.load(manifest_path)
|
|
38
|
+
def sprite_file = File.join(root, output_dir, "icons.svg")
|
|
39
|
+
|
|
40
|
+
# Inline mode embeds <symbol> definitions in the layout instead of
|
|
41
|
+
# referencing an external file — required when assets are served from a
|
|
42
|
+
# CDN, where cross-origin <use href> silently renders nothing.
|
|
43
|
+
def inline_sprite? = !!@inline_sprite
|
|
44
|
+
|
|
45
|
+
def build(**opts) = Builder.new(manifest, root: root, output: output_dir, **opts).call
|
|
46
|
+
|
|
47
|
+
# The dev reloader's build, and the only caller that records what went
|
|
48
|
+
# wrong. Building stays forgiving on purpose — one typo must not stop the
|
|
49
|
+
# other 199 icons — so nothing raises here; the complaint is held until
|
|
50
|
+
# someone actually draws an icon. See #check!.
|
|
51
|
+
def rebuild!
|
|
52
|
+
@build_error = nil
|
|
53
|
+
@missing_icons = build.missing || {}
|
|
54
|
+
rescue StandardError => e
|
|
55
|
+
@build_error = e
|
|
56
|
+
@missing_icons = {}
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Why the last rebuild could not deliver: the exception it died on, and
|
|
60
|
+
# the icons it left out of the sprite.
|
|
61
|
+
attr_reader :build_error
|
|
62
|
+
|
|
63
|
+
def missing_icons = @missing_icons ||= {}
|
|
64
|
+
|
|
65
|
+
# Raised at the call site, because that is the only place with anything
|
|
66
|
+
# useful to say. An icon left out of the sprite still resolves through the
|
|
67
|
+
# manifest and renders a perfectly valid <use> at a symbol that isn't
|
|
68
|
+
# there — an invisible empty box, on a page that 200s. Silence is the one
|
|
69
|
+
# outcome worse than a stack trace.
|
|
70
|
+
#
|
|
71
|
+
# Free in production: only the dev reloader ever fills these in.
|
|
72
|
+
def check!(name = nil)
|
|
73
|
+
raise Error, "icons.yml did not build: #{@build_error.message}" if @build_error
|
|
74
|
+
|
|
75
|
+
reason = missing_icons[name]
|
|
76
|
+
raise Error, "icon #{name.inspect} was left out of the sprite: #{reason}" if reason
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def lockfile = Lockfile.new(File.join(root, "icons.lock"))
|
|
80
|
+
|
|
81
|
+
def font_file = File.join(root, output_dir, "icons.ttf")
|
|
82
|
+
|
|
83
|
+
# The character to print when drawing this icon from the font — for Prawn,
|
|
84
|
+
# where you write the glyph literally. Pinned append-only in icons.lock so
|
|
85
|
+
# a reference in Ruby source never goes stale.
|
|
86
|
+
def codepoint(name)
|
|
87
|
+
cp = lockfile.codepoint_for(name.to_s)
|
|
88
|
+
raise Error, "no icon named #{name.inspect}; run rake fontico:build" if cp.nil?
|
|
89
|
+
|
|
90
|
+
cp
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def glyph(name) = [codepoint(name)].pack("U")
|
|
94
|
+
def reset! = (@manifest = @build_error = @missing_icons = nil)
|
|
95
|
+
|
|
96
|
+
def configure = yield(self)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
require "fontico/railtie" if defined?(::Rails::Railtie)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Only when someone is watching: a colour escape in a CI log or a piped file
|
|
4
|
+
# is noise, and the message reads fine without it.
|
|
5
|
+
def fontico_red(text) = $stdout.tty? ? "\e[31m#{text}\e[0m" : text
|
|
6
|
+
|
|
7
|
+
namespace :fontico do
|
|
8
|
+
desc "Build icon artifacts from icons.yml"
|
|
9
|
+
task :build do
|
|
10
|
+
require "fontico"
|
|
11
|
+
report = Fontico.build
|
|
12
|
+
|
|
13
|
+
puts "fontico: #{report.written.size} artifact(s)"
|
|
14
|
+
report.written.each { puts " #{_1}" }
|
|
15
|
+
puts " fetched #{report.fetched.size}, cached #{report.cached.size}"
|
|
16
|
+
|
|
17
|
+
# Loud, but not fatal: one bad name should not stop the other 199 icons
|
|
18
|
+
# from building. The artifacts simply come out without it.
|
|
19
|
+
if report.missing&.any?
|
|
20
|
+
puts
|
|
21
|
+
puts fontico_red("fontico: #{report.missing.size} icon(s) could not be resolved and were left out:")
|
|
22
|
+
report.missing.each { |name, reason| puts fontico_red(" #{name}: #{reason}") }
|
|
23
|
+
puts fontico_red(" fix the entry in icons.yml, then run rake fontico:build again")
|
|
24
|
+
puts
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
report.skipped.each do |target, names|
|
|
28
|
+
puts " #{target}: skipped #{names.size} multicolor icon(s): #{names.join(", ")}" if names.any?
|
|
29
|
+
end
|
|
30
|
+
puts " pending target(s): #{report.pending.join(", ")}" if report.pending.any?
|
|
31
|
+
|
|
32
|
+
if report.warnings.any?
|
|
33
|
+
puts "\nfontico: #{report.warnings.size} icon(s) need fixing at the source:"
|
|
34
|
+
report.warnings.each { |name, list| puts " #{name}: #{list.join("; ")}" }
|
|
35
|
+
puts " see docs/icon-authoring.html"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
desc "Re-fetch every icon, ignoring icons.lock"
|
|
40
|
+
task :update do
|
|
41
|
+
require "fontico"
|
|
42
|
+
File.delete(File.join(Fontico.root, "icons.lock")) if File.exist?(File.join(Fontico.root, "icons.lock"))
|
|
43
|
+
Rake::Task["fontico:build"].invoke
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Deploys just work: Propshaft serves whatever is in app/assets/builds, and
|
|
48
|
+
# that directory is gitignored in a stock Rails app, so the artifacts have to
|
|
49
|
+
# be regenerated during precompile rather than committed.
|
|
50
|
+
#
|
|
51
|
+
# Same hook jsbundling-rails uses for javascript:build.
|
|
52
|
+
if Rake::Task.task_defined?("assets:precompile")
|
|
53
|
+
Rake::Task["assets:precompile"].enhance(["fontico:build"])
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
if Rake::Task.task_defined?("assets:clobber")
|
|
57
|
+
Rake::Task["assets:clobber"].enhance(["fontico:clobber"])
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
namespace :fontico do
|
|
61
|
+
desc "Remove generated icon artifacts (icons.lock is kept: it is source)"
|
|
62
|
+
task :clobber do
|
|
63
|
+
require "fontico"
|
|
64
|
+
%w[icons.svg icons.css icons.ttf].each do |name|
|
|
65
|
+
path = File.join(Fontico.root, Fontico.output_dir, name)
|
|
66
|
+
File.delete(path) if File.exist?(path)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: fontico
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- nofxx
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rexml
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '3.2'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '3.2'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: prawn
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '2.5'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.5'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rubocop
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '1.79'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '1.79'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rubocop-performance
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '1.25'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '1.25'
|
|
68
|
+
description: Declare every icon in your app in one manifest — from Lucide, Material
|
|
69
|
+
Symbols, any Iconify set, or your own SVG folder — under names you choose. fontico
|
|
70
|
+
resolves, normalises and merges them into a single build artifact, so templates
|
|
71
|
+
never name a vendor.
|
|
72
|
+
executables: []
|
|
73
|
+
extensions: []
|
|
74
|
+
extra_rdoc_files: []
|
|
75
|
+
files:
|
|
76
|
+
- LICENSE.txt
|
|
77
|
+
- README.md
|
|
78
|
+
- docs/expand-check.png
|
|
79
|
+
- docs/icon-authoring.html
|
|
80
|
+
- docs/lucide-glyphs-extracted.png
|
|
81
|
+
- docs/lucide-ttf-glyph.png
|
|
82
|
+
- docs/rails-preview.png
|
|
83
|
+
- docs/sprite-sheet.png
|
|
84
|
+
- docs/stroke-vs-fill.png
|
|
85
|
+
- docs/ttf-glyphs.png
|
|
86
|
+
- lib/fontico.rb
|
|
87
|
+
- lib/fontico/builder.rb
|
|
88
|
+
- lib/fontico/emitters/font.rb
|
|
89
|
+
- lib/fontico/emitters/sprite.rb
|
|
90
|
+
- lib/fontico/emitters/stylesheet.rb
|
|
91
|
+
- lib/fontico/helper.rb
|
|
92
|
+
- lib/fontico/icon.rb
|
|
93
|
+
- lib/fontico/lockfile.rb
|
|
94
|
+
- lib/fontico/manifest.rb
|
|
95
|
+
- lib/fontico/node/build_font.mjs
|
|
96
|
+
- lib/fontico/node/extract_glyphs.mjs
|
|
97
|
+
- lib/fontico/node/package.json
|
|
98
|
+
- lib/fontico/node_runner.rb
|
|
99
|
+
- lib/fontico/outliner.rb
|
|
100
|
+
- lib/fontico/prawn.rb
|
|
101
|
+
- lib/fontico/preprocessor.rb
|
|
102
|
+
- lib/fontico/provider_fonts.rb
|
|
103
|
+
- lib/fontico/railtie.rb
|
|
104
|
+
- lib/fontico/resolver.rb
|
|
105
|
+
- lib/fontico/version.rb
|
|
106
|
+
- lib/tasks/fontico.rake
|
|
107
|
+
homepage: https://github.com/fireho/fontico
|
|
108
|
+
licenses:
|
|
109
|
+
- MIT
|
|
110
|
+
metadata:
|
|
111
|
+
source_code_uri: https://github.com/fireho/fontico
|
|
112
|
+
bug_tracker_uri: https://github.com/fireho/fontico/issues
|
|
113
|
+
changelog_uri: https://github.com/fireho/fontico/blob/main/CHANGELOG.md
|
|
114
|
+
rubygems_mfa_required: 'true'
|
|
115
|
+
rdoc_options: []
|
|
116
|
+
require_paths:
|
|
117
|
+
- lib
|
|
118
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
119
|
+
requirements:
|
|
120
|
+
- - ">="
|
|
121
|
+
- !ruby/object:Gem::Version
|
|
122
|
+
version: '3.1'
|
|
123
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
|
+
requirements:
|
|
125
|
+
- - ">="
|
|
126
|
+
- !ruby/object:Gem::Version
|
|
127
|
+
version: '0'
|
|
128
|
+
requirements: []
|
|
129
|
+
rubygems_version: 3.6.7
|
|
130
|
+
specification_version: 4
|
|
131
|
+
summary: Name icons by intent. Source them from anywhere. Ship one artifact.
|
|
132
|
+
test_files: []
|