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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 33697842a9684e1245b3cf21196579eab29f9df9
4
- data.tar.gz: 3a71b690abfea0de0de196ddcab330f581f461fe
3
+ metadata.gz: 34abb8d5461e92d27d8ee707a1e6fae13c1d1b16
4
+ data.tar.gz: f5a0190aaf203cc07ec3244a9c3bd8636d3244bd
5
5
  SHA512:
6
- metadata.gz: 435f45f0176da56d12aa4258b3e91c57e5869789c3a60b4fa46acab276be667f76b49c9c40b1f930ae4b29ed62f7cb86b3896cb768009547639c409e08f46981
7
- data.tar.gz: 76b974d29562dd17976266b6721b5b066a45cb9e06d0b9f03146f3bd79b08045a9e8cabfb3abe144dc3fc885d09140a227a4372b789b0f604a92436c6974104e
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> &bull;
14
14
  <a href="https://github.com/KrauseFx/produce">produce</a> &bull;
15
15
  <a href="https://github.com/KrauseFx/cert">cert</a> &bull;
16
- <a href="https://github.com/KrauseFx/codes">codes</a> &bull;
17
16
  <a href="https://github.com/fastlane/spaceship">spaceship</a> &bull;
18
17
  <a href="https://github.com/fastlane/pilot">pilot</a> &bull;
19
18
  <a href="https://github.com/fastlane/boarding">boarding</a> &bull;
20
- <a href="https://github.com/fastlane/gym">gym</a>
19
+ <a href="https://github.com/fastlane/gym">gym</a> &bull;
20
+ <a href="https://github.com/fastlane/scan">scan</a>
21
21
  </p>
22
22
  -------
23
23
 
24
24
  <p align="center">
25
- <img src="assets/frameit.png">
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.exists?(path) # we are okay with no config at all
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.exists?value
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.exists?current.fetch('font')
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.exists?value
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.is_test?
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
@@ -8,4 +8,4 @@ module Frameit
8
8
  PORTRAIT = "Vert"
9
9
  LANDSCAPE = "Horz"
10
10
  end
11
- end
11
+ end
@@ -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
- @offset_information = fetch_config['offset'] || Offsets.image_offset(screenshot)
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
- if @offset_information and (@offset_information['offset'] or @offset_information['offset'])
59
- return @offset_information
60
- end
61
- raise "Could not find offset_information for '#{screenshot}'"
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
- # 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
53
+ def offset
54
+ return @offset_information if @offset_information
80
55
 
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']))
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
- # more complex mode: background, frame and title
87
- def complex_framing
88
- background = generate_background
89
-
90
- if self.frame # we have no frame on le mac
91
- resize_frame!
92
- @image = put_into_frame
93
-
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"
97
- end
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
- @image = put_device_into_background(background)
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
- if fetch_config['title']
102
- @image = add_title
103
- end
86
+ # more complex mode: background, frame and title
87
+ def complex_framing
88
+ background = generate_background
104
89
 
105
- image
106
- end
90
+ if self.frame # we have no frame on le mac
91
+ resize_frame!
92
+ @image = put_into_frame
107
93
 
108
- # Padding around the frames
109
- def frame_padding
110
- multi = 1.0
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
- # Returns a correctly sized background image
116
- def generate_background
117
- background = MiniMagick::Image.open(fetch_config['background'])
99
+ @image = put_device_into_background(background)
118
100
 
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
101
+ if fetch_config['title']
102
+ @image = add_title
123
103
  end
124
104
 
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.is_portrait? # even more for portrait mode
105
+ image
106
+ end
129
107
 
130
- if screenshot.is_mini?
131
- # Such small devices need special treatment
132
- bottom_space -= 50 if screenshot.is_portrait?
133
- bottom_space += 65 unless screenshot.is_portrait?
134
- end
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
- self.top_space_above_device = background.height - image.height - bottom_space
115
+ # Returns a correctly sized background image
116
+ def generate_background
117
+ background = MiniMagick::Image.open(fetch_config['background'])
137
118
 
138
- @image = background.composite(image, "png") do |c|
139
- c.compose "Over"
140
- c.geometry "+#{left_space}+#{top_space_above_device}"
141
- end
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
- return image
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
- # 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
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
- # Add the title above the device
155
- def add_title
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
- top_space = (top_space_above_device / 2.0 - (actual_font_size / 2.0 * smaller)).round # centered
180
- left_space = (image.width / 2.0 - sum_width / 2.0).round
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
- # First, put the keyword on top of the screenshot, if we have one
183
- if keyword
184
- @image = image.composite(keyword, "png") do |c|
185
- c.compose "Over"
186
- c.geometry "+#{left_space}+#{top_space}"
187
- end
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
- left_space += keyword.width + (keyword_padding * smaller)
190
- end
191
-
192
- # Then, put the title on top of the screenshot next to the keyword
193
- @image = image.composite(title, "png") do |c|
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
- def actual_font_size
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
- # The space between the keyword and the title
205
- def keyword_padding
206
- (actual_font_size / 2.0).round
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
- # This will build 2 individual images with the title, which will then be added to the real image
210
- def build_title_images(max_width)
211
- words = [:keyword, :title].keep_if{ |a| fetch_text(a) } # optional keyword/title
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
- current_font = font(key)
224
- text = fetch_text(key)
225
- Helper.log.debug "Using #{current_font} as font the #{key} of #{screenshot.path}" if $verbose and current_font
226
- Helper.log.debug "Adding text '#{fetch_text(key)}'"if $verbose
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
- results[key] = title_image
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
- # Loads the config (colors, background, texts, etc.)
244
- # Don't use this method to access the actual text and use `fetch_texts` instead
245
- def fetch_config
246
- return @config if @config
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
- config_path = File.join(File.expand_path("..", screenshot.path), "Framefile.json")
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
- # Fetches the title + keyword for this particular screenshot
256
- def fetch_text(type)
257
- raise "Valid parameters :keyword, :title" unless [:keyword, :title].include?type
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
- # Try to get it from a keyword.strings or title.strings file
260
- strings_path = File.join(File.expand_path("..", screenshot.path), "#{type.to_s}.strings")
261
- if File.exists?strings_path
262
- parsed = StringsParser.parse(strings_path)
263
- result = parsed.find { |k, v| screenshot.path.include?k }
264
- return result.last if result
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
- # No string files, fallback to Framefile config
268
- result = fetch_config[type.to_s]['text']
269
- Helper.log.debug "Falling back to default text as there was nothing specified in the .strings file" if $verbose
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
- if !result and type == :title
272
- # title is mandatory
273
- raise "Could not get title for screenshot #{screenshot.path}. Please provide one in your Framefile.json".red
274
- end
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
- return result
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
- # The font we want to use
280
- def font(key)
281
- single_font = fetch_config[key.to_s]['font']
282
- return single_font if single_font
283
-
284
- fonts = fetch_config[key.to_s]['fonts']
285
- if fonts
286
- fonts.each do |font|
287
- if font['supported']
288
- font['supported'].each do |language|
289
- if screenshot.path.include?language
290
- return font["font"]
291
- end
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 not frames_exist?
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
- unless File.exists?resulting_path
64
- Helper.log.debug "Converting PSD file '#{psd}'".yellow
65
- image = MiniMagick::Image.open(psd)
66
- if image
67
- image.format 'png'
68
- image.trim
69
-
70
- image.write(resulting_path)
71
- else
72
- Helper.log.error "Could not parse PSD file at path '#{psd}'"
73
- end
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
@@ -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
@@ -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
- 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' => '+50+134',
33
- 'width' => 792
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' => '+133+50',
61
- 'width' => 1058
62
- }
63
- end
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
@@ -5,12 +5,12 @@ module Frameit
5
5
  class Runner
6
6
  def initialize
7
7
  converter = FrameConverter.new
8
- unless converter.frames_exist?
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
@@ -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.exists?path
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
- 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'
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 is_triple_density?
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 is_mini?
45
+ def mini?
46
46
  (screen_size == Deliver::AppScreenshot::ScreenSize::IOS_35)
47
47
  end
48
48
 
49
- def is_mac?
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 is_portrait?
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.is_mac?
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.exists?path
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.is_mac?
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 = templates + Dir["#{templates_path}/**/#{parts.join('_')}*.{png,jpg}"] # ~/.frameit folder
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.gsub(" ", "\ ")
30
+ return templates.first.tr(" ", "\ ")
34
31
  end
35
32
  end
36
33
  end
37
- end
34
+ end
@@ -1,3 +1,3 @@
1
1
  module Frameit
2
- VERSION = "2.2.1"
2
+ VERSION = "2.2.2"
3
3
  end
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.1
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-09-08 00:00:00.000000000 Z
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.5
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