-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathsvgtoicons.rb
146 lines (122 loc) · 6.82 KB
/
svgtoicons.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/env ruby
# Down to about line 122 is sort of the library used by the commands below which are all you really need to change to
# get exactly the images you need generated for your project. Or you can just import the file and use in your script or
# in the REPL
# This requires Inkscape (http://www.inkscape.org/) to be installed and you may have to edit the script if you are not on
# a Mac or Inkscape isn't installed in /Applications
# The script is designed to be edited to your requirements to operate on existing SVG files. Make sure you configure the
# 'page' in the SVG file as that is used to work out the frame of the SVG that you want to protect.
# Copyright Human Friendly Ltd. licensed under GPLv2.
# Original inspiration from http://throwachair.com/2013/10/26/generate-all-your-ios-app-icons-with-svg-and-inkscape/:
# ruby -e '[58,120,29,40,80,76,152,1024].each { |x|
# `/Applications/Inkscape.app/Contents/Resources/bin/inkscape
# --export-png icon#{x}.png -w #{x} icon.svg` }'
# Inkscape docs that may prove relevant
# http://linux.die.net/man/1/inkscape
#
# This method returns the path to the inkscape binary.
def inkscape_app
'/Applications/Inkscape.app/Contents/Resources/bin/inkscape'
end
# These just set up some defaults, you can set these to whatever is appropriate.
input_file_name = 'test_files/test_file.svg'
output_folder = 'output'
# Use these variables as starting points or just define exactly what you need
ios_icon_sizes = [58,120,29,40,80,76,152,1024]
ios_launch_image_sizes = [[640,960],[640,1136],[768,1024],[1536,2048],[1024,768],[2048,1536],[320,480],[768,1004],[1536,2008],[1024,748],[2048,1496]]
itunes_store_icon_sizes = [[1024,1024]]
# Returns the dimensions of the SVG image with the file_name. Finds the 'page' dimensions as set in Inkscape.
#
# A bit ugly but it seems to work - It uses a simple regex to find the values in the XML, might
# be better parsing properly but this does the job for now. Only tested on Inkscape files.
def get_svg_size(file_name)
widthRegex = Regexp.new '<svg[^<>]*width="([\d\.]*)"[^<>]*>'
heightRegex = Regexp.new '<svg[^<>]*height="([\d\.]*)"[^<>]*>'
input_file = File.open(file_name)
svg_header_string = input_file.readpartial(4096) # Overkill but if width/height not there definitely not in the file
input_file.close
width = widthRegex.match(svg_header_string)[1].to_f
height = heightRegex.match(svg_header_string)[1].to_f
return {height: height, width: width}
end
# Returns the rect {x0, y0, x1, y1} that minimally contains the original width and height centred within a rectangle
# that matches the aspect ratio of the requested width and height
# NOTE: The output size doesn't match the requested just the shape - the output stage handles the actual output size
def aspect_fit(width, height, original_width, original_height)
width = width.to_f
height = height.to_f
if width == original_width && height == original_height
return {x0: 0, y0: 0, x1: width, y1: height}
end
orig_aspect_ratio = original_width / original_height
req_aspect_ratio = width / height
puts "Original ratio: #{orig_aspect_ratio}, requested ratio: #{req_aspect_ratio}"
ret_rect = {}
if req_aspect_ratio > orig_aspect_ratio
# We are going to be height constrained
ret_rect[:y0] = 0
ret_rect[:y1] = original_height
new_width = original_height * req_aspect_ratio
puts "New width: #{new_width}"
ret_rect[:x0] = 0.5 * (original_width - new_width)
ret_rect[:x1] = 0.5 * (new_width + original_width)
else
# Width constrained
ret_rect[:x0] = 0
ret_rect[:x1] = original_width
new_height = original_width / req_aspect_ratio
puts "New height: #{new_width}"
ret_rect[:y0] = 0.5 * (original_height - new_height)
ret_rect[:y1] = 0.5 * (new_height + original_height)
end
ret_rect
end
# Outputs a png file to the requested name with the requested width and height
# The original image is aspect fitted and centred in the png. The background colour can be set to apply
# to the extension (white is default)
def output_png(png_name,svg_name,width,height,background_colour=nil)
background_colour ||= '#ffffff'
original_dimensions = get_svg_size(svg_name)
export_area = aspect_fit(width,height,original_dimensions[:width],original_dimensions[:height])
export_area_arg = "--export-area=#{export_area[:x0]}:#{export_area[:y0]}:#{export_area[:x1]}:#{export_area[:y1]}"
`#{inkscape_app} --export-background=#{background_colour} #{export_area_arg} --export-png #{png_name} -w #{width} #{svg_name}`
end
# Outputs a png for each array item at the requested size and with the name 'output_prefixX' where X is the size
# The original page is aspect fitted to the square and can optionally have a background_colour set.
# The array is of individual numbers as this is optimised for squares (aspect fit is only done once)
def output_square_icons(sizes=ios_icon_sizes,svg_name=input_file_name,output_prefix='output/icon', background_colour='#ffffff')
export_area_arg = ''
background_colour ||= '#ffffff'
original_dimensions = get_svg_size(svg_name)
if original_dimensions[:height] != original_dimensions[:width]
export_area = aspect_fit(1,1,original_dimensions[:width],original_dimensions[:height])
export_area_arg = "--export-area=#{export_area[:x0]}:#{export_area[:y0]}:#{export_area[:x1]}:#{export_area[:y1]}"
end
sizes.each { |x| `#{inkscape_app} --export-background=#{background_colour} #{export_area_arg} --export-png #{output_prefix}#{x}.png -w #{x} #{svg_name}` }
end
# Outputs pngs for each of the sizes in the array. Each array item can be a different shape
def output_aspect_fit_rectangles( output_prefix='output/launch_image', svg_name='test_files/launch_image.svg', sizes=ios_launch_image_sizes, background_colour='#ffffff')
sizes.each do |size|
width = size[0]
height = size[1]
output_png "#{output_prefix}#{width}x#{height}.png", svg_name, width, height, background_colour
end
end
#####
# The above is effectively the library and the following is the specific commands I run to generate the files I need for Fast Lists
# Run the commands if this file was run directly
if __FILE__ == $0
# This uses the same image for the iTunes 1024 logo as for the launch images. You could obviously make different calls
launch_image = 'test_files/test_file.svg'
output_aspect_fit_rectangles 'output/launch_image', launch_image, ios_launch_image_sizes + itunes_store_icon_sizes
# Create iOS icons plus 100pix square for an App Review Site
icon_sizes = ios_icon_sizes + [100]
output_square_icons ios_icon_sizes, 'test_files/test_icon.svg', 'output/icon'
# This is for the custom requirements of my app (Fast Lists - https://itunes.apple.com/app/fast-lists-checklists-for/id481282554 )
# Add whatever you need for your app here
if '-fl' == $1
tick_box_sizes = [44,66,88,132]
tick_box_files = ['tickBoxChecked','tickBox']
tick_box_files.each { |f| output_square_icons tick_box_sizes, "test_files/#{f}.svg", "output/#{f}" }
end
end