fontcustom 1.2.0 → 1.3.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +16 -0
  3. data/CONTRIBUTING.md +35 -15
  4. data/TODO.md +20 -0
  5. data/lib/fontcustom.rb +11 -24
  6. data/lib/fontcustom/base.rb +56 -0
  7. data/lib/fontcustom/cli.rb +13 -12
  8. data/lib/fontcustom/generator/font.rb +63 -65
  9. data/lib/fontcustom/generator/template.rb +81 -52
  10. data/lib/fontcustom/manifest.rb +42 -0
  11. data/lib/fontcustom/options.rb +93 -80
  12. data/lib/fontcustom/scripts/generate.py +116 -127
  13. data/lib/fontcustom/templates/_fontcustom-rails.scss +13 -18
  14. data/lib/fontcustom/templates/_fontcustom.scss +10 -18
  15. data/lib/fontcustom/templates/fontcustom-preview.html +17 -24
  16. data/lib/fontcustom/templates/fontcustom.css +10 -18
  17. data/lib/fontcustom/templates/fontcustom.yml +3 -3
  18. data/lib/fontcustom/utility.rb +145 -0
  19. data/lib/fontcustom/version.rb +1 -1
  20. data/lib/fontcustom/watcher.rb +1 -1
  21. data/spec/fixtures/generators/.fontcustom-manifest-corrupted.json +12 -5
  22. data/spec/fixtures/generators/.fontcustom-manifest-empty.json +0 -0
  23. data/spec/fixtures/generators/.fontcustom-manifest.json +49 -14
  24. data/spec/fixtures/generators/mixed-output/fontcustom_82a59e769bc60192484f2620570bbb59.eot +0 -0
  25. data/spec/fixtures/generators/mixed-output/fontcustom_82a59e769bc60192484f2620570bbb59.svg +56 -0
  26. data/spec/fixtures/generators/mixed-output/fontcustom_82a59e769bc60192484f2620570bbb59.ttf +0 -0
  27. data/spec/fixtures/generators/mixed-output/fontcustom_82a59e769bc60192484f2620570bbb59.woff +0 -0
  28. data/spec/fixtures/sandbox/.gitkeep +0 -0
  29. data/spec/fontcustom/base_spec.rb +58 -0
  30. data/spec/fontcustom/cli_spec.rb +15 -0
  31. data/spec/fontcustom/generator/font_spec.rb +50 -220
  32. data/spec/fontcustom/generator/template_spec.rb +30 -194
  33. data/spec/fontcustom/generator/template_spec.rb.off +215 -0
  34. data/spec/fontcustom/manifest_spec.rb +16 -0
  35. data/spec/fontcustom/options_spec.rb +206 -208
  36. data/spec/fontcustom/utility_spec.rb +163 -0
  37. data/spec/fontcustom/{watcher_spec.rb → watcher_spec.rb.off} +0 -0
  38. data/spec/spec_helper.rb +77 -19
  39. metadata +44 -54
  40. data/lib/fontcustom/templates/_fontcustom-bootstrap-ie7.scss +0 -22
  41. data/lib/fontcustom/templates/_fontcustom-bootstrap.scss +0 -63
  42. data/lib/fontcustom/templates/fontcustom-bootstrap-ie7.css +0 -22
  43. data/lib/fontcustom/templates/fontcustom-bootstrap.css +0 -63
  44. data/lib/fontcustom/util.rb +0 -62
  45. data/spec/fixtures/generators/mixed-output/fontcustom_cc5ce52f2ae4f9ce2e7ee8131bbfee1e.eot +0 -0
  46. data/spec/fixtures/generators/mixed-output/fontcustom_cc5ce52f2ae4f9ce2e7ee8131bbfee1e.svg +0 -102
  47. data/spec/fixtures/generators/mixed-output/fontcustom_cc5ce52f2ae4f9ce2e7ee8131bbfee1e.ttf +0 -0
  48. data/spec/fixtures/generators/mixed-output/fontcustom_cc5ce52f2ae4f9ce2e7ee8131bbfee1e.woff +0 -0
  49. data/spec/fontcustom/util_spec.rb +0 -82
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1abf5b322a257728ac1e293a8392f3592e9954b7
4
+ data.tar.gz: cb6b2ca5e590ae76878050e2317ab7de0cd74041
5
+ SHA512:
6
+ metadata.gz: 0476cea6aef78118bf1fd85caa008d3de5b2ab465c92619a639eec104ec98178072d4f231a3d1ffee152031f28f9814ad3f2a51fc00c2c5a6a1f1312f2093be4
7
+ data.tar.gz: 40046d513e692b44bf504969844f48fe905b79b67f7cb9c5b5fe4f5b8ef3a32cc6bde44d83f6173a876956a402d1be5c9fc637b059f532aa6803055c46360155
@@ -1,3 +1,19 @@
1
+ ## 1.3.0.beta (11/25/2013)
2
+
3
+ The big news: fixed glyph code points. Automatically assigned for now, but changing them by hand is just a matter of modifying the generated `.fontcustom-manifest.json`.
4
+
5
+ Lots of internal changes / streamlining on this one. A few breaking changes (`css_prefix`, custom template syntax, possibly others). Error messages and docs are still a bit sparse — my apologies in advance.
6
+
7
+ * Fixed glyph code points ([#56](https://github.com/FontCustom/fontcustom/issues/56))
8
+ * Changes `css_prefix` to `css_selector` to allow greater flexibility ([#126](https://github.com/FontCustom/fontcustom/pull/126))
9
+ * Skips compilation if inputs have not changed (and `force` option to bypass checks)
10
+ * Adds CSS template helpers for convenience and DRYness
11
+ * Drops bootstrap templates (maintaince overhead, unsure if anyone was using them)
12
+
13
+ Next up:
14
+
15
+ * Storing relative paths in the manifest for collaborative editing ([#149](https://github.com/FontCustom/fontcustom/pull/149))
16
+
1
17
  ## 1.2.0 (11/2/2013)
2
18
 
3
19
  * Preparation for fixed glyph code points.
@@ -1,31 +1,51 @@
1
1
  ## Help make Font Custom better!
2
2
 
3
3
  This project was born out of an overheard conversation between two devs in a
4
- NYC coffee shop — it's come a long ways thanks to your support.
4
+ NYC coffee shop — it's come a long ways thanks to your support. Here's what's
5
+ on the menu:
5
6
 
6
- ### Wishlist / Roadmap
7
-
8
- * Ruby on Rails integration
9
- * Compass integration
7
+ * **Ruby on Rails integration**
8
+ * **Compass integration**
10
9
  * Base 64 encode fonts into CSS
11
10
  * Templates for LESS, stylus, etc.
12
11
  * Ligature support
13
12
  * Windows support
14
13
  * Make better use of Thor
15
14
 
16
- ### Conventions
15
+ Just [file an issue](https://github.com/FontCustom/fontcustom/issues) if you
16
+ have an idea or would like to claim one.
17
17
 
18
- We try to follow the [Github ruby styleguide](https://github.com/styleguide/ruby)
19
- as much as possible.
18
+ ### Rules of Thumb
20
19
 
21
20
  If you catch a typo or a block of code that could be more elegant — please let
22
21
  us know. No such thing as too small of an improvement.
23
22
 
24
- ### Process
23
+ * Spaces instead of tabs, please.
24
+ * Develop in a topic branch.
25
+ * Include passing tests if applicable.
26
+ * Follow the [Github ruby styleguide](https://github.com/styleguide/ruby) as
27
+ much as possible.
28
+
29
+ ### Getting Started
30
+
31
+ You'll need:
32
+
33
+ * Fontforge with Python scripting (easiest via [Homebrew](http://brew.sh/) on Mac)
34
+ * Ruby 1.9.2+ (via [rbenv](https://github.com/sstephenson/rbenv), [RVM](https://rvm.io/), etc.)
35
+ * Rubygems
36
+ * Bundler
37
+ * Rake
38
+ * Rspec
39
+
40
+ Some helpful links:
41
+
42
+ * http://createdbypete.com/articles/ruby-on-rails-development-with-mac-os-x-mountain-lion/
43
+ * http://guides.rubygems.org/make-your-own-gem/
44
+
45
+ ---
46
+
47
+ That's all there is to it. Thanks again, and please don't hesitate to reach out:
25
48
 
26
- * Visit [issues](https://github.com/FontCustom/fontcustom/issues) for ideas.
27
- * Fork the repo.
28
- * Create a topic branch. `git checkout -b my_sweet_feature`
29
- * Add your tests. Run tests with `rake`.
30
- * Develop your feature.
31
- * Once all tests are passing, submit a pull request!
49
+ [Github Issues](https://github.com/FontCustom/fontcustom/issues)<br>
50
+ [@ezYZ](https://twitter.com/ezYZ)<br>
51
+ [@endtwist](https://twitter.com/endtwist)
data/TODO.md ADDED
@@ -0,0 +1,20 @@
1
+ ## TODO
2
+
3
+ ### 1.3.0
4
+
5
+ * tests, error messages, and docs for generate.py / SVG bugs
6
+ * tests, error messages, and docs for template generator
7
+ * store relative paths in manifest (rebuild in generator/util)
8
+ * documentation for template helpers
9
+ * redirect fontcustom.com to github repo (use wiki for documentation)
10
+
11
+ ### Low Priority
12
+
13
+ * conserve code points: http://stackoverflow.com/questions/8794430/ruby-finding-lowest-free-id-in-an-id-array
14
+ * more flexible input/ouput hashes (regex or file extensions)
15
+ * sass template with variables
16
+ * less template with variables
17
+ * more robust fontforge check than `which`
18
+ * remove redundant requires
19
+ * rename options for succintness (e.g. :project_root => :root)
20
+ * trim options specs down
@@ -1,56 +1,43 @@
1
1
  require "fontcustom/version"
2
2
  require "fontcustom/error"
3
- require "fontcustom/util"
3
+ require "fontcustom/utility"
4
+ require "fontcustom/base"
5
+ require "fontcustom/manifest"
4
6
  require "fontcustom/options"
5
7
  require "fontcustom/generator/font"
6
8
  require "fontcustom/generator/template"
7
9
 
8
10
  module Fontcustom
9
- ##
10
- # Clean Ruby API to workaround Thor
11
- def compile(options)
12
- opts = Options.new options
13
- Generator::Font.start [opts]
14
- Generator::Template.start [opts]
15
- rescue Fontcustom::Error => e
16
- opts.say_message :error, e.message, :red
17
- end
18
-
19
11
  def gem_lib
20
12
  File.expand_path(File.join(File.dirname(__FILE__), "fontcustom"))
21
13
  end
22
-
23
- module_function :compile, :gem_lib
14
+ module_function :gem_lib
24
15
 
25
16
  ##
26
- # These are used in Thor CLI but overridden when the Options class is built
17
+ # Hack to get Thor to show more helpful defaults in `fontcustom help`. These
18
+ # are overwritten in Fontcustom::Options.
27
19
  EXAMPLE_OPTIONS = {
28
20
  :project_root => "`pwd`",
29
21
  :output => "PROJECT_ROOT/FONT_NAME",
30
- :config => "PROJECT_ROOT/fontcustom.yml OR PROJECT_ROOT/config/fontcustom.yml",
22
+ :config => "PROJECT_ROOT/fontcustom.yml -or- PROJECT_ROOT/config/fontcustom.yml",
31
23
  :templates => "css preview",
32
- :manifest => "CONFIG_DIR/.fontcustom-manifest.json OR PROJECT_ROOT/.fontcustom-manifest.json"
24
+ :manifest => "CONFIG_DIR/.fontcustom-manifest.json -or- PROJECT_ROOT/.fontcustom-manifest.json"
33
25
  }
34
26
 
35
27
  DEFAULT_OPTIONS = {
36
- :project_root => Dir.pwd,
28
+ :project_root => nil,
37
29
  :input => nil,
38
30
  :output => nil,
39
31
  :config => nil,
40
32
  :templates => %w|css preview|,
41
33
  :font_name => "fontcustom",
42
- :css_prefix => "icon-",
34
+ :css_selector => ".icon-{{glyph}}",
43
35
  :manifest => nil,
44
36
  :preprocessor_path => nil,
45
37
  :autowidth => false,
46
38
  :no_hash => false,
47
39
  :debug => false,
40
+ :force => false,
48
41
  :quiet => false
49
42
  }
50
-
51
- DATA_MODEL = {
52
- :fonts => [],
53
- :templates => [],
54
- :glyphs => []
55
- }
56
43
  end
@@ -0,0 +1,56 @@
1
+ require "digest/sha2"
2
+
3
+ module Fontcustom
4
+ class Base
5
+ include Utility
6
+
7
+ def initialize(cli_options)
8
+ @cli_options = cli_options
9
+ check_fontforge
10
+ init_manifest
11
+ end
12
+
13
+ def compile
14
+ @manifest[:checksum][:current] = checksum
15
+ if @options[:force] || @manifest[:checksum][:current] != @manifest[:checksum][:previous]
16
+ save_manifest
17
+ start_generators
18
+ @manifest = get_manifest
19
+ @manifest[:checksum][:previous] = @manifest[:checksum][:current]
20
+ save_manifest
21
+ else
22
+ say_message :status, "No changes detected. Skipping compilation."
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def check_fontforge
29
+ fontforge = `which fontforge`
30
+ if fontforge == "" || fontforge == "fontforge not found"
31
+ raise Fontcustom::Error, "Please install fontforge first. Visit <http://fontcustom.com> for instructions."
32
+ end
33
+ end
34
+
35
+ def init_manifest
36
+ file = @cli_options[:manifest] || File.join(Dir.pwd, ".fontcustom-manifest.json")
37
+ @options = Fontcustom::Options.new(@cli_options).options
38
+ @manifest = Fontcustom::Manifest.new(@options).manifest
39
+ end
40
+
41
+ # Calculates a hash of vectors, options, and templates (content and filenames)
42
+ def checksum
43
+ files = Dir.glob File.join(@options[:input][:vectors], "*.svg")
44
+ files += Dir.glob File.join(@options[:input][:templates], "*")
45
+ content = files.map { |file| File.read(file) }.join
46
+ content << files.join
47
+ content << @options.flatten(2).join
48
+ Digest::SHA2.hexdigest(content).to_s
49
+ end
50
+
51
+ def start_generators
52
+ Fontcustom::Generator::Font.new(@options[:manifest]).generate
53
+ Fontcustom::Generator::Template.new(@options[:manifest]).generate
54
+ end
55
+ end
56
+ end
@@ -5,7 +5,7 @@ require "fontcustom/watcher"
5
5
 
6
6
  module Fontcustom
7
7
  class CLI < Thor
8
- include Thor::Actions
8
+ include Utility
9
9
 
10
10
  default_task :show_help
11
11
 
@@ -22,17 +22,17 @@ module Fontcustom
22
22
  :default => EXAMPLE_OPTIONS[:config]
23
23
 
24
24
  class_option :templates, :aliases => "-t", :type => :array,
25
- :desc => "Space-delinated list of templates to generate alongside fonts.",
26
- :enum => %w|preview css scss scss-rails bootstrap bootstrap-scss bootstrap-ie7 bootstrap-ie7-scss|,
25
+ :desc => "Space-delinated list of files to generate alongside fonts. Use stock templates or choose your own.",
26
+ :enum => %w|preview css scss scss-rails|,
27
27
  :default => EXAMPLE_OPTIONS[:templates]
28
28
 
29
29
  class_option :font_name, :aliases => "-f", :type => :string,
30
30
  :desc => "The font's name. Also determines the file names of generated templates.",
31
31
  :default => DEFAULT_OPTIONS[:font_name]
32
32
 
33
- class_option :css_prefix, :aliases => "-p", :type => :string,
34
- :desc => "Prefix for each glyph's CSS class.",
35
- :default => DEFAULT_OPTIONS[:css_prefix]
33
+ class_option :css_selector, :aliases => "-s", :type => :string,
34
+ :desc => "Format of generated CSS selector. \"{{glyph}}\" is substituted for the glyph name.",
35
+ :default => DEFAULT_OPTIONS[:css_selector]
36
36
 
37
37
  class_option :manifest, :aliases => "-m", :type => :string,
38
38
  :desc => "Path to a manifest of generated files. Used for garbage collection.",
@@ -50,6 +50,9 @@ module Fontcustom
50
50
  class_option :debug, :aliases => "-d", :type => :boolean,
51
51
  :desc => "Display debugging messages."
52
52
 
53
+ class_option :force, :aliases => "-F", :type => :boolean,
54
+ :desc => "Forces compilation, even if inputs have not changed."
55
+
53
56
  class_option :quiet, :aliases => "-q", :type => :boolean,
54
57
  :desc => "Hide status messages."
55
58
 
@@ -60,12 +63,10 @@ module Fontcustom
60
63
 
61
64
  desc "compile [INPUT] [OPTIONS]", "Generates webfonts and templates from *.svg and *.eps files in INPUT. Default: `pwd`"
62
65
  def compile(input = nil)
63
- opts = options.merge :input => input
64
- opts = Options.new(opts)
65
- Generator::Font.start [opts]
66
- Generator::Template.start [opts]
66
+ Base.new(options.merge(:input => input)).compile
67
67
  rescue Fontcustom::Error => e
68
- opts.say_message :error, e.message, :red
68
+ say_status :error, e.message, :red
69
+ puts e.backtrace.join("\n") if options[:debug]
69
70
  end
70
71
 
71
72
  desc "watch [INPUT] [OPTIONS]", "Watches INPUT for changes and regenerates files automatically. Ctrl + C to stop. Default: `pwd`"
@@ -78,7 +79,7 @@ module Fontcustom
78
79
  opts = Options.new(opts)
79
80
  Watcher.new(opts).watch
80
81
  rescue Fontcustom::Error => e
81
- opts.say_message :error, e.message, :red
82
+ say_status :error, e.message, :red
82
83
  end
83
84
 
84
85
  desc "config [DIR]", "Generates an annotated configuration file (fontcustom.yml) in DIR. Default: `pwd`"
@@ -1,92 +1,90 @@
1
1
  require "json"
2
2
  require "open3"
3
- require "thor"
4
- require "thor/group"
5
- require "thor/actions"
6
3
 
7
4
  module Fontcustom
8
5
  module Generator
9
- class Font < Thor::Group
10
- include Util
11
- include Thor::Actions
6
+ class Font
7
+ include Utility
12
8
 
13
- # Instead of passing each option individually we're passing the entire options hash as an argument.
14
- # This is DRYier, easier to maintain.
15
- argument :opts
9
+ attr_reader :manifest, :options
16
10
 
17
- def prepare_output_dirs
18
- dirs = opts.output.values.uniq
19
- dirs.each do |dir|
20
- unless File.directory? dir
21
- empty_directory dir, :verbose => ! opts.quiet
22
- end
23
- end
11
+ def initialize(manifest)
12
+ @manifest = get_manifest(manifest)
13
+ @options = @manifest[:options]
24
14
  end
25
15
 
26
- def get_manifest
27
- if File.exists? opts.manifest
28
- begin
29
- data = File.read opts.manifest
30
- data = JSON.parse(data, :symbolize_names => true) unless data.empty?
31
- @data = data.is_a?(Hash) ? symbolize_hash(data) : Fontcustom::DATA_MODEL.dup
32
- rescue
33
- raise Fontcustom::Error, "Couldn't parse `#{relative_to_root(opts.manifest)}`. Delete it to start from scratch. Any previously generated files will need to be deleted manually."
34
- end
35
- else
36
- @data = Fontcustom::DATA_MODEL.dup
37
- end
16
+ def generate
17
+ create_output_dirs
18
+ delete_old_fonts
19
+ set_glyph_info
20
+ create_fonts
38
21
  end
39
22
 
40
- def reset_output
41
- return if @data[:fonts].empty?
42
- begin
43
- deleted = []
44
- @data[:fonts].each do |file|
45
- remove_file file, :verbose => false
46
- deleted << file
23
+ private
24
+
25
+ def create_output_dirs
26
+ dirs = @options[:output].values.uniq
27
+ dirs.each do |dir|
28
+ unless File.directory? dir
29
+ empty_directory dir, :verbose => ! @options[:quiet]
47
30
  end
48
- ensure
49
- @data[:fonts] = @data[:fonts] - deleted
50
- json = JSON.pretty_generate @data
51
- overwrite_file opts.manifest, json
52
- say_changed :delete, deleted
53
31
  end
54
32
  end
55
33
 
56
- def generate
57
- cmd = "fontforge -script #{Fontcustom.gem_lib}/scripts/generate.py #{opts.input[:vectors]} #{opts.output[:fonts]} --name #{opts.font_name}"
58
- cmd += " --autowidth" if opts.autowidth
59
- cmd += " --nohash" if opts.no_hash
60
- cmd += " --debug" if opts.debug
61
- output, err, status = execute_and_clean(cmd)
62
- @json = output.delete_at(0)
63
- say_status :debug, "#{err}\n#{' ' * 14}#{output}", :red if opts.debug
64
- raise Fontcustom::Error, "`fontforge` compilation failed. Try again with --debug for more details." unless status.success?
34
+ def delete_old_fonts
35
+ delete_from_manifest(:fonts)
65
36
  end
66
37
 
67
- def collect_data
68
- json = JSON.parse(@json, :symbolize_names => true)
69
- @data.merge! json
70
- @data[:glyphs].map! { |glyph| glyph.gsub(/\W/, "-") }
71
- @data[:fonts].map! { |font| File.join(opts.output[:fonts], font) }
72
- end
38
+ def set_glyph_info
39
+ codepoint = if ! @manifest[:glyphs].empty?
40
+ codepoints = @manifest[:glyphs].values.map { |data| data[:codepoint] }
41
+ codepoints.max + 1
42
+ else
43
+ 0xf100
44
+ end
73
45
 
74
- def announce_files
75
- say_changed :create, @data[:fonts]
76
- end
46
+ files = Dir.glob File.join(@options[:input][:vectors], "*.svg")
47
+ glyphs = {}
48
+ files.each do |file|
49
+ name = File.basename file, ".svg"
50
+ name = name.strip.gsub(/\W/, "-").downcase
51
+ glyphs[name.to_sym] = { :source => file }
52
+ end
77
53
 
78
- def save_data
79
- json = JSON.pretty_generate @data
80
- overwrite_file opts.manifest, json
81
- end
54
+ # Dir.glob returns a different order depending on ruby
55
+ # version/platform, so we have to sort it first
56
+ glyphs = Hash[glyphs.sort_by { |key, val| key.to_s }]
57
+ glyphs.each do |name, data|
58
+ if @manifest[:glyphs].has_key? name
59
+ data[:codepoint] = @manifest[:glyphs][name][:codepoint]
60
+ else
61
+ data[:codepoint] = codepoint
62
+ codepoint = codepoint + 1
63
+ end
64
+ end
82
65
 
83
- private
66
+ @manifest[:glyphs] = glyphs
67
+ save_manifest
68
+ end
84
69
 
85
- def execute_and_clean(cmd)
70
+ def create_fonts
71
+ cmd = "fontforge -script #{Fontcustom.gem_lib}/scripts/generate.py #{@options[:manifest]}"
86
72
  stdout, stderr, status = Open3::capture3(cmd)
87
73
  stdout = stdout.split("\n")
88
74
  stdout = stdout[1..-1] if stdout[0] == "CreateAllPyModules()"
89
- [stdout, stderr, status]
75
+
76
+ debug_msg = " Try again with --debug for more details."
77
+ if @options[:debug]
78
+ say_message :debug, "#{stderr}\n#{' ' * 14}#{stdout}", :red
79
+ debug_msg = ""
80
+ end
81
+
82
+ if status.success?
83
+ @manifest = get_manifest
84
+ say_changed :create, @manifest[:fonts]
85
+ else
86
+ raise Fontcustom::Error, "`fontforge` compilation failed.#{debug_msg}"
87
+ end
90
88
  end
91
89
  end
92
90
  end