dragonfly_fonts 0.0.8 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +16 -7
  4. data/dragonfly_fonts.gemspec +4 -2
  5. data/lib/dragonfly_fonts.rb +22 -1
  6. data/lib/dragonfly_fonts/analysers/bbox.rb +7 -5
  7. data/lib/dragonfly_fonts/analysers/font_info.rb +7 -2
  8. data/lib/dragonfly_fonts/analysers/glyphs.rb +12 -4
  9. data/lib/dragonfly_fonts/analysers/gsub_tables.rb +12 -4
  10. data/lib/dragonfly_fonts/analysers/ots_sanitize.rb +19 -0
  11. data/lib/dragonfly_fonts/plugin.rb +33 -8
  12. data/lib/dragonfly_fonts/processors/correct_metrics.rb +7 -4
  13. data/lib/dragonfly_fonts/processors/encode.rb +37 -23
  14. data/lib/dragonfly_fonts/processors/extract_glyph.rb +14 -10
  15. data/lib/dragonfly_fonts/processors/fix_dflt_table.rb +4 -1
  16. data/lib/dragonfly_fonts/processors/normalize_names.rb +6 -3
  17. data/lib/dragonfly_fonts/processors/ots_sanitize.rb +25 -0
  18. data/lib/dragonfly_fonts/processors/set_dimensions.rb +10 -6
  19. data/lib/dragonfly_fonts/processors/set_ttf_names.rb +9 -6
  20. data/lib/dragonfly_fonts/processors/set_underline.rb +10 -6
  21. data/lib/dragonfly_fonts/processors/set_width.rb +5 -2
  22. data/lib/dragonfly_fonts/processors/set_woff_metadata.rb +6 -3
  23. data/lib/dragonfly_fonts/processors/ttf_autohint.rb +6 -3
  24. data/lib/dragonfly_fonts/processors/web_friendly.rb +6 -3
  25. data/lib/dragonfly_fonts/version.rb +1 -1
  26. data/samples/sample.eot +0 -0
  27. data/samples/sample.otf +0 -0
  28. data/samples/sample.svg +1220 -0
  29. data/samples/sample.ttf +0 -0
  30. data/samples/{Inconsolata.ttx → sample.ttx} +0 -0
  31. data/samples/sample.woff +0 -0
  32. data/samples/sample.woff2 +0 -0
  33. data/samples/{AppleGothic.ttf → sample_not_sanitized.ttf} +0 -0
  34. data/script/glyphs.py +3 -3
  35. metadata +42 -9
  36. data/lib/dragonfly_fonts/analysers/ot_sanitise.rb +0 -17
  37. data/lib/dragonfly_fonts/processors/ot_sanitise.rb +0 -22
  38. data/samples/Arial.ttf +0 -0
  39. data/samples/Inconsolata.otf +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 8c4f51e0a7125d4d21c70ccc57ee4e52dc72f17c
4
- data.tar.gz: 2db35ec845bc028784147581c92412fadbd78e1e
2
+ SHA256:
3
+ metadata.gz: 44223a769713686e569f9fe08487809b1defcd058f0ba6159f52ff7b24ad9ac7
4
+ data.tar.gz: 15a96a097b74c4c2d95edf2a2456f400a8ddacdba7f6935b8bf76c2185d83820
5
5
  SHA512:
6
- metadata.gz: b592be81310cdc581c6e3a8044ff7e94a141edf09414eedab59654b7707430c74b4f4e555cb1e77f26d0fd39a449013fa34d01c81f75b90a108e49f90c8ef396
7
- data.tar.gz: 1ec958c9a2fc62322031390101d74a992ec82ad91063c0296d07c8fb6ae886bd6e831617fce182156066bf0299319be545a8a8f31794a41dd37e1599d92ef823
6
+ metadata.gz: aeec99d3665329d6fb5d7a5dc459c1e88aeacb7cf92274019ebc01805ec98c52ad7d463b7e82c9dc9127858e8d3c4e14d050be2e61044e3b768bcf592fd81523
7
+ data.tar.gz: d9e576bfca8a265a2e6ac8303f8a51ac8dee821f2c7084889f17cec9518c3ac01ac3da72fe6ee39b40bb22f0ea965e603f29e9bfb5e612e5074628af417d3e29
@@ -0,0 +1,7 @@
1
+ # CHANGELOG
2
+
3
+ ## 1.0.0
4
+
5
+ * add `SUPPORTED_FORMATS` and `SUPPORTED_OUTPUT_FORMATS` and raise errors when formats are not matching
6
+ * add more thorough tests for supported formats
7
+ * skip unnecessary conversion from-to same format
data/README.md CHANGED
@@ -36,9 +36,9 @@ Using debian/ubuntu packages:
36
36
 
37
37
  See [fonttools](https://github.com/behdad/fonttools)
38
38
 
39
- ### ot-sanitise
39
+ ### ots-sanitize
40
40
 
41
- See [ot-sanitise](https://github.com/khaledhosny/ots)
41
+ See [ots-sanitize](https://github.com/khaledhosny/ots)
42
42
 
43
43
  ### ttf2eot
44
44
 
@@ -62,6 +62,15 @@ Dragonfly.app.configure do
62
62
  end
63
63
  ```
64
64
 
65
+ ## Supported Formats
66
+
67
+ List of supported formats is available as:
68
+
69
+ ```ruby
70
+ DragonflyFonts::SUPPORTED_FORMATS # => ["bdf", "dfont", "bin", …]
71
+ DragonflyFonts::SUPPORTED_OUTPUT_FORMATS # => ["eot", "ttf", …]
72
+ ```
73
+
65
74
  ## Analysers
66
75
 
67
76
  ### Bbox
@@ -96,12 +105,12 @@ Returns `Array` of gsub tables in the font.
96
105
  font.gsub_tables
97
106
  ```
98
107
 
99
- ### OT Sanitise
108
+ ### OTS Sanitize
100
109
 
101
- Displays output of the `ot-sanitise` command.
110
+ Displays output of the `ots-sanitize` command.
102
111
 
103
112
  ```ruby
104
- font.ot_sanitise
113
+ font.ots_sanitize
105
114
 
106
115
  ```
107
116
 
@@ -154,10 +163,10 @@ font.normalize_names
154
163
 
155
164
  ### OTS
156
165
 
157
- Sanitise OTF/TTF & WOFF/2 font files.
166
+ Sanitize OTF/TTF & WOFF/2 font files.
158
167
 
159
168
  ```ruby
160
- font.ot_sanitise!
169
+ font.ots_sanitize!
161
170
  ```
162
171
 
163
172
  ### Set Dimensions
@@ -1,5 +1,5 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+
2
+ lib = File.expand_path('lib', __dir__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'dragonfly_fonts/version'
5
5
 
@@ -17,6 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
18
  spec.require_paths = ['lib']
19
19
 
20
+ spec.add_dependency 'activesupport'
20
21
  spec.add_dependency 'dragonfly', '~> 1.0'
21
22
  spec.add_dependency 'nokogiri'
22
23
 
@@ -24,5 +25,6 @@ Gem::Specification.new do |spec|
24
25
  spec.add_development_dependency 'guard'
25
26
  spec.add_development_dependency 'guard-minitest'
26
27
  spec.add_development_dependency 'minitest', '~> 5.0'
28
+ spec.add_development_dependency 'minitest-reporters'
27
29
  spec.add_development_dependency 'rake', '~> 10.0'
28
30
  end
@@ -1,8 +1,29 @@
1
1
  require 'dragonfly'
2
+ require 'active_support/core_ext/object/blank'
3
+
2
4
  require 'dragonfly_fonts/plugin'
3
5
  require 'dragonfly_fonts/unicode_ranges'
4
6
  require 'dragonfly_fonts/version'
5
7
 
6
8
  module DragonflyFonts
7
- SCRIPT_DIR = Pathname.new(File.expand_path('../../script', __FILE__))
9
+ class UnsupportedFormat < RuntimeError; end
10
+ class UnsupportedOutputFormat < RuntimeError; end
11
+
12
+ SCRIPT_DIR = Pathname.new(File.expand_path('../script', __dir__))
13
+
14
+ FONT_FORGE_SUPPORTED_FORMATS = %w[bdf dfont bin hqx otf sfd pfa pfb cid ps svg ttf ttc pcf woff].freeze
15
+ FONT_TOOLS_SUPPORTED_FORMATS = %w[ttx].freeze
16
+ OT_SANITISE_SUPPORTED_FORMATS = %w[otf ttf woff woff2].freeze
17
+ TTF_AUTOHINT_SUPPORTED_FORMATS = %w[ttf].freeze
18
+ WOFF2_SUPPORTED_FORMATS = %w[woff2].freeze
19
+
20
+ SUPPORTED_FORMATS = (
21
+ FONT_FORGE_SUPPORTED_FORMATS +
22
+ FONT_TOOLS_SUPPORTED_FORMATS +
23
+ OT_SANITISE_SUPPORTED_FORMATS +
24
+ TTF_AUTOHINT_SUPPORTED_FORMATS +
25
+ WOFF2_SUPPORTED_FORMATS
26
+ ).uniq.sort
27
+
28
+ SUPPORTED_OUTPUT_FORMATS = %w[eot ttf otf svg woff woff2 ttx].freeze
8
29
  end
@@ -3,27 +3,29 @@ require 'json'
3
3
  module DragonflyFonts
4
4
  module Analysers
5
5
  class Bbox
6
- def call(font, glyph)
7
- res = font.shell_eval do |path|
6
+ def call(content, glyph)
7
+ return {} unless FONT_FORGE_SUPPORTED_FORMATS.include?(content.ext)
8
+
9
+ res = content.shell_eval do |path|
8
10
  "#{fontforge_command} -lang=ff -c 'Open($1); Select(\"#{glyph}\"); Print(GlyphInfo(\"BBox\"));' #{path}"
9
11
  end
10
12
 
13
+ return {} unless res.present?
14
+
11
15
  dimensions = JSON.parse(res)
12
16
 
13
17
  Struct::Bbox.new(
14
18
  glyph,
15
-
16
19
  dimensions[0],
17
20
  dimensions[1],
18
21
  dimensions[2],
19
22
  dimensions[3],
20
-
21
23
  (dimensions[2] - dimensions[0]),
22
24
  (dimensions[3] - dimensions[1])
23
25
  )
24
26
  end
25
27
 
26
- private # =============================================================
28
+ private
27
29
 
28
30
  def fontforge_command
29
31
  'fontforge'
@@ -4,10 +4,15 @@ module DragonflyFonts
4
4
  module Analysers
5
5
  class FontInfo
6
6
  # see http://dmtr.org/ff.php#Font
7
- def call(font)
8
- details = font.shell_eval do |path|
7
+ def call(content)
8
+ return {} unless FONT_FORGE_SUPPORTED_FORMATS.include?(content.ext)
9
+
10
+ details = content.shell_eval do |path|
9
11
  "#{DragonflyFonts::SCRIPT_DIR.join('font_info.py')} #{path}"
10
12
  end
13
+
14
+ return [] unless details.present?
15
+
11
16
  JSON.parse(details)
12
17
  end
13
18
  end
@@ -3,10 +3,18 @@ require 'json'
3
3
  module DragonflyFonts
4
4
  module Analysers
5
5
  class Glyphs
6
- def call(font)
7
- details = font.shell_eval do |path|
8
- "#{DragonflyFonts::SCRIPT_DIR.join('glyphs.py')} #{path}"
9
- end
6
+ def call(content)
7
+ return [] unless FONT_FORGE_SUPPORTED_FORMATS.include?(content.ext)
8
+
9
+ # details = content.shell_eval do |path|
10
+ # "#{DragonflyFonts::SCRIPT_DIR.join('glyphs.py')} #{path}"
11
+ # end
12
+
13
+ command = "#{DragonflyFonts::SCRIPT_DIR.join('glyphs.py')} #{content.path}"
14
+ details, stderr_str, status = Open3.capture3(command)
15
+
16
+ return [] unless details.present?
17
+
10
18
  JSON.parse(details)
11
19
  end
12
20
  end
@@ -3,10 +3,18 @@ require 'json'
3
3
  module DragonflyFonts
4
4
  module Analysers
5
5
  class GsubTables
6
- def call(font)
7
- details = font.shell_eval do |path|
8
- "#{DragonflyFonts::SCRIPT_DIR.join('gsub_tables.py')} #{path}"
9
- end
6
+ def call(content)
7
+ return [] unless FONT_FORGE_SUPPORTED_FORMATS.include?(content.ext)
8
+
9
+ # details = content.shell_eval do |path|
10
+ # "#{DragonflyFonts::SCRIPT_DIR.join('gsub_tables.py')} #{path}"
11
+ # end
12
+
13
+ command = "#{DragonflyFonts::SCRIPT_DIR.join('gsub_tables.py')} #{content.path}"
14
+ details, stderr_str, status = Open3.capture3(command)
15
+
16
+ return [] unless details.present?
17
+
10
18
  JSON.parse(details)
11
19
  end
12
20
  end
@@ -0,0 +1,19 @@
1
+ require 'open3'
2
+
3
+ module DragonflyFonts
4
+ module Analysers
5
+ class OtsSanitize
6
+ def call(content)
7
+ return unless OT_SANITISE_SUPPORTED_FORMATS.include?(content.ext)
8
+
9
+ `#{ots_sanitize_command} #{content.path} 2>&1`
10
+ end
11
+
12
+ private
13
+
14
+ def ots_sanitize_command
15
+ 'ots-sanitize'
16
+ end
17
+ end
18
+ end
19
+ end
@@ -2,14 +2,14 @@ require 'dragonfly_fonts/analysers/bbox'
2
2
  require 'dragonfly_fonts/analysers/font_info'
3
3
  require 'dragonfly_fonts/analysers/glyphs'
4
4
  require 'dragonfly_fonts/analysers/gsub_tables'
5
- require 'dragonfly_fonts/analysers/ot_sanitise'
5
+ require 'dragonfly_fonts/analysers/ots_sanitize'
6
6
 
7
7
  require 'dragonfly_fonts/processors/correct_metrics'
8
8
  require 'dragonfly_fonts/processors/encode'
9
9
  require 'dragonfly_fonts/processors/extract_glyph'
10
10
  require 'dragonfly_fonts/processors/fix_dflt_table'
11
11
  require 'dragonfly_fonts/processors/normalize_names'
12
- require 'dragonfly_fonts/processors/ot_sanitise'
12
+ require 'dragonfly_fonts/processors/ots_sanitize'
13
13
  require 'dragonfly_fonts/processors/set_dimensions'
14
14
  require 'dragonfly_fonts/processors/set_ttf_names'
15
15
  require 'dragonfly_fonts/processors/set_underline'
@@ -20,21 +20,18 @@ require 'dragonfly_fonts/processors/web_friendly'
20
20
 
21
21
  module DragonflyFonts
22
22
  class Plugin
23
- def call(app, _opts = {})
23
+ def call(app, options = {})
24
24
  app.add_analyser :bbox, Analysers::Bbox.new
25
25
  app.add_analyser :font_info, Analysers::FontInfo.new
26
26
  app.add_analyser :glyphs, Analysers::Glyphs.new
27
27
  app.add_analyser :gsub_tables, Analysers::GsubTables.new
28
- app.add_analyser :ot_sanitise, Analysers::OtSanitise.new
29
-
30
- # ---------------------------------------------------------------------
28
+ app.add_analyser :ots_sanitize, Analysers::OtsSanitize.new
31
29
 
32
30
  app.add_processor :correct_metrics, Processors::CorrectMetrics.new
33
- app.add_processor :encode, Processors::Encode.new
34
31
  app.add_processor :extract_glyph, Processors::ExtractGlyph.new
35
32
  app.add_processor :fix_dflt_table, Processors::FixDfltTable.new
36
33
  app.add_processor :normalize_names, Processors::NormalizeNames.new
37
- app.add_processor :ot_sanitise!, Processors::OtSanitise.new
34
+ app.add_processor :ots_sanitize!, Processors::OtsSanitize.new
38
35
  app.add_processor :set_dimensions, Processors::SetDimensions.new
39
36
  app.add_processor :set_ttf_names, Processors::SetTtfNames.new
40
37
  app.add_processor :set_underline, Processors::SetUnderline.new
@@ -42,6 +39,34 @@ module DragonflyFonts
42
39
  app.add_processor :set_woff_metadata, Processors::SetWoffMetadata.new
43
40
  app.add_processor :ttf_autohint, Processors::TtfAutohint.new
44
41
  app.add_processor :web_friendly, Processors::WebFriendly.new
42
+
43
+ app.add_processor :encode do |content, format, options = {}|
44
+ case content.ext
45
+ when 'ttx'
46
+ # when ttf pre-convert to otf
47
+ Processors::Encode.new.call(content, 'otf', options) unless %w[font/ttf font/otf].include?(content.mime_type)
48
+ when 'woff2'
49
+ # when woff2 pre-convert to ttf
50
+ Processors::Encode.new.call(content, 'ttf', options)
51
+ end
52
+
53
+ case format
54
+ when 'eot'
55
+ # when converting to eot supply ttf
56
+ Processors::Encode.new.call(content, 'ttf', options) unless %w[font/ttf].include?(content.mime_type)
57
+ when 'woff2'
58
+ # when converting to woff2 supply otf
59
+ Processors::Encode.new.call(content, 'otf', options) unless %w[font/ttf font/otf].include?(content.mime_type)
60
+ end
61
+
62
+ Processors::Encode.new.call(content, format, options)
63
+ end
64
+
65
+ app.add_mime_type 'ttf', 'font/ttf'
66
+ app.add_mime_type 'otf', 'font/otf'
67
+ app.add_mime_type 'woff', 'font/woff'
68
+ app.add_mime_type 'woff2', 'font/woff2'
69
+ app.add_mime_type 'ttx', 'font/ttx'
45
70
  end
46
71
  end
47
72
  end
@@ -1,17 +1,20 @@
1
1
  module DragonflyFonts
2
2
  module Processors
3
3
  class CorrectMetrics
4
- def call(font)
5
- font.shell_update(ext: font.ext || :ttf) do |old_path, new_path|
4
+ def call(content, options = {})
5
+ # TODO: if other then convert first
6
+ raise UnsupportedFormat unless FONT_FORGE_SUPPORTED_FORMATS.include?(content.ext)
7
+
8
+ content.shell_update(ext: content.ext || 'ttf') do |old_path, new_path|
6
9
  "#{fontforge_command} -lang=ff -c 'Open($1); SetOS2Value(\"HHeadAscent\",$ascent); SetOS2Value(\"HHeadAscentIsOffset\",0); SetOS2Value(\"HHeadDescent\",-$descent); SetOS2Value(\"HHeadDescentIsOffset\",0); SetOS2Value(\"TypoLineGap\",0); Generate($2);' #{old_path} #{new_path}"
7
10
  end
8
11
  end
9
12
 
10
- def update_url(attrs, *_args)
13
+ def update_url(attrs, *args)
11
14
  attrs.style = 'corrmetr'
12
15
  end
13
16
 
14
- private # =============================================================
17
+ private
15
18
 
16
19
  def fontforge_command
17
20
  'fontforge'
@@ -3,24 +3,30 @@ require 'shellwords'
3
3
  module DragonflyFonts
4
4
  module Processors
5
5
  class Encode
6
- class UnsupportedFormat < RuntimeError; end
6
+ def call(content, format, options = {})
7
+ raise UnsupportedFormat unless SUPPORTED_FORMATS.include?(content.ext)
8
+
9
+ format = format.to_s
10
+
11
+ raise UnsupportedOutputFormat unless SUPPORTED_OUTPUT_FORMATS.include?(format)
12
+
13
+ if content.mime_type == Rack::Mime.mime_type(".#{format}")
14
+ content.ext ||= format
15
+ content.meta['format'] = format
16
+ return
17
+ end
7
18
 
8
- def call(content, format)
9
19
  content.shell_update(ext: format, escape: false) do |old_path, new_path|
10
- if content.ext == 'ttx' || content.meta['format'] == 'ttx'
11
- fonttools(old_path, new_path)
12
- else
13
- case format.to_sym
14
- when :eot then ttf2eot(old_path, new_path)
15
- when :otf, :svg, :ttf, :woff then fontforge(old_path, new_path)
16
- when :ttx then fonttools(old_path, new_path)
17
- when :woff2 then woff2(old_path, new_path)
18
- else fail UnsupportedFormat
19
- end
20
+ case
21
+ when content.ext == 'woff2' && format == 'ttf' then woff2_decompress(old_path, new_path)
22
+ when format == 'eot' then ttf2eot(old_path, new_path)
23
+ when format == 'ttx' then fonttools(old_path, new_path)
24
+ when format == 'woff2' then woff2_compress(old_path, new_path)
25
+ else fontforge(old_path, new_path)
20
26
  end
21
27
  end
22
28
 
23
- content.meta['format'] = format.to_s
29
+ content.meta['format'] = format
24
30
  content.ext = format
25
31
  end
26
32
 
@@ -28,7 +34,7 @@ module DragonflyFonts
28
34
  attrs.ext = format.to_s
29
35
  end
30
36
 
31
- private # =============================================================
37
+ private
32
38
 
33
39
  def fontforge(old_path, new_path)
34
40
  "#{fontforge_command} -lang=ff -c 'Open($1); Generate($2)' #{old_path} #{new_path}"
@@ -38,8 +44,6 @@ module DragonflyFonts
38
44
  'fontforge'
39
45
  end
40
46
 
41
- # ---------------------------------------------------------------------
42
-
43
47
  def fonttools(old_path, new_path)
44
48
  "#{fonttools_command} -o #{new_path} #{old_path}"
45
49
  end
@@ -48,8 +52,6 @@ module DragonflyFonts
48
52
  'ttx'
49
53
  end
50
54
 
51
- # ---------------------------------------------------------------------
52
-
53
55
  def ttf2eot(old_path, new_path)
54
56
  "#{ttf2eot_command} < #{old_path} > #{new_path}"
55
57
  end
@@ -58,15 +60,27 @@ module DragonflyFonts
58
60
  'ttf2eot'
59
61
  end
60
62
 
61
- # ---------------------------------------------------------------------
62
-
63
- def woff2(old_path, new_path)
64
- "#{woff2_command} #{old_path} && mv #{old_path.gsub(/\.\w{3,5}\z/, '.woff2')} #{new_path}"
63
+ def woff2_compress(old_path, new_path)
64
+ temp_file = Tempfile.new(['font', '.woff2']).tap do |file|
65
+ file.write(File.read(old_path))
66
+ end
67
+ "#{woff2_compress_command} #{temp_file.path} && mv #{temp_file.path.gsub(/\.\w{3,5}\z/, '.woff2')} #{new_path}"
65
68
  end
66
69
 
67
- def woff2_command
70
+ def woff2_compress_command
68
71
  'woff2_compress'
69
72
  end
73
+
74
+ def woff2_decompress(old_path, new_path)
75
+ temp_file = Tempfile.new(['font', '.woff2']).tap do |file|
76
+ file.write(File.read(old_path))
77
+ end
78
+ "#{woff2_decompress_command} #{temp_file.path} && mv #{temp_file.path.gsub(/\.\w{3,5}\z/, '.ttf')} #{new_path}"
79
+ end
80
+
81
+ def woff2_decompress_command
82
+ 'woff2_decompress'
83
+ end
70
84
  end
71
85
  end
72
86
  end