fontist 0.2.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/macosx.yml +1 -1
  3. data/.github/workflows/ubuntu.yml +1 -1
  4. data/.gitignore +4 -3
  5. data/README.md +91 -5
  6. data/fontist.gemspec +1 -0
  7. data/lib/fontist.rb +26 -8
  8. data/lib/fontist/errors.rb +3 -0
  9. data/lib/fontist/font.rb +78 -0
  10. data/lib/fontist/font_formula.rb +123 -0
  11. data/lib/fontist/formula.rb +90 -0
  12. data/lib/fontist/formulas.rb +15 -0
  13. data/lib/fontist/formulas/andale_font.rb +79 -0
  14. data/lib/fontist/formulas/arial_black_font.rb +78 -0
  15. data/lib/fontist/formulas/cleartype_fonts.rb +226 -0
  16. data/lib/fontist/formulas/comic_font.rb +77 -0
  17. data/lib/fontist/formulas/courier_font.rb +80 -0
  18. data/lib/fontist/formulas/euphemia_font.rb +85 -0
  19. data/lib/fontist/formulas/georgia_font.rb +79 -0
  20. data/lib/fontist/formulas/impact_font.rb +77 -0
  21. data/lib/fontist/formulas/montserrat_font.rb +132 -0
  22. data/lib/fontist/formulas/ms_truetype_fonts.rb +124 -0
  23. data/lib/fontist/formulas/open_sans_fonts.rb +263 -0
  24. data/lib/fontist/formulas/overpass_font.rb +73 -0
  25. data/lib/fontist/formulas/source_fonts.rb +109 -0
  26. data/lib/fontist/formulas/stix_fonts.rb +108 -0
  27. data/lib/fontist/formulas/tahoma_font.rb +146 -0
  28. data/lib/fontist/formulas/webding_font.rb +77 -0
  29. data/lib/fontist/registry.rb +42 -0
  30. data/lib/fontist/system.yml +17 -0
  31. data/lib/fontist/system_font.rb +19 -12
  32. data/lib/fontist/utils.rb +9 -0
  33. data/lib/fontist/utils/downloader.rb +74 -0
  34. data/lib/fontist/utils/dsl.rb +77 -0
  35. data/lib/fontist/utils/exe_extractor.rb +72 -0
  36. data/lib/fontist/utils/zip_extractor.rb +38 -0
  37. data/lib/fontist/version.rb +1 -1
  38. data/spec/fontist/font_formula_spec.rb +67 -0
  39. data/spec/fontist/font_spec.rb +87 -0
  40. data/spec/fontist/formula_spec.rb +67 -0
  41. data/spec/fontist/formulas/andale_font_spec.rb +29 -0
  42. data/spec/fontist/formulas/arial_black_font_spec.rb +29 -0
  43. data/spec/fontist/formulas/cleartype_fonts_spec.rb +38 -0
  44. data/spec/fontist/formulas/comic_font_spec.rb +29 -0
  45. data/spec/fontist/formulas/courier_font_spec.rb +29 -0
  46. data/spec/fontist/formulas/euphemia_font_spec.rb +29 -0
  47. data/spec/fontist/formulas/georgia_font_spec.rb +29 -0
  48. data/spec/fontist/formulas/impact_font_spec.rb +29 -0
  49. data/spec/fontist/formulas/montserrat_font_spec.rb +29 -0
  50. data/spec/fontist/formulas/ms_truetype_fonts_spec.rb +29 -0
  51. data/spec/fontist/formulas/open_sans_fonts_spec.rb +29 -0
  52. data/spec/fontist/formulas/overpass_font_spec.rb +29 -0
  53. data/spec/fontist/formulas/source_fonts_spec.rb +31 -0
  54. data/spec/fontist/formulas/stix_fonts_spec.rb +29 -0
  55. data/spec/fontist/formulas/tahoma_font_spec.rb +29 -0
  56. data/spec/fontist/formulas/webding_font_spec.rb +29 -0
  57. data/spec/fontist/registry_spec.rb +47 -0
  58. data/spec/fontist/system_font_spec.rb +16 -0
  59. data/spec/fontist/{downloader_spec.rb → utils/downloader_spec.rb} +8 -8
  60. data/spec/spec_helper.rb +7 -2
  61. data/spec/support/fontist_helper.rb +10 -0
  62. metadata +65 -14
  63. data/assets/source.yml +0 -54
  64. data/lib/fontist/downloader.rb +0 -69
  65. data/lib/fontist/finder.rb +0 -47
  66. data/lib/fontist/installer.rb +0 -44
  67. data/lib/fontist/ms_vista_font.rb +0 -83
  68. data/lib/fontist/source.rb +0 -31
  69. data/spec/fontist/finder_spec.rb +0 -41
  70. data/spec/fontist/installer_spec.rb +0 -48
  71. data/spec/fontist/ms_vista_font_spec.rb +0 -27
  72. data/spec/fontist/source_spec.rb +0 -13
@@ -0,0 +1,17 @@
1
+ system:
2
+ linux:
3
+ paths:
4
+ - /usr/share/fonts/**/**.{ttf,ttc}
5
+
6
+ windows:
7
+ paths:
8
+ - C:/Windows/Fonts/**/**.{ttc,ttf}
9
+
10
+ macosx:
11
+ paths:
12
+ - /System/Library/Fonts/**/**.{ttf,ttc}
13
+ - /Applications/Microsoft**/Contents/Resources/**/**.{ttf,ttc}
14
+
15
+ unix:
16
+ paths:
17
+ - /usr/share/fonts/**/**.{ttf,ttc}
@@ -3,8 +3,6 @@ module Fontist
3
3
  def initialize(font:, sources: nil)
4
4
  @font = font
5
5
  @user_sources = sources || []
6
-
7
- check_or_create_fontist_path
8
6
  end
9
7
 
10
8
  def self.find(font, sources: [])
@@ -13,6 +11,8 @@ module Fontist
13
11
 
14
12
  def find
15
13
  paths = font_paths.grep(/#{font}/i)
14
+ paths = lookup_using_font_name || [] if paths.empty?
15
+
16
16
  paths.empty? ? nil : paths
17
17
  end
18
18
 
@@ -20,13 +20,6 @@ module Fontist
20
20
 
21
21
  attr_reader :font, :user_sources
22
22
 
23
- def check_or_create_fontist_path
24
- unless fontist_fonts_path.exist?
25
- require "fileutils"
26
- FileUtils.mkdir_p(fontist_fonts_path)
27
- end
28
- end
29
-
30
23
  def font_paths
31
24
  Dir.glob((
32
25
  user_sources +
@@ -35,13 +28,15 @@ module Fontist
35
28
  ).flatten.uniq)
36
29
  end
37
30
 
31
+ def lookup_using_font_name
32
+ font_names = map_name_to_valid_font_names || []
33
+ font_paths.grep(/#{font_names.join("|")}/i) unless font_names.empty?
34
+ end
35
+
38
36
  def fontist_fonts_path
39
37
  @fontist_fonts_path ||= Fontist.fonts_path
40
38
  end
41
39
 
42
- def default_sources
43
- @default_sources ||= Source.all.system[user_os.to_s]
44
- end
45
40
 
46
41
  def user_os
47
42
  @user_os ||= (
@@ -61,5 +56,17 @@ module Fontist
61
56
  )
62
57
  end
63
58
 
59
+ def map_name_to_valid_font_names
60
+ fonts = Formula.find_fonts(font)
61
+ fonts.map { |font| font.styles.map(&:font) }.flatten if fonts
62
+ end
63
+
64
+ def system_path_file
65
+ File.open(Fontist.lib_path.join("fontist", "system.yml"))
66
+ end
67
+
68
+ def default_sources
69
+ @default_sources ||= YAML.load(system_path_file)["system"][user_os.to_s]
70
+ end
64
71
  end
65
72
  end
@@ -0,0 +1,9 @@
1
+ require "fontist/utils/dsl"
2
+ require "fontist/utils/downloader"
3
+ require "fontist/utils/zip_extractor"
4
+ require "fontist/utils/exe_extractor"
5
+
6
+ module Fontist
7
+ module Utils
8
+ end
9
+ end
@@ -0,0 +1,74 @@
1
+ module Fontist
2
+ module Utils
3
+ class Downloader
4
+ def initialize(file, file_size: nil, sha: nil, progress_bar: nil)
5
+ @file = file
6
+ @progress_bar = progress_bar
7
+ @sha = [sha].flatten.compact
8
+ @file_size = (file_size || default_file_size).to_i
9
+ end
10
+
11
+ def download
12
+ file = download_file
13
+
14
+ if !sha.empty? && !sha.include?(Digest::SHA256.file(file).to_s)
15
+ raise(Fontist::Errors::TemparedFileError.new(
16
+ "The downloaded file from #{@file} doesn't " \
17
+ "match with the expected sha256 checksum!"
18
+ ))
19
+ end
20
+
21
+ file
22
+ end
23
+
24
+ def self.download(file, options = {})
25
+ new(file, options).download
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :file, :sha, :file_size
31
+
32
+ def default_file_size
33
+ 5 * byte_to_megabyte
34
+ end
35
+
36
+ def byte_to_megabyte
37
+ @byte_to_megabyte ||= 1024 * 1024
38
+ end
39
+
40
+ def download_path
41
+ options[:download_path] || Fontist.root_path.join("tmp")
42
+ end
43
+
44
+ def download_file
45
+ bar = ProgressBar.new(file_size / byte_to_megabyte)
46
+
47
+ Down.download(
48
+ @file,
49
+ open_timeout: 10,
50
+ read_timeout: 10,
51
+ progress_proc: -> (progress) {
52
+ bar.increment(progress / byte_to_megabyte) if @progress_bar === true
53
+ }
54
+ )
55
+
56
+ rescue Down::NotFound
57
+ raise(Fontist::Errors::InvalidResourceError.new("Invalid URL: #{@file}"))
58
+ end
59
+ end
60
+
61
+ class ProgressBar
62
+ def initialize(total)
63
+ @counter = 1
64
+ @total = total
65
+ end
66
+
67
+ def increment(progress)
68
+ complete = sprintf("%#.2f%%", ((@counter.to_f / @total.to_f) * 100))
69
+ print "\r\e[0KDownloads: #{@counter}MB/#{@total}MB (#{complete})"
70
+ @counter = progress
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,77 @@
1
+ module Fontist
2
+ module Utils
3
+ module Dsl
4
+ def key(key)
5
+ instance.key = key
6
+ end
7
+
8
+ def desc(description)
9
+ instance.description = description
10
+ end
11
+
12
+ def homepage(homepage)
13
+ instance.homepage = homepage
14
+ end
15
+
16
+ def resource(resource_name, &block)
17
+ instance.resources[resource_name] ||= {}
18
+ instance.temp_resource = instance.resources[resource_name]
19
+
20
+ yield(block) if block_given?
21
+ instance.temp_resource = {}
22
+ end
23
+
24
+ def url(url)
25
+ instance.temp_resource.merge!(urls: [url])
26
+ end
27
+
28
+ def urls(urls = [])
29
+ instance.temp_resource.merge!(urls: urls)
30
+ end
31
+
32
+ def sha256(sha256)
33
+ instance.temp_resource.merge!(sha256: sha256)
34
+ end
35
+
36
+ def file_size(file_size)
37
+ instance.temp_resource.merge!(file_size: file_size )
38
+ end
39
+
40
+ def provides_font_collection(name = nil, &block)
41
+ instance.temp_resource = {}
42
+ yield(block) if block_given?
43
+ instance.temp_resource = {}
44
+ end
45
+
46
+ def filename(name)
47
+ instance.temp_resource.merge!(filename: name)
48
+ end
49
+
50
+ def provides_font(font, options = {})
51
+ font_styles = instance.extract_font_styles(options)
52
+ instance.font_list.push(name: font, styles: font_styles)
53
+ end
54
+
55
+ def test
56
+ end
57
+
58
+ def requires_license_agreement(license)
59
+ instance.license = license
60
+ instance.license_required = true
61
+ end
62
+
63
+ def open_license(license)
64
+ instance.license = license
65
+ instance.license_required = false
66
+ end
67
+
68
+ def license_url(url)
69
+ instance.license_url = url
70
+ end
71
+
72
+ def display_progress_bar(value )
73
+ instance.options = (instance.options || {}).merge(progress_bar: value )
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,72 @@
1
+ module Fontist
2
+ module Utils
3
+ module ExeExtractor
4
+ def cab_extract(exe_file, download: true, font_ext: /.tt|.ttc/i)
5
+ download = @downloaded === true ? false : download
6
+
7
+ exe_file = download_file(exe_file).path if download
8
+ cab_file = decompressor.search(exe_file)
9
+ cabbed_fonts = grep_fonts(cab_file.files, font_ext) || []
10
+ fonts_paths = extract_cabbed_fonts_to_assets(cabbed_fonts)
11
+
12
+ yield(fonts_paths) if block_given?
13
+ end
14
+
15
+ def exe_extract(source)
16
+ cab_file = decompressor.search(download_file(source).path)
17
+ yield(build_cab_file_hash(cab_file.files)) if block_given?
18
+ end
19
+
20
+ private
21
+
22
+ def decompressor
23
+ @decompressor ||= (
24
+ require "libmspack"
25
+ LibMsPack::CabDecompressor.new
26
+ )
27
+ end
28
+
29
+ def grep_fonts(file, font_ext)
30
+ Array.new.tap do |fonts|
31
+ while file
32
+ fonts.push(file) if file.filename.match(font_ext)
33
+ file = file.next
34
+ end
35
+ end
36
+ end
37
+
38
+ def extract_cabbed_fonts_to_assets(cabbed_fonts)
39
+ Array.new.tap do |fonts|
40
+ cabbed_fonts.each do |font|
41
+ font_path = fonts_path.join(font.filename).to_s
42
+ decompressor.extract(font, font_path)
43
+
44
+ fonts.push(font_path)
45
+ end
46
+ end
47
+ end
48
+
49
+ def build_cab_file_hash(file)
50
+ Hash.new.tap do |cab_files|
51
+ while file
52
+ filename = file.filename
53
+ if filename.include?("cab")
54
+ file_path = temp_dir.join(filename).to_s
55
+
56
+ decompressor.extract(file, file_path)
57
+ cab_files[filename.to_s] = file_path
58
+ end
59
+
60
+ file = file.next
61
+ end
62
+ end
63
+ end
64
+
65
+ def temp_dir
66
+ @temp_dir ||= raise(
67
+ NotImplementedError.new("You must implement this method"),
68
+ )
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,38 @@
1
+ require "zip"
2
+ require "pathname"
3
+
4
+ module Fontist
5
+ module Utils
6
+ module ZipExtractor
7
+ def zip_extract(resource, download: true, fonts_sub_dir: "")
8
+ zip_file = download_file(resource) if download
9
+ zip_file ||= resource.urls.first
10
+
11
+ fonts_paths = unzip_fonts(zip_file, fonts_sub_dir)
12
+ block_given? ? yield(fonts_paths) : fonts_paths
13
+ end
14
+
15
+ alias_method :unzip, :zip_extract
16
+
17
+ private
18
+
19
+ def unzip_fonts(file, fonts_sub_dir = "")
20
+ Zip.on_exists_proc = true
21
+ Array.new.tap do |fonts|
22
+
23
+ Zip::File.open(file) do |zip_file|
24
+ zip_file.glob("#{fonts_sub_dir}*.{ttf,ttc,otf}").each do |entry|
25
+ if entry.name
26
+ filename = Pathname.new(entry.name).basename
27
+ font_path = fonts_path.join(filename.to_s)
28
+ fonts.push(font_path.to_s)
29
+
30
+ entry.extract(font_path)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -1,3 +1,3 @@
1
1
  module Fontist
2
- VERSION = "0.2.0".freeze
2
+ VERSION = "1.2.0".freeze
3
3
  end
@@ -0,0 +1,67 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "Fontist::Formulas::DemoFormula" do
4
+ describe "initialization" do
5
+ it "registers formula resources through the DSL" do
6
+ formula = Fontist::Formulas::DemoFormula.instance
7
+ resource = formula.resources["demo-formula"]
8
+ demo_font = formula.font_list.first
9
+
10
+ expect(formula.key).to eq(:demo_formula)
11
+ expect(formula.license_required).to be_truthy
12
+ expect(formula.description).to eq("Demo font formula")
13
+ expect(formula.license).to eq("Vendor specific font licences")
14
+ expect(formula.homepage).to eq("https://github.com/fontist/fontist")
15
+
16
+ expect(resource[:file_size]).to eq("1234567890")
17
+ expect(resource[:urls].first).to eq("https://github.com/fontist/fontist")
18
+ expect(resource[:sha256]).to eq("594e0f42e6581add4dead70c1dfb9")
19
+
20
+ expect(demo_font[:styles].count).to eq(2)
21
+ expect(demo_font[:name]).to eq("Demo font")
22
+ expect(demo_font[:styles].first[:font]).to eq("demo-font.ttf")
23
+ end
24
+ end
25
+
26
+ describe "method invokation" do
27
+ it "invokes the correct method for installation" do
28
+ expect {
29
+ Fontist::Formulas::DemoFormula.fetch_font("Demo font", confirmation: "yes")
30
+ }.not_to raise_error
31
+ end
32
+ end
33
+
34
+ module Fontist
35
+ module Formulas
36
+ class DemoFormula < FontFormula
37
+ key :demo_formula
38
+ desc "Demo font formula"
39
+ homepage "https://github.com/fontist/fontist"
40
+ requires_license_agreement "Vendor specific font licences"
41
+
42
+ resource "demo-formula" do
43
+ urls [ "https://github.com/fontist/fontist" ]
44
+ sha256 "594e0f42e6581add4dead70c1dfb9"
45
+ file_size "1234567890"
46
+ end
47
+
48
+ provides_font "Demo font", match_styles_from_file: {
49
+ "Regular" => "demo-font.ttf",
50
+ "Italic" => "demo-fonti.ttf"
51
+ }
52
+
53
+ provides_font_collection("Meiryo Bold") do |coll|
54
+ filename "demo-font.ttc"
55
+ provides_font "Demo collection", extract_styles_from_collection: {
56
+ "Regular" => "demo-col-font.ttf",
57
+ "Italic" => "demo-col-fonti.ttf"
58
+ }
59
+ end
60
+
61
+ def extract
62
+ []
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,87 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe Fontist::Font do
4
+ describe ".all" do
5
+ it "list all supported fonts" do
6
+ fonts = Fontist::Font.all
7
+
8
+ expect(fonts.count).to be > 10
9
+ expect(fonts.first.name).not_to be_nil
10
+ expect(fonts.first.styles).not_to be_nil
11
+ end
12
+ end
13
+
14
+ describe ".find" do
15
+ context "with valid font name" do
16
+ it "returns the fonts path" do
17
+ name = "DejaVuSerif.ttf"
18
+ stub_system_font_finder_to_fixture(name)
19
+ dejavu_ttf = Fontist::Font.find(name)
20
+
21
+ expect(dejavu_ttf.first).to include(name)
22
+ end
23
+ end
24
+
25
+ context "with downloadable font name" do
26
+ it "raises font missing error" do
27
+ name = "Courier"
28
+
29
+ allow(Fontist::SystemFont).to receive(:find).and_return(nil)
30
+ expect {
31
+ Fontist::Font.find(name)
32
+ }.to raise_error(Fontist::Errors::MissingFontError)
33
+ end
34
+ end
35
+
36
+ context "with invalid font name" do
37
+ it "raises font unsupported error" do
38
+ font_name = "InvalidFont.ttf"
39
+
40
+ expect {
41
+ Fontist::Font.find(font_name)
42
+ }.to raise_error(Fontist::Errors::NonSupportedFontError)
43
+ end
44
+ end
45
+ end
46
+
47
+ describe ".install" do
48
+ context "with valid font name" do
49
+ it "installs the font and return the paths" do
50
+ name = "Calibri"
51
+
52
+ stub_fontist_path_to_temp_path
53
+ font_paths = Fontist::Font.install(name, confirmation: "yes")
54
+
55
+ expect(font_paths.join("|").downcase).to include("#{name.downcase}.ttf")
56
+ end
57
+ end
58
+
59
+ context "with existing font name" do
60
+ it "returns the existing font paths" do
61
+ name = "Courier"
62
+ stub_fontist_path_to_temp_path
63
+ Fontist::Font.install(name, confirmation: "yes")
64
+
65
+ font_paths = Fontist::Font.install(name, confirmation: "yes")
66
+
67
+ expect(font_paths.count).to be > 3
68
+ expect(Fontist::Formulas::CourierFont).not_to receive(:fetch_font)
69
+ end
70
+ end
71
+
72
+ context "with unsupported fonts" do
73
+ it "raises an unsupported error" do
74
+ name = "Invalid font name"
75
+
76
+ expect {
77
+ Fontist::Font.install(name, confirmation: "yes")
78
+ }.to raise_error(Fontist::Errors::NonSupportedFontError)
79
+ end
80
+ end
81
+ end
82
+
83
+ def stub_system_font_finder_to_fixture(name)
84
+ allow(Fontist::SystemFont).to receive(:find).
85
+ and_return(["spec/fixtures/fonts/#{name}"])
86
+ end
87
+ end