inkcite 1.14.0 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/assets/init/config.yml +2 -0
- data/assets/social/facebook.png +0 -0
- data/assets/social/instagram.png +0 -0
- data/assets/social/pintrest.png +0 -0
- data/assets/social/twitter.png +0 -0
- data/inkcite.gemspec +2 -2
- data/lib/inkcite.rb +1 -0
- data/lib/inkcite/cli/base.rb +32 -6
- data/lib/inkcite/cli/preview.rb +24 -28
- data/lib/inkcite/cli/server.rb +22 -19
- data/lib/inkcite/cli/test.rb +1 -1
- data/lib/inkcite/email.rb +8 -4
- data/lib/inkcite/facade/animation.rb +4 -0
- data/lib/inkcite/facade/keyframe.rb +26 -3
- data/lib/inkcite/image/base.rb +38 -0
- data/lib/inkcite/image/guetzli_minifier.rb +62 -0
- data/lib/inkcite/image/image_minifier.rb +143 -0
- data/lib/inkcite/image/image_optim_minifier.rb +90 -0
- data/lib/inkcite/image/mozjpeg_minifier.rb +92 -0
- data/lib/inkcite/mailer.rb +201 -112
- data/lib/inkcite/minifier.rb +2 -146
- data/lib/inkcite/post_processor.rb +13 -0
- data/lib/inkcite/renderer.rb +19 -0
- data/lib/inkcite/renderer/background.rb +53 -14
- data/lib/inkcite/renderer/base.rb +29 -15
- data/lib/inkcite/renderer/button.rb +1 -1
- data/lib/inkcite/renderer/carousel.rb +245 -0
- data/lib/inkcite/renderer/container_base.rb +10 -0
- data/lib/inkcite/renderer/div.rb +1 -3
- data/lib/inkcite/renderer/fireworks.rb +54 -40
- data/lib/inkcite/renderer/footnote.rb +22 -2
- data/lib/inkcite/renderer/image.rb +11 -0
- data/lib/inkcite/renderer/image_base.rb +3 -6
- data/lib/inkcite/renderer/in_browser.rb +4 -0
- data/lib/inkcite/renderer/link.rb +39 -12
- data/lib/inkcite/renderer/mobile_image.rb +1 -1
- data/lib/inkcite/renderer/responsive.rb +9 -1
- data/lib/inkcite/renderer/social.rb +31 -3
- data/lib/inkcite/renderer/special_effect.rb +22 -13
- data/lib/inkcite/renderer/sup.rb +32 -0
- data/lib/inkcite/renderer/table_base.rb +3 -0
- data/lib/inkcite/renderer/topic.rb +76 -0
- data/lib/inkcite/renderer/trademark.rb +47 -0
- data/lib/inkcite/renderer/video_preview.rb +3 -2
- data/lib/inkcite/uploader.rb +2 -3
- data/lib/inkcite/util.rb +51 -0
- data/lib/inkcite/version.rb +1 -1
- data/lib/inkcite/view.rb +140 -54
- data/lib/inkcite/view/context.rb +1 -31
- data/lib/inkcite/view/media_query.rb +6 -0
- data/test/animation_spec.rb +7 -0
- data/test/parser_spec.rb +1 -1
- data/test/renderer/background_spec.rb +16 -12
- data/test/renderer/div_spec.rb +11 -0
- data/test/renderer/footnote_spec.rb +5 -1
- data/test/renderer/image_spec.rb +51 -28
- data/test/renderer/link_spec.rb +20 -8
- data/test/renderer/lorem_spec.rb +2 -2
- data/test/renderer/mobile_image_spec.rb +6 -0
- data/test/renderer/mobile_style_spec.rb +3 -3
- data/test/renderer/redacted_spec.rb +2 -2
- data/test/renderer/social_spec.rb +6 -6
- data/test/renderer/table_spec.rb +4 -0
- data/test/renderer/topic_spec.rb +28 -0
- data/test/renderer/trademark_spec.rb +40 -0
- data/test/renderer/video_preview_spec.rb +1 -1
- data/test/test_helper.rb +14 -0
- data/test/view_spec.rb +4 -0
- metadata +26 -12
- data/assets/init/image_optim.yml +0 -37
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6f77f5972f324c7fd1d94e0e62ad167331636c78
|
|
4
|
+
data.tar.gz: 86ce04170afb3b33b76aae7b484eac7247de870d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 78f601288da10bd3db71580b7439c70582a0ec1b9bed2458b9bb79e1e4f6266c8c60cb2c55756fb36809e0ffb003791c150b0a9a010fd375bd4ac9bebd1a5d19
|
|
7
|
+
data.tar.gz: 7e817e4e9e5d38df8950ecc8d390bbfe9f3540703b95149e700b90327ce002fcbbe1a492b94baccff4c9faed55a1efe2b8f8ad5e95f044d8d299f1c0cb880fef
|
data/assets/init/config.yml
CHANGED
|
@@ -17,6 +17,8 @@ minify: true
|
|
|
17
17
|
# https://inkcite.readme.io/v1.0/docs/image-optimization
|
|
18
18
|
optimize-images: true
|
|
19
19
|
|
|
20
|
+
jpg-quality: 90
|
|
21
|
+
|
|
20
22
|
# When empty links are found in content, this is the URL that will be
|
|
21
23
|
# included instead - so that clients understand this link is missing
|
|
22
24
|
# and needs to be provided.
|
data/assets/social/facebook.png
CHANGED
|
Binary file
|
|
Binary file
|
data/assets/social/pintrest.png
CHANGED
|
Binary file
|
data/assets/social/twitter.png
CHANGED
|
Binary file
|
data/inkcite.gemspec
CHANGED
|
@@ -27,17 +27,17 @@ Gem::Specification.new do |spec|
|
|
|
27
27
|
spec.add_dependency 'builder'
|
|
28
28
|
spec.add_dependency 'chunky_png'
|
|
29
29
|
spec.add_dependency 'erubis'
|
|
30
|
-
spec.add_dependency 'faker'
|
|
30
|
+
spec.add_dependency 'faker', '1.7.2'
|
|
31
31
|
spec.add_dependency 'guard'
|
|
32
32
|
spec.add_dependency 'guard-livereload'
|
|
33
33
|
spec.add_dependency 'htmlbeautifier'
|
|
34
34
|
spec.add_dependency 'image_optim'
|
|
35
35
|
spec.add_dependency 'image_optim_pack'
|
|
36
|
-
spec.add_dependency 'kraken-io'
|
|
37
36
|
spec.add_dependency 'listen'
|
|
38
37
|
spec.add_dependency 'litmus'
|
|
39
38
|
spec.add_dependency 'mail'
|
|
40
39
|
spec.add_dependency 'mailgun-ruby'
|
|
40
|
+
spec.add_dependency 'mozjpeg'
|
|
41
41
|
spec.add_dependency 'net-sftp'
|
|
42
42
|
spec.add_dependency 'rack'
|
|
43
43
|
spec.add_dependency 'rack-livereload'
|
data/lib/inkcite.rb
CHANGED
data/lib/inkcite/cli/base.rb
CHANGED
|
@@ -41,20 +41,46 @@ module Inkcite
|
|
|
41
41
|
Cli::Init.invoke(name, options)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
+
desc 'minify', 'Minify the images in the project'
|
|
45
|
+
option :force,
|
|
46
|
+
:aliases => '-f',
|
|
47
|
+
:desc => 'Force re-optimize every image, not just the updated ones',
|
|
48
|
+
:type => :boolean
|
|
49
|
+
option :image,
|
|
50
|
+
:aliases => '-i',
|
|
51
|
+
:desc => 'Optimize a specific image',
|
|
52
|
+
:type => :string
|
|
53
|
+
def minify
|
|
54
|
+
|
|
55
|
+
if options[:image]
|
|
56
|
+
Image::ImageMinifier.minify(email, options[:image], true)
|
|
57
|
+
|
|
58
|
+
else
|
|
59
|
+
Image::ImageMinifier.minify_all(email, options[:force])
|
|
60
|
+
|
|
61
|
+
original_size = Util.dir_size(email.image_dir)
|
|
62
|
+
compressed_size = Util.dir_size(email.optimized_image_dir)
|
|
63
|
+
compressed_percent = Image::ImageMinifier.compressed_percent(original_size, compressed_size)
|
|
64
|
+
|
|
65
|
+
puts "Compressed from #{Util.pretty_file_size(original_size)} to #{Util.pretty_file_size(compressed_size)} (#{compressed_percent}%)"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|
|
69
|
+
|
|
44
70
|
desc 'preview TO [options]', 'Send a preview of the email to a recipient list: developer, internal or client'
|
|
45
71
|
option :version,
|
|
46
72
|
:aliases => '-v',
|
|
47
73
|
:desc => 'Preview a specific version of the email'
|
|
48
|
-
option :
|
|
49
|
-
:aliases => '-
|
|
50
|
-
:desc => '
|
|
51
|
-
:type => :
|
|
74
|
+
option :count,
|
|
75
|
+
:aliases => '-c',
|
|
76
|
+
:desc => 'Override the automatic preview numbering',
|
|
77
|
+
:type => :numeric
|
|
52
78
|
option :'no-upload',
|
|
53
79
|
:desc => 'Skip the asset upload, email the preview immediately',
|
|
54
80
|
:type => :boolean
|
|
55
|
-
def preview
|
|
81
|
+
def preview list=:developer
|
|
56
82
|
require_relative 'preview'
|
|
57
|
-
Cli::Preview.invoke(email,
|
|
83
|
+
Cli::Preview.invoke(email, list, options)
|
|
58
84
|
end
|
|
59
85
|
|
|
60
86
|
desc 'scope [options]', 'Share this email using Litmus Scope (https://litmus.com/scope/)'
|
data/lib/inkcite/cli/preview.rb
CHANGED
|
@@ -4,47 +4,43 @@ module Inkcite
|
|
|
4
4
|
module Cli
|
|
5
5
|
class Preview
|
|
6
6
|
|
|
7
|
-
def self.invoke email,
|
|
7
|
+
def self.invoke email, list, opt
|
|
8
8
|
|
|
9
9
|
# Push the browser preview(s) up to the server to ensure that the
|
|
10
10
|
# latest images and "view in browser" versions are available.
|
|
11
11
|
email.upload unless opt[:'no-upload']
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
# Ensure we're dealing with a symbol rather than string.
|
|
14
|
+
list = list.to_sym
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
# explode those into an array for convenience. Email is already
|
|
18
|
-
# hard enough.
|
|
19
|
-
if also.any? { |a| a.match(',') }
|
|
20
|
-
also = also.collect { |a| a.split(',') }.flatten
|
|
16
|
+
preview_opt = {}
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
# to inject the new array of recipients back into it.
|
|
24
|
-
opt = opt.dup
|
|
25
|
-
opt[:also] = also
|
|
26
|
-
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
case to.to_sym
|
|
18
|
+
case list
|
|
31
19
|
when :client
|
|
32
|
-
|
|
20
|
+
preview_opt[:tag] = 'Preview'
|
|
21
|
+
preview_opt[:bcc] = true
|
|
33
22
|
when :internal
|
|
34
|
-
|
|
23
|
+
preview_opt[:tag] = 'Internal Preview'
|
|
24
|
+
preview_opt[:bcc] = true
|
|
35
25
|
when :developer
|
|
36
|
-
|
|
26
|
+
preview_opt[:tag] = 'Developer Test'
|
|
37
27
|
else
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
28
|
+
preview_opt[:tag] = "#{list.to_s.titleize} Test"
|
|
29
|
+
preview_opt[:bcc] = true
|
|
30
|
+
# abort <<-USAGE.strip_heredoc
|
|
31
|
+
#
|
|
32
|
+
# Oops! Inkcite doesn't recognize that distribution list. It needs
|
|
33
|
+
# to be one of 'client', 'internal' or 'developer':
|
|
34
|
+
#
|
|
35
|
+
# inkcite preview internal
|
|
36
|
+
#
|
|
37
|
+
# USAGE
|
|
38
|
+
#
|
|
39
|
+
# return
|
|
46
40
|
end
|
|
47
41
|
|
|
42
|
+
Mailer.send_to_list email, list, opt.merge(preview_opt)
|
|
43
|
+
|
|
48
44
|
end
|
|
49
45
|
|
|
50
46
|
end
|
data/lib/inkcite/cli/server.rb
CHANGED
|
@@ -11,10 +11,8 @@ module Inkcite
|
|
|
11
11
|
|
|
12
12
|
def self.start email, opts
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
puts 'Documentation available at http://inkcite.readme.io'
|
|
17
|
-
puts
|
|
14
|
+
Util.log "Inkcite #{Inkcite::VERSION} is starting up ..."
|
|
15
|
+
Util.log 'Documentation available at http://inkcite.readme.io'
|
|
18
16
|
|
|
19
17
|
# Read the hostname and port from the opts provided on the command
|
|
20
18
|
# line - or inherit the default of localhost:4567
|
|
@@ -58,10 +56,11 @@ module Inkcite
|
|
|
58
56
|
run InkciteApp.new(email, opts)
|
|
59
57
|
end
|
|
60
58
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
Util.log ''
|
|
60
|
+
Util.log "Your email is being served at http://#{host}:#{port}"
|
|
61
|
+
Util.log "Point your mobile device to http://#{ip}:#{port}" if ip
|
|
62
|
+
Util.log 'Press CTRL-C to exit server mode'
|
|
63
|
+
Util.log ''
|
|
65
64
|
|
|
66
65
|
begin
|
|
67
66
|
|
|
@@ -109,11 +108,15 @@ module Inkcite
|
|
|
109
108
|
# Minify the image if the source version in images/ is newer
|
|
110
109
|
# or if the configuration file controlling optimization has
|
|
111
110
|
# been updated since the last time the image was requested.
|
|
112
|
-
|
|
111
|
+
Image::ImageMinifier.minify(@email, File.basename(path), false) if can_serve(path)
|
|
113
112
|
|
|
114
113
|
# Let the super method handle the actual serving of the image.
|
|
115
|
-
super
|
|
114
|
+
res = super
|
|
115
|
+
|
|
116
|
+
# Install cache control into the response. Tried using
|
|
117
|
+
res[1][Rack::CACHE_CONTROL] = NO_CACHE
|
|
116
118
|
|
|
119
|
+
res
|
|
117
120
|
end
|
|
118
121
|
|
|
119
122
|
end
|
|
@@ -136,6 +139,11 @@ module Inkcite
|
|
|
136
139
|
response = Rack::Response.new
|
|
137
140
|
response[Rack::CONTENT_TYPE] = 'text/html'
|
|
138
141
|
|
|
142
|
+
# Speedup for local development ensuring that a substantial number of reloads
|
|
143
|
+
# (generated when developing multi-version emails simultaneously) can cause
|
|
144
|
+
# the browser to slow down.
|
|
145
|
+
response[Rack::CACHE_CONTROL] = NO_CACHE
|
|
146
|
+
|
|
139
147
|
begin
|
|
140
148
|
|
|
141
149
|
# Allow the designer to specify both short- and long-form versions of
|
|
@@ -146,12 +154,7 @@ module Inkcite
|
|
|
146
154
|
format = Util.detect(params['f'], params['format'], @opts[:format])
|
|
147
155
|
version = Util.detect(params['v'], params['version'], @opts[:version])
|
|
148
156
|
|
|
149
|
-
|
|
150
|
-
# messages are associated with this reload.
|
|
151
|
-
ts = "[#{Time.now.strftime(DATEFORMAT)}]"
|
|
152
|
-
|
|
153
|
-
puts ''
|
|
154
|
-
puts "#{ts} Rendering your email [environment=#{environment}, format=#{format}, version=#{version || 'default'}]"
|
|
157
|
+
Util.log "Rendering your email", :environment => environment, :format => format, :version => version || 'default'
|
|
155
158
|
|
|
156
159
|
view = @email.view(environment, format, version)
|
|
157
160
|
|
|
@@ -163,8 +166,8 @@ module Inkcite
|
|
|
163
166
|
|
|
164
167
|
unless view.errors.blank?
|
|
165
168
|
error_count = view.errors.count
|
|
166
|
-
|
|
167
|
-
|
|
169
|
+
Util.log "#{error_count} error#{'s' if error_count > 1} or warning#{'s' if error_count > 1}:"
|
|
170
|
+
view.errors.each { |e| Util.log(e) }
|
|
168
171
|
end
|
|
169
172
|
|
|
170
173
|
response.write html
|
|
@@ -186,7 +189,7 @@ module Inkcite
|
|
|
186
189
|
|
|
187
190
|
REQUEST_ROOT = '/'
|
|
188
191
|
|
|
189
|
-
|
|
192
|
+
NO_CACHE = 'no-cache, no-store'
|
|
190
193
|
|
|
191
194
|
end
|
|
192
195
|
end
|
data/lib/inkcite/cli/test.rb
CHANGED
data/lib/inkcite/email.rb
CHANGED
|
@@ -24,7 +24,11 @@ module Inkcite
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def config
|
|
27
|
-
Util.read_yml(
|
|
27
|
+
Util.read_yml(config_file, :fail_if_not_exists => true)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def config_file
|
|
31
|
+
File.join(path, 'config.yml')
|
|
28
32
|
end
|
|
29
33
|
|
|
30
34
|
def formats env=nil
|
|
@@ -57,12 +61,12 @@ module Inkcite
|
|
|
57
61
|
# Optimizes this email's images if optimize-images is enabled
|
|
58
62
|
# in the email configuration.
|
|
59
63
|
def optimize_images
|
|
60
|
-
|
|
64
|
+
Image::ImageMinifier.minify_all(self, false) if optimize_images?
|
|
61
65
|
end
|
|
62
66
|
|
|
63
67
|
# Optimizes all of the images in this email.
|
|
64
68
|
def optimize_images!
|
|
65
|
-
|
|
69
|
+
Image::ImageMinifier.minify_all(self, true)
|
|
66
70
|
end
|
|
67
71
|
|
|
68
72
|
def optimize_images?
|
|
@@ -72,7 +76,7 @@ module Inkcite
|
|
|
72
76
|
# Returns the directory that optimized, compressed images
|
|
73
77
|
# have been saved to.
|
|
74
78
|
def optimized_image_dir
|
|
75
|
-
File.join(path, optimize_images??
|
|
79
|
+
File.join(path, optimize_images?? Image::ImageMinifier::IMAGE_CACHE : IMAGES)
|
|
76
80
|
end
|
|
77
81
|
|
|
78
82
|
def project_file file
|
|
@@ -36,6 +36,10 @@ module Inkcite
|
|
|
36
36
|
EASE_IN_OUT = 'ease-in-out'
|
|
37
37
|
EASE_OUT = 'ease-out'
|
|
38
38
|
|
|
39
|
+
# Advanced easing functions courtesy of https://matthewlein.com/ceaser/
|
|
40
|
+
EASE_IN_CUBIC = 'cubic-bezier(0.550, 0.055, 0.675, 0.190)'
|
|
41
|
+
EASE_OUT_QUART = 'cubic-bezier(0.165, 0.840, 0.440, 1.000)'
|
|
42
|
+
|
|
39
43
|
# Animation name, view context and array of keyframes
|
|
40
44
|
attr_reader :name, :ctx
|
|
41
45
|
|
|
@@ -9,11 +9,15 @@ module Inkcite
|
|
|
9
9
|
# of 19.9% would render as 25%, 39.9% { ... }
|
|
10
10
|
attr_accessor :duration
|
|
11
11
|
|
|
12
|
+
# Alternative to duration, the ending percentage
|
|
13
|
+
attr_accessor :end_percent
|
|
14
|
+
|
|
12
15
|
def initialize percent, ctx, styles={}
|
|
13
16
|
|
|
14
17
|
# Animation percents are always rounded to the nearest whole number.
|
|
15
|
-
@percent = percent
|
|
16
|
-
@
|
|
18
|
+
@percent = percent
|
|
19
|
+
@end_percent = nil
|
|
20
|
+
@duration = nil
|
|
17
21
|
|
|
18
22
|
# Instantiate a new Style for this percentage.
|
|
19
23
|
@style = Inkcite::Renderer::Style.new(nil, ctx, styles)
|
|
@@ -55,7 +59,10 @@ module Inkcite
|
|
|
55
59
|
|
|
56
60
|
def to_css prefix
|
|
57
61
|
css = "#{@percent}%"
|
|
58
|
-
|
|
62
|
+
|
|
63
|
+
ends_at = calc_ends_at
|
|
64
|
+
css << ", #{ends_at}%" if ends_at
|
|
65
|
+
|
|
59
66
|
css << ' { '
|
|
60
67
|
css << @style.to_inline_css(prefix)
|
|
61
68
|
css << ' }'
|
|
@@ -64,6 +71,22 @@ module Inkcite
|
|
|
64
71
|
|
|
65
72
|
private
|
|
66
73
|
|
|
74
|
+
# Returns the percent at which the animation keyframe ends
|
|
75
|
+
# based on either end_percent (preferred) or duration (deprecated)
|
|
76
|
+
# being set. Will return nil if neither is present.
|
|
77
|
+
def calc_ends_at
|
|
78
|
+
ends_at = if @end_percent
|
|
79
|
+
@end_percent
|
|
80
|
+
elsif @duration
|
|
81
|
+
(@percent + @duration).round(1)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Ensure that the ending percentage never exceeds 100%
|
|
85
|
+
ends_at = 100 if ends_at && ends_at >= 100.0
|
|
86
|
+
|
|
87
|
+
ends_at
|
|
88
|
+
end
|
|
89
|
+
|
|
67
90
|
# Creates a copy of the array of styles with the appropriate
|
|
68
91
|
# properties (e.g. transform) prefixed.
|
|
69
92
|
def get_prefixed_styles prefix
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Inkcite
|
|
2
|
+
module Image
|
|
3
|
+
class ImageMinifier
|
|
4
|
+
|
|
5
|
+
# Base class for all image minifiers in the optimization pipeline
|
|
6
|
+
class Base
|
|
7
|
+
|
|
8
|
+
attr_reader :name
|
|
9
|
+
|
|
10
|
+
def initialize name
|
|
11
|
+
@name = name
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def minify! email, source_img, cache_img
|
|
15
|
+
raise 'The extending class must implement this method'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
protected
|
|
19
|
+
|
|
20
|
+
# Common configuration names
|
|
21
|
+
JPG_QUALITY = :'jpg-quality'
|
|
22
|
+
|
|
23
|
+
# JPG quality bounds
|
|
24
|
+
MIN_QUALITY = 0
|
|
25
|
+
MAX_QUALITY = 100
|
|
26
|
+
|
|
27
|
+
def get_jpg_quality config, override_key, default
|
|
28
|
+
quality = (config[override_key] || config[JPG_QUALITY] || default).to_i
|
|
29
|
+
quality = MIN_QUALITY if quality < MIN_QUALITY
|
|
30
|
+
quality = MAX_QUALITY if quality > MAX_QUALITY
|
|
31
|
+
quality
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Inkcite
|
|
2
|
+
module Image
|
|
3
|
+
class GuetzliMinifier < ImageMinifier::Base
|
|
4
|
+
|
|
5
|
+
def initialize
|
|
6
|
+
super('Guetzli')
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def minify! email, source_img, cache_img
|
|
10
|
+
|
|
11
|
+
# Grab the full path to the guetzli binary
|
|
12
|
+
guetzli_path = `which guetzli`.delete("\n")
|
|
13
|
+
unless guetzli_path.blank?
|
|
14
|
+
|
|
15
|
+
cmd = []
|
|
16
|
+
cmd << guetzli_path
|
|
17
|
+
|
|
18
|
+
config = email.config
|
|
19
|
+
|
|
20
|
+
quality = get_jpg_quality(config, GUETZLI_QUALITY, DEFAULT_QUALITY)
|
|
21
|
+
if quality > 0 && quality < MAX_QUALITY
|
|
22
|
+
|
|
23
|
+
# Per the Guetzli documentation, a value less than 84 isn't useful.
|
|
24
|
+
quality = MIN_GUETZLI_QUALITY if quality < MIN_GUETZLI_QUALITY
|
|
25
|
+
cmd << "--quality #{quality}"
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
cmd << %Q("#{source_img}")
|
|
30
|
+
cmd << %Q("#{cache_img}")
|
|
31
|
+
|
|
32
|
+
Util::exec(cmd)
|
|
33
|
+
|
|
34
|
+
true
|
|
35
|
+
|
|
36
|
+
else
|
|
37
|
+
|
|
38
|
+
# No guetzli, so simply move the source image into the destination
|
|
39
|
+
# position without compression.
|
|
40
|
+
FileUtils.copy(source_img, cache_img)
|
|
41
|
+
|
|
42
|
+
false
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
# Default quality is zero - which lets Guetzli decide how best to
|
|
51
|
+
# optimize the image.
|
|
52
|
+
DEFAULT_QUALITY = 0
|
|
53
|
+
|
|
54
|
+
# The minimum quality setting per the Guetzli runtime.
|
|
55
|
+
MIN_GUETZLI_QUALITY = 84
|
|
56
|
+
|
|
57
|
+
# Configuration field names
|
|
58
|
+
GUETZLI_QUALITY = :'guetzli-quality'
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|