fontist 0.2.0 → 1.2.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 (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