fontcustom 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +12 -4
- data/README.md +9 -3
- data/lib/fontcustom/cli.rb +3 -0
- data/lib/fontcustom/generator.rb +20 -2
- data/lib/fontcustom/scripts/eotlitetool.py +466 -0
- data/lib/fontcustom/scripts/generate.py +22 -1
- data/lib/fontcustom/templates/fontcustom-ie7.css +18 -0
- data/lib/fontcustom/templates/test.html +100 -0
- data/lib/fontcustom/version.rb +1 -1
- metadata +5 -2
data/CHANGELOG.md
CHANGED
@@ -1,12 +1,20 @@
|
|
1
|
+
## 0.1.3 (2/2/2013)
|
2
|
+
* Add --debug CLI option, which shows fontforge output ([#37](https://github.com/FontCustom/fontcustom/issues/37))
|
3
|
+
* Patch for Illustrator CS6 SVG output ([#42](https://github.com/FontCustom/fontcustom/pull/42))
|
4
|
+
* Generate IE7 stylesheet ([#43](https://github.com/FontCustom/fontcustom/pull/43))
|
5
|
+
* Option to set custom font path for @font-face ([#43](https://github.com/FontCustom/fontcustom/pull/43))
|
6
|
+
* Option to generate test HTML file showing all glyphs ([#43](https://github.com/FontCustom/fontcustom/pull/43))
|
7
|
+
* Use eotlite.py instead of mkeot ([#43](https://github.com/FontCustom/fontcustom/pull/43))
|
8
|
+
|
1
9
|
## 0.1.0 (12/2/2012)
|
2
10
|
|
3
11
|
* Changed API to use Thor `class_option`s
|
4
|
-
* Added option to change the name of the font and generated files ([#6](https://github.com/
|
5
|
-
* Added option to disable the file name hash ([#13](https://github.com/
|
12
|
+
* Added option to change the name of the font and generated files ([#6](https://github.com/FontCustom/fontcustom/issues/6))
|
13
|
+
* Added option to disable the file name hash ([#13](https://github.com/FontCustom/fontcustom/issues/13))
|
6
14
|
* `fontcustom watch` compiles automatically on the first run
|
7
15
|
* Better help messages
|
8
16
|
|
9
17
|
## 0.0.2 (11/26/2012)
|
10
18
|
|
11
|
-
* Fixed gemspec dependency bug ([#2](https://github.com/
|
12
|
-
* Fixed Windows Chrome PUA bug ([#1](https://github.com/
|
19
|
+
* Fixed gemspec dependency bug ([#2](https://github.com/FontCustom/fontcustom/pull/2))
|
20
|
+
* Fixed Windows Chrome PUA bug ([#1](https://github.com/FontCustom/fontcustom/issues/1))
|
data/README.md
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
FontCustom v0.1.
|
1
|
+
FontCustom v0.1.3
|
2
2
|
==========
|
3
3
|
|
4
4
|
**Generate custom icon webfonts from the comfort of the command line.**
|
5
5
|
|
6
|
-
[Full documentation](http://
|
7
|
-
[Feedback and issues](https://github.com/
|
6
|
+
[Full documentation](http://fontcustom.github.com/fontcustom/)<br/>
|
7
|
+
[Feedback and issues](https://github.com/FontCustom/fontcustom/issues)
|
8
8
|
|
9
9
|
|
10
10
|
Installation
|
@@ -26,3 +26,9 @@ fontcustom watch path/to/vectors # Watch for changes
|
|
26
26
|
```
|
27
27
|
|
28
28
|
Optional second parameter allows you to specify an output directory.
|
29
|
+
|
30
|
+
Need help?
|
31
|
+
|
32
|
+
```sh
|
33
|
+
fontcustom --help
|
34
|
+
```
|
data/lib/fontcustom/cli.rb
CHANGED
@@ -7,6 +7,9 @@ module Fontcustom
|
|
7
7
|
class_option :output, :aliases => '-o', :desc => 'Specify an output directory. Default: $DIR/fontcustom'
|
8
8
|
class_option :name, :aliases => '-n', :desc => 'Specify a font name. This will be used in the generated fonts and CSS. Default: fontcustom'
|
9
9
|
class_option :nohash, :type => :boolean, :default => false, :desc => 'Disable filename hashes. Default: false'
|
10
|
+
class_option :debug, :type => :boolean, :default => false, :desc => 'Display debug messages. Default: false'
|
11
|
+
class_option :font_path, :aliases => '-f', :desc => 'Specify a path for fonts in css @font-face declaration. Default: none'
|
12
|
+
class_option :html, :type => :boolean, :default => false, :desc => 'Generate html page with icons'
|
10
13
|
|
11
14
|
desc 'compile DIR [options]', 'Generates webfonts and CSS from *.svg and *.eps files in DIR.'
|
12
15
|
def compile(*args)
|
data/lib/fontcustom/generator.rb
CHANGED
@@ -11,6 +11,9 @@ module Fontcustom
|
|
11
11
|
class_option :output, :aliases => '-o'
|
12
12
|
class_option :name, :aliases => '-n'
|
13
13
|
class_option :nohash, :type => :boolean, :default => false
|
14
|
+
class_option :debug, :type => :boolean, :default => false
|
15
|
+
class_option :font_path, :aliases => '-f'
|
16
|
+
class_option :html, :type => :boolean, :default => false
|
14
17
|
|
15
18
|
def self.source_root
|
16
19
|
File.dirname(__FILE__)
|
@@ -45,6 +48,8 @@ module Fontcustom
|
|
45
48
|
|
46
49
|
def cleanup_output_dir
|
47
50
|
css = File.join(@output, 'fontcustom.css')
|
51
|
+
css_ie7 = File.join(@output, 'fontcustom-ie7.css')
|
52
|
+
test_html = File.join(@output, 'test.html')
|
48
53
|
old_name = if File.exists? css
|
49
54
|
line = IO.readlines(css)[5] # font-family: "Example Font";
|
50
55
|
line.scan(/".+"/)[0][1..-2].gsub(/\W/, '-').downcase # => 'example-font'
|
@@ -54,6 +59,8 @@ module Fontcustom
|
|
54
59
|
|
55
60
|
old_files = Dir[File.join(@output, old_name + '-*.{woff,ttf,eot,svg}')]
|
56
61
|
old_files << css if File.exists?(css)
|
62
|
+
old_files << css_ie7 if File.exists?(css_ie7)
|
63
|
+
old_files << test_html if File.exists?(test_html)
|
57
64
|
old_files.each {|file| remove_file file }
|
58
65
|
end
|
59
66
|
|
@@ -64,7 +71,11 @@ module Fontcustom
|
|
64
71
|
|
65
72
|
# suppress fontforge message
|
66
73
|
# TODO get font name and classes from script (without showing fontforge message)
|
67
|
-
|
74
|
+
cmd = "fontforge -script #{gem_file_path}/scripts/generate.py #{input} #{@output + name + nohash}"
|
75
|
+
unless options.debug
|
76
|
+
cmd += " > /dev/null 2>&1"
|
77
|
+
end
|
78
|
+
`#{cmd}`
|
68
79
|
end
|
69
80
|
|
70
81
|
def show_paths
|
@@ -79,9 +90,16 @@ module Fontcustom
|
|
79
90
|
def create_stylesheet
|
80
91
|
files = Dir[File.join(input, '*.{svg,eps}')]
|
81
92
|
@classes = files.map {|file| File.basename(file)[0..-5].gsub(/\W/, '-').downcase }
|
82
|
-
|
93
|
+
if(!options.font_path.nil?)
|
94
|
+
font_path = (options.font_path) ? options.font_path : ''
|
95
|
+
@path = File.join(font_path, File.basename(@path))
|
96
|
+
else
|
97
|
+
@path = File.basename(@path)
|
98
|
+
end
|
83
99
|
|
84
100
|
template('templates/fontcustom.css', File.join(@output, 'fontcustom.css'))
|
101
|
+
template('templates/fontcustom-ie7.css', File.join(@output, 'fontcustom-ie7.css'))
|
102
|
+
template('templates/test.html', File.join(@output, 'test.html')) if options.html
|
85
103
|
end
|
86
104
|
end
|
87
105
|
end
|
@@ -0,0 +1,466 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# ***** BEGIN LICENSE BLOCK *****
|
3
|
+
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
4
|
+
#
|
5
|
+
# The contents of this file are subject to the Mozilla Public License Version
|
6
|
+
# 1.1 (the "License"); you may not use this file except in compliance with
|
7
|
+
# the License. You may obtain a copy of the License at
|
8
|
+
# http://www.mozilla.org/MPL/
|
9
|
+
#
|
10
|
+
# Software distributed under the License is distributed on an "AS IS" basis,
|
11
|
+
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
12
|
+
# for the specific language governing rights and limitations under the
|
13
|
+
# License.
|
14
|
+
#
|
15
|
+
# The Original Code is font utility code.
|
16
|
+
#
|
17
|
+
# The Initial Developer of the Original Code is Mozilla Corporation.
|
18
|
+
# Portions created by the Initial Developer are Copyright (C) 2009
|
19
|
+
# the Initial Developer. All Rights Reserved.
|
20
|
+
#
|
21
|
+
# Contributor(s):
|
22
|
+
# John Daggett <jdaggett@mozilla.com>
|
23
|
+
#
|
24
|
+
# Alternatively, the contents of this file may be used under the terms of
|
25
|
+
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
26
|
+
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
27
|
+
# in which case the provisions of the GPL or the LGPL are applicable instead
|
28
|
+
# of those above. If you wish to allow use of your version of this file only
|
29
|
+
# under the terms of either the GPL or the LGPL, and not to allow others to
|
30
|
+
# use your version of this file under the terms of the MPL, indicate your
|
31
|
+
# decision by deleting the provisions above and replace them with the notice
|
32
|
+
# and other provisions required by the GPL or the LGPL. If you do not delete
|
33
|
+
# the provisions above, a recipient may use your version of this file under
|
34
|
+
# the terms of any one of the MPL, the GPL or the LGPL.
|
35
|
+
#
|
36
|
+
# ***** END LICENSE BLOCK ***** */
|
37
|
+
|
38
|
+
# eotlitetool.py - create EOT version of OpenType font for use with IE
|
39
|
+
#
|
40
|
+
# Usage: eotlitetool.py [-o output-filename] font1 [font2 ...]
|
41
|
+
#
|
42
|
+
|
43
|
+
# OpenType file structure
|
44
|
+
# http://www.microsoft.com/typography/otspec/otff.htm
|
45
|
+
#
|
46
|
+
# Types:
|
47
|
+
#
|
48
|
+
# BYTE 8-bit unsigned integer.
|
49
|
+
# CHAR 8-bit signed integer.
|
50
|
+
# USHORT 16-bit unsigned integer.
|
51
|
+
# SHORT 16-bit signed integer.
|
52
|
+
# ULONG 32-bit unsigned integer.
|
53
|
+
# Fixed 32-bit signed fixed-point number (16.16)
|
54
|
+
# LONGDATETIME Date represented in number of seconds since 12:00 midnight, January 1, 1904. The value is represented as a signed 64-bit integer.
|
55
|
+
#
|
56
|
+
# SFNT Header
|
57
|
+
#
|
58
|
+
# Fixed sfnt version // 0x00010000 for version 1.0.
|
59
|
+
# USHORT numTables // Number of tables.
|
60
|
+
# USHORT searchRange // (Maximum power of 2 <= numTables) x 16.
|
61
|
+
# USHORT entrySelector // Log2(maximum power of 2 <= numTables).
|
62
|
+
# USHORT rangeShift // NumTables x 16-searchRange.
|
63
|
+
#
|
64
|
+
# Table Directory
|
65
|
+
#
|
66
|
+
# ULONG tag // 4-byte identifier.
|
67
|
+
# ULONG checkSum // CheckSum for this table.
|
68
|
+
# ULONG offset // Offset from beginning of TrueType font file.
|
69
|
+
# ULONG length // Length of this table.
|
70
|
+
#
|
71
|
+
# OS/2 Table (Version 4)
|
72
|
+
#
|
73
|
+
# USHORT version // 0x0004
|
74
|
+
# SHORT xAvgCharWidth
|
75
|
+
# USHORT usWeightClass
|
76
|
+
# USHORT usWidthClass
|
77
|
+
# USHORT fsType
|
78
|
+
# SHORT ySubscriptXSize
|
79
|
+
# SHORT ySubscriptYSize
|
80
|
+
# SHORT ySubscriptXOffset
|
81
|
+
# SHORT ySubscriptYOffset
|
82
|
+
# SHORT ySuperscriptXSize
|
83
|
+
# SHORT ySuperscriptYSize
|
84
|
+
# SHORT ySuperscriptXOffset
|
85
|
+
# SHORT ySuperscriptYOffset
|
86
|
+
# SHORT yStrikeoutSize
|
87
|
+
# SHORT yStrikeoutPosition
|
88
|
+
# SHORT sFamilyClass
|
89
|
+
# BYTE panose[10]
|
90
|
+
# ULONG ulUnicodeRange1 // Bits 0-31
|
91
|
+
# ULONG ulUnicodeRange2 // Bits 32-63
|
92
|
+
# ULONG ulUnicodeRange3 // Bits 64-95
|
93
|
+
# ULONG ulUnicodeRange4 // Bits 96-127
|
94
|
+
# CHAR achVendID[4]
|
95
|
+
# USHORT fsSelection
|
96
|
+
# USHORT usFirstCharIndex
|
97
|
+
# USHORT usLastCharIndex
|
98
|
+
# SHORT sTypoAscender
|
99
|
+
# SHORT sTypoDescender
|
100
|
+
# SHORT sTypoLineGap
|
101
|
+
# USHORT usWinAscent
|
102
|
+
# USHORT usWinDescent
|
103
|
+
# ULONG ulCodePageRange1 // Bits 0-31
|
104
|
+
# ULONG ulCodePageRange2 // Bits 32-63
|
105
|
+
# SHORT sxHeight
|
106
|
+
# SHORT sCapHeight
|
107
|
+
# USHORT usDefaultChar
|
108
|
+
# USHORT usBreakChar
|
109
|
+
# USHORT usMaxContext
|
110
|
+
#
|
111
|
+
#
|
112
|
+
# The Naming Table is organized as follows:
|
113
|
+
#
|
114
|
+
# [name table header]
|
115
|
+
# [name records]
|
116
|
+
# [string data]
|
117
|
+
#
|
118
|
+
# Name Table Header
|
119
|
+
#
|
120
|
+
# USHORT format // Format selector (=0).
|
121
|
+
# USHORT count // Number of name records.
|
122
|
+
# USHORT stringOffset // Offset to start of string storage (from start of table).
|
123
|
+
#
|
124
|
+
# Name Record
|
125
|
+
#
|
126
|
+
# USHORT platformID // Platform ID.
|
127
|
+
# USHORT encodingID // Platform-specific encoding ID.
|
128
|
+
# USHORT languageID // Language ID.
|
129
|
+
# USHORT nameID // Name ID.
|
130
|
+
# USHORT length // String length (in bytes).
|
131
|
+
# USHORT offset // String offset from start of storage area (in bytes).
|
132
|
+
#
|
133
|
+
# head Table
|
134
|
+
#
|
135
|
+
# Fixed tableVersion // Table version number 0x00010000 for version 1.0.
|
136
|
+
# Fixed fontRevision // Set by font manufacturer.
|
137
|
+
# ULONG checkSumAdjustment // To compute: set it to 0, sum the entire font as ULONG, then store 0xB1B0AFBA - sum.
|
138
|
+
# ULONG magicNumber // Set to 0x5F0F3CF5.
|
139
|
+
# USHORT flags
|
140
|
+
# USHORT unitsPerEm // Valid range is from 16 to 16384. This value should be a power of 2 for fonts that have TrueType outlines.
|
141
|
+
# LONGDATETIME created // Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer
|
142
|
+
# LONGDATETIME modified // Number of seconds since 12:00 midnight, January 1, 1904. 64-bit integer
|
143
|
+
# SHORT xMin // For all glyph bounding boxes.
|
144
|
+
# SHORT yMin
|
145
|
+
# SHORT xMax
|
146
|
+
# SHORT yMax
|
147
|
+
# USHORT macStyle
|
148
|
+
# USHORT lowestRecPPEM // Smallest readable size in pixels.
|
149
|
+
# SHORT fontDirectionHint
|
150
|
+
# SHORT indexToLocFormat // 0 for short offsets, 1 for long.
|
151
|
+
# SHORT glyphDataFormat // 0 for current format.
|
152
|
+
#
|
153
|
+
#
|
154
|
+
#
|
155
|
+
# Embedded OpenType (EOT) file format
|
156
|
+
# http://www.w3.org/Submission/EOT/
|
157
|
+
#
|
158
|
+
# EOT version 0x00020001
|
159
|
+
#
|
160
|
+
# An EOT font consists of a header with the original OpenType font
|
161
|
+
# appended at the end. Most of the data in the EOT header is simply a
|
162
|
+
# copy of data from specific tables within the font data. The exceptions
|
163
|
+
# are the 'Flags' field and the root string name field. The root string
|
164
|
+
# is a set of names indicating domains for which the font data can be
|
165
|
+
# used. A null root string implies the font data can be used anywhere.
|
166
|
+
# The EOT header is in little-endian byte order but the font data remains
|
167
|
+
# in big-endian order as specified by the OpenType spec.
|
168
|
+
#
|
169
|
+
# Overall structure:
|
170
|
+
#
|
171
|
+
# [EOT header]
|
172
|
+
# [EOT name records]
|
173
|
+
# [font data]
|
174
|
+
#
|
175
|
+
# EOT header
|
176
|
+
#
|
177
|
+
# ULONG eotSize // Total structure length in bytes (including string and font data)
|
178
|
+
# ULONG fontDataSize // Length of the OpenType font (FontData) in bytes
|
179
|
+
# ULONG version // Version number of this format - 0x00020001
|
180
|
+
# ULONG flags // Processing Flags (0 == no special processing)
|
181
|
+
# BYTE fontPANOSE[10] // OS/2 Table panose
|
182
|
+
# BYTE charset // DEFAULT_CHARSET (0x01)
|
183
|
+
# BYTE italic // 0x01 if ITALIC in OS/2 Table fsSelection is set, 0 otherwise
|
184
|
+
# ULONG weight // OS/2 Table usWeightClass
|
185
|
+
# USHORT fsType // OS/2 Table fsType (specifies embedding permission flags)
|
186
|
+
# USHORT magicNumber // Magic number for EOT file - 0x504C.
|
187
|
+
# ULONG unicodeRange1 // OS/2 Table ulUnicodeRange1
|
188
|
+
# ULONG unicodeRange2 // OS/2 Table ulUnicodeRange2
|
189
|
+
# ULONG unicodeRange3 // OS/2 Table ulUnicodeRange3
|
190
|
+
# ULONG unicodeRange4 // OS/2 Table ulUnicodeRange4
|
191
|
+
# ULONG codePageRange1 // OS/2 Table ulCodePageRange1
|
192
|
+
# ULONG codePageRange2 // OS/2 Table ulCodePageRange2
|
193
|
+
# ULONG checkSumAdjustment // head Table CheckSumAdjustment
|
194
|
+
# ULONG reserved[4] // Reserved - must be 0
|
195
|
+
# USHORT padding1 // Padding - must be 0
|
196
|
+
#
|
197
|
+
# EOT name records
|
198
|
+
#
|
199
|
+
# USHORT FamilyNameSize // Font family name size in bytes
|
200
|
+
# BYTE FamilyName[FamilyNameSize] // Font family name (name ID = 1), little-endian UTF-16
|
201
|
+
# USHORT Padding2 // Padding - must be 0
|
202
|
+
#
|
203
|
+
# USHORT StyleNameSize // Style name size in bytes
|
204
|
+
# BYTE StyleName[StyleNameSize] // Style name (name ID = 2), little-endian UTF-16
|
205
|
+
# USHORT Padding3 // Padding - must be 0
|
206
|
+
#
|
207
|
+
# USHORT VersionNameSize // Version name size in bytes
|
208
|
+
# bytes VersionName[VersionNameSize] // Version name (name ID = 5), little-endian UTF-16
|
209
|
+
# USHORT Padding4 // Padding - must be 0
|
210
|
+
#
|
211
|
+
# USHORT FullNameSize // Full name size in bytes
|
212
|
+
# BYTE FullName[FullNameSize] // Full name (name ID = 4), little-endian UTF-16
|
213
|
+
# USHORT Padding5 // Padding - must be 0
|
214
|
+
#
|
215
|
+
# USHORT RootStringSize // Root string size in bytes
|
216
|
+
# BYTE RootString[RootStringSize] // Root string, little-endian UTF-16
|
217
|
+
|
218
|
+
|
219
|
+
|
220
|
+
import optparse
|
221
|
+
import struct
|
222
|
+
|
223
|
+
class FontError(Exception):
|
224
|
+
"""Error related to font handling"""
|
225
|
+
pass
|
226
|
+
|
227
|
+
def multichar(str):
|
228
|
+
vals = struct.unpack('4B', str[:4])
|
229
|
+
return (vals[0] << 24) + (vals[1] << 16) + (vals[2] << 8) + vals[3]
|
230
|
+
|
231
|
+
def multicharval(v):
|
232
|
+
return struct.pack('4B', (v >> 24) & 0xFF, (v >> 16) & 0xFF, (v >> 8) & 0xFF, v & 0xFF)
|
233
|
+
|
234
|
+
class EOT:
|
235
|
+
EOT_VERSION = 0x00020001
|
236
|
+
EOT_MAGIC_NUMBER = 0x504c
|
237
|
+
EOT_DEFAULT_CHARSET = 0x01
|
238
|
+
EOT_FAMILY_NAME_INDEX = 0 # order of names in variable portion of EOT header
|
239
|
+
EOT_STYLE_NAME_INDEX = 1
|
240
|
+
EOT_VERSION_NAME_INDEX = 2
|
241
|
+
EOT_FULL_NAME_INDEX = 3
|
242
|
+
EOT_NUM_NAMES = 4
|
243
|
+
|
244
|
+
EOT_HEADER_PACK = '<4L10B2BL2H7L18x'
|
245
|
+
|
246
|
+
class OpenType:
|
247
|
+
SFNT_CFF = multichar('OTTO') # Postscript CFF SFNT version
|
248
|
+
SFNT_TRUE = 0x10000 # Standard TrueType version
|
249
|
+
SFNT_APPLE = multichar('true') # Apple TrueType version
|
250
|
+
|
251
|
+
SFNT_UNPACK = '>I4H'
|
252
|
+
TABLE_DIR_UNPACK = '>4I'
|
253
|
+
|
254
|
+
TABLE_HEAD = multichar('head') # TrueType table tags
|
255
|
+
TABLE_NAME = multichar('name')
|
256
|
+
TABLE_OS2 = multichar('OS/2')
|
257
|
+
TABLE_GLYF = multichar('glyf')
|
258
|
+
TABLE_CFF = multichar('CFF ')
|
259
|
+
|
260
|
+
OS2_FSSELECTION_ITALIC = 0x1
|
261
|
+
OS2_UNPACK = '>4xH2xH22x10B4L4xH14x2L'
|
262
|
+
|
263
|
+
HEAD_UNPACK = '>8xL'
|
264
|
+
|
265
|
+
NAME_RECORD_UNPACK = '>6H'
|
266
|
+
NAME_ID_FAMILY = 1
|
267
|
+
NAME_ID_STYLE = 2
|
268
|
+
NAME_ID_UNIQUE = 3
|
269
|
+
NAME_ID_FULL = 4
|
270
|
+
NAME_ID_VERSION = 5
|
271
|
+
NAME_ID_POSTSCRIPT = 6
|
272
|
+
PLATFORM_ID_UNICODE = 0 # Mac OS uses this typically
|
273
|
+
PLATFORM_ID_MICROSOFT = 3
|
274
|
+
ENCODING_ID_MICROSOFT_UNICODEBMP = 1 # with Microsoft platformID BMP-only Unicode encoding
|
275
|
+
LANG_ID_MICROSOFT_EN_US = 0x0409 # with Microsoft platformID EN US lang code
|
276
|
+
|
277
|
+
def eotname(ttf):
|
278
|
+
i = ttf.rfind('.')
|
279
|
+
if i != -1:
|
280
|
+
ttf = ttf[:i]
|
281
|
+
return ttf + '.eotlite'
|
282
|
+
|
283
|
+
def readfont(f):
|
284
|
+
data = open(f, 'rb').read()
|
285
|
+
return data
|
286
|
+
|
287
|
+
def get_table_directory(data):
|
288
|
+
"""read the SFNT header and table directory"""
|
289
|
+
datalen = len(data)
|
290
|
+
sfntsize = struct.calcsize(OpenType.SFNT_UNPACK)
|
291
|
+
if sfntsize > datalen:
|
292
|
+
raise FontError, 'truncated font data'
|
293
|
+
sfntvers, numTables = struct.unpack(OpenType.SFNT_UNPACK, data[:sfntsize])[:2]
|
294
|
+
if sfntvers != OpenType.SFNT_CFF and sfntvers != OpenType.SFNT_TRUE:
|
295
|
+
raise FontError, 'invalid font type';
|
296
|
+
|
297
|
+
font = {}
|
298
|
+
font['version'] = sfntvers
|
299
|
+
font['numTables'] = numTables
|
300
|
+
|
301
|
+
# create set of offsets, lengths for tables
|
302
|
+
table_dir_size = struct.calcsize(OpenType.TABLE_DIR_UNPACK)
|
303
|
+
if sfntsize + table_dir_size * numTables > datalen:
|
304
|
+
raise FontError, 'truncated font data, table directory extends past end of data'
|
305
|
+
table_dir = {}
|
306
|
+
for i in range(0, numTables):
|
307
|
+
start = sfntsize + i * table_dir_size
|
308
|
+
end = start + table_dir_size
|
309
|
+
tag, check, bongo, dirlen = struct.unpack(OpenType.TABLE_DIR_UNPACK, data[start:end])
|
310
|
+
table_dir[tag] = {'offset': bongo, 'length': dirlen, 'checksum': check}
|
311
|
+
|
312
|
+
font['tableDir'] = table_dir
|
313
|
+
|
314
|
+
return font
|
315
|
+
|
316
|
+
def get_name_records(nametable):
|
317
|
+
"""reads through the name records within name table"""
|
318
|
+
name = {}
|
319
|
+
# read the header
|
320
|
+
headersize = 6
|
321
|
+
count, strOffset = struct.unpack('>2H', nametable[2:6])
|
322
|
+
namerecsize = struct.calcsize(OpenType.NAME_RECORD_UNPACK)
|
323
|
+
if count * namerecsize + headersize > len(nametable):
|
324
|
+
raise FontError, 'names exceed size of name table'
|
325
|
+
name['count'] = count
|
326
|
+
name['strOffset'] = strOffset
|
327
|
+
|
328
|
+
# read through the name records
|
329
|
+
namerecs = {}
|
330
|
+
for i in range(0, count):
|
331
|
+
start = headersize + i * namerecsize
|
332
|
+
end = start + namerecsize
|
333
|
+
platformID, encodingID, languageID, nameID, namelen, offset = struct.unpack(OpenType.NAME_RECORD_UNPACK, nametable[start:end])
|
334
|
+
if platformID != OpenType.PLATFORM_ID_MICROSOFT or \
|
335
|
+
encodingID != OpenType.ENCODING_ID_MICROSOFT_UNICODEBMP or \
|
336
|
+
languageID != OpenType.LANG_ID_MICROSOFT_EN_US:
|
337
|
+
continue
|
338
|
+
namerecs[nameID] = {'offset': offset, 'length': namelen}
|
339
|
+
|
340
|
+
name['namerecords'] = namerecs
|
341
|
+
return name
|
342
|
+
|
343
|
+
def make_eot_name_headers(fontdata, nameTableDir):
|
344
|
+
"""extracts names from the name table and generates the names header portion of the EOT header"""
|
345
|
+
nameoffset = nameTableDir['offset']
|
346
|
+
namelen = nameTableDir['length']
|
347
|
+
name = get_name_records(fontdata[nameoffset : nameoffset + namelen])
|
348
|
+
namestroffset = name['strOffset']
|
349
|
+
namerecs = name['namerecords']
|
350
|
+
|
351
|
+
eotnames = (OpenType.NAME_ID_FAMILY, OpenType.NAME_ID_STYLE, OpenType.NAME_ID_VERSION, OpenType.NAME_ID_FULL)
|
352
|
+
nameheaders = []
|
353
|
+
for nameid in eotnames:
|
354
|
+
if nameid in namerecs:
|
355
|
+
namerecord = namerecs[nameid]
|
356
|
+
noffset = namerecord['offset']
|
357
|
+
nlen = namerecord['length']
|
358
|
+
nformat = '%dH' % (nlen / 2) # length is in number of bytes
|
359
|
+
start = nameoffset + namestroffset + noffset
|
360
|
+
end = start + nlen
|
361
|
+
nstr = struct.unpack('>' + nformat, fontdata[start:end])
|
362
|
+
nameheaders.append(struct.pack('<H' + nformat + '2x', nlen, *nstr))
|
363
|
+
else:
|
364
|
+
nameheaders.append(struct.pack('4x')) # len = 0, padding = 0
|
365
|
+
|
366
|
+
return ''.join(nameheaders)
|
367
|
+
|
368
|
+
# just return a null-string (len = 0)
|
369
|
+
def make_root_string():
|
370
|
+
return struct.pack('2x')
|
371
|
+
|
372
|
+
def make_eot_header(fontdata):
|
373
|
+
"""given ttf font data produce an EOT header"""
|
374
|
+
fontDataSize = len(fontdata)
|
375
|
+
font = get_table_directory(fontdata)
|
376
|
+
|
377
|
+
# toss out .otf fonts, t2embed library doesn't support these
|
378
|
+
tableDir = font['tableDir']
|
379
|
+
|
380
|
+
# check for required tables
|
381
|
+
required = (OpenType.TABLE_HEAD, OpenType.TABLE_NAME, OpenType.TABLE_OS2)
|
382
|
+
for table in required:
|
383
|
+
if not (table in tableDir):
|
384
|
+
raise FontError, 'missing required table ' + multicharval(table)
|
385
|
+
|
386
|
+
# read name strings
|
387
|
+
|
388
|
+
# pull out data from individual tables to construct fixed header portion
|
389
|
+
# need to calculate eotSize before packing
|
390
|
+
version = EOT.EOT_VERSION
|
391
|
+
flags = 0
|
392
|
+
charset = EOT.EOT_DEFAULT_CHARSET
|
393
|
+
magicNumber = EOT.EOT_MAGIC_NUMBER
|
394
|
+
|
395
|
+
# read values from OS/2 table
|
396
|
+
os2Dir = tableDir[OpenType.TABLE_OS2]
|
397
|
+
os2offset = os2Dir['offset']
|
398
|
+
os2size = struct.calcsize(OpenType.OS2_UNPACK)
|
399
|
+
|
400
|
+
if os2size > os2Dir['length']:
|
401
|
+
raise FontError, 'OS/2 table invalid length'
|
402
|
+
|
403
|
+
os2fields = struct.unpack(OpenType.OS2_UNPACK, fontdata[os2offset : os2offset + os2size])
|
404
|
+
|
405
|
+
panose = []
|
406
|
+
urange = []
|
407
|
+
codepage = []
|
408
|
+
|
409
|
+
weight, fsType = os2fields[:2]
|
410
|
+
panose[:10] = os2fields[2:12]
|
411
|
+
urange[:4] = os2fields[12:16]
|
412
|
+
fsSelection = os2fields[16]
|
413
|
+
codepage[:2] = os2fields[17:19]
|
414
|
+
|
415
|
+
italic = fsSelection & OpenType.OS2_FSSELECTION_ITALIC
|
416
|
+
|
417
|
+
# read in values from head table
|
418
|
+
headDir = tableDir[OpenType.TABLE_HEAD]
|
419
|
+
headoffset = headDir['offset']
|
420
|
+
headsize = struct.calcsize(OpenType.HEAD_UNPACK)
|
421
|
+
|
422
|
+
if headsize > headDir['length']:
|
423
|
+
raise FontError, 'head table invalid length'
|
424
|
+
|
425
|
+
headfields = struct.unpack(OpenType.HEAD_UNPACK, fontdata[headoffset : headoffset + headsize])
|
426
|
+
checkSumAdjustment = headfields[0]
|
427
|
+
|
428
|
+
# make name headers
|
429
|
+
nameheaders = make_eot_name_headers(fontdata, tableDir[OpenType.TABLE_NAME])
|
430
|
+
rootstring = make_root_string()
|
431
|
+
|
432
|
+
# calculate the total eot size
|
433
|
+
eotSize = struct.calcsize(EOT.EOT_HEADER_PACK) + len(nameheaders) + len(rootstring) + fontDataSize
|
434
|
+
fixed = struct.pack(EOT.EOT_HEADER_PACK,
|
435
|
+
*([eotSize, fontDataSize, version, flags] + panose + [charset, italic] +
|
436
|
+
[weight, fsType, magicNumber] + urange + codepage + [checkSumAdjustment]))
|
437
|
+
|
438
|
+
return ''.join((fixed, nameheaders, rootstring))
|
439
|
+
|
440
|
+
|
441
|
+
def write_eot_font(eot, header, data):
|
442
|
+
open(eot,'wb').write(''.join((header, data)))
|
443
|
+
return
|
444
|
+
|
445
|
+
def main():
|
446
|
+
|
447
|
+
# deal with options
|
448
|
+
p = optparse.OptionParser()
|
449
|
+
p.add_option('--output', '-o', default="world")
|
450
|
+
options, args = p.parse_args()
|
451
|
+
|
452
|
+
# iterate over font files
|
453
|
+
for f in args:
|
454
|
+
data = readfont(f)
|
455
|
+
if len(data) == 0:
|
456
|
+
print 'Error reading %s' % f
|
457
|
+
else:
|
458
|
+
eot = eotname(f)
|
459
|
+
header = make_eot_header(data)
|
460
|
+
write_eot_font(eot, header, data)
|
461
|
+
|
462
|
+
|
463
|
+
if __name__ == '__main__':
|
464
|
+
main()
|
465
|
+
|
466
|
+
|
@@ -10,6 +10,7 @@ try:
|
|
10
10
|
parser.add_argument('dir', metavar='directory', type=unicode, nargs=2, help='directory of vector files')
|
11
11
|
parser.add_argument('--name', metavar='fontname', type=unicode, nargs='?', default='fontcustom', help='reference name of the font (no spaces)')
|
12
12
|
parser.add_argument('--nohash', '-n', action='store_true', help='disable hash fingerprinting of font files')
|
13
|
+
parser.add_argument('--debug', '-d', action='store_true', help='display debug messages')
|
13
14
|
args = parser.parse_args()
|
14
15
|
indir = args.dir[0]
|
15
16
|
outdir = args.dir[1]
|
@@ -19,6 +20,7 @@ except ImportError:
|
|
19
20
|
parser = optparse.OptionParser(description='Convert a directory of svg and eps files into a unified font file.')
|
20
21
|
parser.add_option('--name', metavar='fontname', type='string', nargs='?', default='fontcustom', help='reference name of the font (no spaces)')
|
21
22
|
parser.add_option('--nohash', '-n', action='store_true', help='disable hash fingerprinting of font files')
|
23
|
+
parser.add_argument('--debug', '-d', action='store_true', help='display debug messages')
|
22
24
|
(args, posargs) = parser.parse_args()
|
23
25
|
indir = posargs[0]
|
24
26
|
outdir = posargs[1]
|
@@ -39,6 +41,23 @@ for dirname, dirnames, filenames in os.walk(indir):
|
|
39
41
|
size = os.path.getsize(filePath)
|
40
42
|
|
41
43
|
if ext in ['.svg', '.eps']:
|
44
|
+
if ext in ['.svg']:
|
45
|
+
# hack removal of <switch> </switch> tags
|
46
|
+
svgfile = open(filePath, 'r+')
|
47
|
+
svgtext = svgfile.read()
|
48
|
+
svgfile.seek(0)
|
49
|
+
|
50
|
+
# replace the <switch> </switch> tags with 'nothing'
|
51
|
+
svgtext = svgtext.replace('<switch>', '')
|
52
|
+
svgtext = svgtext.replace('</switch>', '')
|
53
|
+
|
54
|
+
# remove all contents of file so that we can write out the new contents
|
55
|
+
svgfile.truncate()
|
56
|
+
svgfile.write(svgtext)
|
57
|
+
|
58
|
+
svgfile.close()
|
59
|
+
# end hack
|
60
|
+
|
42
61
|
m.update(filename + str(size) + ';')
|
43
62
|
glyph = f.createChar(cp)
|
44
63
|
glyph.importOutlines(filePath)
|
@@ -82,7 +101,9 @@ except OSError:
|
|
82
101
|
# sfnt2woff from source, simplifying install.
|
83
102
|
subprocess.call(['sfnt2woff', fontfile + '.ttf'])
|
84
103
|
|
85
|
-
|
104
|
+
# eotlitetool.py script to generate IE7-compatible .eot fonts
|
105
|
+
subprocess.call('python ' + scriptPath + '/eotlitetool.py ' + fontfile + '.ttf -o ' + fontfile + '.eot', shell=True)
|
106
|
+
subprocess.call('mv ' + fontfile + '.eotlite ' + fontfile + '.eot', shell=True)
|
86
107
|
|
87
108
|
# Hint the TTF file
|
88
109
|
subprocess.call('ttfautohint -s -n ' + fontfile + '.ttf ' + fontfile + '-hinted.ttf > /dev/null 2>&1 && mv ' + fontfile + '-hinted.ttf ' + fontfile + '.ttf', shell=True)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
[class^="icon-"],
|
2
|
+
[class*=" icon-"] {
|
3
|
+
font-family: "<%= @name %>";
|
4
|
+
font-style: normal;
|
5
|
+
font-weight: normal;
|
6
|
+
}
|
7
|
+
.btn.dropdown-toggle [class^="icon-"],
|
8
|
+
.btn.dropdown-toggle [class*=" icon-"] {
|
9
|
+
/* keeps button heights with and without icons the same */
|
10
|
+
|
11
|
+
line-height: 1.4em;
|
12
|
+
}
|
13
|
+
.icon-large {
|
14
|
+
font-size: 1.3333em;
|
15
|
+
}
|
16
|
+
|
17
|
+
<% @classes.each_with_index do |name, index| %>
|
18
|
+
.icon-<%= name %> { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#x<%= (61696+index).to_s(16) %>; ');} <% end %>
|
@@ -0,0 +1,100 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Your Font/Glyphs</title>
|
5
|
+
<!--[if IE]><script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
|
6
|
+
|
7
|
+
<link rel="stylesheet" href="fontcustom.css" />
|
8
|
+
<!--[if lte IE 7]>
|
9
|
+
<link rel="stylesheet" href="fontcustom-ie7.css" />
|
10
|
+
<![endif]-->
|
11
|
+
|
12
|
+
|
13
|
+
<style>
|
14
|
+
section, header, footer {display: block;}
|
15
|
+
body {
|
16
|
+
font-family: sans-serif;
|
17
|
+
color: #444;
|
18
|
+
line-height: 1.5;
|
19
|
+
font-size: 1em;
|
20
|
+
}
|
21
|
+
* {
|
22
|
+
-moz-box-sizing: border-box;
|
23
|
+
-webkit-box-sizing: border-box;
|
24
|
+
box-sizing: border-box;
|
25
|
+
margin: 0;
|
26
|
+
padding: 0;
|
27
|
+
}
|
28
|
+
.glyph {
|
29
|
+
float: left;
|
30
|
+
text-align: center;
|
31
|
+
background: #eee;
|
32
|
+
padding: .75em;
|
33
|
+
margin: .75em 1.5em .75em 0;
|
34
|
+
width: 7em;
|
35
|
+
border-radius: .25em;
|
36
|
+
box-shadow: inset 0 0 0 1px #f8f8f8, 0 0 0 1px #CCC;
|
37
|
+
}
|
38
|
+
.glyph input {
|
39
|
+
width: 100%;
|
40
|
+
text-align: center;
|
41
|
+
font-family: consolas, monospace;
|
42
|
+
}
|
43
|
+
.glyph input, .mtm {
|
44
|
+
margin-top: .75em;
|
45
|
+
}
|
46
|
+
.w-main {
|
47
|
+
width: 80%;
|
48
|
+
}
|
49
|
+
.centered {
|
50
|
+
margin-left: auto;
|
51
|
+
margin-right: auto;
|
52
|
+
}
|
53
|
+
.fs1 {
|
54
|
+
font-size: 2em;
|
55
|
+
}
|
56
|
+
header {
|
57
|
+
margin: 2em 0;
|
58
|
+
padding-bottom: .5em;
|
59
|
+
color: #666;
|
60
|
+
box-shadow: 0 2px #eee;
|
61
|
+
}
|
62
|
+
header h1 {
|
63
|
+
font-size: 2em;
|
64
|
+
font-weight: normal;
|
65
|
+
}
|
66
|
+
.clearfix:before, .clearfix:after { content: ""; display: table; }
|
67
|
+
.clearfix:after, .clear { clear: both; }
|
68
|
+
footer {
|
69
|
+
margin-top: 2em;
|
70
|
+
padding: .5em 0;
|
71
|
+
box-shadow: 0 -2px #eee;
|
72
|
+
}
|
73
|
+
a, a:visited {
|
74
|
+
color: #B35047;
|
75
|
+
text-decoration: none;
|
76
|
+
}
|
77
|
+
a:hover, a:focus {color: #000;}
|
78
|
+
.box1 {
|
79
|
+
display: inline-block;
|
80
|
+
width: 15em;
|
81
|
+
padding: .25em .5em;
|
82
|
+
background: #eee;
|
83
|
+
margin: .5em 1em .5em 0;
|
84
|
+
}
|
85
|
+
</style>
|
86
|
+
</head>
|
87
|
+
<body>
|
88
|
+
<div class="w-main centered">
|
89
|
+
<section class="mtm clearfix" id="glyphs">
|
90
|
+
<header> <h1>Your font contains the following glyphs</h1> </header>
|
91
|
+
<% @classes.each_with_index do |name, index| %>
|
92
|
+
<div class="glyph">
|
93
|
+
<div class="fs1" ><i class="icon-<%= name %>"></i></div>
|
94
|
+
<input type="text" readonly="readonly" value="&#x<%= (61696+index).to_s(16) %>;" />
|
95
|
+
</div>
|
96
|
+
<% end %>
|
97
|
+
</section>
|
98
|
+
</div>
|
99
|
+
</body>
|
100
|
+
</html>
|
data/lib/fontcustom/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fontcustom
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2013-02-02 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: json
|
@@ -210,9 +210,12 @@ files:
|
|
210
210
|
- lib/fontcustom.rb
|
211
211
|
- lib/fontcustom/cli.rb
|
212
212
|
- lib/fontcustom/generator.rb
|
213
|
+
- lib/fontcustom/scripts/eotlitetool.py
|
213
214
|
- lib/fontcustom/scripts/generate.py
|
214
215
|
- lib/fontcustom/scripts/sfnt2woff
|
216
|
+
- lib/fontcustom/templates/fontcustom-ie7.css
|
215
217
|
- lib/fontcustom/templates/fontcustom.css
|
218
|
+
- lib/fontcustom/templates/test.html
|
216
219
|
- lib/fontcustom/version.rb
|
217
220
|
- lib/fontcustom/watcher.rb
|
218
221
|
- spec/fixtures/empty/no_vectors_here.txt
|