jekyll-imgflow 0.3.3 → 0.4.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/README.md +22 -0
- data/lib/jekyll-imgflow/parser.rb +32 -25
- data/lib/jekyll-imgflow/picture_tag_preset_migrator.rb +73 -69
- data/lib/jekyll-imgflow/preset_manager.rb +42 -38
- data/lib/jekyll-imgflow/providers/base_provider.rb +198 -0
- data/lib/jekyll-imgflow/providers/flyimg.rb +71 -141
- data/lib/jekyll-imgflow/providers/imagemagick.rb +57 -97
- data/lib/jekyll-imgflow/providers/imgproxy.rb +53 -126
- data/lib/jekyll-imgflow/providers/libvips.rb +53 -81
- data/lib/jekyll-imgflow/providers/sharp.rb +57 -100
- data/lib/jekyll-imgflow/providers/weserv.rb +69 -124
- data/lib/jekyll-imgflow/tag_scanner.rb +19 -24
- data/lib/jekyll-imgflow/tags/base_tag.rb +33 -0
- data/lib/jekyll-imgflow/tags/crop_tag.rb +24 -47
- data/lib/jekyll-imgflow/tags/opacity_tag.rb +1 -20
- data/lib/jekyll-imgflow/tags/optimize_tag.rb +0 -9
- data/lib/jekyll-imgflow/tags/quality_tag.rb +0 -11
- data/lib/jekyll-imgflow/tags/resize_tag.rb +18 -11
- data/lib/jekyll-imgflow/tags/watermark_tag.rb +0 -17
- data/lib/jekyll-imgflow/version.rb +1 -1
- metadata +1 -1
|
@@ -7,24 +7,17 @@ require_relative "base_provider"
|
|
|
7
7
|
module JekyllImgFlow
|
|
8
8
|
module Providers
|
|
9
9
|
# Libvips provider implementation using the standardized tag interface
|
|
10
|
-
class Libvips <
|
|
10
|
+
class Libvips < CliBase
|
|
11
11
|
def available?
|
|
12
|
-
|
|
13
|
-
_, _, status = Open3.capture3("which", "vips")
|
|
14
|
-
status.success?
|
|
12
|
+
cli_available?("vips")
|
|
15
13
|
end
|
|
16
14
|
|
|
17
|
-
def
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
# Build and execute vips commands (array form, no shell).
|
|
21
|
-
# Cleanup markers ([:cleanup, path]) are handled by execute_command.
|
|
22
|
-
commands = build_vips_commands(input_path, output_path)
|
|
23
|
-
commands.each { |cmd_array| execute_command(cmd_array) }
|
|
15
|
+
def build_commands(input_path, output_path)
|
|
16
|
+
build_vips_commands(input_path, output_path)
|
|
17
|
+
end
|
|
24
18
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
reset_operations
|
|
19
|
+
def run_command(cmd)
|
|
20
|
+
execute_command(cmd)
|
|
28
21
|
end
|
|
29
22
|
|
|
30
23
|
# Build all vips commands as an array of command arrays.
|
|
@@ -48,10 +41,10 @@ module JekyllImgFlow
|
|
|
48
41
|
|
|
49
42
|
def operation_flags
|
|
50
43
|
{
|
|
51
|
-
crop:
|
|
52
|
-
resize:
|
|
53
|
-
watermark:
|
|
54
|
-
alpha:
|
|
44
|
+
crop: op?(:crop),
|
|
45
|
+
resize: op?(:resize),
|
|
46
|
+
watermark: op?(:watermark),
|
|
47
|
+
alpha: op?(:alpha_opacity)
|
|
55
48
|
}
|
|
56
49
|
end
|
|
57
50
|
|
|
@@ -72,19 +65,19 @@ module JekyllImgFlow
|
|
|
72
65
|
end
|
|
73
66
|
|
|
74
67
|
def build_alpha_pipeline(input_path, output_path, has_crop, has_resize)
|
|
75
|
-
|
|
76
|
-
commands, base_path = build_base_pipeline(input_path,
|
|
68
|
+
temp = temp_path(input_path, "tmp_base.jpg")
|
|
69
|
+
commands, base_path = build_base_pipeline(input_path, temp, has_crop, has_resize)
|
|
77
70
|
commands << build_alpha_command(base_path, output_path)
|
|
78
|
-
commands << [:cleanup,
|
|
71
|
+
commands << [:cleanup, temp] unless base_path == input_path
|
|
79
72
|
commands
|
|
80
73
|
end
|
|
81
74
|
|
|
82
75
|
def build_watermark_pipeline(input_path, output_path, has_crop, has_resize)
|
|
83
|
-
|
|
84
|
-
commands, base_path = build_base_pipeline(input_path,
|
|
76
|
+
temp = temp_path(input_path, "tmp_base.jpg")
|
|
77
|
+
commands, base_path = build_base_pipeline(input_path, temp, has_crop, has_resize)
|
|
85
78
|
base_path, commands = apply_watermark_alpha(base_path, input_path, commands)
|
|
86
79
|
commands.concat(build_composite_commands(base_path, output_path))
|
|
87
|
-
cleanup_paths(commands, input_path,
|
|
80
|
+
cleanup_paths(commands, input_path, temp, base_path)
|
|
88
81
|
commands
|
|
89
82
|
end
|
|
90
83
|
|
|
@@ -97,10 +90,10 @@ module JekyllImgFlow
|
|
|
97
90
|
end
|
|
98
91
|
|
|
99
92
|
def apply_watermark_alpha(base_path, input_path, commands)
|
|
100
|
-
alpha_op =
|
|
93
|
+
alpha_op = find_op(:alpha_opacity)
|
|
101
94
|
return [base_path, commands] unless alpha_op
|
|
102
95
|
|
|
103
|
-
alpha_temp = input_path
|
|
96
|
+
alpha_temp = temp_path(input_path, "tmp_alpha.jpg")
|
|
104
97
|
[alpha_temp, commands << build_alpha_command(base_path, alpha_temp)]
|
|
105
98
|
end
|
|
106
99
|
|
|
@@ -110,26 +103,24 @@ module JekyllImgFlow
|
|
|
110
103
|
end
|
|
111
104
|
|
|
112
105
|
def build_composite_commands(base_path, output_path)
|
|
113
|
-
wm_op =
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
wm_temp = watermark_path.gsub(/\.[^.]+$/, ".tmp_wm.png")
|
|
120
|
-
alpha_value = opacity
|
|
106
|
+
wm_op = find_op(:watermark)
|
|
107
|
+
parts = watermark_parts(wm_op)
|
|
108
|
+
|
|
109
|
+
if parts[:opacity] && parts[:opacity] < 1.0
|
|
110
|
+
wm_temp = temp_path(parts[:watermark_path], "tmp_wm.png")
|
|
111
|
+
alpha_value = parts[:opacity]
|
|
121
112
|
# Step 1: Apply alpha to watermark
|
|
122
|
-
alpha_cmd = ["vips", "linear", watermark_path,
|
|
113
|
+
alpha_cmd = ["vips", "linear", parts[:watermark_path],
|
|
123
114
|
"#{wm_temp}[alpha]", "1 1 1 #{alpha_value}", "0"]
|
|
124
115
|
# Step 2: Composite
|
|
125
|
-
xy_args = position_to_vips_xy(position, base_path, watermark_path)
|
|
116
|
+
xy_args = position_to_vips_xy(parts[:position], base_path, parts[:watermark_path])
|
|
126
117
|
composite_cmd = ["vips", "composite2", base_path, wm_temp,
|
|
127
118
|
build_format_spec(output_path), "over"] + xy_args
|
|
128
119
|
# Step 3: Cleanup wm_temp
|
|
129
120
|
[alpha_cmd, composite_cmd, [:cleanup, wm_temp]]
|
|
130
121
|
else
|
|
131
|
-
xy_args = position_to_vips_xy(position, base_path, watermark_path)
|
|
132
|
-
[["vips", "composite2", base_path, watermark_path,
|
|
122
|
+
xy_args = position_to_vips_xy(parts[:position], base_path, parts[:watermark_path])
|
|
123
|
+
[["vips", "composite2", base_path, parts[:watermark_path],
|
|
133
124
|
build_format_spec(output_path), "over"] + xy_args]
|
|
134
125
|
end
|
|
135
126
|
end
|
|
@@ -168,9 +159,8 @@ module JekyllImgFlow
|
|
|
168
159
|
end
|
|
169
160
|
|
|
170
161
|
def build_alpha_command(input_path, output_path)
|
|
171
|
-
alpha_op =
|
|
172
|
-
|
|
173
|
-
alpha_value = opacity.round(2)
|
|
162
|
+
alpha_op = find_op(:alpha_opacity)
|
|
163
|
+
alpha_value = alpha_op[:opacity].round(2)
|
|
174
164
|
|
|
175
165
|
# vips linear multiplies alpha band: "1 1 1 alpha" scales alpha
|
|
176
166
|
["vips", "linear", input_path, output_path,
|
|
@@ -180,22 +170,22 @@ module JekyllImgFlow
|
|
|
180
170
|
def build_sequential_crop_resize(input_path, output_path)
|
|
181
171
|
if svg?(input_path)
|
|
182
172
|
# SVG: use thumbnail with --crop to do crop+resize in one step
|
|
183
|
-
resize_op =
|
|
173
|
+
resize_op = find_op(:resize)
|
|
184
174
|
format_spec = build_format_spec(output_path)
|
|
185
175
|
width = resize_op[:width]
|
|
186
176
|
height = resize_op[:height]
|
|
187
177
|
[["vips", "thumbnail", input_path, format_spec,
|
|
188
178
|
width.to_s, "--height=#{height}", "--crop=attention"]]
|
|
189
179
|
else
|
|
190
|
-
|
|
191
|
-
[build_crop_command(input_path,
|
|
192
|
-
build_resize_command(
|
|
193
|
-
[:cleanup,
|
|
180
|
+
temp = temp_path(input_path, "tmp_crop.jpg")
|
|
181
|
+
[build_crop_command(input_path, temp),
|
|
182
|
+
build_resize_command(temp, output_path),
|
|
183
|
+
[:cleanup, temp]]
|
|
194
184
|
end
|
|
195
185
|
end
|
|
196
186
|
|
|
197
187
|
def build_resize_command(input_path, output_path)
|
|
198
|
-
resize_op =
|
|
188
|
+
resize_op = find_op(:resize)
|
|
199
189
|
format_spec = build_format_spec(output_path)
|
|
200
190
|
width = resize_op[:width]
|
|
201
191
|
height = resize_op[:height]
|
|
@@ -216,35 +206,23 @@ module JekyllImgFlow
|
|
|
216
206
|
end
|
|
217
207
|
|
|
218
208
|
def build_crop_command(input_path, output_path)
|
|
219
|
-
crop_op =
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
keep = opts[:keep] || params[:keep] || params[:position]
|
|
223
|
-
return build_smartcrop_command(crop_op, opts, keep, input_path, output_path) if smartcrop?(crop_op, keep)
|
|
209
|
+
crop_op = find_op(:crop)
|
|
210
|
+
geo = crop_geometry(crop_op)
|
|
211
|
+
return build_smartcrop_command(geo, input_path, output_path) if geo[:smartcrop]
|
|
224
212
|
|
|
225
|
-
build_extract_command(
|
|
213
|
+
build_extract_command(geo, input_path, output_path)
|
|
226
214
|
end
|
|
227
215
|
|
|
228
|
-
def
|
|
229
|
-
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
def build_smartcrop_command(_crop_op, opts, keep, input_path, output_path)
|
|
233
|
-
interestingness = %w[center centre].include?(keep.to_s) ? "centre" : keep.to_s
|
|
234
|
-
interestingness = "attention" unless %w[entropy centre].include?(interestingness)
|
|
216
|
+
def build_smartcrop_command(geo, input_path, output_path)
|
|
217
|
+
interestingness = smartcrop_interestingness(geo[:keep])
|
|
235
218
|
["vips", "smartcrop", input_path, output_path,
|
|
236
|
-
|
|
219
|
+
geo[:width].to_s, geo[:height].to_s,
|
|
237
220
|
"--interesting=#{interestingness}"]
|
|
238
221
|
end
|
|
239
222
|
|
|
240
|
-
def build_extract_command(
|
|
241
|
-
ratio = crop_op[:ratio]
|
|
242
|
-
x = ratio ? opts[:calculated_x] : (opts[:x] || 0)
|
|
243
|
-
y = ratio ? opts[:calculated_y] : (opts[:y] || 0)
|
|
244
|
-
width = ratio ? opts[:calculated_width] : opts[:width]
|
|
245
|
-
height = ratio ? opts[:calculated_height] : opts[:height]
|
|
223
|
+
def build_extract_command(geo, input_path, output_path)
|
|
246
224
|
["vips", "extract_area", input_path, output_path,
|
|
247
|
-
x.to_s, y.to_s, width.to_s, height.to_s]
|
|
225
|
+
geo[:x].to_s, geo[:y].to_s, geo[:width].to_s, geo[:height].to_s]
|
|
248
226
|
end
|
|
249
227
|
|
|
250
228
|
# SVG crop: use `vips thumbnail` with --crop to crop during thumbnailing.
|
|
@@ -254,18 +232,12 @@ module JekyllImgFlow
|
|
|
254
232
|
# the most interesting region — this is the correct behavior for SVGs
|
|
255
233
|
# since they have no fixed pixel dimensions.
|
|
256
234
|
def build_svg_crop_pipeline(input_path, output_path)
|
|
257
|
-
crop_op =
|
|
258
|
-
|
|
259
|
-
crop_width =
|
|
260
|
-
crop_height =
|
|
235
|
+
crop_op = find_op(:crop)
|
|
236
|
+
geo = crop_geometry(crop_op)
|
|
237
|
+
crop_width = geo[:width] || 1000
|
|
238
|
+
crop_height = geo[:height] || 1000
|
|
261
239
|
format_spec = build_format_spec(output_path)
|
|
262
|
-
|
|
263
|
-
keep = opts[:keep] || crop_op[:params]&.[](:keep) || crop_op[:params]&.[](:position)
|
|
264
|
-
interestingness = case keep.to_s
|
|
265
|
-
when "entropy" then "entropy"
|
|
266
|
-
when "center", "centre" then "centre"
|
|
267
|
-
else "attention"
|
|
268
|
-
end
|
|
240
|
+
interestingness = smartcrop_interestingness(geo[:keep])
|
|
269
241
|
|
|
270
242
|
[["vips", "thumbnail", input_path, format_spec,
|
|
271
243
|
crop_width.to_s, "--height=#{crop_height}",
|
|
@@ -287,8 +259,8 @@ module JekyllImgFlow
|
|
|
287
259
|
# are executed via Open3.capture3 array form (no shell).
|
|
288
260
|
def build_format_spec(output_path)
|
|
289
261
|
# Build vips output specification with format and quality
|
|
290
|
-
format_op =
|
|
291
|
-
quality_op =
|
|
262
|
+
format_op = find_op(:format)
|
|
263
|
+
quality_op = find_op(:quality)
|
|
292
264
|
|
|
293
265
|
# Simple case: no format/quality operations
|
|
294
266
|
return output_path unless format_op || quality_op
|
|
@@ -7,37 +7,23 @@ require_relative "base_provider"
|
|
|
7
7
|
module JekyllImgFlow
|
|
8
8
|
module Providers
|
|
9
9
|
# Sharp provider implementation using the standardized tag interface
|
|
10
|
-
class Sharp <
|
|
10
|
+
class Sharp < CliBase
|
|
11
11
|
def available?
|
|
12
|
-
|
|
13
|
-
_, _, status = Open3.capture3("which", "sharp")
|
|
14
|
-
status.success?
|
|
12
|
+
cli_available?("sharp")
|
|
15
13
|
end
|
|
16
14
|
|
|
17
|
-
def
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
# Build Sharp command
|
|
21
|
-
command = build_sharp_command(input_path, output_path)
|
|
22
|
-
execute_command(command)
|
|
23
|
-
|
|
24
|
-
output_path
|
|
25
|
-
ensure
|
|
26
|
-
reset_operations
|
|
15
|
+
def build_commands(input_path, output_path)
|
|
16
|
+
build_sharp_command(input_path, output_path)
|
|
27
17
|
end
|
|
28
18
|
|
|
29
19
|
def build_sharp_command(input_path, output_path)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if has_watermark
|
|
35
|
-
build_watermark_pipeline(input_path, output_path, has_crop, has_resize)
|
|
36
|
-
elsif has_crop && has_resize
|
|
20
|
+
if op?(:watermark)
|
|
21
|
+
build_watermark_pipeline(input_path, output_path, op?(:crop), op?(:resize))
|
|
22
|
+
elsif op?(:crop) && op?(:resize)
|
|
37
23
|
build_sequential_crop_resize(input_path, output_path)
|
|
38
|
-
elsif
|
|
24
|
+
elsif op?(:resize)
|
|
39
25
|
build_resize_command(input_path, output_path)
|
|
40
|
-
elsif
|
|
26
|
+
elsif op?(:crop)
|
|
41
27
|
build_crop_command(input_path, output_path)
|
|
42
28
|
else
|
|
43
29
|
build_copy_command(input_path, output_path)
|
|
@@ -45,47 +31,41 @@ module JekyllImgFlow
|
|
|
45
31
|
end
|
|
46
32
|
|
|
47
33
|
def build_watermark_pipeline(input_path, output_path, has_crop, has_resize)
|
|
48
|
-
|
|
34
|
+
temp = temp_path(input_path, "tmp_base.jpg")
|
|
49
35
|
commands = []
|
|
50
36
|
|
|
51
37
|
if has_crop && has_resize
|
|
52
|
-
commands << build_sequential_crop_resize(input_path,
|
|
53
|
-
base_path =
|
|
38
|
+
commands << build_sequential_crop_resize(input_path, temp)
|
|
39
|
+
base_path = temp
|
|
54
40
|
elsif has_resize
|
|
55
|
-
commands << build_resize_command(input_path,
|
|
56
|
-
base_path =
|
|
41
|
+
commands << build_resize_command(input_path, temp)
|
|
42
|
+
base_path = temp
|
|
57
43
|
elsif has_crop
|
|
58
|
-
commands << build_crop_command(input_path,
|
|
59
|
-
base_path =
|
|
44
|
+
commands << build_crop_command(input_path, temp)
|
|
45
|
+
base_path = temp
|
|
60
46
|
else
|
|
61
47
|
base_path = input_path
|
|
62
48
|
end
|
|
63
49
|
|
|
64
50
|
commands << build_composite_command(base_path, output_path)
|
|
65
|
-
commands << "rm -f #{
|
|
51
|
+
commands << "rm -f #{temp.shellescape}" unless base_path == input_path
|
|
66
52
|
commands.join(" && ")
|
|
67
53
|
end
|
|
68
54
|
|
|
69
55
|
def build_composite_command(base_path, output_path)
|
|
70
|
-
wm_op =
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
opacity = wm_op[:options][:opacity]
|
|
74
|
-
|
|
75
|
-
gravity = translate_position(position)
|
|
56
|
+
wm_op = find_op(:watermark)
|
|
57
|
+
parts = watermark_parts(wm_op)
|
|
58
|
+
gravity = translate_position(parts[:position])
|
|
76
59
|
|
|
77
60
|
command_parts = ["sharp", "-i", base_path.shellescape,
|
|
78
61
|
"-o", output_path.shellescape]
|
|
79
62
|
|
|
80
|
-
|
|
81
|
-
quality_op = @operations.find { |op| op[:type] == :quality }
|
|
82
|
-
command_parts += ["-f", sharp_format(format_op[:format])] if format_op
|
|
83
|
-
command_parts += ["-q#{quality_op[:quality]}"] if quality_op
|
|
63
|
+
add_format_quality(command_parts)
|
|
84
64
|
|
|
85
|
-
if opacity && opacity < 1.0
|
|
86
|
-
wm_temp =
|
|
87
|
-
alpha_value = (opacity
|
|
88
|
-
temp_cmd = ["sharp", "-i", watermark_path.shellescape,
|
|
65
|
+
if parts[:opacity] && parts[:opacity] < 1.0
|
|
66
|
+
wm_temp = temp_path(parts[:watermark_path], "tmp_wm.png")
|
|
67
|
+
alpha_value = alpha_byte_value(parts[:opacity])
|
|
68
|
+
temp_cmd = ["sharp", "-i", parts[:watermark_path].shellescape,
|
|
89
69
|
"-o", wm_temp.shellescape,
|
|
90
70
|
"ensureAlpha", alpha_value.to_s].join(" ")
|
|
91
71
|
command_parts += ["composite", wm_temp.shellescape,
|
|
@@ -93,7 +73,7 @@ module JekyllImgFlow
|
|
|
93
73
|
"#{temp_cmd} && #{command_parts.join(' ')} " \
|
|
94
74
|
"&& rm -f #{wm_temp.shellescape}"
|
|
95
75
|
else
|
|
96
|
-
command_parts += ["composite", watermark_path.shellescape,
|
|
76
|
+
command_parts += ["composite", parts[:watermark_path].shellescape,
|
|
97
77
|
"--gravity", gravity, "--blend", "over"]
|
|
98
78
|
command_parts.join(" ")
|
|
99
79
|
end
|
|
@@ -112,20 +92,20 @@ module JekyllImgFlow
|
|
|
112
92
|
|
|
113
93
|
def build_sequential_crop_resize(input_path, output_path)
|
|
114
94
|
# Build temp file path for intermediate crop result
|
|
115
|
-
|
|
95
|
+
temp = temp_path(input_path, "tmp_crop.jpg")
|
|
116
96
|
|
|
117
97
|
# First command: crop only
|
|
118
|
-
crop_cmd = build_crop_command(input_path,
|
|
98
|
+
crop_cmd = build_crop_command(input_path, temp)
|
|
119
99
|
|
|
120
100
|
# Second command: resize + format + quality + alpha (all together)
|
|
121
|
-
resize_cmd = build_resize_command(
|
|
101
|
+
resize_cmd = build_resize_command(temp, output_path)
|
|
122
102
|
|
|
123
103
|
# Combine with && and cleanup temp file
|
|
124
|
-
"#{crop_cmd} && #{resize_cmd} && rm -f #{
|
|
104
|
+
"#{crop_cmd} && #{resize_cmd} && rm -f #{temp.shellescape}"
|
|
125
105
|
end
|
|
126
106
|
|
|
127
107
|
def build_resize_command(input_path, output_path)
|
|
128
|
-
resize_op =
|
|
108
|
+
resize_op = find_op(:resize)
|
|
129
109
|
|
|
130
110
|
# sharp -i input.jpg -o output.jpg resize width [height] -f format -q quality
|
|
131
111
|
command_parts = ["sharp", "-i", input_path.shellescape, "-o", output_path.shellescape]
|
|
@@ -137,58 +117,28 @@ module JekyllImgFlow
|
|
|
137
117
|
["resize", resize_op[:width].to_s]
|
|
138
118
|
end
|
|
139
119
|
|
|
140
|
-
|
|
141
|
-
format_op = @operations.find { |op| op[:type] == :format }
|
|
142
|
-
quality_op = @operations.find { |op| op[:type] == :quality }
|
|
143
|
-
alpha_op = @operations.find { |op| op[:type] == :alpha_opacity }
|
|
144
|
-
|
|
145
|
-
command_parts += ["-f", sharp_format(format_op[:format])] if format_op
|
|
146
|
-
command_parts += ["-q#{quality_op[:quality]}"] if quality_op
|
|
147
|
-
if alpha_op
|
|
148
|
-
alpha_value = (alpha_op[:opacity] * 255).round
|
|
149
|
-
command_parts += ["alpha", "{alpha:#{alpha_value}}"]
|
|
150
|
-
end
|
|
151
|
-
|
|
120
|
+
add_format_quality_alpha(command_parts)
|
|
152
121
|
command_parts.join(" ")
|
|
153
122
|
end
|
|
154
123
|
|
|
155
124
|
def build_crop_command(input_path, output_path)
|
|
156
|
-
crop_op =
|
|
157
|
-
|
|
158
|
-
params = crop_op[:params] || {}
|
|
125
|
+
crop_op = find_op(:crop)
|
|
126
|
+
geo = crop_geometry(crop_op)
|
|
159
127
|
|
|
160
|
-
|
|
161
|
-
# JPT keep options: attention (default), entropy, center
|
|
162
|
-
# Check both options (from CropTag) and params (from OperationProcessor)
|
|
163
|
-
keep = opts[:keep] || params[:keep] || params[:position]
|
|
164
|
-
|
|
165
|
-
if crop_op[:ratio] && keep && %w[attention entropy center
|
|
166
|
-
centre].include?(keep.to_s)
|
|
128
|
+
if geo[:smartcrop]
|
|
167
129
|
# Use smartcrop for intelligent cropping (Sharp uses libvips backend)
|
|
168
|
-
|
|
169
|
-
crop_height = opts[:calculated_height]
|
|
170
|
-
|
|
171
|
-
# Map keep parameter to libvips interestingness
|
|
172
|
-
interestingness = case keep.to_s
|
|
173
|
-
when "entropy" then "entropy"
|
|
174
|
-
when "center", "centre" then "centre"
|
|
175
|
-
else "attention" # default
|
|
176
|
-
end
|
|
130
|
+
interestingness = smartcrop_interestingness(geo[:keep])
|
|
177
131
|
|
|
178
132
|
# sharp -i input.jpg -o output.jpg smartcrop width height --interesting=attention
|
|
179
133
|
["sharp", "-i", input_path.shellescape, "-o", output_path.shellescape,
|
|
180
|
-
"smartcrop",
|
|
134
|
+
"smartcrop", geo[:width].to_s, geo[:height].to_s,
|
|
181
135
|
"--interesting=#{interestingness}"].join(" ")
|
|
182
136
|
else
|
|
183
137
|
# Use basic extract for manual cropping or when no keep parameter
|
|
184
|
-
crop_x = crop_op[:ratio] ? opts[:calculated_x] : (opts[:x] || 0)
|
|
185
|
-
crop_y = crop_op[:ratio] ? opts[:calculated_y] : (opts[:y] || 0)
|
|
186
|
-
crop_width = crop_op[:ratio] ? opts[:calculated_width] : opts[:width]
|
|
187
|
-
crop_height = crop_op[:ratio] ? opts[:calculated_height] : opts[:height]
|
|
188
|
-
|
|
189
138
|
# sharp -i input.jpg -o output.jpg extract top left width height
|
|
190
139
|
["sharp", "-i", input_path.shellescape, "-o", output_path.shellescape,
|
|
191
|
-
"extract",
|
|
140
|
+
"extract", geo[:y].to_s, geo[:x].to_s, geo[:width].to_s,
|
|
141
|
+
geo[:height].to_s].join(" ")
|
|
192
142
|
end
|
|
193
143
|
end
|
|
194
144
|
|
|
@@ -196,17 +146,7 @@ module JekyllImgFlow
|
|
|
196
146
|
# sharp -i input.jpg -o output.jpg -f format -q quality
|
|
197
147
|
command_parts = ["sharp", "-i", input_path.shellescape, "-o", output_path.shellescape]
|
|
198
148
|
|
|
199
|
-
|
|
200
|
-
quality_op = @operations.find { |op| op[:type] == :quality }
|
|
201
|
-
alpha_op = @operations.find { |op| op[:type] == :alpha_opacity }
|
|
202
|
-
|
|
203
|
-
command_parts += ["-f", sharp_format(format_op[:format])] if format_op
|
|
204
|
-
command_parts += ["-q#{quality_op[:quality]}"] if quality_op
|
|
205
|
-
if alpha_op
|
|
206
|
-
alpha_value = (alpha_op[:opacity] * 255).round
|
|
207
|
-
command_parts += ["alpha", "{alpha:#{alpha_value}}"]
|
|
208
|
-
end
|
|
209
|
-
|
|
149
|
+
add_format_quality_alpha(command_parts)
|
|
210
150
|
command_parts.join(" ")
|
|
211
151
|
end
|
|
212
152
|
|
|
@@ -215,6 +155,23 @@ module JekyllImgFlow
|
|
|
215
155
|
def sharp_format(format)
|
|
216
156
|
format.to_s == "jpg" ? "jpeg" : format
|
|
217
157
|
end
|
|
158
|
+
|
|
159
|
+
private
|
|
160
|
+
|
|
161
|
+
def add_format_quality(command_parts)
|
|
162
|
+
format_op = find_op(:format)
|
|
163
|
+
quality_op = find_op(:quality)
|
|
164
|
+
command_parts.push("-f", sharp_format(format_op[:format])) if format_op
|
|
165
|
+
command_parts.push("-q#{quality_op[:quality]}") if quality_op
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def add_format_quality_alpha(command_parts)
|
|
169
|
+
add_format_quality(command_parts)
|
|
170
|
+
alpha_op = find_op(:alpha_opacity)
|
|
171
|
+
return unless alpha_op
|
|
172
|
+
|
|
173
|
+
command_parts.push("alpha", "{alpha:#{alpha_byte_value(alpha_op[:opacity])}}")
|
|
174
|
+
end
|
|
218
175
|
end
|
|
219
176
|
end
|
|
220
177
|
end
|