fontist 1.20.0 → 1.21.1
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.
- checksums.yaml +4 -4
- data/.github/workflows/test-and-release.yml +2 -1
- data/README.adoc +1 -1
- data/exe/fontist +1 -2
- data/lib/fontist/cli/thor_ext.rb +79 -0
- data/lib/fontist/cli.rb +2 -0
- data/lib/fontist/config.rb +2 -1
- data/lib/fontist/font.rb +5 -0
- data/lib/fontist/font_installer.rb +22 -51
- data/lib/fontist/formula.rb +6 -0
- data/lib/fontist/helpers.rb +2 -0
- data/lib/fontist/import/create_formula.rb +77 -35
- data/lib/fontist/import/formula_builder.rb +63 -81
- data/lib/fontist/import/google/api.rb +25 -0
- data/lib/fontist/import/google/create_google_formula.rb +89 -0
- data/lib/fontist/import/google_import.rb +63 -32
- data/lib/fontist/import/recursive_extraction.rb +0 -16
- data/lib/fontist/manifest/locations.rb +2 -0
- data/lib/fontist/resources/archive_resource.rb +55 -0
- data/lib/fontist/resources/google_resource.rb +64 -0
- data/lib/fontist/style_version.rb +4 -0
- data/lib/fontist/utils/cache.rb +16 -0
- data/lib/fontist/utils/downloader.rb +9 -2
- data/lib/fontist/utils/ui.rb +10 -2
- data/lib/fontist/version.rb +1 -1
- data/lib/fontist.rb +5 -1
- metadata +8 -6
- data/lib/fontist/import/google/new_fonts_fetcher.rb +0 -146
- data/lib/fontist/import/google/skiplist.yml +0 -12
- data/lib/fontist/import/google_check.rb +0 -27
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 0cc55cf30c5781a41e3ff4ff617acacb24367c3148dc6c1db104ff5a1aad8105
         | 
| 4 | 
            +
              data.tar.gz: 118207311bf22488c6adbff254fd3646d24f5b1f09f09d8eaf319489798c5002
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: aebf180824a2e45fc23e6d795e98fa8394e735d1a0c6f58d5ec5740ea2343e31787d9075db3060a071ce3a8dc0b193502e6b76e76fba58c345522cc47ddd4b74
         | 
| 7 | 
            +
              data.tar.gz: e411af70f8dd20ab8f7a55540fa24b5741a37813420fd14b0e3518f30a1d0a477565add1da666f6f91207ec90343ab2eabc04cd791d69a22c3c6c08c01ea639f
         | 
| @@ -16,6 +16,7 @@ env: | |
| 16 16 | 
             
            # does not cause bundler gem reinstalls
         | 
| 17 17 | 
             
            # bundler/rubygems 2.3.22 is a minimal requirement to support gnu/musl differentiation
         | 
| 18 18 | 
             
            # https://github.com/rubygems/rubygems/pull/4488
         | 
| 19 | 
            +
              GOOGLE_FONTS_API_KEY: ${{secrets.FONTIST_CI_GOOGLE_FONTS_API_KEY}}
         | 
| 19 20 |  | 
| 20 21 | 
             
            jobs:
         | 
| 21 22 | 
             
              prepare:
         | 
| @@ -83,7 +84,7 @@ jobs: | |
| 83 84 | 
             
                needs: prepare
         | 
| 84 85 | 
             
                if: needs.prepare.outputs.push-for-tag != 'true'
         | 
| 85 86 |  | 
| 86 | 
            -
                continue-on-error: ${{ matrix.ruby.experimental || matrix.os == 'windows-latest' }} # workaround https://github.com/metanorma/metanorma/issues/288
         | 
| 87 | 
            +
                continue-on-error: true # ${{ matrix.ruby.experimental || matrix.os == 'windows-latest' }} # workaround https://github.com/metanorma/metanorma/issues/288
         | 
| 87 88 | 
             
                strategy:
         | 
| 88 89 | 
             
                  fail-fast: false
         | 
| 89 90 | 
             
                  max-parallel: 5
         | 
    
        data/README.adoc
    CHANGED
    
    | @@ -1066,7 +1066,7 @@ Fontist's https://github.com/fontist/formulas[formula library] includes support | |
| 1066 1066 | 
             
            for all openly-licensed fonts provided through Google Fonts, and maintains
         | 
| 1067 1067 | 
             
            Fontist formulas for all such fonts.
         | 
| 1068 1068 |  | 
| 1069 | 
            -
            https://github.com/fontist/formulas/blob/ | 
| 1069 | 
            +
            https://github.com/fontist/formulas/blob/v4/.github/workflows/google.yml[A GHA
         | 
| 1070 1070 | 
             
            workflow] checks for updated fonts on Google Fonts daily. In case an update is
         | 
| 1071 1071 | 
             
            found, it's added to the repo by the workflow.
         | 
| 1072 1072 |  | 
    
        data/exe/fontist
    CHANGED
    
    
| @@ -0,0 +1,79 @@ | |
| 1 | 
            +
            require "fontist/utils/ui"
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Fontist
         | 
| 4 | 
            +
              module ThorExt
         | 
| 5 | 
            +
                # Sources:
         | 
| 6 | 
            +
                # - https://github.com/mattbrictson/gem/blob/main/lib/example/thor_ext.rb
         | 
| 7 | 
            +
                # - https://mattbrictson.com/blog/fixing-thor-cli-behavior
         | 
| 8 | 
            +
                #
         | 
| 9 | 
            +
                # Configures Thor to behave more like a typical CLI, with better help
         | 
| 10 | 
            +
                # and error handling.
         | 
| 11 | 
            +
                #
         | 
| 12 | 
            +
                # - Passing -h or --help to a command will show help for that command.
         | 
| 13 | 
            +
                # - Unrecognized options will be treated as errors.
         | 
| 14 | 
            +
                # - Error messages will be printed in red to stderr, without stack trace.
         | 
| 15 | 
            +
                # - Errors will cause Thor to exit with a non-zero status.
         | 
| 16 | 
            +
                #
         | 
| 17 | 
            +
                # To take advantage of this behavior, your CLI should subclass Thor
         | 
| 18 | 
            +
                # and extend this module.
         | 
| 19 | 
            +
                #
         | 
| 20 | 
            +
                #   class CLI < Thor
         | 
| 21 | 
            +
                #     extend ThorExt::Start
         | 
| 22 | 
            +
                #   end
         | 
| 23 | 
            +
                #
         | 
| 24 | 
            +
                # Start your CLI with:
         | 
| 25 | 
            +
                #
         | 
| 26 | 
            +
                #   CLI.start
         | 
| 27 | 
            +
                #
         | 
| 28 | 
            +
                # In tests, prevent Kernel.exit from being called when an error occurs,
         | 
| 29 | 
            +
                # like this:
         | 
| 30 | 
            +
                #
         | 
| 31 | 
            +
                #   CLI.start(args, exit_on_failure: false)
         | 
| 32 | 
            +
                module Start
         | 
| 33 | 
            +
                  def self.extended(base)
         | 
| 34 | 
            +
                    super
         | 
| 35 | 
            +
                    base.check_unknown_options!
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                  def start(given_args = ARGV, config = {})
         | 
| 39 | 
            +
                    config[:shell] ||= Thor::Base.shell.new
         | 
| 40 | 
            +
                    handle_help_switches(given_args) do |args|
         | 
| 41 | 
            +
                      dispatch(nil, args, nil, config)
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
                  rescue StandardError => e
         | 
| 44 | 
            +
                    handle_exception_on_start(e, config)
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  private
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
         | 
| 50 | 
            +
                  def handle_help_switches(given_args)
         | 
| 51 | 
            +
                    yield(given_args.dup)
         | 
| 52 | 
            +
                  rescue Thor::UnknownArgumentError => e
         | 
| 53 | 
            +
                    retry_with_args = []
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                    if given_args.first == "help"
         | 
| 56 | 
            +
                      retry_with_args = ["help"] if given_args.length > 1
         | 
| 57 | 
            +
                    elsif e.unknown.intersect?(%w[-h --help])
         | 
| 58 | 
            +
                      retry_with_args = ["help", (given_args - e.unknown).first]
         | 
| 59 | 
            +
                    end
         | 
| 60 | 
            +
                    raise unless retry_with_args.any?
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    yield(retry_with_args)
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  def handle_exception_on_start(error, config)
         | 
| 66 | 
            +
                    return if error.is_a?(Errno::EPIPE)
         | 
| 67 | 
            +
                    raise if Fontist.ui.debug? || !config.fetch(:exit_on_failure, true)
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                    message = error.message.to_s
         | 
| 70 | 
            +
                    if message.empty? || !error.is_a?(Thor::Error)
         | 
| 71 | 
            +
                      message.prepend("[#{error.class}] ")
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
                    config[:shell]&.say_error(message, :red)
         | 
| 74 | 
            +
                    exit(false)
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
                  # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
              end
         | 
| 79 | 
            +
            end
         | 
    
        data/lib/fontist/cli.rb
    CHANGED
    
    | @@ -1,5 +1,6 @@ | |
| 1 1 | 
             
            require "thor"
         | 
| 2 2 | 
             
            require "fontist/cli/class_options"
         | 
| 3 | 
            +
            require "fontist/cli/thor_ext"
         | 
| 3 4 | 
             
            require "fontist/repo_cli"
         | 
| 4 5 | 
             
            require "fontist/cache_cli"
         | 
| 5 6 | 
             
            require "fontist/import_cli"
         | 
| @@ -9,6 +10,7 @@ require "fontist/config_cli" | |
| 9 10 | 
             
            module Fontist
         | 
| 10 11 | 
             
              class CLI < Thor
         | 
| 11 12 | 
             
                include ClassOptions
         | 
| 13 | 
            +
                extend ThorExt::Start
         | 
| 12 14 |  | 
| 13 15 | 
             
                STATUS_SUCCESS = 0
         | 
| 14 16 | 
             
                STATUS_UNKNOWN_ERROR = 1
         | 
    
        data/lib/fontist/config.rb
    CHANGED
    
    
    
        data/lib/fontist/font.rb
    CHANGED
    
    | @@ -216,6 +216,11 @@ module Fontist | |
| 216 216 | 
             
                  confirmation = check_and_confirm_required_license(formula)
         | 
| 217 217 | 
             
                  paths = font_installer(formula).install(confirmation: confirmation)
         | 
| 218 218 |  | 
| 219 | 
            +
                  if paths.nil? || paths.empty?
         | 
| 220 | 
            +
                    Fontist.ui.error("Fonts not found in formula #{formula}")
         | 
| 221 | 
            +
                    return
         | 
| 222 | 
            +
                  end
         | 
| 223 | 
            +
             | 
| 219 224 | 
             
                  Fontist.ui.say("Fonts installed at:")
         | 
| 220 225 | 
             
                  paths.each do |path|
         | 
| 221 226 | 
             
                    Fontist.ui.say("- #{path}")
         | 
| @@ -1,5 +1,7 @@ | |
| 1 1 | 
             
            require "fontist/utils"
         | 
| 2 2 | 
             
            require "excavate"
         | 
| 3 | 
            +
            require_relative "resources/archive_resource"
         | 
| 4 | 
            +
            require_relative "resources/google_resource"
         | 
| 3 5 |  | 
| 4 6 | 
             
            module Fontist
         | 
| 5 7 | 
             
              class FontInstaller
         | 
| @@ -48,63 +50,32 @@ module Fontist | |
| 48 50 | 
             
                end
         | 
| 49 51 |  | 
| 50 52 | 
             
                def install_font
         | 
| 51 | 
            -
                  fonts_paths =  | 
| 53 | 
            +
                  fonts_paths = do_install_font
         | 
| 52 54 | 
             
                  fonts_paths.empty? ? nil : fonts_paths
         | 
| 53 55 | 
             
                end
         | 
| 54 56 |  | 
| 55 | 
            -
                def  | 
| 56 | 
            -
                   | 
| 57 | 
            -
                    @temp_dir = Pathname.new(dir)
         | 
| 58 | 
            -
             | 
| 59 | 
            -
                    result = yield
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                    @temp_dir = nil
         | 
| 62 | 
            -
             | 
| 63 | 
            -
                    result
         | 
| 64 | 
            -
                  end
         | 
| 65 | 
            -
                end
         | 
| 66 | 
            -
             | 
| 67 | 
            -
                def extract
         | 
| 68 | 
            -
                  archive = download_file(@formula.resources.first)
         | 
| 69 | 
            -
             | 
| 70 | 
            -
                  install_fonts_from_archive(archive)
         | 
| 71 | 
            -
                end
         | 
| 72 | 
            -
             | 
| 73 | 
            -
                def install_fonts_from_archive(archive)
         | 
| 74 | 
            -
                  Fontist.ui.say(%(Installing font "#{@formula.key}".))
         | 
| 57 | 
            +
                def do_install_font
         | 
| 58 | 
            +
                  Fontist.ui.say(%(Installing from formula "#{@formula.key}".))
         | 
| 75 59 |  | 
| 76 60 | 
             
                  Array.new.tap do |fonts_paths|
         | 
| 77 | 
            -
                     | 
| 61 | 
            +
                    resource.files(source_files) do |path|
         | 
| 78 62 | 
             
                      fonts_paths << install_font_file(path) if font_file?(path)
         | 
| 79 63 | 
             
                    end
         | 
| 80 64 | 
             
                  end
         | 
| 81 65 | 
             
                end
         | 
| 82 66 |  | 
| 83 | 
            -
                def  | 
| 84 | 
            -
                   | 
| 85 | 
            -
             | 
| 86 | 
            -
             | 
| 87 | 
            -
             | 
| 88 | 
            -
             | 
| 89 | 
            -
                    result = try_download_file(request, source)
         | 
| 90 | 
            -
                    return result unless result.is_a?(Errors::InvalidResourceError)
         | 
| 91 | 
            -
             | 
| 92 | 
            -
                    errors << result
         | 
| 93 | 
            -
                  end
         | 
| 67 | 
            +
                def resource
         | 
| 68 | 
            +
                  resource_class = if @formula.source == "google"
         | 
| 69 | 
            +
                                     Resources::GoogleResource
         | 
| 70 | 
            +
                                   else
         | 
| 71 | 
            +
                                     Resources::ArchiveResource
         | 
| 72 | 
            +
                                   end
         | 
| 94 73 |  | 
| 95 | 
            -
                   | 
| 74 | 
            +
                  resource_class.new(resource_options, no_progress: @no_progress)
         | 
| 96 75 | 
             
                end
         | 
| 97 76 |  | 
| 98 | 
            -
                def  | 
| 99 | 
            -
                   | 
| 100 | 
            -
                    request,
         | 
| 101 | 
            -
                    sha: source.sha256,
         | 
| 102 | 
            -
                    file_size: source.file_size,
         | 
| 103 | 
            -
                    progress_bar: !@no_progress
         | 
| 104 | 
            -
                  )
         | 
| 105 | 
            -
                rescue Errors::InvalidResourceError => e
         | 
| 106 | 
            -
                  Fontist.ui.say(e.message)
         | 
| 107 | 
            -
                  e
         | 
| 77 | 
            +
                def resource_options
         | 
| 78 | 
            +
                  @formula.resources.first
         | 
| 108 79 | 
             
                end
         | 
| 109 80 |  | 
| 110 81 | 
             
                def font_file?(path)
         | 
| @@ -116,16 +87,16 @@ module Fontist | |
| 116 87 | 
             
                end
         | 
| 117 88 |  | 
| 118 89 | 
             
                def source_files
         | 
| 119 | 
            -
                  @source_files ||=  | 
| 120 | 
            -
                     | 
| 121 | 
            -
             | 
| 122 | 
            -
                     | 
| 90 | 
            +
                  @source_files ||= fonts.flat_map do |font|
         | 
| 91 | 
            +
                    font.styles.map do |style|
         | 
| 92 | 
            +
                      style.source_font || style.font
         | 
| 93 | 
            +
                    end
         | 
| 123 94 | 
             
                  end
         | 
| 124 95 | 
             
                end
         | 
| 125 96 |  | 
| 126 | 
            -
                def  | 
| 127 | 
            -
                   | 
| 128 | 
            -
                     | 
| 97 | 
            +
                def fonts
         | 
| 98 | 
            +
                  @formula.fonts.select do |font|
         | 
| 99 | 
            +
                    @font_name.nil? || font.name.casecmp?(@font_name)
         | 
| 129 100 | 
             
                  end
         | 
| 130 101 | 
             
                end
         | 
| 131 102 |  | 
    
        data/lib/fontist/formula.rb
    CHANGED
    
    
    
        data/lib/fontist/helpers.rb
    CHANGED
    
    
| @@ -1,6 +1,5 @@ | |
| 1 1 | 
             
            require "fontist/import"
         | 
| 2 2 | 
             
            require_relative "recursive_extraction"
         | 
| 3 | 
            -
            require_relative "helpers/hash_helper"
         | 
| 4 3 | 
             
            require_relative "formula_builder"
         | 
| 5 4 |  | 
| 6 5 | 
             
            module Fontist
         | 
| @@ -12,31 +11,102 @@ module Fontist | |
| 12 11 | 
             
                  end
         | 
| 13 12 |  | 
| 14 13 | 
             
                  def call
         | 
| 15 | 
            -
                    save | 
| 14 | 
            +
                    builder.save
         | 
| 16 15 | 
             
                  end
         | 
| 17 16 |  | 
| 18 17 | 
             
                  private
         | 
| 19 18 |  | 
| 20 19 | 
             
                  def builder
         | 
| 21 20 | 
             
                    builder = FormulaBuilder.new
         | 
| 22 | 
            -
                    setup_strings(builder | 
| 21 | 
            +
                    setup_strings(builder)
         | 
| 23 22 | 
             
                    setup_files(builder)
         | 
| 24 23 | 
             
                    builder
         | 
| 25 24 | 
             
                  end
         | 
| 26 25 |  | 
| 27 | 
            -
                  def setup_strings(builder | 
| 28 | 
            -
                    builder.archive = archive
         | 
| 29 | 
            -
                    builder.url = @url
         | 
| 26 | 
            +
                  def setup_strings(builder)
         | 
| 30 27 | 
             
                    builder.options = @options
         | 
| 28 | 
            +
                    builder.resources = resources
         | 
| 31 29 | 
             
                  end
         | 
| 32 30 |  | 
| 33 31 | 
             
                  def setup_files(builder)
         | 
| 34 | 
            -
                    builder. | 
| 32 | 
            +
                    builder.operations = extractor.operations
         | 
| 35 33 | 
             
                    builder.font_files = extractor.font_files
         | 
| 36 34 | 
             
                    builder.font_collection_files = extractor.font_collection_files
         | 
| 37 35 | 
             
                    builder.license_text = extractor.license_text
         | 
| 38 36 | 
             
                  end
         | 
| 39 37 |  | 
| 38 | 
            +
                  def resources
         | 
| 39 | 
            +
                    @resources ||= { filename(archive) => resource_options }
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  def filename(file)
         | 
| 43 | 
            +
                    if file.respond_to?(:original_filename)
         | 
| 44 | 
            +
                      file.original_filename
         | 
| 45 | 
            +
                    else
         | 
| 46 | 
            +
                      File.basename(file)
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  def resource_options
         | 
| 51 | 
            +
                    if @options[:skip_sha]
         | 
| 52 | 
            +
                      resource_options_without_sha
         | 
| 53 | 
            +
                    else
         | 
| 54 | 
            +
                      resource_options_with_sha
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  def resource_options_without_sha
         | 
| 59 | 
            +
                    { urls: [@url] + mirrors, file_size: file_size }
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  def resource_options_with_sha
         | 
| 63 | 
            +
                    urls = []
         | 
| 64 | 
            +
                    sha = []
         | 
| 65 | 
            +
                    downloads do |url, path|
         | 
| 66 | 
            +
                      urls << url
         | 
| 67 | 
            +
                      sha << Digest::SHA256.file(path).to_s
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                    sha = prepare_sha256(sha)
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                    { urls: urls, sha256: sha, file_size: file_size }
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  def downloads
         | 
| 76 | 
            +
                    yield @url, archive
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                    mirrors.each do |url|
         | 
| 79 | 
            +
                      path = download_mirror(url)
         | 
| 80 | 
            +
                      next unless path
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                      yield url, path
         | 
| 83 | 
            +
                    end
         | 
| 84 | 
            +
                  end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                  def mirrors
         | 
| 87 | 
            +
                    @options[:mirror] || []
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  def download_mirror(url)
         | 
| 91 | 
            +
                    Fontist::Utils::Downloader.download(url, progress_bar: true).path
         | 
| 92 | 
            +
                  rescue Errors::InvalidResourceError
         | 
| 93 | 
            +
                    Fontist.ui.error("WARN: a mirror is not found '#{url}'")
         | 
| 94 | 
            +
                    nil
         | 
| 95 | 
            +
                  end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                  def prepare_sha256(input)
         | 
| 98 | 
            +
                    output = input.uniq
         | 
| 99 | 
            +
                    return output.first if output.size == 1
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                    checksums = output.join(", ")
         | 
| 102 | 
            +
                    Fontist.ui.error("WARN: SHA256 differs (#{checksums})")
         | 
| 103 | 
            +
                    output
         | 
| 104 | 
            +
                  end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                  def file_size
         | 
| 107 | 
            +
                    File.size(archive)
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
             | 
| 40 110 | 
             
                  def extractor
         | 
| 41 111 | 
             
                    @extractor ||=
         | 
| 42 112 | 
             
                      RecursiveExtraction.new(archive,
         | 
| @@ -53,34 +123,6 @@ module Fontist | |
| 53 123 |  | 
| 54 124 | 
             
                    Fontist::Utils::Downloader.download(url, progress_bar: true).path
         | 
| 55 125 | 
             
                  end
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                  def save(builder)
         | 
| 58 | 
            -
                    path = vacant_path
         | 
| 59 | 
            -
                    yaml = YAML.dump(Helpers::HashHelper.stringify_keys(builder.formula))
         | 
| 60 | 
            -
                    File.write(path, yaml)
         | 
| 61 | 
            -
                    path
         | 
| 62 | 
            -
                  end
         | 
| 63 | 
            -
             | 
| 64 | 
            -
                  def vacant_path
         | 
| 65 | 
            -
                    path = path_from_name
         | 
| 66 | 
            -
                    return path unless @options[:keep_existing] && File.exist?(path)
         | 
| 67 | 
            -
             | 
| 68 | 
            -
                    2.upto(9) do |i|
         | 
| 69 | 
            -
                      candidate = path.sub(/\.yml$/, "#{i}.yml")
         | 
| 70 | 
            -
                      return candidate unless File.exist?(candidate)
         | 
| 71 | 
            -
                    end
         | 
| 72 | 
            -
             | 
| 73 | 
            -
                    raise Errors::GeneralError, "Formula #{path} already exists."
         | 
| 74 | 
            -
                  end
         | 
| 75 | 
            -
             | 
| 76 | 
            -
                  def path_from_name
         | 
| 77 | 
            -
                    filename = Import.name_to_filename(builder.name)
         | 
| 78 | 
            -
                    if @options[:formula_dir]
         | 
| 79 | 
            -
                      File.join(@options[:formula_dir], filename)
         | 
| 80 | 
            -
                    else
         | 
| 81 | 
            -
                      filename
         | 
| 82 | 
            -
                    end
         | 
| 83 | 
            -
                  end
         | 
| 84 126 | 
             
                end
         | 
| 85 127 | 
             
              end
         | 
| 86 128 | 
             
            end
         | 
| @@ -1,49 +1,70 @@ | |
| 1 1 | 
             
            require "shellwords"
         | 
| 2 2 | 
             
            require_relative "text_helper"
         | 
| 3 | 
            +
            require_relative "helpers/hash_helper"
         | 
| 3 4 |  | 
| 4 5 | 
             
            module Fontist
         | 
| 5 6 | 
             
              module Import
         | 
| 6 7 | 
             
                class FormulaBuilder
         | 
| 7 | 
            -
                  FORMULA_ATTRIBUTES = %i[platforms description homepage resources
         | 
| 8 | 
            +
                  FORMULA_ATTRIBUTES = %i[name platforms description homepage resources
         | 
| 8 9 | 
             
                                          font_collections fonts extract copyright
         | 
| 9 10 | 
             
                                          license_url requires_license_agreement
         | 
| 10 11 | 
             
                                          open_license digest command].freeze
         | 
| 11 12 |  | 
| 12 | 
            -
                  attr_writer : | 
| 13 | 
            -
                              :url,
         | 
| 14 | 
            -
                              :extractor,
         | 
| 13 | 
            +
                  attr_writer :resources,
         | 
| 15 14 | 
             
                              :options,
         | 
| 16 15 | 
             
                              :font_files,
         | 
| 17 16 | 
             
                              :font_collection_files,
         | 
| 18 17 | 
             
                              :license_text,
         | 
| 19 | 
            -
                              : | 
| 18 | 
            +
                              :operations
         | 
| 20 19 |  | 
| 21 20 | 
             
                  def initialize
         | 
| 22 21 | 
             
                    @options = {}
         | 
| 22 | 
            +
                    @font_files = []
         | 
| 23 | 
            +
                    @font_collection_files = []
         | 
| 23 24 | 
             
                  end
         | 
| 24 25 |  | 
| 25 26 | 
             
                  def formula
         | 
| 26 27 | 
             
                    formula_attributes.map { |name| [name, send(name)] }.to_h.compact
         | 
| 27 28 | 
             
                  end
         | 
| 28 29 |  | 
| 30 | 
            +
                  def save
         | 
| 31 | 
            +
                    path = vacant_path
         | 
| 32 | 
            +
                    yaml = YAML.dump(Helpers::HashHelper.stringify_keys(formula))
         | 
| 33 | 
            +
                    File.write(path, yaml)
         | 
| 34 | 
            +
                    path
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  private
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  def formula_attributes
         | 
| 40 | 
            +
                    FORMULA_ATTRIBUTES
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
             | 
| 29 43 | 
             
                  def name
         | 
| 44 | 
            +
                    @name ||= generate_name
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  def generate_name
         | 
| 30 48 | 
             
                    return @options[:name] if @options[:name]
         | 
| 31 49 |  | 
| 32 | 
            -
                    common =  | 
| 33 | 
            -
                      .map { |attr| both_fonts.map(&attr).uniq }
         | 
| 34 | 
            -
                      .map { |names| TextHelper.longest_common_prefix(names) }
         | 
| 35 | 
            -
                      .map { |prefix| prefix unless prefix == "Regular" }
         | 
| 36 | 
            -
                      .compact
         | 
| 37 | 
            -
                      .join(" ")
         | 
| 50 | 
            +
                    common = common_prefix
         | 
| 38 51 | 
             
                    return common unless common.empty?
         | 
| 39 52 |  | 
| 40 53 | 
             
                    both_fonts.map(&:family_name).first
         | 
| 41 54 | 
             
                  end
         | 
| 42 55 |  | 
| 43 | 
            -
                   | 
| 56 | 
            +
                  def common_prefix
         | 
| 57 | 
            +
                    family_prefix = common_prefix_by_attr(:family_name)
         | 
| 58 | 
            +
                    style_prefix = common_prefix_by_attr(:type)
         | 
| 44 59 |  | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 60 | 
            +
                    [family_prefix, style_prefix].compact.join(" ")
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  def common_prefix_by_attr(attr)
         | 
| 64 | 
            +
                    names = both_fonts.map(&attr).uniq
         | 
| 65 | 
            +
                    prefix = TextHelper.longest_common_prefix(names)
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    prefix unless prefix == "Regular"
         | 
| 47 68 | 
             
                  end
         | 
| 48 69 |  | 
| 49 70 | 
             
                  def both_fonts
         | 
| @@ -70,69 +91,7 @@ module Fontist | |
| 70 91 | 
             
                  end
         | 
| 71 92 |  | 
| 72 93 | 
             
                  def resources
         | 
| 73 | 
            -
                     | 
| 74 | 
            -
             | 
| 75 | 
            -
                    { filename => resource_options }
         | 
| 76 | 
            -
                  end
         | 
| 77 | 
            -
             | 
| 78 | 
            -
                  def resource_options
         | 
| 79 | 
            -
                    if @options[:skip_sha]
         | 
| 80 | 
            -
                      resource_options_without_sha
         | 
| 81 | 
            -
                    else
         | 
| 82 | 
            -
                      resource_options_with_sha
         | 
| 83 | 
            -
                    end
         | 
| 84 | 
            -
                  end
         | 
| 85 | 
            -
             | 
| 86 | 
            -
                  def resource_options_without_sha
         | 
| 87 | 
            -
                    { urls: [@url] + mirrors, file_size: file_size }
         | 
| 88 | 
            -
                  end
         | 
| 89 | 
            -
             | 
| 90 | 
            -
                  def resource_options_with_sha
         | 
| 91 | 
            -
                    urls = []
         | 
| 92 | 
            -
                    sha = []
         | 
| 93 | 
            -
                    downloads do |url, path|
         | 
| 94 | 
            -
                      urls << url
         | 
| 95 | 
            -
                      sha << Digest::SHA256.file(path).to_s
         | 
| 96 | 
            -
                    end
         | 
| 97 | 
            -
             | 
| 98 | 
            -
                    sha = prepare_sha256(sha)
         | 
| 99 | 
            -
             | 
| 100 | 
            -
                    { urls: urls, sha256: sha, file_size: file_size }
         | 
| 101 | 
            -
                  end
         | 
| 102 | 
            -
             | 
| 103 | 
            -
                  def downloads
         | 
| 104 | 
            -
                    yield @url, @archive
         | 
| 105 | 
            -
             | 
| 106 | 
            -
                    mirrors.each do |url|
         | 
| 107 | 
            -
                      path = download(url)
         | 
| 108 | 
            -
                      next unless path
         | 
| 109 | 
            -
             | 
| 110 | 
            -
                      yield url, path
         | 
| 111 | 
            -
                    end
         | 
| 112 | 
            -
                  end
         | 
| 113 | 
            -
             | 
| 114 | 
            -
                  def mirrors
         | 
| 115 | 
            -
                    @options[:mirror] || []
         | 
| 116 | 
            -
                  end
         | 
| 117 | 
            -
             | 
| 118 | 
            -
                  def download(url)
         | 
| 119 | 
            -
                    Fontist::Utils::Downloader.download(url, progress_bar: true).path
         | 
| 120 | 
            -
                  rescue Errors::InvalidResourceError
         | 
| 121 | 
            -
                    Fontist.ui.error("WARN: a mirror is not found '#{url}'")
         | 
| 122 | 
            -
                    nil
         | 
| 123 | 
            -
                  end
         | 
| 124 | 
            -
             | 
| 125 | 
            -
                  def prepare_sha256(input)
         | 
| 126 | 
            -
                    output = input.uniq
         | 
| 127 | 
            -
                    return output.first if output.size == 1
         | 
| 128 | 
            -
             | 
| 129 | 
            -
                    checksums = output.join(", ")
         | 
| 130 | 
            -
                    Fontist.ui.error("WARN: SHA256 differs (#{checksums})")
         | 
| 131 | 
            -
                    output
         | 
| 132 | 
            -
                  end
         | 
| 133 | 
            -
             | 
| 134 | 
            -
                  def file_size
         | 
| 135 | 
            -
                    File.size(@archive)
         | 
| 94 | 
            +
                    @resources || raise("Resources should be set.")
         | 
| 136 95 | 
             
                  end
         | 
| 137 96 |  | 
| 138 97 | 
             
                  def font_collections
         | 
| @@ -175,7 +134,7 @@ module Fontist | |
| 175 134 | 
             
                  end
         | 
| 176 135 |  | 
| 177 136 | 
             
                  def extract
         | 
| 178 | 
            -
                    @ | 
| 137 | 
            +
                    @operations || {}
         | 
| 179 138 | 
             
                  end
         | 
| 180 139 |  | 
| 181 140 | 
             
                  def copyright
         | 
| @@ -197,9 +156,11 @@ module Fontist | |
| 197 156 |  | 
| 198 157 | 
             
                    return unless @license_text
         | 
| 199 158 |  | 
| 200 | 
            -
                     | 
| 201 | 
            -
             | 
| 202 | 
            -
             | 
| 159 | 
            +
                    unless @options[:open_license]
         | 
| 160 | 
            +
                      Fontist.ui.error("WARN: ensure it's an open license, otherwise " \
         | 
| 161 | 
            +
                                       "change the 'open_license' attribute to " \
         | 
| 162 | 
            +
                                       "'requires_license_agreement'")
         | 
| 163 | 
            +
                    end
         | 
| 203 164 |  | 
| 204 165 | 
             
                    TextHelper.cleanup(@license_text)
         | 
| 205 166 | 
             
                  end
         | 
| @@ -211,6 +172,27 @@ module Fontist | |
| 211 172 | 
             
                  def command
         | 
| 212 173 | 
             
                    Shellwords.shelljoin(ARGV)
         | 
| 213 174 | 
             
                  end
         | 
| 175 | 
            +
             | 
| 176 | 
            +
                  def vacant_path
         | 
| 177 | 
            +
                    path = path_from_name
         | 
| 178 | 
            +
                    return path unless @options[:keep_existing] && File.exist?(path)
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                    2.upto(9) do |i|
         | 
| 181 | 
            +
                      candidate = path.sub(/\.yml$/, "#{i}.yml")
         | 
| 182 | 
            +
                      return candidate unless File.exist?(candidate)
         | 
| 183 | 
            +
                    end
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                    raise Errors::GeneralError, "Formula #{path} already exists."
         | 
| 186 | 
            +
                  end
         | 
| 187 | 
            +
             | 
| 188 | 
            +
                  def path_from_name
         | 
| 189 | 
            +
                    filename = Import.name_to_filename(name)
         | 
| 190 | 
            +
                    if @options[:formula_dir]
         | 
| 191 | 
            +
                      File.join(@options[:formula_dir], filename)
         | 
| 192 | 
            +
                    else
         | 
| 193 | 
            +
                      filename
         | 
| 194 | 
            +
                    end
         | 
| 195 | 
            +
                  end
         | 
| 214 196 | 
             
                end
         | 
| 215 197 | 
             
              end
         | 
| 216 198 | 
             
            end
         | 
| @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            module Fontist
         | 
| 2 | 
            +
              module Import
         | 
| 3 | 
            +
                module Google
         | 
| 4 | 
            +
                  class Api
         | 
| 5 | 
            +
                    class << self
         | 
| 6 | 
            +
                      def items
         | 
| 7 | 
            +
                        db["items"]
         | 
| 8 | 
            +
                      end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                      def db
         | 
| 11 | 
            +
                        @db ||= JSON.parse(Net::HTTP.get(URI(url)))
         | 
| 12 | 
            +
                      end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                      def url
         | 
| 15 | 
            +
                        "https://www.googleapis.com/webfonts/v1/webfonts?key=#{api_key}"
         | 
| 16 | 
            +
                      end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                      def api_key
         | 
| 19 | 
            +
                        Fontist.google_fonts_key
         | 
| 20 | 
            +
                      end
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         |