/
githubmirror
/
fastlane
Обзор
Документация
Войти
/
githubmirror
/
fastlane
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
frameit/frames_generator/Rakefile
288 строк
11 KB
sathoeni
[frameit] iPhone 14 Frames (#21727)
17 янв 2024, 23:15
Не верифицирован
17 янв 2024, 23:15
eccd410
Код
Авторство
О чём код?
require 'colored' require 'mini_magick' require 'json' DEVICES_TO_SKIP = [ "Apple iMac", # we don't currently support iMac # some super old Android devices: "Nokia 220", "Nokia 230", "Nokia C3-00", "Nokia Asha 230", "Samsung Galaxy Y", # unsupported platforms: "Microsoft Lumia 950", "Microsoft Surface Pro", # unsupported sides: "Xiaomi Mi Mix Alpha Back", "Xiaomi Mi Mix Alpha Side", "Landscape" ] # longer colors (more than one word) must be in the beginning DEVICE_COLORS = [ "Space Gray", "Rose Gold", "Jet Black", "Matte Black", "Clearly White", "Just Black", "Not Pink", "Silver Titanium", "Arctic Silver", "Coral Blue", "Maple Gold", "Midnight Black", "Midnight Green", "Orchid Gray", "Burgundy Red", "Lilac Purple", "Sunrise Gold", "Titanium Gray", "Flamingo Pink", "Prism Black", "Prism Blue", "Prism Green", "Prism White", "Ceramic White", "Oh So Orange", "Aura Black", "Aura Glow", "Aura Pink", "Aura Red", "Aura White", "Aura Blue", "Black", "White", "Gold", "Silver", "Blue", "Red", "Yellow", "Green", "Pink", "Green", "Gray", "Coral", "Purple" ] # The versions / renaming scheme should be updated in case FB updates iPads IPAD_PRO_12_9_VERSION = "4th generation" IPAD_AIR_VERSION = "2019" IPAD_MINI_VERSION = "2019" IPAD_RENAMING_SCHEME = { 'iPad Pro 11' => 'iPad Pro (11-inch)', 'iPad Pro 13' => "iPad Pro (12.9-inch) (#{IPAD_PRO_12_9_VERSION})", 'iPad Pro' => 'iPad Pro', # don't rename the iPad Pro 1st or 2nd gen from the 'legacy' folder 'iPad Air' => "iPad Air (#{IPAD_AIR_VERSION})", 'iPad Mini' => "iPad Mini (#{IPAD_MINI_VERSION})", 'iPad' => 'iPad 10.2' # must be the last one so that only 'iPad' without any version will be renamed to 10.2 } task(:generate_device_frames) do MiniMagick.configure do |config| config.validate_on_create = false end puts("Download and extract the latest devices from Facebook") puts("") puts(" https://design.facebook.com/toolsandresources/devices/") puts("") confirm puts("Currently the script renames iPad files according to the following scheme:") puts(IPAD_RENAMING_SCHEME) puts("Update the script if they differ from the latest Facebook devices versions or naming schemes.") confirm puts("Move the content of the zip file into './raw'") mkdir("raw") unless File.directory?("raw") system("open ./raw") confirm # Let's find all the image resources we need and copy it into the correct directory current_time = Time.now.to_i.to_s output = File.join("output", current_time) sh("rm -rf output") mkdir_p(output) unless File.directory?(output) image_assets = [] devices = Hash.new { |h, k| h[k] = [] } (Dir["raw/Phones/*/Device/*.png"] + Dir["raw/Computers/Apple*/Device/*.png"] + Dir["raw/Tablets/*/Device/*.png"] + Dir["raw/Tablets/*/Device/Device without Pencil/*.png"] + Dir["legacy/*.png"]).each do |current_raw| basename = File.basename(current_raw) next if should_skip_device?(basename) cp(current_raw, output) output_file_path = File.join(output, basename) sanitized_basename = sanitize_filename(basename) sanitized_file_path = File.join(output, sanitized_basename) mv(output_file_path, sanitized_file_path) unless output_file_path == sanitized_file_path convert_image_resource(sanitized_file_path) image_assets << sanitized_basename x, y, width = measure_slot(sanitized_file_path) device_name = sanitize_device_name(sanitized_basename) devices[device_name] << { filename: sanitized_basename, x: x, y: y, width: width } end raise "Could not find any images, make sure to copy all extracted files into ./raw" if image_assets.count < 10 image_assets = image_assets.sort File.write(File.join(output, "files.json"), image_assets.to_json) puts("Wrote names of available files to files.json") File.write(File.join(output, "version.txt"), current_time) puts("Wrote the current time stamp to version.txt") # Create `offsets.json` file offsets = {} devices.each do |device, models| offsets[device] = { offset: "+#{models[0][:x]}+#{models[0][:y]}", width: models[0][:width] } end offsets = offsets.sort.to_h File.write(File.join(output, "offsets.json"), JSON.pretty_generate({ 'portrait' => offsets })) puts("Wrote the slot offsets to offsets.json") # Check if the offsets for a device are identical for all colors devices.each do |device, models| next if models.all? { |m| m[:x] == (models[0][:x]) && m[:y] == (models[0][:y]) && m[:width] == (models[0][:width]) } puts puts("The offsets for #{device} are not identical for all colors:".red) models.each { |m| puts("#{m[:filename]} +#{m[:x]}+#{m[:y]}, #{m[:width]}") } puts("You can either keep the value in `offset.json` or replace it with a more appropriate value from above.") puts end cp_r(output, "output/latest") system("open ./output") puts("Done writing all the things to './output', please upload both the 'latest' and the time stamp '#{current_time}' folder to GitHub".green) puts("To do so, clone https://github.com/fastlane/frameit-frames, make sure to check out the `gh-pages` branch".green) puts("and overwrite the 'latest' folder, and copy the time stamp folder into the root.".green) puts("Push the changes, and run `fastlane frameit update_frames` to ensure everything worked as expected".green) end def should_skip_device?(device_file_name) DEVICES_TO_SKIP.each do |device_to_skip| return true if device_file_name.include?(device_to_skip) end false end def confirm puts("Press any key when you're ready to continue".yellow) STDIN.gets end # Facebook's device naming is inconsistent. This method fixes the file names. def sanitize_filename(filename) renamed = filename .gsub('Grey', 'Gray') # some Apple devices have "Grey" instead of "Gray" color -> unify .gsub('Golden', 'Gold') # some Apple devices have "Golden" instead of "Gold" color -> unify .gsub('(PRODUCT)Red', 'Red') # some Apple devices have "(Product)Red" instead of "Red" -> unify .gsub('(PRODUCT)RED', 'Red') # some Apple devices have "(Product)Red" instead of "Red" -> unify .gsub('Space Black', 'Black') # "iPhone 14 Pro and Pro Max use "Space Black" instead of "Black" -> unify .gsub('Deep Purple', 'Purple') # "iPhone 14 Pro and Pro Max use "Deep Purple" instead of "Purple" -> unify .gsub(' - Portrait', '') # iPads Pro include Portrait and Landscape - we just need Portrait; Landscape filtered via DEVICES_TO_SKIP .gsub(' - ', ' ') # Google Pixel device names are separated from their colors by a dash -> remove .gsub(' – ', ' ') # some Apple devices are separated from their colors by an en dash -> remove .gsub(' — ', ' ') # some Apple devices are separated from their colors by an em dash -> remove .gsub('Note10', 'Note 10') # Samsung Galaxy Note 10 is missing a space in "Note10" .gsub('Mi Mix Alpha Front', 'Mi Mix Alpha') perform_ipad_renaming(renamed) # Xiaomi Mi Mix Alpha contains the words "Front", "Back" and "Side" => back and side are filtered via DEVICES_TO_SKIP, "Front" removed from the name here end # Facebook doesn't include versions in iPad files - this function makes sure the correct versions are added # to the file names so that frames of older iPad versions are not accidentally overwritten by newer versions # to ensure backward compatibility. def perform_ipad_renaming(filename) if filename.include?('iPad') IPAD_RENAMING_SCHEME.each do |fb_name, frameit_name| renamed = filename.gsub!(fb_name, frameit_name) return renamed unless renamed.nil? end end filename end # This will remove the white space around the actual frame def convert_image_resource(path) puts("Updating and trimming image file '#{path}'") image = MiniMagick::Image.open(path) image.combine_options do |co| co.fuzz("90%") # color dissimilarity threshold to get all shadows, see https://github.com/fastlane/fastlane/pull/14199 co.trim("+repage") # `+repage` removes meta-data end image.write(path) end def measure_slot(path) tmp_output = Time.now.to_i.to_s + ".png" begin command_output = MiniMagick::Tool::Magick.new do |magick| magick << path # some devices (e. g. Samsung Galaxy S9) may have shadows inside their screen to show the round edges - we need # to remove these shadows, otherwise "connected-components" could not detect the region (would return "too many objects" error): magick.channel("alpha") # we work on the alpha channel magick.threshold("99%") # let all semitransparent pixels become fully transparent # rubocop:disable Lint/Void magick.channel.+ # reset the channel (we work on all channels again) # rubocop:enable Lint/Void magick.alpha("background") # let all transparent pixels have the same background color so that the "connected-components" detects them as a single region magick.negate # it makes white, black and black, white, adjusting all the colors to match # connected-component analysis uniquely labels connected components in an image magick.define("connected-components:verbose=true") # it enables a verbose output to the shell console magick.define("connected-components:area-threshold=100") # it eliminates small objects by merging them with their larger neighbors magick.connected_components("8") # it visits 8 neighbors magick.auto_level magick << tmp_output end rescue StandardError => e puts(e) puts("The functionality of slot measurements does not work with ImageMagick older than 7.x".red) exit! end sh("rm #{tmp_output}", verbose: false) # The example of the command output: # # Objects (id: bounding-box centroid area mean-color): # 494: 640x1136+67+239 386.5,806.5 727040 srgb(255,255,255) # 4: 767x1605+0+0 385.4,801.2 478199 srgb(0,0,0) # 522: 115x1004+0+601 12.1,1244.6 10023 srgb(255,255,255) # 0: 517x232+0+0 125.3,36.3 8303 srgb(255,255,255) # 8: 131x115+636+0 731.1,26.2 3908 srgb(255,255,255) # 561: 108x108+659+1497 740.4,1578.0 2954 srgb(255,255,255) # 500: 7x48+0+301 3.0,323.7 325 srgb(255,255,255) # 511: 7x42+0+454 3.0,473.7 283 srgb(255,255,255) # raw_dimensions = command_output.split("\n")[1].strip dimensions = /(\d+)x(\d+)\+(\d+)\+(\d+)/.match(raw_dimensions) x = dimensions[3] y = dimensions[4] width = dimensions[1] height = dimensions[2] return x.to_i, y.to_i, width.to_i, height.to_i end def sanitize_device_name(filename) basename = File .basename(filename, File.extname(filename)) .gsub("Apple", "") .gsub("-", " ") .gsub(' - ', ' ') # Google Pixel device names are separated from their colors by a dash -> remove .gsub(' – ', ' ') # some Apple devices are separated from their colors by this weird dash -> remove .gsub(' — ', ' ') # some Apple devices are separated from their colors by this weird dash -> remove # Directory is named "Nexus 5X" but file named "Nexus 5x" # Need to rename to "Nexus 5X" basename = basename.gsub("Nexus 5x", "Nexus 5X") DEVICE_COLORS.each { |color| basename.slice!(color) } basename.strip.to_s end task(default: [:generate_device_frames])