fontprocessor 27.1.3

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 (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rbenv-version +1 -0
  4. data/.rspec +0 -0
  5. data/.travis.yml +17 -0
  6. data/CHANGELOG.md +64 -0
  7. data/CHANGELOG_FPV14.md +20 -0
  8. data/Dockerfile +3 -0
  9. data/Gemfile +14 -0
  10. data/Gemfile.lock +156 -0
  11. data/Guardfile +5 -0
  12. data/README.md +6 -0
  13. data/Rakefile +53 -0
  14. data/Rakefile.base +57 -0
  15. data/bin/process-file +41 -0
  16. data/config/development.yml +5 -0
  17. data/config/production.yml +3 -0
  18. data/config/staging.yml +3 -0
  19. data/config/test.yml +5 -0
  20. data/fontprocessor.gemspec +37 -0
  21. data/lib/fontprocessor/config.rb +85 -0
  22. data/lib/fontprocessor/external/batik/batik-ttf2svg.jar +0 -0
  23. data/lib/fontprocessor/external/batik/lib/batik-svggen.jar +0 -0
  24. data/lib/fontprocessor/external/batik/lib/batik-util.jar +0 -0
  25. data/lib/fontprocessor/external/fontforge/subset.py +30 -0
  26. data/lib/fontprocessor/external/fontforge/utils.py +66 -0
  27. data/lib/fontprocessor/external_execution.rb +117 -0
  28. data/lib/fontprocessor/font_file.rb +149 -0
  29. data/lib/fontprocessor/font_file_naming_strategy.rb +63 -0
  30. data/lib/fontprocessor/font_format.rb +29 -0
  31. data/lib/fontprocessor/process_font_job.rb +227 -0
  32. data/lib/fontprocessor/processed_font_iterator.rb +89 -0
  33. data/lib/fontprocessor/processor.rb +790 -0
  34. data/lib/fontprocessor/version.rb +3 -0
  35. data/lib/fontprocessor.rb +16 -0
  36. data/scripts/build_and_test.sh +15 -0
  37. data/scripts/get_production_source_map.rb +53 -0
  38. data/spec/fixtures/bad_os2_width_class.otf +0 -0
  39. data/spec/fixtures/extra_language_names.otf +0 -0
  40. data/spec/fixtures/fixtures.rb +35 -0
  41. data/spec/fixtures/locked.otf +0 -0
  42. data/spec/fixtures/op_size.otf +0 -0
  43. data/spec/fixtures/ots_failure_font.otf +0 -0
  44. data/spec/fixtures/postscript.otf +0 -0
  45. data/spec/fixtures/stat_font.otf +0 -0
  46. data/spec/fixtures/truetype.otf +0 -0
  47. data/spec/lib/fontprocessor/config_spec.rb +38 -0
  48. data/spec/lib/fontprocessor/external_execution_spec.rb +33 -0
  49. data/spec/lib/fontprocessor/font_file_naming_strategy_spec.rb +13 -0
  50. data/spec/lib/fontprocessor/font_file_spec.rb +110 -0
  51. data/spec/lib/fontprocessor/process_font_job_spec.rb +317 -0
  52. data/spec/lib/fontprocessor/processed_font_iterator_spec.rb +128 -0
  53. data/spec/lib/fontprocessor/processor_spec.rb +466 -0
  54. data/spec/spec_helper.rb +4 -0
  55. data/tasks/fonts.rake +30 -0
  56. data/worker.rb +23 -0
  57. metadata +312 -0
@@ -0,0 +1,128 @@
1
+ require 'spec_helper'
2
+ require 'tempfile'
3
+
4
+ describe FontProcessor::ProcessedFontIterator do
5
+ before(:each) do
6
+ @fontbase_id = "a00000000000000000000001"
7
+ @charset_id = 1
8
+ @fpv = 20
9
+ @directory = File.join(Dir.tmpdir, "processed-font-iterator-#{Time.now.to_i}")
10
+ end
11
+ subject { FontProcessor::ProcessedFontIterator.new(@fontbase_id, @charset_id, @fpv, @directory, true) }
12
+
13
+ describe ".valid" do
14
+ before(:each) do
15
+ FileUtils.mkdir_p(@directory)
16
+ end
17
+ after(:each) do
18
+ FileUtils.rm_r(@directory)
19
+ end
20
+
21
+ it "is true when a truetype file is successfully processed" do
22
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-otf.ttf"))
23
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-woff.ttf"))
24
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-dyna_base.ttf"))
25
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-woff2.ttf"))
26
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-inst.ttf"))
27
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-woff_raw.ttf"))
28
+ expect(subject.valid?).to be true
29
+ end
30
+ it "isn't true when a truetype file is missing a file" do
31
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-otf.ttf"))
32
+ expect(subject.valid?).to be false
33
+ end
34
+ it "is true when a postscript file is successfully processed" do
35
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-otf.ttf"))
36
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-inst.ttf"))
37
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-woff.ttf"))
38
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-dyna_base.ttf"))
39
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-woff2.ttf"))
40
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-woff_raw.ttf"))
41
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-otf.cff"))
42
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-inst.cff"))
43
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-dyna_base.cff"))
44
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-woff.cff"))
45
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-woff2.cff"))
46
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-woff_raw.cff"))
47
+ expect(subject.valid?).to be true
48
+ end
49
+ it "isn't true when a postscript file is missing a postscript file" do
50
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-otf.ttf"))
51
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-woff.ttf"))
52
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-otf.cff"))
53
+ expect(subject.valid?).to be false
54
+ end
55
+ it "isn't true when a postscript file is missing a truetype file" do
56
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-woff.ttf"))
57
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-otf.cff"))
58
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-woff.cff"))
59
+ expect(subject.valid?).to be false
60
+ end
61
+ it "is true when a postscript file is not converting to a truetype file" do
62
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-dyna_base.cff"))
63
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-otf.cff"))
64
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-inst.cff"))
65
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-woff.cff"))
66
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-woff2.cff"))
67
+ FileUtils.touch(File.join(@directory, "charset-#{@charset_id}-woff_raw.cff"))
68
+ no_ttf_iterator = FontProcessor::ProcessedFontIterator.new(@fontbase_id, @charset_id, @fpv, @directory, false)
69
+ expect(no_ttf_iterator.valid?).to be true
70
+ end
71
+ end
72
+
73
+ describe ".s3_key" do
74
+ it "can return a proper s3 key for a normal file" do
75
+ expect(subject.s3_key("otf", "ttf")).to eq "#{@fontbase_id}/#{@fpv}/#{@charset_id}/otf.ttf"
76
+ end
77
+ end
78
+
79
+ describe ".each" do
80
+ before(:each) do
81
+ FileUtils.mkdir_p(@directory)
82
+ end
83
+ after(:each) do
84
+ FileUtils.rm_r(@directory)
85
+ end
86
+
87
+ it "can successfully iterate" do
88
+ @file_1 = File.join(@directory, "charset-#{@charset_id}-otf.ttf")
89
+ @file_2 = File.join(@directory, "charset-#{@charset_id}-woff.ttf")
90
+ @file_3 = File.join(@directory, "charset-#{@charset_id}-woff2.ttf")
91
+ @file_4 = File.join(@directory, "charset-#{@charset_id}-woff_raw.ttf")
92
+ @file_5 = File.join(@directory, "charset-#{@charset_id}-inst.ttf")
93
+ @file_6 = File.join(@directory, "charset-#{@charset_id}-dyna_base.ttf")
94
+ @file_7 = File.join(@directory, "charset-#{@charset_id}-otf.otf")
95
+ @file_8 = File.join(@directory, "charset-#{@charset_id}-woff.otf")
96
+ @file_9 = File.join(@directory, "charset-#{@charset_id}-woff2.otf")
97
+ @file_10 = File.join(@directory, "charset-#{@charset_id}-woff_raw.otf")
98
+ @file_11 = File.join(@directory, "charset-#{@charset_id}-inst.otf")
99
+ @file_12 = File.join(@directory, "charset-#{@charset_id}-dyna_base.otf")
100
+ @file_13 = File.join(@directory, "source.otf")
101
+
102
+ [@file_1, @file_2, @file_3, @file_4, @file_5, @file_6, @file_7, @file_8, @file_9, @file_10, @file_11, @file_12, @file_13].each { |f| FileUtils.touch(f) }
103
+
104
+ results = []
105
+ subject.each { |file, s3_key| results << [s3_key, file] }
106
+ results.sort!
107
+
108
+ expect(results).to eq [
109
+ ["a00000000000000000000001/20/1/dyna_base.cff", @file_12],
110
+ ["a00000000000000000000001/20/1/dyna_base.ttf", @file_6],
111
+ ["a00000000000000000000001/20/1/inst.cff", @file_11],
112
+ ["a00000000000000000000001/20/1/inst.ttf", @file_5],
113
+ ["a00000000000000000000001/20/1/otf.cff", @file_7],
114
+ ["a00000000000000000000001/20/1/otf.ttf", @file_1],
115
+ ["a00000000000000000000001/20/1/woff.cff", @file_8],
116
+ ["a00000000000000000000001/20/1/woff.ttf", @file_2],
117
+ ["a00000000000000000000001/20/1/woff2.cff", @file_9],
118
+ ["a00000000000000000000001/20/1/woff2.ttf", @file_3],
119
+ ["a00000000000000000000001/20/1/woff_raw.cff", @file_10],
120
+ ["a00000000000000000000001/20/1/woff_raw.ttf", @file_4]
121
+ ]
122
+ end
123
+
124
+ it "won't iterate over missing files" do
125
+ expect { subject.each }.to raise_error(FontProcessor::MissingFilesException)
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,466 @@
1
+ require 'set'
2
+ require 'spec_helper'
3
+ require 'fixtures/fixtures'
4
+
5
+ describe FontProcessor do
6
+ def self.setup_fonts(fixture)
7
+ before(:all) do
8
+ @char_set = JSON.parse('{"charset_id":"1","features":"ALL","unicode":""}')
9
+ @formats = JSON.parse('{"process_original":true,"convert":true,"derivatives":["woff","svg","swf","inst"]}')
10
+ @mini_char_set = JSON.parse('{"charset_id":"a","features":"ALL","unicode":"65..120"}')
11
+ @char_set_uni_vals = [0x20..0x7e,0xa0..0xffff]
12
+ @mini_char_set_uni_vals = 65..120
13
+ @fixture = FontProcessor::Specs::Fixture.new(fixture)
14
+ @naming_strategy = @fixture.naming_strategy
15
+ @license_url = "http://custom-url"
16
+ end
17
+
18
+ subject { FontProcessor::Processor.new(@naming_strategy, @license_url, "01") }
19
+
20
+ after(:all) do
21
+ @fixture.destroy if @fixture
22
+ end
23
+ end
24
+
25
+ context "with a bad PostScript file" do
26
+ describe "ots failure" do
27
+ setup_fonts(:ots_failure_font)
28
+ it "will raise an exception when subsetting the font" do
29
+ expect {
30
+ subject.generate_char_set(@char_set, @formats)
31
+ }.to raise_error(Exception)
32
+ end
33
+ end
34
+ end
35
+
36
+ context "with a bad PostScript file" do
37
+ describe "ots failure" do
38
+ setup_fonts(:ots_failure_font)
39
+ it "will raise an exception when trying to wrap the font as WOFF" do
40
+ expect {
41
+ subject.wrap_woff(@char_set, FontProcessor::FontFormat.new(:cff, :otf))
42
+ }.to raise_error
43
+ end
44
+ end
45
+ end
46
+
47
+ context "with a font with a STAT table" do
48
+ describe "stripping" do
49
+ setup_fonts(:stat_font)
50
+
51
+ it "will retain STAT referenced strings" do
52
+ path = File.join(@fixture.temp_directory, "stripped.otf")
53
+ fm = Skytype::FontManipulator.new(File.binread(@naming_strategy.source))
54
+ fm.save(path)
55
+ subject.strip(path)
56
+
57
+ #reload the fm, make sure it's name table has a STAT string, ID 256
58
+ fm = Skytype::FontManipulator.new(File.binread(path))
59
+ name_table = Skytype::NameTable.new(fm.get_table("name"))
60
+ expect(name_table.get_string(256, Skytype::NameTable::EN_LANGUAGE_ID).nil?).to be_falsey
61
+ end
62
+ end
63
+ end
64
+ context "with a PostScript file" do
65
+ describe ".file_metadata" do
66
+ setup_fonts(:postscript)
67
+
68
+ it "can detect outlines" do
69
+ expect(subject.file_metadata(@formats)).to have_postscript_outlines
70
+ expect(subject.file_metadata(@formats)).not_to have_truetype_outlines
71
+ end
72
+ end
73
+
74
+
75
+ describe ".generate_char_set" do
76
+ setup_fonts(:postscript)
77
+
78
+ it "can generate all font files for a given character set" do
79
+ subject.generate_char_set(@char_set, @formats)
80
+
81
+ expect(File.exist?(File.join(@fixture.temp_directory, "source.otf"))).to be true
82
+ expect(File.exist?(File.join(@fixture.temp_directory, "source.ttf"))).to be true
83
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.otf"))).to be true
84
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.ttf"))).to be true
85
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-woff.otf"))).to be true
86
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-woff.ttf"))).to be true
87
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-woff_raw.otf"))).to be true
88
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-woff_raw.ttf"))).to be true
89
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-swf.ttf"))).to be true
90
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-swf.otf"))).to be true
91
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-inst.ttf"))).to be true
92
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-inst.otf"))).to be true
93
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-dyna_base.ttf"))).to be true
94
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-dyna_base.otf"))).to be true
95
+
96
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-svg.ttf"))).to be false
97
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-eot.otf"))).to be false
98
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-eot.ttf"))).to be false
99
+ end
100
+
101
+ it "verifies that PUA glyphs are present" do
102
+ subject.generate_char_set(@char_set, @formats)
103
+ all_cs = File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.otf")
104
+ fm = Skytype::FontManipulator.new(File.binread(all_cs))
105
+ FontProcessor::Processor::PUA_PAIRS.each do | pair |
106
+ expect(fm.get_glyph_for_unicode( pair[0] )).not_to eq 0
107
+ end
108
+ end
109
+
110
+ it "verifies that dyna_base has proper CFF NameDict entries for dyna_base and inst" do
111
+ subject.generate_char_set(@char_set, @formats)
112
+ dyna_base_font = File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-dyna_base.otf")
113
+ fm = Skytype::FontManipulator.new(File.binread(dyna_base_font))
114
+ name_table = Skytype::NameTable.new(fm.get_table("name"))
115
+ expect(fm.get_cff_name).to eq name_table.get_string(Skytype::NameTable::POSTSCRIPT_NAME_ID, Skytype::NameTable::EN_LANGUAGE_ID)
116
+
117
+ inst_font = File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-inst.otf")
118
+ fm = Skytype::FontManipulator.new(File.binread(inst_font))
119
+ name_table = Skytype::NameTable.new(fm.get_table("name"))
120
+ expect(fm.get_cff_name).to eq name_table.get_string(Skytype::NameTable::POSTSCRIPT_NAME_ID, Skytype::NameTable::EN_LANGUAGE_ID)
121
+ end
122
+
123
+ context "with a temporarily unlocked subset" do
124
+ before(:all) do
125
+ @filename = File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.otf")
126
+ end
127
+
128
+ it "contains only the unicode in the character set" do
129
+ subject.generate_char_set(@char_set, @formats)
130
+ subject.unlock(@filename)
131
+ orig = FontProcessor::FontFile.new(File.join(@fixture.temp_directory, "source.otf"))
132
+ orig_characters = Set.new(orig.unicode)
133
+ orig_characters.add(FontProcessor::Processor::NON_BREAKING_SPACE)
134
+ expanded_set = Array.new()
135
+ @char_set_uni_vals.each { |range| expanded_set += range.entries }
136
+ expected_characters = (orig_characters & expanded_set).to_a.sort
137
+
138
+ subset = FontProcessor::FontFile.new(@filename)
139
+ expect(subset.unicode).to eq expected_characters
140
+ subject.lock(@filename)
141
+ end
142
+ end
143
+
144
+ it "can convert to TrueType outlines" do
145
+ subject.generate_char_set(@char_set, @formats)
146
+ font_file = FontProcessor::FontFile.new(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.ttf"))
147
+ expect(font_file.has_truetype_outlines?).to be true
148
+ expect(font_file.has_postscript_outlines?).to be false
149
+ end
150
+
151
+ it "adds license urls to ttf character set files" do
152
+ subject.generate_char_set(@char_set, @formats)
153
+ metadata = FontProcessor::FontFile.new(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.otf"))
154
+ expect(metadata.names['en']['License URL']).to eq @license_url
155
+ end
156
+
157
+ it "obfuscates the name table" do
158
+ subject.generate_char_set(@char_set, @formats)
159
+ metadata = FontProcessor::FontFile.new(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.otf"))
160
+ expect(metadata.names['en']['Family']).to be_nil
161
+ expect(metadata.names['en']['Full name']).to eq "-"
162
+ expect(metadata.names['en']['PostScript name']).to eq "-"
163
+ expect(metadata.names['en']['Unique ID']).not_to eq "Fontin"
164
+ end
165
+
166
+ end
167
+
168
+ describe " .generate_char_set with invalid data" do
169
+ setup_fonts(:postscript)
170
+
171
+ it "raises an exception" do
172
+ File.open(File.join(@fixture.temp_directory, "postscript.otf"), "w") { |f| f.puts "I am not a font" }
173
+ expect { subject.generate_char_set(@char_set, @formats) }.to raise_error(FontProcessor::Processor::UnknownFontFormat)
174
+ end
175
+ end
176
+
177
+ context "internally" do
178
+ setup_fonts(:postscript)
179
+
180
+ it "does not automatically generate a non-breaking space when missing" do
181
+ min_char_set = { "charset_id" => "min", "unicode" => "32..65", "features" => "NONE" }
182
+ max_char_set = { "charset_id" => "max", "unicode" => "32..160", "features" => "NONE" }
183
+ cff = FontProcessor::FontFormat.new(:cff, :otf)
184
+
185
+ subject.subset(min_char_set,
186
+ @naming_strategy.source,
187
+ @naming_strategy.source,
188
+ @naming_strategy.char_set(min_char_set['charset_id'], cff))
189
+ subject.subset(max_char_set,
190
+ @naming_strategy.source,
191
+ @naming_strategy.char_set(min_char_set['charset_id'], cff),
192
+ @naming_strategy.char_set(max_char_set['charset_id'], cff))
193
+
194
+ font_file = FontProcessor::FontFile.new(@naming_strategy.char_set(max_char_set['charset_id'], cff))
195
+ expect(font_file.unicode).not_to include(0xA0)
196
+ end
197
+
198
+ it "automatically generates a non-breaking space when missing" do
199
+ max_char_set = { "charset_id" => "max", "unicode" => "32..160", "features" => "NONE" }
200
+ cff = FontProcessor::FontFormat.new(:cff, :otf)
201
+
202
+ subject.generate_char_set(max_char_set, @formats)
203
+
204
+ font_file = FontProcessor::FontFile.new(@naming_strategy.char_set(max_char_set['charset_id'], cff))
205
+ expect(font_file.unicode).to include(0xA0)
206
+ expect(font_file.unicode).to include(0xAD)
207
+ end
208
+
209
+ it "can copy a name entry over another" do
210
+ char_set = { "charset_id" => "copy", "unicode" => "32..65", "features" => "NONE" }
211
+ cff = FontProcessor::FontFormat.new(:cff, :otf)
212
+ filename = @naming_strategy.char_set(char_set['charset_id'], cff)
213
+ subject.subset(char_set,
214
+ @naming_strategy.source,
215
+ @naming_strategy.source,
216
+ filename)
217
+ unique_string = "asdfasdfasdfsa"
218
+
219
+ subject.obfuscate_names(filename, unique_string)
220
+ subject.copy_name_record(filename,
221
+ FontProcessor::Processor::UNIQUE_ID,
222
+ FontProcessor::Processor::FAMILY_ID)
223
+
224
+ file = FontProcessor::FontFile.new(filename)
225
+ expect(file.names["en"]["Family"]).to eq unique_string
226
+ expect(file.names["en"]["Full name"]).to eq "-"
227
+ expect(file.names["en"]["PostScript name"]).to eq "-"
228
+ expect(file.names["en"]["Unique ID"]).to eq unique_string
229
+ end
230
+
231
+ end
232
+ end
233
+
234
+ context "with a TrueType file" do
235
+ describe ".file_metadata" do
236
+ setup_fonts(:truetype)
237
+
238
+ it "can detect outlines" do
239
+ expect(subject.file_metadata(@formats)).not_to have_postscript_outlines
240
+ expect(subject.file_metadata(@formats)).to have_truetype_outlines
241
+ end
242
+ end
243
+
244
+ describe " .generate_char_set" do
245
+ setup_fonts(:truetype)
246
+
247
+ it "can generate all font files for a given character set" do
248
+ subject.generate_char_set(@mini_char_set, @formats)
249
+ expect(File.exist?(File.join(@fixture.temp_directory, "source.ttf"))).to be true
250
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@mini_char_set['charset_id']}-otf.ttf"))).to be true
251
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@mini_char_set['charset_id']}-woff.ttf"))).to be true
252
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@mini_char_set['charset_id']}-woff_raw.ttf"))).to be true
253
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@mini_char_set['charset_id']}-svg.ttf"))).to be false
254
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@mini_char_set['charset_id']}-inst.ttf"))).to be true
255
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@mini_char_set['charset_id']}-eot.ttf"))).to be false
256
+ end
257
+
258
+ context "with a temporarily unlocked subset" do
259
+ before(:all) do
260
+ @filename = File.join(@fixture.temp_directory, "charset-#{@mini_char_set['charset_id']}-otf.ttf")
261
+ end
262
+
263
+ it "contains only the unicode in the character set" do
264
+ subject.generate_char_set(@mini_char_set, @formats)
265
+ subject.unlock(@filename)
266
+ orig = FontProcessor::FontFile.new(File.join(@fixture.temp_directory, "source.ttf"))
267
+ orig_characters = Set.new(orig.unicode)
268
+
269
+ expected_characters = (orig_characters & @mini_char_set_uni_vals.entries).to_a.sort
270
+
271
+ subset = FontProcessor::FontFile.new(@filename)
272
+ expect(subset.unicode).to eq expected_characters
273
+ subject.lock(@filename)
274
+ end
275
+ end
276
+
277
+ it "adds license urls to raw fonts" do
278
+ subject.generate_char_set(@mini_char_set, @formats)
279
+ metadata = FontProcessor::FontFile.new(File.join(@fixture.temp_directory, "charset-#{@mini_char_set['charset_id']}-otf.ttf"))
280
+ expect(metadata.names['en']['License URL']).to eq @license_url
281
+ end
282
+
283
+ it "obfuscates the name table" do
284
+ subject.generate_char_set(@mini_char_set, @formats)
285
+ metadata = FontProcessor::FontFile.new(File.join(@fixture.temp_directory, "charset-#{@mini_char_set['charset_id']}-otf.ttf"))
286
+ expect(metadata.names['en']['Family']).to be_nil
287
+ expect(metadata.names['en']['Full name']).to eq "-"
288
+ expect(metadata.names['en']['PostScript name']).to eq "-"
289
+ expect(metadata.names['en']['Unique ID']).not_to include "Bleeding Cowboys"
290
+ end
291
+
292
+ end
293
+
294
+ describe " .generate_char_set with limited formats" do
295
+ setup_fonts(:postscript)
296
+ it "produces just the original CFF format and no others" do
297
+ subject.generate_char_set(@char_set, JSON.parse('{"process_original":true,"convert":false,"derivatives":[]}'))
298
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.otf"))).to be true
299
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-woff.otf"))).to be false
300
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-svg.otf"))).to be false
301
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-swf.otf"))).to be false
302
+ end
303
+
304
+ it "produces just the original CFF format and a WOFF" do
305
+ subject.generate_char_set(@char_set, JSON.parse('{"process_original":true,"convert":false,"derivatives":["woff"]}'))
306
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.otf"))).to be true
307
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-woff.otf"))).to be true
308
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-svg.otf"))).to be false
309
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-swf.otf"))).to be false
310
+ end
311
+
312
+ it "produces the original CFF format and a WOFF, but no SVF because the PS original is not converted" do
313
+ subject.generate_char_set(@char_set, JSON.parse('{"process_original":true,"convert":false,"derivatives":["woff", "svg"]}'))
314
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.otf"))).to be true
315
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-woff.otf"))).to be true
316
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-svg.otf"))).to be false
317
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-swf.otf"))).to be false
318
+ end
319
+
320
+ it "produces the original CFF format, a WOFF and a SWF" do
321
+ subject.generate_char_set(@char_set, JSON.parse('{"process_original":true,"convert":false,"derivatives":["woff", "swf"]}'))
322
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.otf"))).to be true
323
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-woff.otf"))).to be true
324
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-svg.otf"))).to be false
325
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-swf.otf"))).to be true
326
+ end
327
+
328
+ it "produces the a WOFF and a SWF, but not original format file" do
329
+ subject.generate_char_set(@char_set, JSON.parse('{"process_original":false,"convert":false,"derivatives":["woff", "swf", "svg"]}'))
330
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.otf"))).to be false
331
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-woff.otf"))).to be true
332
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-swf.otf"))).to be true
333
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-svg.otf"))).to be false
334
+ end
335
+
336
+ it "to throw an error if an invalid format is supplied, even if the JSON is valid" do
337
+ format_data = '{"process_original":false,"convert":false,"derivatives":[]}'
338
+ expect {
339
+ subject.generate_char_set(@char_set, JSON.parse(format_data))
340
+ }.to raise_error "Invalid format data {\"process_original\"=>false, \"convert\"=>false, \"derivatives\"=>[]}"
341
+ end
342
+
343
+ it "to throw an error if an invalid format is supplied, even if the JSON is valid" do
344
+ format_data = '{"process_original":false,"convert":false,"derivatives":["svg"]}'
345
+ expect {
346
+ subject.generate_char_set(@char_set, JSON.parse(format_data))
347
+ }.to raise_error "Invalid format data {\"process_original\"=>false, \"convert\"=>false, \"derivatives\"=>[\"svg\"]}"
348
+ end
349
+ end
350
+
351
+ describe " .generate_char_set with conversion and derivative specifiers" do
352
+ setup_fonts(:postscript)
353
+ it "produces just the original CFF format and only the converted file" do
354
+ subject.generate_char_set(@char_set, JSON.parse('{"process_original":true,"convert":true,"derivatives":[]}'))
355
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.otf"))).to be true
356
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.ttf"))).to be true
357
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-woff.otf"))).to be false
358
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-svg.otf"))).to be false
359
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-swf.otf"))).to be false
360
+ end
361
+ it "produces only the converted file" do
362
+ subject.generate_char_set(@char_set, JSON.parse('{"process_original":false,"convert":true,"derivatives":[]}'))
363
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.otf"))).to be false
364
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.ttf"))).to be true
365
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-woff.otf"))).to be false
366
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-svg.otf"))).to be false
367
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-swf.otf"))).to be false
368
+ end
369
+ it "produces only the converted file formats" do
370
+ subject.generate_char_set(@char_set, JSON.parse('{"process_original":false,"convert":true,"derivatives":["woff", "swf", "svg"]}'))
371
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.otf"))).to be false
372
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.ttf"))).to be true
373
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-woff.ttf"))).to be true
374
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-svg.ttf"))).to be false
375
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-swf.ttf"))).to be true
376
+ end
377
+ end
378
+
379
+ describe " .generate_char_set from TrueType with limited formats" do
380
+ setup_fonts(:truetype)
381
+
382
+ it "ignores a SVG derivtive from a TrueType font" do
383
+ subject.generate_char_set(@char_set, JSON.parse('{"process_original":false,"convert":false,"derivatives":["svg"]}'))
384
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.otf"))).to be false
385
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.ttf"))).to be false
386
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-woff.ttf"))).to be false
387
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-svg.ttf"))).to be false
388
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-swf.ttf"))).to be false
389
+ end
390
+
391
+ it "produces just the derivatives from a TrueType font" do
392
+ subject.generate_char_set(@char_set, JSON.parse('{"process_original":false,"convert":false,"derivatives":["woff", "swf", "svg"]}'))
393
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.otf"))).to be false
394
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.ttf"))).to be false
395
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-woff.ttf"))).to be true
396
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-svg.ttf"))).to be false
397
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-swf.ttf"))).to be true
398
+ end
399
+ end
400
+
401
+
402
+ describe " .generate_char_set with invalid data" do
403
+ setup_fonts(:truetype)
404
+
405
+ it "raises an exception" do
406
+ File.open(File.join(@fixture.temp_directory, "truetype.otf"), "w") { |f| f.puts "I am not a font" }
407
+ expect { subject.generate_char_set(@char_set, @formats) }.to raise_error(FontProcessor::Processor::UnknownFontFormat)
408
+ end
409
+ end
410
+
411
+ describe " .generate_char_set" do
412
+ setup_fonts(:locked)
413
+
414
+ it "can generate all font files for a given character set" do
415
+ subject.generate_char_set(@char_set, @formats)
416
+ expect(File.exist?(File.join(@fixture.temp_directory, "source.ttf"))).to be true
417
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-otf.ttf"))).to be true
418
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-woff.ttf"))).to be true
419
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-eot.ttf"))).to be false
420
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-svg.ttf"))).to be false
421
+ expect(File.exist?(File.join(@fixture.temp_directory, "charset-#{@char_set['charset_id']}-swf.ttf"))).to be true
422
+ end
423
+ end
424
+
425
+ context "internally" do
426
+ setup_fonts(:truetype)
427
+
428
+ it "can copy a name entry over another" do
429
+ char_set = { "charset_id" => "copy", "unicode" => "32..65", "features" => "NONE" }
430
+ ttf = FontProcessor::FontFormat.new(:ttf, :otf)
431
+ filename = @naming_strategy.char_set(char_set['charset_id'], ttf)
432
+ subject.subset(char_set,
433
+ @naming_strategy.source,
434
+ @naming_strategy.source,
435
+ filename)
436
+ unique_string = "asdfasdfasdfsa"
437
+
438
+ subject.obfuscate_names(filename, unique_string)
439
+ subject.copy_name_record(filename,
440
+ FontProcessor::Processor::UNIQUE_ID,
441
+ FontProcessor::Processor::FAMILY_ID)
442
+
443
+ file = FontProcessor::FontFile.new(filename)
444
+ expect(file.names["en"]["Family"]).to eq unique_string
445
+ expect(file.names["en"]["Full name"]).to eq "-"
446
+ expect(file.names["en"]["PostScript name"]).to eq "-"
447
+ expect(file.names["en"]["Unique ID"]).to eq unique_string
448
+ end
449
+
450
+ context "with a file containing bad os2 width values" do
451
+ setup_fonts(:bad_os2_width_class)
452
+
453
+ it "corrects the values" do
454
+ filename = @naming_strategy.source
455
+ subject.fix_os2_weight_and_width(filename)
456
+
457
+ fm = Skytype::FontManipulator.new(File.binread(filename))
458
+ os2_data = fm.get_table("OS/2")
459
+ os2_table = Skytype::OS2Table.new(os2_data)
460
+
461
+ expect(os2_table.get_width).to eq 1
462
+ end
463
+ end
464
+ end
465
+ end
466
+ end
@@ -0,0 +1,4 @@
1
+ require 'bundler'
2
+ Bundler.require
3
+
4
+ ENV['RACK'] = 'test'
data/tasks/fonts.rake ADDED
@@ -0,0 +1,30 @@
1
+ namespace :fonts do
2
+ desc "Convert a font to SVG"
3
+ task :convert_svg => :merb_env do
4
+ TMP_DIR = "/tmp/font-convert"
5
+ TRUETYPE = FontFormat.new(:ttf, :otf)
6
+ POSTSCRIPT = FontFormat.new(:cff, :otf)
7
+ SVG = FontFormat.new(:ttf, :svg)
8
+
9
+ file = ARGV[1]
10
+ filename = File.basename(file)
11
+ output_filename = filename.split('.')[0] + ".svg"
12
+
13
+ FileUtils.mkdir(TMP_DIR) unless File.exist? TMP_DIR
14
+ FileUtils.cp(file, TMP_DIR)
15
+
16
+ naming_strategy = FontFileNamingStrategy.new(filename, TMP_DIR)
17
+ converter = FontProcessing::FontConverter.new(naming_strategy)
18
+
19
+ original_font_format = filename =~ /\.ttf$/ ? TRUETYPE : POSTSCRIPT
20
+ FileUtils.cp(naming_strategy.source, naming_strategy.source(original_font_format))
21
+
22
+ converter.convert(TRUETYPE) if original_font_format.outline_format != :ttf
23
+
24
+ FileUtils.cp(naming_strategy.source(TRUETYPE), naming_strategy.char_set(CharSet.default, TRUETYPE))
25
+ converter.convert_char_set(CharSet.default, TRUETYPE, SVG)
26
+
27
+ FileUtils.cp(naming_strategy.char_set(CharSet.default, SVG), File.join(File.dirname(file), output_filename))
28
+ FileUtils.rmdir(TMP_DIR)
29
+ end
30
+ end
data/worker.rb ADDED
@@ -0,0 +1,23 @@
1
+ require 'bundler'
2
+ Bundler.require(:default)
3
+ Resque.redis = FontProcessor::Config.redis_server
4
+
5
+ require 'resque/failure/airbrake'
6
+ Resque::Failure::Airbrake.configure do |config|
7
+ config.api_key = FontProcessor::Config.airbrake_api_key
8
+ config.secure = true
9
+ end
10
+ Resque::Failure.backend = Resque::Failure::Airbrake
11
+
12
+ queues = ["27"]
13
+
14
+ worker = Resque::Worker.new(*queues)
15
+ Resque.logger.level = Logger::DEBUG
16
+
17
+ if ENV['PIDFILE']
18
+ File.open(ENV['PIDFILE'], 'w') { |f| f << Process.pid.to_s }
19
+ end
20
+
21
+ worker.log "Starting worker #{worker}"
22
+
23
+ worker.work(5)