gkh-fontcustom 1.3.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/.travis.yml +20 -0
- data/CHANGELOG.md +137 -0
- data/CONTRIBUTING.md +50 -0
- data/Gemfile +4 -0
- data/LICENSES.txt +60 -0
- data/README.md +112 -0
- data/Rakefile +8 -0
- data/bin/fontcustom +5 -0
- data/fontcustom.gemspec +28 -0
- data/gemfiles/Gemfile.listen_1 +6 -0
- data/lib/fontcustom.rb +43 -0
- data/lib/fontcustom/base.rb +54 -0
- data/lib/fontcustom/cli.rb +110 -0
- data/lib/fontcustom/error.rb +4 -0
- data/lib/fontcustom/generator/font.rb +109 -0
- data/lib/fontcustom/generator/template.rb +222 -0
- data/lib/fontcustom/manifest.rb +75 -0
- data/lib/fontcustom/options.rb +192 -0
- data/lib/fontcustom/scripts/eotlitetool.py +466 -0
- data/lib/fontcustom/scripts/generate.py +128 -0
- data/lib/fontcustom/scripts/sfnt2woff +0 -0
- data/lib/fontcustom/templates/_fontcustom-rails.scss +14 -0
- data/lib/fontcustom/templates/_fontcustom.scss +16 -0
- data/lib/fontcustom/templates/fontcustom-preview.html +174 -0
- data/lib/fontcustom/templates/fontcustom.css +14 -0
- data/lib/fontcustom/templates/fontcustom.yml +96 -0
- data/lib/fontcustom/utility.rb +117 -0
- data/lib/fontcustom/version.rb +3 -0
- data/lib/fontcustom/watcher.rb +90 -0
- data/spec/fixtures/example/_example-rails.scss +50 -0
- data/spec/fixtures/example/example-preview.html +253 -0
- data/spec/fixtures/example/example.css +50 -0
- data/spec/fixtures/example/example.eot +0 -0
- data/spec/fixtures/example/example.svg +75 -0
- data/spec/fixtures/example/example.ttf +0 -0
- data/spec/fixtures/example/example.woff +0 -0
- data/spec/fixtures/generators/.fontcustom-manifest-corrupted.json +25 -0
- data/spec/fixtures/generators/.fontcustom-manifest-empty.json +0 -0
- data/spec/fixtures/generators/.fontcustom-manifest.json +52 -0
- data/spec/fixtures/generators/fontcustom.yml +1 -0
- data/spec/fixtures/generators/mixed-output/another-font.ttf +0 -0
- data/spec/fixtures/generators/mixed-output/dont-delete-me.bro +0 -0
- data/spec/fixtures/generators/mixed-output/fontcustom.css +108 -0
- data/spec/fixtures/generators/mixed-output/fontcustom_82a59e769bc60192484f2620570bbb59.eot +0 -0
- data/spec/fixtures/generators/mixed-output/fontcustom_82a59e769bc60192484f2620570bbb59.svg +56 -0
- data/spec/fixtures/generators/mixed-output/fontcustom_82a59e769bc60192484f2620570bbb59.ttf +0 -0
- data/spec/fixtures/generators/mixed-output/fontcustom_82a59e769bc60192484f2620570bbb59.woff +0 -0
- data/spec/fixtures/options/any-file-name.yml +1 -0
- data/spec/fixtures/options/config-is-in-dir/fontcustom.yml +1 -0
- data/spec/fixtures/options/fontcustom-empty.yml +1 -0
- data/spec/fixtures/options/fontcustom-malformed.yml +1 -0
- data/spec/fixtures/options/fontcustom.yml +1 -0
- data/spec/fixtures/options/no-config-here/.gitkeep +0 -0
- data/spec/fixtures/options/rails-like/config/fontcustom.yml +1 -0
- data/spec/fixtures/shared/not-a-dir +0 -0
- data/spec/fixtures/shared/templates/custom.css +4 -0
- data/spec/fixtures/shared/templates/regular.css +4 -0
- data/spec/fixtures/shared/vectors-empty/no_vectors_here.txt +0 -0
- data/spec/fixtures/shared/vectors/C.svg +14 -0
- data/spec/fixtures/shared/vectors/D.svg +15 -0
- data/spec/fixtures/shared/vectors/a_R3ally-eXotic f1Le Name.svg +6 -0
- data/spec/fontcustom/base_spec.rb +45 -0
- data/spec/fontcustom/cli_spec.rb +30 -0
- data/spec/fontcustom/generator/font_spec.rb +72 -0
- data/spec/fontcustom/generator/template_spec.rb +99 -0
- data/spec/fontcustom/manifest_spec.rb +17 -0
- data/spec/fontcustom/options_spec.rb +315 -0
- data/spec/fontcustom/utility_spec.rb +82 -0
- data/spec/fontcustom/watcher_spec.rb +121 -0
- data/spec/spec_helper.rb +103 -0
- metadata +252 -0
@@ -0,0 +1,128 @@
|
|
1
|
+
import fontforge
|
2
|
+
import os
|
3
|
+
import subprocess
|
4
|
+
import tempfile
|
5
|
+
import json
|
6
|
+
|
7
|
+
#
|
8
|
+
# Manifest / Options
|
9
|
+
# Older Pythons don't have argparse, so we use optparse instead
|
10
|
+
#
|
11
|
+
|
12
|
+
try:
|
13
|
+
import argparse
|
14
|
+
parser = argparse.ArgumentParser()
|
15
|
+
parser.add_argument('manifest', help='Path to .fontcustom-manifest.json')
|
16
|
+
args = parser.parse_args()
|
17
|
+
manifestfile = open(args.manifest, 'r+')
|
18
|
+
except ImportError:
|
19
|
+
import optparse
|
20
|
+
parser = optparse.OptionParser()
|
21
|
+
(nothing, args) = parser.parse_args()
|
22
|
+
manifestfile = open(args[0], 'r+')
|
23
|
+
|
24
|
+
manifest = json.load(manifestfile)
|
25
|
+
options = manifest['options']
|
26
|
+
|
27
|
+
#
|
28
|
+
# Font
|
29
|
+
#
|
30
|
+
|
31
|
+
design_px = options['font_em'] / options['font_design_size']
|
32
|
+
|
33
|
+
font = fontforge.font()
|
34
|
+
font.encoding = 'UnicodeFull'
|
35
|
+
font.design_size = options['font_design_size']
|
36
|
+
font.em = options['font_em']
|
37
|
+
font.ascent = options['font_ascent']
|
38
|
+
font.descent = options['font_descent']
|
39
|
+
font.fontname = options['font_name']
|
40
|
+
font.familyname = options['font_name']
|
41
|
+
font.fullname = options['font_name']
|
42
|
+
if options['autowidth']:
|
43
|
+
font.autoWidth(0, 0, options['font_em'])
|
44
|
+
|
45
|
+
#
|
46
|
+
# Glyphs
|
47
|
+
#
|
48
|
+
|
49
|
+
def removeSwitchFromSvg( file ):
|
50
|
+
svgfile = open(file, 'r')
|
51
|
+
svgtext = svgfile.read()
|
52
|
+
svgfile.close()
|
53
|
+
tmpsvgfile = tempfile.NamedTemporaryFile(suffix=".svg", delete=False)
|
54
|
+
svgtext = svgtext.replace('<switch>', '')
|
55
|
+
svgtext = svgtext.replace('</switch>', '')
|
56
|
+
tmpsvgfile.file.write(svgtext)
|
57
|
+
tmpsvgfile.file.close()
|
58
|
+
|
59
|
+
return tmpsvgfile.name
|
60
|
+
|
61
|
+
def createGlyph( name, source, code ):
|
62
|
+
frag, ext = os.path.splitext(source)
|
63
|
+
|
64
|
+
if ext == '.svg':
|
65
|
+
temp = removeSwitchFromSvg(source)
|
66
|
+
glyph = font.createChar(code)
|
67
|
+
glyph.importOutlines(temp)
|
68
|
+
os.unlink(temp)
|
69
|
+
|
70
|
+
if options['autowidth']:
|
71
|
+
glyph.left_side_bearing = glyph.right_side_bearing = 0
|
72
|
+
glyph.round()
|
73
|
+
else:
|
74
|
+
glyph.width = options['font_em']
|
75
|
+
width = glyph.width - glyph.left_side_bearing - glyph.right_side_bearing
|
76
|
+
aligned_to_pixel_grid = (width % design_px == 0)
|
77
|
+
if (aligned_to_pixel_grid):
|
78
|
+
shift = glyph.left_side_bearing % design_px
|
79
|
+
glyph.left_side_bearing = glyph.left_side_bearing - shift
|
80
|
+
glyph.right_side_bearing = glyph.right_side_bearing + shift
|
81
|
+
|
82
|
+
for glyph, data in manifest['glyphs'].iteritems():
|
83
|
+
name = createGlyph(glyph, data['source'], data['codepoint'])
|
84
|
+
|
85
|
+
#
|
86
|
+
# Generate Files
|
87
|
+
#
|
88
|
+
|
89
|
+
try:
|
90
|
+
fontfile = options['output']['fonts'] + '/' + options['font_name']
|
91
|
+
if not options['no_hash']:
|
92
|
+
fontfile += '_' + manifest['checksum']['current'][:32]
|
93
|
+
|
94
|
+
# Generate TTF and SVG
|
95
|
+
font.generate(fontfile + '.ttf')
|
96
|
+
font.generate(fontfile + '.svg')
|
97
|
+
manifest['fonts'].append(fontfile + '.ttf')
|
98
|
+
manifest['fonts'].append(fontfile + '.svg')
|
99
|
+
|
100
|
+
# Fix SVG header for webkit
|
101
|
+
# from: https://github.com/fontello/font-builder/blob/master/bin/fontconvert.py
|
102
|
+
svgfile = open(fontfile + '.svg', 'r+')
|
103
|
+
svgtext = svgfile.read()
|
104
|
+
svgfile.seek(0)
|
105
|
+
svgfile.write(svgtext.replace('''<svg>''', '''<svg xmlns="http://www.w3.org/2000/svg">'''))
|
106
|
+
svgfile.close()
|
107
|
+
|
108
|
+
# Convert WOFF
|
109
|
+
scriptPath = os.path.dirname(os.path.realpath(__file__))
|
110
|
+
try:
|
111
|
+
subprocess.Popen([scriptPath + '/sfnt2woff', fontfile + '.ttf'], stdout=subprocess.PIPE)
|
112
|
+
except OSError:
|
113
|
+
# If the local version of sfnt2woff fails (i.e., on Linux), try to use the
|
114
|
+
# global version. This allows us to avoid forcing OS X users to compile
|
115
|
+
# sfnt2woff from source, simplifying install.
|
116
|
+
subprocess.call(['sfnt2woff', fontfile + '.ttf'])
|
117
|
+
manifest['fonts'].append(fontfile + '.woff')
|
118
|
+
|
119
|
+
# Convert EOT for IE7
|
120
|
+
subprocess.call('python ' + scriptPath + '/eotlitetool.py ' + fontfile + '.ttf -o ' + fontfile + '.eot', shell=True)
|
121
|
+
subprocess.call('mv ' + fontfile + '.eotlite ' + fontfile + '.eot', shell=True)
|
122
|
+
manifest['fonts'].append(fontfile + '.eot')
|
123
|
+
|
124
|
+
finally:
|
125
|
+
manifestfile.seek(0)
|
126
|
+
manifestfile.write(json.dumps(manifest, indent=2, sort_keys=True))
|
127
|
+
manifestfile.truncate()
|
128
|
+
manifestfile.close()
|
Binary file
|
@@ -0,0 +1,14 @@
|
|
1
|
+
//
|
2
|
+
// Icon Font: <%= font_name %>
|
3
|
+
//
|
4
|
+
|
5
|
+
<%= font_face(url: "font-url", path: @font_path_alt) %>
|
6
|
+
|
7
|
+
[data-icon]:before { content: attr(data-icon); }
|
8
|
+
|
9
|
+
[data-icon]:before,
|
10
|
+
<%= glyph_selectors %> {
|
11
|
+
<%= glyph_properties %>
|
12
|
+
}
|
13
|
+
|
14
|
+
<%= glyphs %>
|
@@ -0,0 +1,16 @@
|
|
1
|
+
//
|
2
|
+
// Icon Font: <%= font_name %>
|
3
|
+
//
|
4
|
+
|
5
|
+
<%= font_face(path: @font_path_alt) %>
|
6
|
+
|
7
|
+
[data-icon]:before { content: attr(data-icon); }
|
8
|
+
|
9
|
+
[data-icon]:before,
|
10
|
+
<%= glyph_selectors %> {
|
11
|
+
<%= glyph_properties %>
|
12
|
+
}
|
13
|
+
|
14
|
+
<%= glyphs %>
|
15
|
+
<% @glyphs.each do |name, value| %>
|
16
|
+
$font-<%= font_name.gsub(/[^\w\d_]/, '-') %>-<%= name.to_s %>: "\<%= value[:codepoint].to_s(16) %>";<% end %>
|
@@ -0,0 +1,174 @@
|
|
1
|
+
<% scale = %w|12 14 16 18 21 24 36 48 60 72| %><!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title><%= font_name %> glyphs preview</title>
|
5
|
+
|
6
|
+
<style>
|
7
|
+
/* Page Styles */
|
8
|
+
|
9
|
+
* {
|
10
|
+
-moz-box-sizing: border-box;
|
11
|
+
-webkit-box-sizing: border-box;
|
12
|
+
box-sizing: border-box;
|
13
|
+
margin: 0;
|
14
|
+
padding: 0;
|
15
|
+
}
|
16
|
+
|
17
|
+
body {
|
18
|
+
background: #fff;
|
19
|
+
color: #444;
|
20
|
+
font: 16px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
|
21
|
+
}
|
22
|
+
|
23
|
+
a,
|
24
|
+
a:visited {
|
25
|
+
color: #888;
|
26
|
+
text-decoration: underline;
|
27
|
+
}
|
28
|
+
a:hover,
|
29
|
+
a:focus { color: #000; }
|
30
|
+
|
31
|
+
header {
|
32
|
+
border-bottom: 2px solid #ddd;
|
33
|
+
margin-bottom: 20px;
|
34
|
+
overflow: hidden;
|
35
|
+
padding: 20px 0;
|
36
|
+
}
|
37
|
+
|
38
|
+
header h1 {
|
39
|
+
color: #888;
|
40
|
+
float: left;
|
41
|
+
font-size: 36px;
|
42
|
+
font-weight: 300;
|
43
|
+
}
|
44
|
+
|
45
|
+
header a {
|
46
|
+
float: right;
|
47
|
+
font-size: 14px;
|
48
|
+
}
|
49
|
+
|
50
|
+
.container {
|
51
|
+
margin: 0 auto;
|
52
|
+
max-width: 1200px;
|
53
|
+
min-width: 960px;
|
54
|
+
padding: 0 40px;
|
55
|
+
width: 90%;
|
56
|
+
}
|
57
|
+
|
58
|
+
.glyph {
|
59
|
+
border-bottom: 1px dotted #ccc;
|
60
|
+
padding: 10px 0 20px;
|
61
|
+
margin-bottom: 20px;
|
62
|
+
}
|
63
|
+
|
64
|
+
.preview-glyphs { vertical-align: bottom; }
|
65
|
+
|
66
|
+
.preview-scale {
|
67
|
+
color: #888;
|
68
|
+
font-size: 12px;
|
69
|
+
margin-top: 5px;
|
70
|
+
}
|
71
|
+
|
72
|
+
.step {
|
73
|
+
display: inline-block;
|
74
|
+
line-height: 1;
|
75
|
+
position: relative;
|
76
|
+
width: 10%;
|
77
|
+
}
|
78
|
+
|
79
|
+
.step .letters,
|
80
|
+
.step i {
|
81
|
+
-webkit-transition: opacity .3s;
|
82
|
+
-moz-transition: opacity .3s;
|
83
|
+
-ms-transition: opacity .3s;
|
84
|
+
-o-transition: opacity .3s;
|
85
|
+
transition: opacity .3s;
|
86
|
+
}
|
87
|
+
|
88
|
+
.step:hover .letters { opacity: 1; }
|
89
|
+
.step:hover i { opacity: .3; }
|
90
|
+
|
91
|
+
.letters {
|
92
|
+
opacity: .3;
|
93
|
+
position: absolute;
|
94
|
+
}
|
95
|
+
|
96
|
+
.characters-off .letters { display: none; }
|
97
|
+
.characters-off .step:hover i { opacity: 1; }
|
98
|
+
|
99
|
+
<% scale.each do |n| %>
|
100
|
+
.size-<%= n %> { font-size: <%= n %>px; }
|
101
|
+
<% end %>
|
102
|
+
|
103
|
+
.usage { margin-top: 10px; }
|
104
|
+
|
105
|
+
.usage input {
|
106
|
+
font-family: monospace;
|
107
|
+
margin-right: 3px;
|
108
|
+
padding: 2px 5px;
|
109
|
+
text-align: center;
|
110
|
+
}
|
111
|
+
|
112
|
+
.usage .point { width: 150px; }
|
113
|
+
|
114
|
+
.usage .class { width: 250px; }
|
115
|
+
|
116
|
+
footer {
|
117
|
+
color: #888;
|
118
|
+
font-size: 12px;
|
119
|
+
padding: 20px 0;
|
120
|
+
}
|
121
|
+
|
122
|
+
/* Icon Font: <%= font_name %> */
|
123
|
+
|
124
|
+
<%= font_face(path: @font_path_preview) %>
|
125
|
+
|
126
|
+
[data-icon]:before { content: attr(data-icon); }
|
127
|
+
|
128
|
+
[data-icon]:before,
|
129
|
+
<%= glyph_selectors %> {
|
130
|
+
<%= glyph_properties %>
|
131
|
+
}
|
132
|
+
|
133
|
+
<%= glyphs %>
|
134
|
+
</style>
|
135
|
+
|
136
|
+
<!--[if lte IE 8]><script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
|
137
|
+
|
138
|
+
<script>
|
139
|
+
function toggleCharacters() {
|
140
|
+
var body = document.getElementsByTagName('body')[0];
|
141
|
+
body.className = body.className === 'characters-off' ? '' : 'characters-off';
|
142
|
+
}
|
143
|
+
</script>
|
144
|
+
</head>
|
145
|
+
|
146
|
+
<body class="characters-off">
|
147
|
+
<div id="page" class="container">
|
148
|
+
<header>
|
149
|
+
<h1><%= font_name %> contains <%= @glyphs.length %> glyphs:</h1>
|
150
|
+
<a onclick="toggleCharacters(); return false;" href="#">Toggle Preview Characters</a>
|
151
|
+
</header>
|
152
|
+
|
153
|
+
<% @glyphs.each do |name, value|
|
154
|
+
selector = @options[:css_selector].sub('{{glyph}}', name.to_s) %>
|
155
|
+
<div class="glyph">
|
156
|
+
<div class="preview-glyphs">
|
157
|
+
<% scale.each do |n| %><span class="step size-<%= n %>"><span class="letters">Pp</span><i id="<%= selector[1..-1] %>" class="<%= selector[1..-1].gsub('.', ' ') %>"></i></span><% end %>
|
158
|
+
</div>
|
159
|
+
<div class="preview-scale">
|
160
|
+
<% scale.each do |n| %><span class="step"><%= n %></span><% end %>
|
161
|
+
</div>
|
162
|
+
<div class="usage">
|
163
|
+
<input class="class" type="text" readonly="readonly" onClick="this.select();" value="<%= selector %>" />
|
164
|
+
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&#x<%= value[:codepoint].to_s(16) %>;" />
|
165
|
+
</div>
|
166
|
+
</div>
|
167
|
+
<% end %>
|
168
|
+
|
169
|
+
<footer>
|
170
|
+
Made with love using <a href="http://fontcustom.com">Font Custom</a>.
|
171
|
+
</footer>
|
172
|
+
</div>
|
173
|
+
</body>
|
174
|
+
</html>
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# =============================================================================
|
2
|
+
# Font Custom Configuration
|
3
|
+
# This file should live in the directory where you run `fontcustom compile`.
|
4
|
+
# For more info, visit <https://github.com/FontCustom/fontcustom>.
|
5
|
+
# =============================================================================
|
6
|
+
|
7
|
+
|
8
|
+
# -----------------------------------------------------------------------------
|
9
|
+
# Project Info
|
10
|
+
# -----------------------------------------------------------------------------
|
11
|
+
|
12
|
+
# The font's name. Also determines the file names of generated templates.
|
13
|
+
#font_name: icons
|
14
|
+
|
15
|
+
# Format of CSS selectors. {{glyph}} is substituted for the glyph name.
|
16
|
+
#css_selector: .i-{{glyph}}
|
17
|
+
|
18
|
+
# Generate fonts without asset-busting hashes.
|
19
|
+
#no_hash: true
|
20
|
+
|
21
|
+
# Encode WOFF fonts into the generated CSS.
|
22
|
+
#base64: true
|
23
|
+
|
24
|
+
# Forces compilation, even if inputs have not changed
|
25
|
+
#force: true
|
26
|
+
|
27
|
+
# Display (possibly useful) debugging messages.
|
28
|
+
#debug: true
|
29
|
+
|
30
|
+
# Hide status messages.
|
31
|
+
#quiet: true
|
32
|
+
|
33
|
+
|
34
|
+
# -----------------------------------------------------------------------------
|
35
|
+
# Input / Output Locations
|
36
|
+
# You can save generated fonts, CSS, and other files to different locations
|
37
|
+
# here. Font Custom can also read input vectors and templates from different
|
38
|
+
# places.
|
39
|
+
#
|
40
|
+
# NOTE:
|
41
|
+
# - Be sure to preserve the whitespace in these YAML hashes.
|
42
|
+
# - INPUT[:vectors] and OUTPUT[:fonts] are required. Everything else is
|
43
|
+
# optional.
|
44
|
+
# - Specify output locations for custom templates by including their file
|
45
|
+
# names as the key.
|
46
|
+
# -----------------------------------------------------------------------------
|
47
|
+
|
48
|
+
#input:
|
49
|
+
# vectors: my/vectors
|
50
|
+
# templates: my/templates
|
51
|
+
|
52
|
+
#output:
|
53
|
+
# fonts: app/assets/fonts
|
54
|
+
# css: app/assets/stylesheets
|
55
|
+
# preview: app/views/styleguide
|
56
|
+
# my-custom-template.yml: path/to/template/output
|
57
|
+
|
58
|
+
|
59
|
+
# -----------------------------------------------------------------------------
|
60
|
+
# Templates
|
61
|
+
# A YAML array of templates and files to generate alongside fonts. Custom
|
62
|
+
# templates should be saved in the INPUT[:templates] directory and referenced
|
63
|
+
# by their base file name.
|
64
|
+
#
|
65
|
+
# For Rails and Compass templates, set `preprocessor_path` as the relative
|
66
|
+
# path from OUTPUT[:css] to OUTPUT[:fonts]. By default, these are the same
|
67
|
+
# directory.
|
68
|
+
#
|
69
|
+
# Included in Font Custom: preview, css, scss, scss-rails
|
70
|
+
# Default: css, preview
|
71
|
+
# -----------------------------------------------------------------------------
|
72
|
+
|
73
|
+
#templates:
|
74
|
+
#- scss-rails
|
75
|
+
#- preview
|
76
|
+
#- my-custom-template.yml
|
77
|
+
|
78
|
+
#preprocessor_path: ../fonts/
|
79
|
+
|
80
|
+
|
81
|
+
# -----------------------------------------------------------------------------
|
82
|
+
# Font Settings (defaults shown)
|
83
|
+
# -----------------------------------------------------------------------------
|
84
|
+
|
85
|
+
# Size (in pica points) for which your font is designed.
|
86
|
+
#font_design_size: 16
|
87
|
+
|
88
|
+
# The em size. Setting this will scale the entire font to the given size.
|
89
|
+
#font_em: 512
|
90
|
+
|
91
|
+
# The font's descent and descent. Used to calculate the baseline.
|
92
|
+
#font_ascent: 448
|
93
|
+
#font_descent: 64
|
94
|
+
|
95
|
+
# Horizontally fit glyphs to their individual vector widths.
|
96
|
+
#autowidth: false
|