frameit 2.2.1 → 2.2.2
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/README.md +8 -4
- data/bin/frameit +3 -5
- data/lib/frameit.rb +3 -2
- data/lib/frameit/config_parser.rb +17 -19
- data/lib/frameit/dependency_checker.rb +4 -4
- data/lib/frameit/device_types.rb +1 -1
- data/lib/frameit/editor.rb +216 -214
- data/lib/frameit/frame_converter.rb +14 -14
- data/lib/frameit/mac_editor.rb +1 -2
- data/lib/frameit/offsets.rb +56 -56
- data/lib/frameit/runner.rb +10 -10
- data/lib/frameit/screenshot.rb +20 -20
- data/lib/frameit/strings_parser.rb +6 -6
- data/lib/frameit/template_finder.rb +4 -7
- data/lib/frameit/version.rb +1 -1
- metadata +45 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 34abb8d5461e92d27d8ee707a1e6fae13c1d1b16
|
4
|
+
data.tar.gz: f5a0190aaf203cc07ec3244a9c3bd8636d3244bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cbab4d88a66db2754bd73f34458e8d3eac2a47e29a38430775025cc7399c38e5391933c605a226437235c94cf5a53cb8655dd2297edd6cd76178acd97e285093
|
7
|
+
data.tar.gz: 24a29d8ef715756269fb3265e013ec76e35b343c60c0225f890dcd81ff59acfe4849c958bbef2df5e91a5fdd749e988ed52673c705272120a3cdcc724942d2d4
|
data/README.md
CHANGED
@@ -13,16 +13,16 @@
|
|
13
13
|
<a href="https://github.com/KrauseFx/sigh">sigh</a> •
|
14
14
|
<a href="https://github.com/KrauseFx/produce">produce</a> •
|
15
15
|
<a href="https://github.com/KrauseFx/cert">cert</a> •
|
16
|
-
<a href="https://github.com/KrauseFx/codes">codes</a> •
|
17
16
|
<a href="https://github.com/fastlane/spaceship">spaceship</a> •
|
18
17
|
<a href="https://github.com/fastlane/pilot">pilot</a> •
|
19
18
|
<a href="https://github.com/fastlane/boarding">boarding</a> •
|
20
|
-
<a href="https://github.com/fastlane/gym">gym</a>
|
19
|
+
<a href="https://github.com/fastlane/gym">gym</a> •
|
20
|
+
<a href="https://github.com/fastlane/scan">scan</a>
|
21
21
|
</p>
|
22
22
|
-------
|
23
23
|
|
24
24
|
<p align="center">
|
25
|
-
|
25
|
+
<img src="assets/frameit.png" height="110">
|
26
26
|
</p>
|
27
27
|
|
28
28
|
frameit
|
@@ -233,17 +233,21 @@ Check out the [MindNode example project](https://github.com/fastlane/examples/tr
|
|
233
233
|
- [`sigh`](https://github.com/KrauseFx/sigh): Because you would rather spend your time building stuff than fighting provisioning
|
234
234
|
- [`produce`](https://github.com/KrauseFx/produce): Create new iOS apps on iTunes Connect and Dev Portal using the command line
|
235
235
|
- [`cert`](https://github.com/KrauseFx/cert): Automatically create and maintain iOS code signing certificates
|
236
|
-
- [`codes`](https://github.com/KrauseFx/codes): Create promo codes for iOS Apps using the command line
|
237
236
|
- [`spaceship`](https://github.com/fastlane/spaceship): Ruby library to access the Apple Dev Center and iTunes Connect
|
238
237
|
- [`pilot`](https://github.com/fastlane/pilot): The best way to manage your TestFlight testers and builds from your terminal
|
239
238
|
- [`boarding`](https://github.com/fastlane/boarding): The easiest way to invite your TestFlight beta testers
|
240
239
|
- [`gym`](https://github.com/fastlane/gym): Building your iOS apps has never been easier
|
240
|
+
- [`scan`](https://github.com/fastlane/scan): The easiest way to run tests of your iOS and Mac app
|
241
241
|
|
242
242
|
##### [Like this tool? Be the first to know about updates and new fastlane tools](https://tinyletter.com/krausefx)
|
243
243
|
|
244
244
|
## Generate localized screenshots
|
245
245
|
Check out [`snapshot`](https://github.com/KrauseFx/snapshot) to automatically generate screenshots using ```UI Automation```.
|
246
246
|
|
247
|
+
## Alternative location to store device_frames
|
248
|
+
|
249
|
+
Device frames can also be stored in a ```./fastlane/screenshots/device_frames``` directory if you prefer rather than in the ```~/.frameit/device_frames``` directory. If doing so please be aware that Apple's images are copyrighted and should not be redistributed as part of a repository so you may want to include them in your .gitignore file.
|
250
|
+
|
247
251
|
## White background of frames
|
248
252
|
|
249
253
|
Some stock images provided by Apple still have a white background instead of a transparent one. You'll have to edit the Photoshop file to remove the white background, delete the generated `.png` file and run `frameit` again.
|
data/bin/frameit
CHANGED
@@ -22,7 +22,6 @@ class FrameItApplication
|
|
22
22
|
|
23
23
|
default_command :black
|
24
24
|
|
25
|
-
|
26
25
|
command :black do |c|
|
27
26
|
c.syntax = 'frameit black'
|
28
27
|
c.description = "Adds a black frame around all screenshots."
|
@@ -35,7 +34,7 @@ class FrameItApplication
|
|
35
34
|
command :silver do |c|
|
36
35
|
c.syntax = 'frameit silver'
|
37
36
|
c.description = "Adds a silver frame around all screenshots."
|
38
|
-
|
37
|
+
|
39
38
|
c.action do |args, options|
|
40
39
|
Frameit::Runner.new.run('.', Frameit::Color::SILVER)
|
41
40
|
end
|
@@ -44,7 +43,7 @@ class FrameItApplication
|
|
44
43
|
command :setup do |c|
|
45
44
|
c.syntax = 'frameit setup'
|
46
45
|
c.description = "Helps you adding new frames."
|
47
|
-
|
46
|
+
|
48
47
|
c.action do |args, options|
|
49
48
|
Frameit::FrameConverter.new.run
|
50
49
|
end
|
@@ -56,11 +55,10 @@ class FrameItApplication
|
|
56
55
|
end
|
57
56
|
end
|
58
57
|
|
59
|
-
|
60
58
|
begin
|
61
59
|
FastlaneCore::UpdateChecker.start_looking_for_update('frameit')
|
62
60
|
Frameit::DependencyChecker.check_dependencies
|
63
61
|
FrameItApplication.new.run
|
64
62
|
ensure
|
65
63
|
FastlaneCore::UpdateChecker.show_update_status('frameit', Frameit::VERSION)
|
66
|
-
end
|
64
|
+
end
|
data/lib/frameit.rb
CHANGED
@@ -18,10 +18,11 @@ module Frameit
|
|
18
18
|
Helper = FastlaneCore::Helper # you gotta love Ruby: Helper.* should use the Helper class contained in FastlaneCore
|
19
19
|
end
|
20
20
|
|
21
|
-
|
21
|
+
# rubocop:disable all
|
22
22
|
class ::Hash
|
23
23
|
def fastlane_deep_merge(second)
|
24
24
|
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
|
25
25
|
self.merge(second, &merger)
|
26
26
|
end
|
27
|
-
end
|
27
|
+
end
|
28
|
+
# rubocop:enable all
|
@@ -1,14 +1,12 @@
|
|
1
1
|
module Frameit
|
2
2
|
class ConfigParser
|
3
3
|
def load(path)
|
4
|
-
return nil unless File.
|
4
|
+
return nil unless File.exist?(path) # we are okay with no config at all
|
5
5
|
Helper.log.info "Parsing config file '#{path}'".yellow if $verbose
|
6
6
|
@path = path
|
7
7
|
self.parse(File.read(path))
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
10
|
# @param data (String) the JSON data to be parsed
|
13
11
|
def parse(data)
|
14
12
|
begin
|
@@ -24,8 +22,8 @@ module Frameit
|
|
24
22
|
# Fetches the finished configuration for a given path. This will try to look for a specific value
|
25
23
|
# and fallback to a default value if nothing was found
|
26
24
|
def fetch_value(path)
|
27
|
-
specific = @data['data'].find { |a| path.include?a['filter'] }
|
28
|
-
|
25
|
+
specific = @data['data'].find { |a| path.include? a['filter'] }
|
26
|
+
|
29
27
|
default = @data['default']
|
30
28
|
|
31
29
|
values = default.fastlane_deep_merge(specific || {})
|
@@ -39,18 +37,18 @@ module Frameit
|
|
39
37
|
# Use absolute paths instead of relative
|
40
38
|
def change_paths_to_absolutes!(values)
|
41
39
|
values.each do |key, value|
|
42
|
-
if value.kind_of?Hash
|
40
|
+
if value.kind_of? Hash
|
43
41
|
change_paths_to_absolutes!(value) # recursive call
|
44
|
-
elsif value.kind_of?Array
|
42
|
+
elsif value.kind_of? Array
|
45
43
|
value.each do |current|
|
46
|
-
change_paths_to_absolutes!(current) if current.kind_of?Hash # recursive call
|
44
|
+
change_paths_to_absolutes!(current) if current.kind_of? Hash # recursive call
|
47
45
|
end
|
48
46
|
else
|
49
|
-
if ['font', 'background'].include?key
|
47
|
+
if ['font', 'background'].include? key
|
50
48
|
# Change the paths to relative ones
|
51
49
|
# `replace`: to change the content of the string, so it's actually stored
|
52
50
|
if @path # where is the config file. We don't have a config file in tests
|
53
|
-
containing_folder = File.expand_path('..', @path)
|
51
|
+
containing_folder = File.expand_path('..', @path)
|
54
52
|
value.replace File.join(containing_folder, value)
|
55
53
|
end
|
56
54
|
end
|
@@ -61,36 +59,36 @@ module Frameit
|
|
61
59
|
# Make sure the paths/colors are valid
|
62
60
|
def validate_values(values)
|
63
61
|
values.each do |key, value|
|
64
|
-
if value.kind_of?Hash
|
62
|
+
if value.kind_of? Hash
|
65
63
|
validate_values(value) # recursive call
|
66
64
|
else
|
67
65
|
if key == 'font'
|
68
|
-
raise "Could not find font at path '#{File.expand_path(value)}'" unless File.
|
66
|
+
raise "Could not find font at path '#{File.expand_path(value)}'" unless File.exist? value
|
69
67
|
end
|
70
68
|
|
71
69
|
if key == 'fonts'
|
72
|
-
raise "`fonts` must be an array" unless value.kind_of?Array
|
70
|
+
raise "`fonts` must be an array" unless value.kind_of? Array
|
73
71
|
|
74
72
|
value.each do |current|
|
75
73
|
raise "You must specify a font path" if current.fetch('font', '').length == 0
|
76
|
-
raise "Could not find font at path '#{File.expand_path(current.fetch('font'))}'" unless File.
|
77
|
-
raise "`supported` must be an array" unless current.fetch('supported', []).kind_of?Array
|
74
|
+
raise "Could not find font at path '#{File.expand_path(current.fetch('font'))}'" unless File.exist? current.fetch('font')
|
75
|
+
raise "`supported` must be an array" unless current.fetch('supported', []).kind_of? Array
|
78
76
|
end
|
79
77
|
end
|
80
78
|
|
81
79
|
if key == 'background'
|
82
|
-
raise "Could not find background image at path '#{File.expand_path(value)}'" unless File.
|
80
|
+
raise "Could not find background image at path '#{File.expand_path(value)}'" unless File.exist? value
|
83
81
|
end
|
84
82
|
|
85
83
|
if key == 'color'
|
86
|
-
raise "Invalid color '#{value}'. Must be valid Hex #123123" unless value.include?"#"
|
84
|
+
raise "Invalid color '#{value}'. Must be valid Hex #123123" unless value.include? "#"
|
87
85
|
end
|
88
86
|
|
89
87
|
if key == 'padding'
|
90
|
-
raise "padding must be type integer" unless value.kind_of?Integer
|
88
|
+
raise "padding must be type integer" unless value.kind_of? Integer
|
91
89
|
end
|
92
90
|
end
|
93
91
|
end
|
94
92
|
end
|
95
93
|
end
|
96
|
-
end
|
94
|
+
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module Frameit
|
2
2
|
class DependencyChecker
|
3
3
|
def self.check_dependencies
|
4
|
-
return if Helper.
|
5
|
-
|
4
|
+
return if Helper.test?
|
5
|
+
|
6
6
|
self.check_image_magick
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.check_image_magick
|
10
|
-
unless `which convert`.include?"convert"
|
10
|
+
unless `which convert`.include? "convert"
|
11
11
|
Helper.log.fatal '#############################################################'
|
12
12
|
Helper.log.fatal "# You have to install the ImageMagick to use FrameIt"
|
13
13
|
Helper.log.fatal "# Install it using 'brew update && brew install imagemagick'"
|
@@ -17,4 +17,4 @@ module Frameit
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
20
|
-
end
|
20
|
+
end
|
data/lib/frameit/device_types.rb
CHANGED
data/lib/frameit/editor.rb
CHANGED
@@ -33,273 +33,275 @@ module Frameit
|
|
33
33
|
@image = MiniMagick::Image.open(screenshot.path)
|
34
34
|
end
|
35
35
|
|
36
|
-
|
37
36
|
private
|
38
|
-
def store_result
|
39
|
-
output_path = screenshot.path.gsub('.png', '_framed.png').gsub('.PNG', '_framed.png')
|
40
|
-
image.format "png"
|
41
|
-
image.write output_path
|
42
|
-
Helper.log.info "Added frame: '#{File.expand_path(output_path)}'".green
|
43
|
-
end
|
44
|
-
|
45
|
-
# puts the screenshot into the frame
|
46
|
-
def put_into_frame
|
47
|
-
@image = frame.composite(image, "png") do |c|
|
48
|
-
c.compose "Over"
|
49
|
-
c.geometry offset['offset']
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def offset
|
54
|
-
return @offset_information if @offset_information
|
55
37
|
|
56
|
-
|
38
|
+
def store_result
|
39
|
+
output_path = screenshot.path.gsub('.png', '_framed.png').gsub('.PNG', '_framed.png')
|
40
|
+
image.format "png"
|
41
|
+
image.write output_path
|
42
|
+
Helper.log.info "Added frame: '#{File.expand_path(output_path)}'".green
|
43
|
+
end
|
57
44
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
45
|
+
# puts the screenshot into the frame
|
46
|
+
def put_into_frame
|
47
|
+
@image = frame.composite(image, "png") do |c|
|
48
|
+
c.compose "Over"
|
49
|
+
c.geometry offset['offset']
|
62
50
|
end
|
51
|
+
end
|
63
52
|
|
64
|
-
|
65
|
-
|
66
|
-
#########################################################################################
|
67
|
-
|
68
|
-
# this is used to correct the 1:1 offset information
|
69
|
-
# the offset information is stored to work for the template images
|
70
|
-
# since we resize the template images to have higher quality screenshots
|
71
|
-
# we need to modify the offset information by a certain factor
|
72
|
-
def modify_offset(multiplicator)
|
73
|
-
# Format: "+133+50"
|
74
|
-
hash = offset['offset']
|
75
|
-
x = hash.split("+")[1].to_f * multiplicator
|
76
|
-
y = hash.split("+")[2].to_f * multiplicator
|
77
|
-
new_offset = "+#{x.round}+#{y.round}"
|
78
|
-
@offset_information['offset'] = new_offset
|
79
|
-
end
|
53
|
+
def offset
|
54
|
+
return @offset_information if @offset_information
|
80
55
|
|
81
|
-
|
82
|
-
|
83
|
-
|
56
|
+
@offset_information = fetch_config['offset'] || Offsets.image_offset(screenshot)
|
57
|
+
|
58
|
+
if @offset_information and (@offset_information['offset'] or @offset_information['offset'])
|
59
|
+
return @offset_information
|
84
60
|
end
|
61
|
+
raise "Could not find offset_information for '#{screenshot}'"
|
62
|
+
end
|
85
63
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
64
|
+
#########################################################################################
|
65
|
+
# Everything below is related to title, background, etc. and is not used in the easy mode
|
66
|
+
#########################################################################################
|
67
|
+
|
68
|
+
# this is used to correct the 1:1 offset information
|
69
|
+
# the offset information is stored to work for the template images
|
70
|
+
# since we resize the template images to have higher quality screenshots
|
71
|
+
# we need to modify the offset information by a certain factor
|
72
|
+
def modify_offset(multiplicator)
|
73
|
+
# Format: "+133+50"
|
74
|
+
hash = offset['offset']
|
75
|
+
x = hash.split("+")[1].to_f * multiplicator
|
76
|
+
y = hash.split("+")[2].to_f * multiplicator
|
77
|
+
new_offset = "+#{x.round}+#{y.round}"
|
78
|
+
@offset_information['offset'] = new_offset
|
79
|
+
end
|
98
80
|
|
99
|
-
|
81
|
+
# Do we add a background and title as well?
|
82
|
+
def should_add_title?
|
83
|
+
return (fetch_config['background'] and (fetch_config['title'] or fetch_config['keyword']))
|
84
|
+
end
|
100
85
|
|
101
|
-
|
102
|
-
|
103
|
-
|
86
|
+
# more complex mode: background, frame and title
|
87
|
+
def complex_framing
|
88
|
+
background = generate_background
|
104
89
|
|
105
|
-
|
106
|
-
|
90
|
+
if self.frame # we have no frame on le mac
|
91
|
+
resize_frame!
|
92
|
+
@image = put_into_frame
|
107
93
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
multi = 1.7 if self.screenshot.is_triple_density?
|
112
|
-
return fetch_config['padding'] * multi
|
94
|
+
# Decrease the size of the framed screenshot to fit into the defined padding + background
|
95
|
+
frame_width = background.width - frame_padding * 2
|
96
|
+
image.resize "#{frame_width}x"
|
113
97
|
end
|
114
98
|
|
115
|
-
|
116
|
-
def generate_background
|
117
|
-
background = MiniMagick::Image.open(fetch_config['background'])
|
99
|
+
@image = put_device_into_background(background)
|
118
100
|
|
119
|
-
|
120
|
-
|
121
|
-
end
|
122
|
-
background
|
101
|
+
if fetch_config['title']
|
102
|
+
@image = add_title
|
123
103
|
end
|
124
104
|
|
125
|
-
|
126
|
-
|
127
|
-
bottom_space = -(image.height / 10).round # to be just a bit below the image bottom
|
128
|
-
bottom_space -= 40 if screenshot.is_portrait? # even more for portrait mode
|
105
|
+
image
|
106
|
+
end
|
129
107
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
108
|
+
# Padding around the frames
|
109
|
+
def frame_padding
|
110
|
+
multi = 1.0
|
111
|
+
multi = 1.7 if self.screenshot.triple_density?
|
112
|
+
return fetch_config['padding'] * multi
|
113
|
+
end
|
135
114
|
|
136
|
-
|
115
|
+
# Returns a correctly sized background image
|
116
|
+
def generate_background
|
117
|
+
background = MiniMagick::Image.open(fetch_config['background'])
|
137
118
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
119
|
+
if background.height != screenshot.size[1]
|
120
|
+
background.resize "#{screenshot.size[0]}x#{screenshot.size[1]}!" # `!` says it should ignore the ratio
|
121
|
+
end
|
122
|
+
background
|
123
|
+
end
|
124
|
+
|
125
|
+
def put_device_into_background(background)
|
126
|
+
left_space = (background.width / 2.0 - image.width / 2.0).round
|
127
|
+
bottom_space = -(image.height / 10).round # to be just a bit below the image bottom
|
128
|
+
bottom_space -= 40 if screenshot.portrait? # even more for portrait mode
|
142
129
|
|
143
|
-
|
130
|
+
if screenshot.mini?
|
131
|
+
# Such small devices need special treatment
|
132
|
+
bottom_space -= 50 if screenshot.portrait?
|
133
|
+
bottom_space += 65 unless screenshot.portrait?
|
144
134
|
end
|
145
135
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
modify_offset(multiplicator) # modify the offset to properly insert the screenshot into the frame later
|
136
|
+
self.top_space_above_device = background.height - image.height - bottom_space
|
137
|
+
|
138
|
+
@image = background.composite(image, "png") do |c|
|
139
|
+
c.compose "Over"
|
140
|
+
c.geometry "+#{left_space}+#{top_space_above_device}"
|
152
141
|
end
|
153
142
|
|
154
|
-
|
155
|
-
|
156
|
-
title_images = build_title_images(image.width)
|
157
|
-
keyword = title_images[:keyword]
|
158
|
-
title = title_images[:title]
|
159
|
-
|
160
|
-
# sum_width: the width of both labels together including the space inbetween
|
161
|
-
# is used to calculate the ratio
|
162
|
-
sum_width = title.width
|
163
|
-
sum_width += keyword.width + keyword_padding if keyword
|
164
|
-
|
165
|
-
# Resize the 2 labels if necessary
|
166
|
-
smaller = 1.0 # default
|
167
|
-
ratio = (sum_width + keyword_padding * 2) / image.width.to_f
|
168
|
-
if ratio > 1.0
|
169
|
-
# too large - resizing now
|
170
|
-
smaller = (1.0 / ratio)
|
171
|
-
|
172
|
-
Helper.log.debug "Text for image #{self.screenshot.path} is quite long, reducing font size by #{(ratio - 1.0).round(2)}" if $verbose
|
173
|
-
|
174
|
-
title.resize"#{(smaller * title.width).round}x"
|
175
|
-
keyword.resize"#{(smaller * keyword.width).round}x" if keyword
|
176
|
-
sum_width *= smaller
|
177
|
-
end
|
143
|
+
return image
|
144
|
+
end
|
178
145
|
|
179
|
-
|
180
|
-
|
146
|
+
# Resize the frame as it's too low quality by default
|
147
|
+
def resize_frame!
|
148
|
+
multiplicator = (screenshot.size[0].to_f / offset['width'].to_f) # by how much do we have to change this?
|
149
|
+
new_frame_width = multiplicator * frame.width # the new width for the frame
|
150
|
+
frame.resize "#{new_frame_width.round}x" # resize it to the calculated witdth
|
151
|
+
modify_offset(multiplicator) # modify the offset to properly insert the screenshot into the frame later
|
152
|
+
end
|
181
153
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
154
|
+
# Add the title above the device
|
155
|
+
# rubocop:disable Metrics/AbcSize
|
156
|
+
def add_title
|
157
|
+
title_images = build_title_images(image.width)
|
158
|
+
keyword = title_images[:keyword]
|
159
|
+
title = title_images[:title]
|
160
|
+
|
161
|
+
# sum_width: the width of both labels together including the space inbetween
|
162
|
+
# is used to calculate the ratio
|
163
|
+
sum_width = title.width
|
164
|
+
sum_width += keyword.width + keyword_padding if keyword
|
165
|
+
|
166
|
+
# Resize the 2 labels if necessary
|
167
|
+
smaller = 1.0 # default
|
168
|
+
ratio = (sum_width + keyword_padding * 2) / image.width.to_f
|
169
|
+
if ratio > 1.0
|
170
|
+
# too large - resizing now
|
171
|
+
smaller = (1.0 / ratio)
|
172
|
+
|
173
|
+
Helper.log.debug "Text for image #{self.screenshot.path} is quite long, reducing font size by #{(ratio - 1.0).round(2)}" if $verbose
|
174
|
+
|
175
|
+
title.resize "#{(smaller * title.width).round}x"
|
176
|
+
keyword.resize "#{(smaller * keyword.width).round}x" if keyword
|
177
|
+
sum_width *= smaller
|
178
|
+
end
|
188
179
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
180
|
+
top_space = (top_space_above_device / 2.0 - (actual_font_size / 2.0 * smaller)).round # centered
|
181
|
+
left_space = (image.width / 2.0 - sum_width / 2.0).round
|
182
|
+
|
183
|
+
# First, put the keyword on top of the screenshot, if we have one
|
184
|
+
if keyword
|
185
|
+
@image = image.composite(keyword, "png") do |c|
|
194
186
|
c.compose "Over"
|
195
187
|
c.geometry "+#{left_space}+#{top_space}"
|
196
188
|
end
|
197
|
-
image
|
198
|
-
end
|
199
189
|
|
200
|
-
|
201
|
-
[top_space_above_device / 3.0, @image.width / 30.0].max.round
|
190
|
+
left_space += keyword.width + (keyword_padding * smaller)
|
202
191
|
end
|
203
192
|
|
204
|
-
#
|
205
|
-
|
206
|
-
|
193
|
+
# Then, put the title on top of the screenshot next to the keyword
|
194
|
+
@image = image.composite(title, "png") do |c|
|
195
|
+
c.compose "Over"
|
196
|
+
c.geometry "+#{left_space}+#{top_space}"
|
207
197
|
end
|
198
|
+
image
|
199
|
+
end
|
200
|
+
# rubocop:enable Metrics/AbcSize
|
208
201
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
results = {}
|
213
|
-
words.each do |key|
|
214
|
-
# Create empty background
|
215
|
-
empty_path = File.join(Helper.gem_path('frameit'), "lib/assets/empty.png")
|
216
|
-
title_image = MiniMagick::Image.open(empty_path)
|
217
|
-
image_height = actual_font_size * 2 # gets trimmed afterwards anyway, and on the iPad the `y` would get cut
|
218
|
-
title_image.combine_options do |i|
|
219
|
-
# * 2.0 as the text might be larger than the actual image. We're trimming afterwards anyway
|
220
|
-
i.resize "#{max_width * 2.0}x#{image_height}!" # `!` says it should ignore the ratio
|
221
|
-
end
|
202
|
+
def actual_font_size
|
203
|
+
[top_space_above_device / 3.0, @image.width / 30.0].max.round
|
204
|
+
end
|
222
205
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
# Add the actual title
|
229
|
-
title_image.combine_options do |i|
|
230
|
-
i.font current_font if current_font
|
231
|
-
i.gravity "Center"
|
232
|
-
i.pointsize actual_font_size
|
233
|
-
i.draw "text 0,0 '#{fetch_text(key)}'"
|
234
|
-
i.fill fetch_config[key.to_s]['color']
|
235
|
-
end
|
236
|
-
title_image.trim # remove white space
|
206
|
+
# The space between the keyword and the title
|
207
|
+
def keyword_padding
|
208
|
+
(actual_font_size / 2.0).round
|
209
|
+
end
|
237
210
|
|
238
|
-
|
211
|
+
# This will build 2 individual images with the title, which will then be added to the real image
|
212
|
+
def build_title_images(max_width)
|
213
|
+
words = [:keyword, :title].keep_if { |a| fetch_text(a) } # optional keyword/title
|
214
|
+
results = {}
|
215
|
+
words.each do |key|
|
216
|
+
# Create empty background
|
217
|
+
empty_path = File.join(Helper.gem_path('frameit'), "lib/assets/empty.png")
|
218
|
+
title_image = MiniMagick::Image.open(empty_path)
|
219
|
+
image_height = actual_font_size * 2 # gets trimmed afterwards anyway, and on the iPad the `y` would get cut
|
220
|
+
title_image.combine_options do |i|
|
221
|
+
# * 2.0 as the text might be larger than the actual image. We're trimming afterwards anyway
|
222
|
+
i.resize "#{max_width * 2.0}x#{image_height}!" # `!` says it should ignore the ratio
|
239
223
|
end
|
240
|
-
results
|
241
|
-
end
|
242
224
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
225
|
+
current_font = font(key)
|
226
|
+
text = fetch_text(key)
|
227
|
+
Helper.log.debug "Using #{current_font} as font the #{key} of #{screenshot.path}" if $verbose and current_font
|
228
|
+
Helper.log.debug "Adding text '#{text}'" if $verbose
|
229
|
+
|
230
|
+
# Add the actual title
|
231
|
+
title_image.combine_options do |i|
|
232
|
+
i.font current_font if current_font
|
233
|
+
i.gravity "Center"
|
234
|
+
i.pointsize actual_font_size
|
235
|
+
i.draw "text 0,0 '#{text}'"
|
236
|
+
i.fill fetch_config[key.to_s]['color']
|
237
|
+
end
|
238
|
+
title_image.trim # remove white space
|
247
239
|
|
248
|
-
|
249
|
-
config_path = File.join(File.expand_path("../..", screenshot.path), "Framefile.json") unless File.exists?config_path
|
250
|
-
file = ConfigParser.new.load(config_path)
|
251
|
-
return {} unless file # no config file at all
|
252
|
-
@config = file.fetch_value(screenshot.path)
|
240
|
+
results[key] = title_image
|
253
241
|
end
|
242
|
+
results
|
243
|
+
end
|
254
244
|
|
255
|
-
|
256
|
-
|
257
|
-
|
245
|
+
# Loads the config (colors, background, texts, etc.)
|
246
|
+
# Don't use this method to access the actual text and use `fetch_texts` instead
|
247
|
+
def fetch_config
|
248
|
+
return @config if @config
|
258
249
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
end
|
250
|
+
config_path = File.join(File.expand_path("..", screenshot.path), "Framefile.json")
|
251
|
+
config_path = File.join(File.expand_path("../..", screenshot.path), "Framefile.json") unless File.exist? config_path
|
252
|
+
file = ConfigParser.new.load(config_path)
|
253
|
+
return {} unless file # no config file at all
|
254
|
+
@config = file.fetch_value(screenshot.path)
|
255
|
+
end
|
266
256
|
|
267
|
-
|
268
|
-
|
269
|
-
|
257
|
+
# Fetches the title + keyword for this particular screenshot
|
258
|
+
def fetch_text(type)
|
259
|
+
raise "Valid parameters :keyword, :title" unless [:keyword, :title].include? type
|
270
260
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
261
|
+
# Try to get it from a keyword.strings or title.strings file
|
262
|
+
strings_path = File.join(File.expand_path("..", screenshot.path), "#{type}.strings")
|
263
|
+
if File.exist? strings_path
|
264
|
+
parsed = StringsParser.parse(strings_path)
|
265
|
+
result = parsed.find { |k, v| screenshot.path.include? k }
|
266
|
+
return result.last if result
|
267
|
+
end
|
275
268
|
|
276
|
-
|
269
|
+
# No string files, fallback to Framefile config
|
270
|
+
result = fetch_config[type.to_s]['text']
|
271
|
+
Helper.log.debug "Falling back to default text as there was nothing specified in the .strings file" if $verbose
|
272
|
+
|
273
|
+
if !result and type == :title
|
274
|
+
# title is mandatory
|
275
|
+
raise "Could not get title for screenshot #{screenshot.path}. Please provide one in your Framefile.json".red
|
277
276
|
end
|
278
277
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
278
|
+
return result
|
279
|
+
end
|
280
|
+
|
281
|
+
# The font we want to use
|
282
|
+
def font(key)
|
283
|
+
single_font = fetch_config[key.to_s]['font']
|
284
|
+
return single_font if single_font
|
285
|
+
|
286
|
+
fonts = fetch_config[key.to_s]['fonts']
|
287
|
+
if fonts
|
288
|
+
fonts.each do |font|
|
289
|
+
if font['supported']
|
290
|
+
font['supported'].each do |language|
|
291
|
+
if screenshot.path.include? language
|
292
|
+
return font["font"]
|
292
293
|
end
|
293
|
-
else
|
294
|
-
# No `supported` array, this will always be true
|
295
|
-
Helper.log.debug "Found a font with no list of supported languages, using this now" if $verbose
|
296
|
-
return font["font"]
|
297
294
|
end
|
295
|
+
else
|
296
|
+
# No `supported` array, this will always be true
|
297
|
+
Helper.log.debug "Found a font with no list of supported languages, using this now" if $verbose
|
298
|
+
return font["font"]
|
298
299
|
end
|
299
300
|
end
|
300
|
-
|
301
|
-
Helper.log.debug "No custom font specified for #{screenshot}, using the default one" if $verbose
|
302
|
-
return nil
|
303
301
|
end
|
302
|
+
|
303
|
+
Helper.log.debug "No custom font specified for #{screenshot}, using the default one" if $verbose
|
304
|
+
return nil
|
305
|
+
end
|
304
306
|
end
|
305
307
|
end
|
@@ -2,7 +2,7 @@ module Frameit
|
|
2
2
|
class FrameConverter
|
3
3
|
DOWNLOAD_URL = 'https://developer.apple.com/app-store/marketing/guidelines/#images'
|
4
4
|
FRAME_PATH = '.frameit/devices_frames'
|
5
|
-
|
5
|
+
|
6
6
|
def run
|
7
7
|
self.setup_frames
|
8
8
|
end
|
@@ -14,7 +14,7 @@ module Frameit
|
|
14
14
|
puts "Press Enter to get started".green
|
15
15
|
puts "----------------------------------------------------".green
|
16
16
|
STDIN.gets
|
17
|
-
|
17
|
+
|
18
18
|
system("open '#{DOWNLOAD_URL}'")
|
19
19
|
puts "----------------------------------------------------".green
|
20
20
|
puts "Download the zip files for the following devices".green
|
@@ -34,7 +34,7 @@ module Frameit
|
|
34
34
|
puts "----------------------------------------------------".green
|
35
35
|
STDIN.gets
|
36
36
|
|
37
|
-
if
|
37
|
+
if !frames_exist?
|
38
38
|
puts "Sorry, I can't find the PSD files. Make sure you unzipped them into '#{templates_path}'".red
|
39
39
|
else
|
40
40
|
break # everything is finished
|
@@ -60,17 +60,17 @@ module Frameit
|
|
60
60
|
|
61
61
|
Dir["#{templates_path}/**/*.psd"].each do |psd|
|
62
62
|
resulting_path = psd.gsub('.psd', '.png')
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
63
|
+
next if File.exist?(resulting_path)
|
64
|
+
|
65
|
+
Helper.log.debug "Converting PSD file '#{psd}'".yellow
|
66
|
+
image = MiniMagick::Image.open(psd)
|
67
|
+
if image
|
68
|
+
image.format 'png'
|
69
|
+
image.trim
|
70
|
+
|
71
|
+
image.write(resulting_path)
|
72
|
+
else
|
73
|
+
Helper.log.error "Could not parse PSD file at path '#{psd}'"
|
74
74
|
end
|
75
75
|
end
|
76
76
|
end
|
data/lib/frameit/mac_editor.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
module Frameit
|
2
2
|
# Responsible for framing Mac Screenshots
|
3
3
|
class MacEditor < Editor
|
4
|
-
|
5
4
|
def prepare_image
|
6
5
|
image = super
|
7
6
|
image.resize("#{offset['width']}x") if offset['width']
|
@@ -30,4 +29,4 @@ module Frameit
|
|
30
29
|
MiniMagick::Image.open(fetch_config['background']) # no resizing on the Mac
|
31
30
|
end
|
32
31
|
end
|
33
|
-
end
|
32
|
+
end
|
data/lib/frameit/offsets.rb
CHANGED
@@ -5,62 +5,62 @@ module Frameit
|
|
5
5
|
def self.image_offset(screenshot)
|
6
6
|
size = Deliver::AppScreenshot::ScreenSize
|
7
7
|
case screenshot.orientation_name
|
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
|
-
|
8
|
+
when Orientation::PORTRAIT
|
9
|
+
case screenshot.screen_size
|
10
|
+
when size::IOS_55
|
11
|
+
return {
|
12
|
+
'offset' => '+41+146',
|
13
|
+
'width' => 541
|
14
|
+
}
|
15
|
+
when size::IOS_47
|
16
|
+
return {
|
17
|
+
'offset' => '+40+153',
|
18
|
+
'width' => 532
|
19
|
+
}
|
20
|
+
when size::IOS_40
|
21
|
+
return {
|
22
|
+
'offset' => "+54+197",
|
23
|
+
'width' => 544
|
24
|
+
}
|
25
|
+
when size::IOS_35
|
26
|
+
return {
|
27
|
+
'offset' => "+59+260",
|
28
|
+
'width' => 647
|
29
|
+
}
|
30
|
+
when size::IOS_IPAD
|
31
|
+
return {
|
32
|
+
'offset' => '+47+135',
|
33
|
+
'width' => 737
|
34
|
+
}
|
35
|
+
end
|
36
|
+
when Orientation::LANDSCAPE
|
37
|
+
case screenshot.screen_size
|
38
|
+
when size::IOS_55
|
39
|
+
return {
|
40
|
+
'offset' => "+146+41",
|
41
|
+
'width' => 960
|
42
|
+
}
|
43
|
+
when size::IOS_47
|
44
|
+
return {
|
45
|
+
'offset' => "+153+41",
|
46
|
+
'width' => 946
|
47
|
+
}
|
48
|
+
when size::IOS_40
|
49
|
+
return {
|
50
|
+
'offset' => "+201+48",
|
51
|
+
'width' => 970
|
52
|
+
}
|
53
|
+
when size::IOS_35
|
54
|
+
return {
|
55
|
+
'offset' => "+258+52",
|
56
|
+
'width' => 966
|
57
|
+
}
|
58
|
+
when size::IOS_IPAD
|
59
|
+
return {
|
60
|
+
'offset' => '+135+47',
|
61
|
+
'width' => 983
|
62
|
+
}
|
63
|
+
end
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
data/lib/frameit/runner.rb
CHANGED
@@ -5,12 +5,12 @@ module Frameit
|
|
5
5
|
class Runner
|
6
6
|
def initialize
|
7
7
|
converter = FrameConverter.new
|
8
|
-
|
9
|
-
# First run
|
10
|
-
converter.run
|
11
|
-
else
|
8
|
+
if converter.frames_exist?
|
12
9
|
# Just make sure, the PSD files are converted to PNG
|
13
10
|
converter.convert_frames
|
11
|
+
else
|
12
|
+
# First run
|
13
|
+
converter.run
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
@@ -19,11 +19,11 @@ module Frameit
|
|
19
19
|
|
20
20
|
if screenshots.count > 0
|
21
21
|
screenshots.each do |full_path|
|
22
|
-
next if full_path.include?"_framed.png"
|
23
|
-
next if full_path.include?".itmsp/" # a package file, we don't want to modify that
|
24
|
-
next if full_path.include?"device_frames/" # these are the device frames the user is using
|
25
|
-
next if full_path.downcase.include?"watch" # we don't care about watches right now
|
26
|
-
|
22
|
+
next if full_path.include? "_framed.png"
|
23
|
+
next if full_path.include? ".itmsp/" # a package file, we don't want to modify that
|
24
|
+
next if full_path.include? "device_frames/" # these are the device frames the user is using
|
25
|
+
next if full_path.downcase.include? "watch" # we don't care about watches right now
|
26
|
+
|
27
27
|
begin
|
28
28
|
screenshot = Screenshot.new(full_path, color)
|
29
29
|
screenshot.frame!
|
@@ -32,7 +32,7 @@ module Frameit
|
|
32
32
|
Helper.log.error "Backtrace:\n\t#{ex.backtrace.join("\n\t")}" if $verbose
|
33
33
|
end
|
34
34
|
end
|
35
|
-
else
|
35
|
+
else
|
36
36
|
Helper.log.error "Could not find screenshots"
|
37
37
|
end
|
38
38
|
end
|
data/lib/frameit/screenshot.rb
CHANGED
@@ -9,44 +9,44 @@ module Frameit
|
|
9
9
|
# path: Path to screenshot
|
10
10
|
# color: Color to use for the frame
|
11
11
|
def initialize(path, color)
|
12
|
-
raise "Couldn't find file at path '#{path}'".red unless File.
|
12
|
+
raise "Couldn't find file at path '#{path}'".red unless File.exist? path
|
13
13
|
@color = color
|
14
14
|
@path = path
|
15
15
|
@size = FastImage.size(path)
|
16
16
|
|
17
|
-
@screen_size = ENV["FRAMEIT_FORCE_DEVICE_TYPE"] || Deliver::AppScreenshot.calculate_screen_size(path)
|
17
|
+
@screen_size = ENV["FRAMEIT_FORCE_DEVICE_TYPE"] || Deliver::AppScreenshot.calculate_screen_size(path)
|
18
18
|
end
|
19
19
|
|
20
20
|
# Device name for a given screen size. Used to use the correct template
|
21
21
|
def device_name
|
22
22
|
sizes = Deliver::AppScreenshot::ScreenSize
|
23
23
|
case @screen_size
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
24
|
+
when sizes::IOS_55
|
25
|
+
return 'iPhone_6_Plus'
|
26
|
+
when sizes::IOS_47
|
27
|
+
return 'iPhone_6'
|
28
|
+
when sizes::IOS_40
|
29
|
+
return 'iPhone_5s'
|
30
|
+
when sizes::IOS_35
|
31
|
+
return 'iPhone_4'
|
32
|
+
when sizes::IOS_IPAD
|
33
|
+
return 'iPad_mini'
|
34
|
+
when sizes::MAC
|
35
|
+
return 'Mac'
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
39
|
# Is the device a 3x device? (e.g. 6 Plus)
|
40
|
-
def
|
40
|
+
def triple_density?
|
41
41
|
(screen_size == Deliver::AppScreenshot::ScreenSize::IOS_55)
|
42
42
|
end
|
43
43
|
|
44
44
|
# Super old devices
|
45
|
-
def
|
45
|
+
def mini?
|
46
46
|
(screen_size == Deliver::AppScreenshot::ScreenSize::IOS_35)
|
47
47
|
end
|
48
48
|
|
49
|
-
def
|
49
|
+
def mac?
|
50
50
|
return device_name == 'Mac'
|
51
51
|
end
|
52
52
|
|
@@ -56,7 +56,7 @@ module Frameit
|
|
56
56
|
return Orientation::LANDSCAPE
|
57
57
|
end
|
58
58
|
|
59
|
-
def
|
59
|
+
def portrait?
|
60
60
|
return (orientation_name == Orientation::PORTRAIT)
|
61
61
|
end
|
62
62
|
|
@@ -66,11 +66,11 @@ module Frameit
|
|
66
66
|
|
67
67
|
# Add the device frame, this will also call the method that adds the background + title
|
68
68
|
def frame!
|
69
|
-
if self.
|
69
|
+
if self.mac?
|
70
70
|
MacEditor.new.frame!(self)
|
71
71
|
else
|
72
72
|
Editor.new.frame!(self)
|
73
73
|
end
|
74
74
|
end
|
75
75
|
end
|
76
|
-
end
|
76
|
+
end
|
@@ -2,8 +2,8 @@ module Frameit
|
|
2
2
|
# This class will parse the .string files
|
3
3
|
class StringsParser
|
4
4
|
def self.parse(path)
|
5
|
-
raise "Couldn't find strings file at path '#{path}'".red unless File.
|
6
|
-
raise "Must be .strings file, only got '#{path}'".red unless path.end_with?".strings"
|
5
|
+
raise "Couldn't find strings file at path '#{path}'".red unless File.exist? path
|
6
|
+
raise "Must be .strings file, only got '#{path}'".red unless path.end_with? ".strings"
|
7
7
|
|
8
8
|
result = {}
|
9
9
|
|
@@ -13,19 +13,19 @@ module Frameit
|
|
13
13
|
content.split("\n").each do |line|
|
14
14
|
begin
|
15
15
|
# We don't care about comments and empty lines
|
16
|
-
if line.start_with?'"'
|
16
|
+
if line.start_with? '"'
|
17
17
|
key = line.match(/"(.*)" \= /)[1]
|
18
18
|
value = line.match(/ \= "(.*)"/)[1]
|
19
19
|
|
20
20
|
result[key] = value
|
21
21
|
end
|
22
22
|
rescue => ex
|
23
|
-
Helper.log.error ex
|
23
|
+
Helper.log.error ex
|
24
24
|
Helper.log.error line
|
25
25
|
end
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
result
|
29
29
|
end
|
30
30
|
end
|
31
|
-
end
|
31
|
+
end
|
@@ -1,10 +1,9 @@
|
|
1
1
|
module Frameit
|
2
2
|
# Responsible for finding the correct device
|
3
3
|
class TemplateFinder
|
4
|
-
|
5
4
|
# This will detect the screen size and choose the correct template
|
6
5
|
def self.get_template(screenshot)
|
7
|
-
return nil if screenshot.
|
6
|
+
return nil if screenshot.mac?
|
8
7
|
parts = [
|
9
8
|
screenshot.device_name,
|
10
9
|
screenshot.orientation_name,
|
@@ -12,11 +11,9 @@ module Frameit
|
|
12
11
|
]
|
13
12
|
parts << "sRGB" if screenshot.device_name == 'iPad_mini'
|
14
13
|
|
15
|
-
|
16
14
|
templates_path = [ENV['HOME'], FrameConverter::FRAME_PATH].join('/')
|
17
15
|
templates = Dir["../**/#{parts.join('_')}*.{png,jpg}"] # local directory
|
18
|
-
templates
|
19
|
-
|
16
|
+
templates += Dir["#{templates_path}/**/#{parts.join('_')}*.{png,jpg}"] # ~/.frameit folder
|
20
17
|
|
21
18
|
if templates.count == 0
|
22
19
|
if screenshot.screen_size == Deliver::AppScreenshot::ScreenSize::IOS_35
|
@@ -30,8 +27,8 @@ module Frameit
|
|
30
27
|
end
|
31
28
|
return nil
|
32
29
|
else
|
33
|
-
return templates.first.
|
30
|
+
return templates.first.tr(" ", "\ ")
|
34
31
|
end
|
35
32
|
end
|
36
33
|
end
|
37
|
-
end
|
34
|
+
end
|
data/lib/frameit/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: frameit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
4
|
+
version: 2.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Felix Krause
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-10-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fastlane_core
|
@@ -142,6 +142,20 @@ dependencies:
|
|
142
142
|
- - "~>"
|
143
143
|
- !ruby/object:Gem::Version
|
144
144
|
version: 0.8.7.4
|
145
|
+
- !ruby/object:Gem::Dependency
|
146
|
+
name: webmock
|
147
|
+
requirement: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - "~>"
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: 1.19.0
|
152
|
+
type: :development
|
153
|
+
prerelease: false
|
154
|
+
version_requirements: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - "~>"
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: 1.19.0
|
145
159
|
- !ruby/object:Gem::Dependency
|
146
160
|
name: coveralls
|
147
161
|
requirement: !ruby/object:Gem::Requirement
|
@@ -156,6 +170,34 @@ dependencies:
|
|
156
170
|
- - ">="
|
157
171
|
- !ruby/object:Gem::Version
|
158
172
|
version: '0'
|
173
|
+
- !ruby/object:Gem::Dependency
|
174
|
+
name: fastlane
|
175
|
+
requirement: !ruby/object:Gem::Requirement
|
176
|
+
requirements:
|
177
|
+
- - ">="
|
178
|
+
- !ruby/object:Gem::Version
|
179
|
+
version: '0'
|
180
|
+
type: :development
|
181
|
+
prerelease: false
|
182
|
+
version_requirements: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - ">="
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: '0'
|
187
|
+
- !ruby/object:Gem::Dependency
|
188
|
+
name: rubocop
|
189
|
+
requirement: !ruby/object:Gem::Requirement
|
190
|
+
requirements:
|
191
|
+
- - "~>"
|
192
|
+
- !ruby/object:Gem::Version
|
193
|
+
version: '0.34'
|
194
|
+
type: :development
|
195
|
+
prerelease: false
|
196
|
+
version_requirements: !ruby/object:Gem::Requirement
|
197
|
+
requirements:
|
198
|
+
- - "~>"
|
199
|
+
- !ruby/object:Gem::Version
|
200
|
+
version: '0.34'
|
159
201
|
description: Quickly put your screenshots into the right device frames
|
160
202
|
email:
|
161
203
|
- frameit@krausefx.com
|
@@ -201,7 +243,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
201
243
|
version: '0'
|
202
244
|
requirements: []
|
203
245
|
rubyforge_project:
|
204
|
-
rubygems_version: 2.4.
|
246
|
+
rubygems_version: 2.4.8
|
205
247
|
signing_key:
|
206
248
|
specification_version: 4
|
207
249
|
summary: Quickly put your screenshots into the right device frames
|