kt-paperclip-vips 1.2.2 → 1.2.4
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/.rubocop.yml +135 -0
- data/Gemfile +6 -2
- data/README.md +2 -2
- data/Rakefile +1 -1
- data/bin/console +4 -3
- data/lib/paperclip-vips/paperclip/vips.rb +95 -66
- data/lib/paperclip-vips/version.rb +1 -1
- data/lib/paperclip-vips.rb +1 -1
- data/paperclip-vips.gemspec +8 -6
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5abb1916de9e20f18378d68ad36d1ad68444d9f4810b2af60a503f061e62173b
|
4
|
+
data.tar.gz: '038faff11b80ab241544d7afd510c3fbff8907be56e5abe6c485872fc735a3ab'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4f189349bf4e1dc8b3a56799ec1a1639c9e47810200aa9818eadb2fc5f8d2c6f1a0df69d27abcb3d129a6a76a70fedf1e91d79f3522fcb120c9ab5261dad63e9
|
7
|
+
data.tar.gz: '082eda71b1c8cbcb2b3f99713036e0944cea35c3c88f45fe87e72c6c6b6a4f5063a946ce7fac2edc0cda87b501444c26d518d800c0f180026850189b7b2fe2a1'
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
plugins:
|
2
|
+
- rubocop-performance
|
3
|
+
- rubocop-rake
|
4
|
+
- rubocop-rspec
|
5
|
+
|
6
|
+
AllCops:
|
7
|
+
TargetRubyVersion: 3.2
|
8
|
+
DisabledByDefault: false
|
9
|
+
DisplayCopNames: true
|
10
|
+
NewCops: enable
|
11
|
+
Exclude:
|
12
|
+
- "**/bin/*"
|
13
|
+
- "**/config.ru"
|
14
|
+
- "**/db/**/*"
|
15
|
+
- "**/schema.rb"
|
16
|
+
- "**/node_modules/**/*"
|
17
|
+
- "**/vendor/**/*"
|
18
|
+
- "**/vendor/**/*.txt"
|
19
|
+
- "**/test/**/*"
|
20
|
+
- "**/tmp/**/*"
|
21
|
+
|
22
|
+
# Dim down rubocop's annoyance level below:
|
23
|
+
|
24
|
+
Layout/ArgumentAlignment:
|
25
|
+
Enabled: true
|
26
|
+
EnforcedStyle: with_fixed_indentation
|
27
|
+
|
28
|
+
Layout/CaseIndentation:
|
29
|
+
Enabled: true
|
30
|
+
EnforcedStyle: end
|
31
|
+
|
32
|
+
Layout/EndAlignment:
|
33
|
+
Enabled: true
|
34
|
+
EnforcedStyleAlignWith: variable
|
35
|
+
AutoCorrect: true
|
36
|
+
|
37
|
+
Layout/LineLength:
|
38
|
+
Exclude:
|
39
|
+
- app/views/**/*.rb
|
40
|
+
- app/components/**/*.rb
|
41
|
+
- lib/generators/**/*.rb
|
42
|
+
Max: 125
|
43
|
+
|
44
|
+
Layout/SpaceInsideBlockBraces:
|
45
|
+
Enabled: true
|
46
|
+
EnforcedStyleForEmptyBraces: space
|
47
|
+
|
48
|
+
Layout/SpaceInsideArrayLiteralBrackets:
|
49
|
+
EnforcedStyle: no_space
|
50
|
+
|
51
|
+
Layout/SpaceInsideHashLiteralBraces:
|
52
|
+
Enabled: true
|
53
|
+
EnforcedStyle: space
|
54
|
+
EnforcedStyleForEmptyBraces: no_space
|
55
|
+
|
56
|
+
Lint/MissingSuper:
|
57
|
+
Enabled: false
|
58
|
+
|
59
|
+
Metrics:
|
60
|
+
Enabled: false
|
61
|
+
|
62
|
+
Naming/FileName:
|
63
|
+
Exclude:
|
64
|
+
- lib/paperclip-vips.rb
|
65
|
+
|
66
|
+
RSpec/ExampleLength:
|
67
|
+
Enabled: false
|
68
|
+
|
69
|
+
RSpec/IndexedLet:
|
70
|
+
Enabled: false
|
71
|
+
|
72
|
+
RSpec/AlignLeftLetBrace:
|
73
|
+
Enabled: true
|
74
|
+
|
75
|
+
RSpec/MultipleExpectations:
|
76
|
+
Enabled: false
|
77
|
+
|
78
|
+
RSpec/MultipleMemoizedHelpers:
|
79
|
+
Max: 10
|
80
|
+
|
81
|
+
RSpec/NestedGroups:
|
82
|
+
Max: 4
|
83
|
+
|
84
|
+
RSpec/PendingWithoutReason:
|
85
|
+
Enabled: false
|
86
|
+
|
87
|
+
RSpec/PredicateMatcher:
|
88
|
+
Enabled: false
|
89
|
+
|
90
|
+
RSpec/SpecFilePathFormat:
|
91
|
+
Enabled: false
|
92
|
+
|
93
|
+
Style/ClassAndModuleChildren:
|
94
|
+
EnforcedStyle: nested
|
95
|
+
Exclude:
|
96
|
+
- app/events/**/*.rb
|
97
|
+
# - app/views/**/*.rb
|
98
|
+
|
99
|
+
Style/CommentedKeyword:
|
100
|
+
Enabled: false
|
101
|
+
|
102
|
+
Style/Documentation:
|
103
|
+
Enabled: false
|
104
|
+
|
105
|
+
Style/EmptyMethod:
|
106
|
+
Enabled: false
|
107
|
+
|
108
|
+
Style/FormatStringToken:
|
109
|
+
EnforcedStyle: template
|
110
|
+
|
111
|
+
Style/FrozenStringLiteralComment:
|
112
|
+
Enabled: true
|
113
|
+
EnforcedStyle: never
|
114
|
+
|
115
|
+
Style/HashSyntax:
|
116
|
+
Enabled: true
|
117
|
+
EnforcedShorthandSyntax: always
|
118
|
+
|
119
|
+
Style/PercentLiteralDelimiters:
|
120
|
+
PreferredDelimiters:
|
121
|
+
default: "()"
|
122
|
+
"%i": "[]"
|
123
|
+
"%I": "[]"
|
124
|
+
"%r": "{}"
|
125
|
+
"%w": "[]"
|
126
|
+
"%W": "[]"
|
127
|
+
|
128
|
+
Style/StringLiterals:
|
129
|
+
EnforcedStyle: double_quotes
|
130
|
+
|
131
|
+
Style/TrailingCommaInHashLiteral:
|
132
|
+
EnforcedStyleForMultiline: comma
|
133
|
+
|
134
|
+
Style/TrailingCommaInArrayLiteral:
|
135
|
+
EnforcedStyleForMultiline: comma
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -7,7 +7,7 @@ A custom paperclip processor that uses the [libvips image processing library](ht
|
|
7
7
|
Add this line to your application's Gemfile:
|
8
8
|
|
9
9
|
```ruby
|
10
|
-
gem 'paperclip-vips'
|
10
|
+
gem 'kt-paperclip-vips'
|
11
11
|
```
|
12
12
|
|
13
13
|
And then execute:
|
@@ -16,7 +16,7 @@ And then execute:
|
|
16
16
|
|
17
17
|
Or install it yourself as:
|
18
18
|
|
19
|
-
$ gem install paperclip-vips
|
19
|
+
$ gem install kt-paperclip-vips
|
20
20
|
|
21
21
|
Note: You will need to have libvips installed on your machine for this processor to work correctly.
|
22
22
|
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'paperclip/vips'
|
5
6
|
|
6
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
8
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +11,5 @@ require "paperclip/vips"
|
|
10
11
|
# require "pry"
|
11
12
|
# Pry.start
|
12
13
|
|
13
|
-
require
|
14
|
+
require 'irb'
|
14
15
|
IRB.start(__FILE__)
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module Paperclip
|
2
2
|
class Vips < Processor
|
3
3
|
attr_accessor :auto_orient, :convert_options, :current_geometry, :format, :source_file_options,
|
4
|
-
|
4
|
+
:target_geometry, :whiny
|
5
5
|
|
6
6
|
def initialize(file, options = {}, attachment = nil)
|
7
7
|
super
|
8
8
|
|
9
9
|
geometry = options[:geometry].to_s
|
10
|
-
@should_crop = geometry[-1,1] ==
|
10
|
+
@should_crop = geometry[-1, 1] == "#"
|
11
11
|
@target_geometry = options.fetch(:string_geometry_parser, Geometry).parse(geometry)
|
12
12
|
@current_geometry = options.fetch(:file_geometry_parser, Geometry).from_file(@file)
|
13
13
|
@whiny = options.fetch(:whiny, true)
|
@@ -27,96 +27,125 @@ module Paperclip
|
|
27
27
|
destination = TempfileFactory.new.generate(filename)
|
28
28
|
|
29
29
|
begin
|
30
|
-
|
31
|
-
|
30
|
+
if @target_geometry.present?
|
31
|
+
target_width = @target_geometry.width
|
32
|
+
target_height = @target_geometry.height
|
33
|
+
modifier = @target_geometry.modifier # e.g., ">", "#", etc.
|
34
|
+
|
35
|
+
# Use thumbnail for efficient resizing and cropping
|
36
|
+
crop = (modifier == "#")
|
37
|
+
thumbnail = ::Vips::Image.thumbnail(
|
38
|
+
source.path,
|
39
|
+
target_width,
|
40
|
+
height: crop ? target_height : nil,
|
41
|
+
size: (modifier == ">" ? :down : :both), # ">" means shrink only
|
42
|
+
crop: crop ? :centre : :none
|
43
|
+
)
|
44
|
+
|
45
|
+
# Additional cropping for exact "#" dimensions if needed
|
46
|
+
if crop && (thumbnail.width != target_width || thumbnail.height != target_height)
|
47
|
+
left = [0, (thumbnail.width - target_width) / 2].max
|
48
|
+
top = [0, (thumbnail.height - target_height) / 2].max
|
49
|
+
crop_width = [target_width, thumbnail.width - left].min
|
50
|
+
crop_height = [target_height, thumbnail.height - top].min
|
51
|
+
if crop_width.positive? && crop_height.positive?
|
52
|
+
thumbnail = thumbnail.crop(left, top, crop_width,
|
53
|
+
crop_height)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Fallback to original image if no geometry
|
59
|
+
thumbnail = ::Vips::Image.new_from_file(source.path) unless defined?(thumbnail) && thumbnail
|
60
|
+
|
61
|
+
# Apply convert options
|
32
62
|
thumbnail = process_convert_options(thumbnail)
|
33
|
-
save_thumbnail(thumbnail, destination.path)
|
34
|
-
|
35
|
-
rescue => e
|
36
|
-
p e.message, e.backtrace
|
37
63
|
|
64
|
+
# Save the processed image
|
65
|
+
save_thumbnail(thumbnail, destination.path)
|
66
|
+
rescue StandardError => e
|
67
|
+
puts e.message, e.backtrace
|
38
68
|
if @whiny
|
39
|
-
message = "There was an error processing the thumbnail for #{@basename}:\n
|
69
|
+
message = "There was an error processing the thumbnail for #{@basename}:\n#{e.message}"
|
40
70
|
raise Paperclip::Error, message
|
41
71
|
end
|
42
72
|
end
|
43
73
|
|
44
|
-
|
74
|
+
destination # Return the Tempfile object
|
45
75
|
end
|
46
76
|
|
47
77
|
private
|
48
|
-
def crop
|
49
|
-
if @should_crop
|
50
|
-
return @options[:crop] || :centre
|
51
|
-
end
|
52
78
|
|
53
|
-
|
54
|
-
|
79
|
+
def crop
|
80
|
+
return @options[:crop] || :centre if @should_crop
|
55
81
|
|
56
|
-
|
57
|
-
|
58
|
-
return extension if extension.present?
|
82
|
+
nil
|
83
|
+
end
|
59
84
|
|
60
|
-
|
61
|
-
|
85
|
+
def current_format(file)
|
86
|
+
extension = File.extname(file.path)
|
87
|
+
return extension if extension.present?
|
62
88
|
|
63
|
-
|
64
|
-
|
89
|
+
extension = File.extname(file.original_filename)
|
90
|
+
return extension if extension.present?
|
65
91
|
|
66
|
-
|
67
|
-
|
68
|
-
end
|
69
|
-
|
70
|
-
def height
|
71
|
-
@target_geometry&.height || @current_geometry.height
|
72
|
-
end
|
73
|
-
|
74
|
-
def process_convert_options(image)
|
75
|
-
if image
|
76
|
-
commands = parsed_convert_commands(@convert_options)
|
77
|
-
commands.each do |cmd|
|
78
|
-
image = ::Vips::Operation.call(cmd[:cmd], [image, *cmd[:args]], cmd[:optional] || {})
|
79
|
-
end
|
80
|
-
end
|
92
|
+
""
|
93
|
+
end
|
81
94
|
|
82
|
-
|
83
|
-
|
95
|
+
def width
|
96
|
+
@target_geometry&.width || @current_geometry.width
|
97
|
+
end
|
84
98
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
rescue
|
89
|
-
commands = []
|
90
|
-
end
|
99
|
+
def height
|
100
|
+
@target_geometry&.height || @current_geometry.height
|
101
|
+
end
|
91
102
|
|
92
|
-
|
93
|
-
|
94
|
-
|
103
|
+
def process_convert_options(image)
|
104
|
+
if image && @convert_options.present?
|
105
|
+
# Handle string-based convert_options (e.g., "-quality 80 -strip")
|
106
|
+
quality = @convert_options[/quality (\d+)/, 1]&.to_i
|
107
|
+
strip = @convert_options.include?("-strip")
|
95
108
|
|
96
|
-
|
97
|
-
|
109
|
+
# Apply options via instance variables for save_thumbnail
|
110
|
+
@processed_quality = quality if quality
|
111
|
+
@processed_strip = strip if strip
|
98
112
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
when ".gif"
|
104
|
-
save_gif(thumbnail, path)
|
105
|
-
when ".png"
|
106
|
-
save_png(thumbnail, path)
|
113
|
+
# Handle JSON-based commands if present
|
114
|
+
commands = parsed_convert_commands(@convert_options)
|
115
|
+
commands.each do |cmd|
|
116
|
+
image = ::Vips::Operation.call(cmd[:cmd], [image, *cmd[:args]], cmd[:optional] || {})
|
107
117
|
end
|
108
118
|
end
|
119
|
+
image
|
120
|
+
end
|
109
121
|
|
110
|
-
|
111
|
-
|
122
|
+
def parsed_convert_commands(convert_options)
|
123
|
+
begin
|
124
|
+
commands = JSON.parse(convert_options, symbolize_names: true)
|
125
|
+
rescue StandardError
|
126
|
+
commands = []
|
112
127
|
end
|
113
128
|
|
114
|
-
|
115
|
-
thumbnail.magicksave(path)
|
116
|
-
end
|
129
|
+
commands.unshift({ cmd: "autorot" }) if @auto_orient && commands.none? { |cmd| cmd[:cmd] == "autorot" }
|
117
130
|
|
118
|
-
|
119
|
-
|
131
|
+
commands
|
132
|
+
end
|
133
|
+
|
134
|
+
def save_thumbnail(thumbnail, path)
|
135
|
+
quality = @processed_quality || 75
|
136
|
+
strip = @processed_strip || false
|
137
|
+
|
138
|
+
case @format.to_s.downcase
|
139
|
+
when ".jpeg", ".jpg", "jpeg", "jpg", "webp"
|
140
|
+
thumbnail.jpegsave(path, Q: quality, strip:)
|
141
|
+
when ".gif", "gif"
|
142
|
+
thumbnail.magicksave(path, strip:) # No quality for GIF
|
143
|
+
when ".png", "png"
|
144
|
+
compression = 9 - (quality / 11.1).floor # Map 0-100 to 9-0
|
145
|
+
thumbnail.pngsave(path, compression:, strip:)
|
146
|
+
else
|
147
|
+
thumbnail.write_to_file(path) # Fallback
|
120
148
|
end
|
149
|
+
end
|
121
150
|
end
|
122
151
|
end
|
data/lib/paperclip-vips.rb
CHANGED
data/paperclip-vips.gemspec
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path("../lib", __FILE__)
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require "paperclip-vips/version"
|
5
4
|
|
@@ -9,19 +8,22 @@ Gem::Specification.new do |spec|
|
|
9
8
|
spec.authors = ["Ken Greeff", "Mikael Henriksson"]
|
10
9
|
spec.email = ["ken@kengreeff.com", "mikael@mhenrixon.com"]
|
11
10
|
|
12
|
-
spec.summary =
|
11
|
+
spec.summary = "Uses Ruby Vips to when creating thumbnails for faster generation."
|
13
12
|
spec.homepage = "https://github.com/realhub/paperclip-vips"
|
14
13
|
spec.license = "MIT"
|
15
14
|
|
15
|
+
spec.required_ruby_version = ">= 3.2.0"
|
16
|
+
|
16
17
|
# Specify which files should be added to the gem when it is released.
|
17
18
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
18
|
-
spec.files
|
19
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
19
20
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|Gemfile|mise.toml/bin)/}) }
|
20
21
|
end
|
21
22
|
spec.bindir = "exe"
|
22
23
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
24
|
spec.require_paths = ["lib"]
|
24
25
|
|
25
|
-
spec.
|
26
|
-
spec.
|
26
|
+
spec.add_dependency "kt-paperclip", ">= 5.0.0"
|
27
|
+
spec.add_dependency "ruby-vips", ">= 2.1"
|
28
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
27
29
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kt-paperclip-vips
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ken Greeff
|
8
8
|
- Mikael Henriksson
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-02-
|
11
|
+
date: 2025-02-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: kt-paperclip
|
@@ -47,6 +47,7 @@ extra_rdoc_files: []
|
|
47
47
|
files:
|
48
48
|
- ".gitignore"
|
49
49
|
- ".rspec"
|
50
|
+
- ".rubocop.yml"
|
50
51
|
- ".travis.yml"
|
51
52
|
- CHANGELOG.md
|
52
53
|
- CODE_OF_CONDUCT.md
|
@@ -63,7 +64,8 @@ files:
|
|
63
64
|
homepage: https://github.com/realhub/paperclip-vips
|
64
65
|
licenses:
|
65
66
|
- MIT
|
66
|
-
metadata:
|
67
|
+
metadata:
|
68
|
+
rubygems_mfa_required: 'true'
|
67
69
|
rdoc_options: []
|
68
70
|
require_paths:
|
69
71
|
- lib
|
@@ -71,7 +73,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
71
73
|
requirements:
|
72
74
|
- - ">="
|
73
75
|
- !ruby/object:Gem::Version
|
74
|
-
version:
|
76
|
+
version: 3.2.0
|
75
77
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
78
|
requirements:
|
77
79
|
- - ">="
|