From 22d384655114756dea22c92f472ace0eaf2eb131 Mon Sep 17 00:00:00 2001 From: Aaron-212 Date: Tue, 28 May 2024 21:28:15 +0800 Subject: [PATCH] add font-casker command, commits squashed Signed-off-by: Aaron-212 --- cmd/font-casker.rb | 143 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100755 cmd/font-casker.rb diff --git a/cmd/font-casker.rb b/cmd/font-casker.rb new file mode 100755 index 000000000000..ee09c8b6d336 --- /dev/null +++ b/cmd/font-casker.rb @@ -0,0 +1,143 @@ +# frozen_string_literal: true + +require "cli/parser" +require "open3" +require "digest" + +module Homebrew + module_function + + sig { returns(CLI::Parser) } + def font_casker_args + Homebrew::CLI::Parser.new do + usage_banner <<~EOS + `font-casker` + + Generates cask stanzas from OTF/TTF files within . + EOS + + named_args :archive_path, min: 1, max: 1 + + hide_from_man_page! + end + end + + FONT_EXT_PATTERN = /.(otf|ttf)\Z/i + + FONT_WEIGHTS = [ + /black/i, + /bold/i, + /book/i, + /hairline/i, + /heavy/i, + /light/i, + /medium/i, + /normal/i, + /regular/i, + /roman/i, + /thin/i, + /ultra/i, + ].freeze + + FONT_STYLES = [ + /italic/i, + /oblique/i, + /roman/i, + /slanted/i, + /upright/i, + ].freeze + + FONT_WIDTHS = [ + /compressed/i, + /condensed/i, + /extended/i, + /narrow/i, + /wide/i, + ].freeze + + def mce(enum) + enum.group_by(&:itself).values.max_by(&:size).first + end + + def eval_bin_cmd(cmd, blob) + IO.popen(cmd, "r+b") do |io| + io.print(blob) + io.close_write + io.read + end + end + + def font_paths(archive) + cmd = ["zipinfo", "-1", archive] + + IO.popen(cmd, "r", &:read) + .chomp.split("\n") + .grep(FONT_EXT_PATTERN) + .reject { |x| x.start_with?("__MACOSX") } + .grep_v(%r{(?:\A|/)\._}) + .sort + end + + def font_blobs(archive, paths) + paths.map { |x| IO.popen(["unzip", "-p", archive, x], "rb", &:read) } + end + + def stanzify(stanza_name, val = "") + if val.respond_to?(:map) + val.map { |x| " #{stanza_name} \"#{x}\"" }.join("\n") + else + " #{stanza_name} \"#{val}\"" + end + end + + def caskify(family, version, sha, paths) + output = ["FAMILY: #{family}"] + output << "" + output << "cask 'FIXME' do" + output << stanzify("version", version) + output << stanzify("sha256", sha) + output << "" + output << stanzify("url", "") + output << stanzify("name", "") + output << stanzify("homepage", "") + output << "" + output << stanzify("font", paths) + output << "" + output << " # No zap stanza required" + output << "end" + output.join("\n") + end + + def shasum(archive) + Digest::SHA256.file(archive) + end + + def font_version(fontblobs) + cmd = ["otfinfo", "-v"] + versions = fontblobs.map { |x| eval_bin_cmd(cmd, x) } + .map { |x| (m = /\A(?:Version\s+)?(\d[^\s,;]*)/i.match(x)) ? m[1] : x.delete("\n") } + mce(versions) + end + + def font_family(fontblobs) + cmd = ["otfinfo", "-a"] + families = fontblobs.map { |x| eval_bin_cmd(cmd, x) } + .map { |x| x.delete("\n") } + mce(families) + end + + def font_casker + args = font_casker_args.parse + archive_path = args.named.first + + paths = font_paths(archive_path) + blobs = font_blobs(archive_path, paths) + + puts caskify( + font_family(blobs), + font_version(blobs), + shasum(archive_path), + paths, + ) + end +end