dragonfly_fonts 0.0.8 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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