frameit 2.2.1 → 2.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|