dragonfly-lossless_rotate 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +169 -0
- data/README.md +13 -3
- data/dragonfly-lossless_rotate.gemspec +25 -23
- data/lib/dragonfly/lossless_rotate.rb +102 -93
- data/lib/dragonfly/lossless_rotate/version.rb +7 -5
- metadata +2 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 980d9f58a025a965308c14d93100cf22bf293803ae7d7df58ef8ae484c50690b
|
4
|
+
data.tar.gz: b7e6c1af2f809f227a84a5db967b5d0abfee1ae2f2098df9da626fd678fd216e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8ca9e89edd39b58071e79cc297b698bd60493a7a649e785e015dfe2d8de87b05da9e785fc8d69e29470f76ff28fa3f11328d63c57b158e29c20ea8c04d5546e
|
7
|
+
data.tar.gz: 4e3137e0458fcc1649df916dfd3459767e27859e85b743e721ebae6a8cc1e957b35b323f33eb3313f10c2a2acd822b01817e213b70358ce8d8c02673492d7ed4
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.4
|
3
|
+
# RuboCop has a bunch of cops enabled by default. This setting tells RuboCop
|
4
|
+
# to ignore them, so only the ones explicitly set in this file are enabled.
|
5
|
+
DisabledByDefault: true
|
6
|
+
Exclude:
|
7
|
+
- '**/templates/**/*'
|
8
|
+
- '**/vendor/**/*'
|
9
|
+
- 'actionpack/lib/action_dispatch/journey/parser.rb'
|
10
|
+
- 'railties/test/fixtures/tmp/**/*'
|
11
|
+
|
12
|
+
# Prefer &&/|| over and/or.
|
13
|
+
Style/AndOr:
|
14
|
+
Enabled: true
|
15
|
+
|
16
|
+
# Do not use braces for hash literals when they are the last argument of a
|
17
|
+
# method call.
|
18
|
+
Style/BracesAroundHashParameters:
|
19
|
+
Enabled: true
|
20
|
+
EnforcedStyle: context_dependent
|
21
|
+
|
22
|
+
# Align `when` with `case`.
|
23
|
+
Layout/CaseIndentation:
|
24
|
+
Enabled: true
|
25
|
+
|
26
|
+
# Align comments with method definitions.
|
27
|
+
Layout/CommentIndentation:
|
28
|
+
Enabled: true
|
29
|
+
|
30
|
+
Layout/ElseAlignment:
|
31
|
+
Enabled: true
|
32
|
+
|
33
|
+
# Align `end` with the matching keyword or starting expression except for
|
34
|
+
# assignments, where it should be aligned with the LHS.
|
35
|
+
Layout/EndAlignment:
|
36
|
+
Enabled: true
|
37
|
+
EnforcedStyleAlignWith: variable
|
38
|
+
AutoCorrect: true
|
39
|
+
|
40
|
+
Layout/EmptyLineAfterMagicComment:
|
41
|
+
Enabled: true
|
42
|
+
|
43
|
+
Layout/EmptyLinesAroundBlockBody:
|
44
|
+
Enabled: true
|
45
|
+
|
46
|
+
# In a regular class definition, no empty lines around the body.
|
47
|
+
Layout/EmptyLinesAroundClassBody:
|
48
|
+
Enabled: true
|
49
|
+
|
50
|
+
# In a regular method definition, no empty lines around the body.
|
51
|
+
Layout/EmptyLinesAroundMethodBody:
|
52
|
+
Enabled: true
|
53
|
+
|
54
|
+
# In a regular module definition, no empty lines around the body.
|
55
|
+
Layout/EmptyLinesAroundModuleBody:
|
56
|
+
Enabled: true
|
57
|
+
|
58
|
+
Layout/FirstParameterIndentation:
|
59
|
+
Enabled: true
|
60
|
+
|
61
|
+
# Use Ruby >= 1.9 syntax for hashes. Prefer { a: :b } over { :a => :b }.
|
62
|
+
Style/HashSyntax:
|
63
|
+
Enabled: true
|
64
|
+
|
65
|
+
# Method definitions after `private` or `protected` isolated calls need one
|
66
|
+
# extra level of indentation.
|
67
|
+
Layout/IndentationConsistency:
|
68
|
+
Enabled: true
|
69
|
+
EnforcedStyle: rails
|
70
|
+
|
71
|
+
# Two spaces, no tabs (for indentation).
|
72
|
+
Layout/IndentationWidth:
|
73
|
+
Enabled: true
|
74
|
+
|
75
|
+
Layout/LeadingCommentSpace:
|
76
|
+
Enabled: true
|
77
|
+
|
78
|
+
Layout/SpaceAfterColon:
|
79
|
+
Enabled: true
|
80
|
+
|
81
|
+
Layout/SpaceAfterComma:
|
82
|
+
Enabled: true
|
83
|
+
|
84
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
85
|
+
Enabled: true
|
86
|
+
|
87
|
+
Layout/SpaceAroundKeyword:
|
88
|
+
Enabled: true
|
89
|
+
|
90
|
+
Layout/SpaceAroundOperators:
|
91
|
+
Enabled: true
|
92
|
+
|
93
|
+
Layout/SpaceBeforeComma:
|
94
|
+
Enabled: true
|
95
|
+
|
96
|
+
Layout/SpaceBeforeFirstArg:
|
97
|
+
Enabled: true
|
98
|
+
|
99
|
+
Style/DefWithParentheses:
|
100
|
+
Enabled: true
|
101
|
+
|
102
|
+
# Defining a method with parameters needs parentheses.
|
103
|
+
Style/MethodDefParentheses:
|
104
|
+
Enabled: true
|
105
|
+
|
106
|
+
Style/FrozenStringLiteralComment:
|
107
|
+
Enabled: true
|
108
|
+
EnforcedStyle: always
|
109
|
+
Exclude:
|
110
|
+
- 'actionview/test/**/*.builder'
|
111
|
+
- 'actionview/test/**/*.ruby'
|
112
|
+
- 'actionpack/test/**/*.builder'
|
113
|
+
- 'actionpack/test/**/*.ruby'
|
114
|
+
- 'activestorage/db/migrate/**/*.rb'
|
115
|
+
|
116
|
+
# Use `foo {}` not `foo{}`.
|
117
|
+
Layout/SpaceBeforeBlockBraces:
|
118
|
+
Enabled: true
|
119
|
+
|
120
|
+
# Use `foo { bar }` not `foo {bar}`.
|
121
|
+
Layout/SpaceInsideBlockBraces:
|
122
|
+
Enabled: true
|
123
|
+
|
124
|
+
# Use `{ a: 1 }` not `{a:1}`.
|
125
|
+
Layout/SpaceInsideHashLiteralBraces:
|
126
|
+
Enabled: true
|
127
|
+
|
128
|
+
Layout/SpaceInsideParens:
|
129
|
+
Enabled: true
|
130
|
+
|
131
|
+
# Check quotes usage according to lint rule below.
|
132
|
+
Style/StringLiterals:
|
133
|
+
Enabled: true
|
134
|
+
EnforcedStyle: double_quotes
|
135
|
+
|
136
|
+
# Detect hard tabs, no hard tabs.
|
137
|
+
Layout/Tab:
|
138
|
+
Enabled: true
|
139
|
+
|
140
|
+
# Blank lines should not have any spaces.
|
141
|
+
Layout/TrailingBlankLines:
|
142
|
+
Enabled: true
|
143
|
+
|
144
|
+
# No trailing whitespace.
|
145
|
+
Layout/TrailingWhitespace:
|
146
|
+
Enabled: true
|
147
|
+
|
148
|
+
# Use quotes for string literals when they are enough.
|
149
|
+
Style/UnneededPercentQ:
|
150
|
+
Enabled: true
|
151
|
+
|
152
|
+
# Use my_method(my_arg) not my_method( my_arg ) or my_method my_arg.
|
153
|
+
Lint/RequireParentheses:
|
154
|
+
Enabled: true
|
155
|
+
|
156
|
+
Lint/StringConversionInInterpolation:
|
157
|
+
Enabled: true
|
158
|
+
|
159
|
+
Style/RedundantReturn:
|
160
|
+
Enabled: true
|
161
|
+
AllowMultipleReturnValues: true
|
162
|
+
|
163
|
+
Style/Semicolon:
|
164
|
+
Enabled: true
|
165
|
+
AllowAsExpressionSeparator: true
|
166
|
+
|
167
|
+
# Prefer Foo.method over Foo::method
|
168
|
+
Style/ColonMethodCall:
|
169
|
+
Enabled: true
|
data/README.md
CHANGED
@@ -49,9 +49,6 @@ JPEG only:
|
|
49
49
|
@image.process(:lossless_rotate, 180)
|
50
50
|
@image.process(:lossless_rotate, 270)
|
51
51
|
@image.process(:lossless_rotate, -90)
|
52
|
-
|
53
|
-
# Without JPEG optimization
|
54
|
-
@image.process(:lossless_rotate, 90, optimize: false)
|
55
52
|
```
|
56
53
|
|
57
54
|
With fallback for other formats (rotate via ImageMagick):
|
@@ -59,6 +56,19 @@ With fallback for other formats (rotate via ImageMagick):
|
|
59
56
|
@image.process(:safe_lossless_rotate)
|
60
57
|
```
|
61
58
|
|
59
|
+
Other options:
|
60
|
+
```ruby
|
61
|
+
# Without JPEG optimization (default: true)
|
62
|
+
@image.process(:lossless_rotate, 90, optimize: false)
|
63
|
+
# Set default value
|
64
|
+
plugin :lossless_rotate, libjpeg_optimize: false
|
65
|
+
|
66
|
+
# Create progressive JPEG file (default: false)
|
67
|
+
@image.process(:lossless_rotate, 90, progressive: true)
|
68
|
+
# Set default value
|
69
|
+
plugin :lossless_rotate, libjpeg_progressive: true
|
70
|
+
```
|
71
|
+
|
62
72
|
## Benchmark
|
63
73
|
|
64
74
|
- _libjpeg_ version 9b (17-Jan-2016)
|
@@ -1,23 +1,25 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
spec.
|
9
|
-
spec.
|
10
|
-
spec.
|
11
|
-
spec.
|
12
|
-
spec.
|
13
|
-
spec.
|
14
|
-
|
15
|
-
spec.
|
16
|
-
|
17
|
-
spec.requirements << "
|
18
|
-
spec.requirements << "
|
19
|
-
|
20
|
-
spec.
|
21
|
-
|
22
|
-
spec.
|
23
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path("lib", __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require "dragonfly/lossless_rotate/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |spec|
|
8
|
+
spec.name = "dragonfly-lossless_rotate"
|
9
|
+
spec.version = Dragonfly::LosslessRotate::VERSION
|
10
|
+
spec.summary = "Dragonfly lossless image rotate"
|
11
|
+
spec.description = "Lossless rotating and compressing JPEG images via libjpeg tools"
|
12
|
+
spec.author = "Maxim Perepelitsa"
|
13
|
+
spec.email = "n0xff@outlook.com"
|
14
|
+
spec.homepage = "https://github.com/n0xff/dragonfly-lossless_rotate"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.requirements << "cjpeg"
|
18
|
+
spec.requirements << "djpeg"
|
19
|
+
spec.requirements << "jpegtran"
|
20
|
+
spec.requirements << "pnmflip"
|
21
|
+
|
22
|
+
spec.files = `git ls-files`.split($/)
|
23
|
+
|
24
|
+
spec.add_runtime_dependency "dragonfly", "~> 1.0"
|
25
|
+
end
|
@@ -1,93 +1,102 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
app.env[:
|
12
|
-
app.env[:
|
13
|
-
app.env[:
|
14
|
-
|
15
|
-
|
16
|
-
app.
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dragonfly"
|
4
|
+
|
5
|
+
Dragonfly::App.register_plugin(:lossless_rotate) { Dragonfly::LosslessRotate::Plugin.new }
|
6
|
+
|
7
|
+
module Dragonfly
|
8
|
+
class LosslessRotate
|
9
|
+
class Plugin
|
10
|
+
def call(app, opts = {})
|
11
|
+
app.env[:cjpeg_bin] = opts[:cjpeg_bin] || "cjpeg"
|
12
|
+
app.env[:djpeg_bin] = opts[:djpeg_bin] || "djpeg"
|
13
|
+
app.env[:jpegtran_bin] = opts[:jpegtran_bin] || "jpegtran"
|
14
|
+
app.env[:pnmflip_bin] = opts[:pnmflip_bin] || "pnmflip"
|
15
|
+
|
16
|
+
app.env[:libjpeg_optimize] = opts[:libjpeg_optimize] || true
|
17
|
+
app.env[:libjpeg_progressive] = opts[:libjpeg_progressive] || false
|
18
|
+
|
19
|
+
app.add_processor :lossless_rotate, Dragonfly::LosslessRotate::Rotate.new
|
20
|
+
app.add_processor :safe_lossless_rotate, Dragonfly::LosslessRotate::SafeRotate.new
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Only JPEG format
|
25
|
+
class Rotate
|
26
|
+
def call(content, degree = 90, optimize: nil, progressive: nil)
|
27
|
+
unless [90, 180, 270, -90, -180, -270].include?(degree)
|
28
|
+
warn "Rotate only by 90, 180 and 270 degrees allowed"
|
29
|
+
degree = 90
|
30
|
+
end
|
31
|
+
|
32
|
+
optimize ||= content.env[:libjpeg_optimize]
|
33
|
+
progressive ||= content.env[:libjpeg_progressive]
|
34
|
+
|
35
|
+
rotate(content, degree, optimize, progressive)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def rotate(content, degree, optimize, progressive)
|
41
|
+
cjpeg_bin = content.env[:cjpeg_bin] || "cjpeg"
|
42
|
+
djpeg_bin = content.env[:djpeg_bin] || "djpeg"
|
43
|
+
jpegtran_bin = content.env[:jpegtran_bin] || "jpegtran"
|
44
|
+
pnmflip_bin = content.env[:pnmflip_bin] || "pnmflip"
|
45
|
+
|
46
|
+
content.shell_update escape: false do |old_path, new_path|
|
47
|
+
optimize_option = " -optimize" if optimize
|
48
|
+
progressive_option = " -progressive" if progressive
|
49
|
+
|
50
|
+
output_command = if optimize
|
51
|
+
" #{pnmflip_bin} -r#{pnmflip_degrees(degree)} | #{cjpeg_bin}#{progressive_option}#{optimize_option} > '#{new_path}'"
|
52
|
+
else
|
53
|
+
" convert - -rotate #{degree} 'JPG:#{new_path}'"
|
54
|
+
end
|
55
|
+
|
56
|
+
lossless_rotate_command = "#{jpegtran_bin} -rotate #{jpegtran_degrees(degree)} -perfect#{progressive_option}#{optimize_option} '#{old_path}' > '#{new_path}'"
|
57
|
+
lossy_rotate_command = "#{djpeg_bin} '#{old_path}' |#{output_command}"
|
58
|
+
|
59
|
+
"#{lossless_rotate_command} || #{lossy_rotate_command}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def pnmflip_degrees(degree)
|
64
|
+
{
|
65
|
+
90 => 270,
|
66
|
+
180 => 180,
|
67
|
+
270 => 90,
|
68
|
+
-90 => 90,
|
69
|
+
-180 => 180,
|
70
|
+
-270 => 270
|
71
|
+
}[degree]
|
72
|
+
end
|
73
|
+
|
74
|
+
def jpegtran_degrees(degree)
|
75
|
+
{
|
76
|
+
90 => 90,
|
77
|
+
180 => 180,
|
78
|
+
270 => 180,
|
79
|
+
-90 => 270,
|
80
|
+
-180 => 180,
|
81
|
+
-270 => 90
|
82
|
+
}[degree]
|
83
|
+
end
|
84
|
+
|
85
|
+
def jpeg?(content)
|
86
|
+
content.shell_eval { |path| "identify -format \%m #{path}" } == "JPEG"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# All formats support by ImageMagick
|
91
|
+
class SafeRotate < Rotate
|
92
|
+
def rotate(content, degree, optimize, progressive)
|
93
|
+
return super if jpeg?(content)
|
94
|
+
|
95
|
+
content.shell_update do |old_path, new_path|
|
96
|
+
"convert #{old_path} -rotate #{degree} #{new_path}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
private :rotate
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dragonfly-lossless_rotate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maxim Perepelitsa
|
@@ -30,6 +30,7 @@ executables: []
|
|
30
30
|
extensions: []
|
31
31
|
extra_rdoc_files: []
|
32
32
|
files:
|
33
|
+
- ".rubocop.yml"
|
33
34
|
- README.md
|
34
35
|
- dragonfly-lossless_rotate.gemspec
|
35
36
|
- lib/dragonfly/lossless_rotate.rb
|