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 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