jekyll-imgflow 0.3.2 → 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 +33 -2
- data/lib/jekyll-imgflow/build_time_processor.rb +3 -2
- data/lib/jekyll-imgflow/config.rb +25 -18
- data/lib/jekyll-imgflow/filename_generator.rb +2 -0
- data/lib/jekyll-imgflow/html_generator.rb +6 -6
- data/lib/jekyll-imgflow/imgflow_tag.rb +86 -52
- data/lib/jekyll-imgflow/manifest_manager.rb +64 -57
- data/lib/jekyll-imgflow/operation_processor.rb +44 -64
- data/lib/jekyll-imgflow/parser.rb +32 -25
- data/lib/jekyll-imgflow/picture_tag_adaptor.rb +113 -106
- 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 +199 -0
- data/lib/jekyll-imgflow/providers/flyimg.rb +72 -142
- 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 +105 -138
- 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
|
@@ -9,25 +9,16 @@ module JekyllImgFlow
|
|
|
9
9
|
def process(input_path, output_path, options = {})
|
|
10
10
|
ensure_output_dir(output_path)
|
|
11
11
|
|
|
12
|
-
# Validate that crop information is provided
|
|
13
12
|
ratio = options[:ratio] || options[:aspect_ratio]
|
|
14
13
|
has_pixel_crop = options[:width] || options[:height]
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
raise ArgumentError,
|
|
18
|
-
"Crop operation requires ratio or at least one dimension (width or height)"
|
|
19
|
-
end
|
|
15
|
+
validate_crop_request(ratio, has_pixel_crop)
|
|
20
16
|
|
|
21
17
|
if ratio
|
|
22
|
-
# Handle aspect ratio cropping (e.g., "16:9", "4:3")
|
|
23
18
|
validate_ratio(ratio)
|
|
24
19
|
handle_aspect_ratio_crop(input_path, output_path, ratio, options)
|
|
25
20
|
else
|
|
26
|
-
|
|
27
|
-
validate_positive_integer(options[:width], "width") if options[:width]
|
|
28
|
-
validate_positive_integer(options[:height], "height") if options[:height]
|
|
29
|
-
validate_positive_integer(options[:x], "x") if options[:x]
|
|
30
|
-
validate_positive_integer(options[:y], "y") if options[:y]
|
|
21
|
+
validate_pixel_dimensions(options)
|
|
31
22
|
handle_pixel_crop(input_path, output_path, options)
|
|
32
23
|
end
|
|
33
24
|
|
|
@@ -36,16 +27,25 @@ module JekyllImgFlow
|
|
|
36
27
|
|
|
37
28
|
private
|
|
38
29
|
|
|
30
|
+
def validate_crop_request(ratio, has_pixel_crop)
|
|
31
|
+
return if ratio || has_pixel_crop
|
|
32
|
+
|
|
33
|
+
raise ArgumentError,
|
|
34
|
+
"Crop operation requires ratio or at least one dimension (width or height)"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def validate_pixel_dimensions(options)
|
|
38
|
+
validate_positive_integer(options[:width], "width") if options[:width]
|
|
39
|
+
validate_positive_integer(options[:height], "height") if options[:height]
|
|
40
|
+
validate_positive_integer(options[:x], "x") if options[:x]
|
|
41
|
+
validate_positive_integer(options[:y], "y") if options[:y]
|
|
42
|
+
end
|
|
43
|
+
|
|
39
44
|
# Handle aspect ratio cropping
|
|
40
45
|
def handle_aspect_ratio_crop(input_path, _output_path, ratio, options)
|
|
41
46
|
# Calculate optimal crop dimensions for the aspect ratio
|
|
42
47
|
calculated_options = calculate_aspect_ratio_dimensions(input_path, ratio, options)
|
|
43
|
-
|
|
44
|
-
# Add keep parameter for smartcrop (JPT compatibility)
|
|
45
|
-
if options[:keep] && %w[attention entropy center
|
|
46
|
-
centre].include?(options[:keep].to_s)
|
|
47
|
-
calculated_options[:keep] = options[:keep]
|
|
48
|
-
end
|
|
48
|
+
add_keep_option(calculated_options, options[:keep])
|
|
49
49
|
|
|
50
50
|
# Pass ratio and calculated options to provider (uniform interface)
|
|
51
51
|
@provider.crop(ratio, calculated_options)
|
|
@@ -53,57 +53,34 @@ module JekyllImgFlow
|
|
|
53
53
|
|
|
54
54
|
# Handle flexible pixel-based cropping
|
|
55
55
|
def handle_pixel_crop(input_path, _output_path, options)
|
|
56
|
-
# Get original image dimensions
|
|
57
56
|
original_width, original_height = get_original_dimensions(input_path)
|
|
58
57
|
|
|
59
|
-
# Parse dimensions (support pixels and percentages)
|
|
60
58
|
crop_width = parse_dimension(options[:width], original_width)
|
|
61
59
|
crop_height = parse_dimension(options[:height], original_height)
|
|
62
60
|
|
|
63
|
-
# At least one dimension must be specified
|
|
64
61
|
raise ArgumentError, "At least width or height must be specified for cropping" unless crop_width || crop_height
|
|
65
62
|
|
|
66
63
|
# Calculate missing dimension if only one provided
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
elsif crop_height && !crop_width
|
|
70
|
-
crop_width = original_width # Keep original width
|
|
71
|
-
end
|
|
64
|
+
crop_width ||= original_width # Keep original width
|
|
65
|
+
crop_height ||= original_height # Keep original height
|
|
72
66
|
|
|
73
67
|
# Parse positions with smart defaults - always provide values
|
|
74
68
|
crop_x = parse_position_with_default(options[:x], crop_width, original_width)
|
|
75
69
|
crop_y = parse_position_with_default(options[:y], crop_height, original_height)
|
|
76
70
|
|
|
77
|
-
# Validate crop area is not bigger than original
|
|
78
71
|
validate_crop_dimensions(crop_x, crop_y, crop_width, crop_height, original_width,
|
|
79
72
|
original_height)
|
|
80
73
|
|
|
81
|
-
|
|
82
|
-
crop_options
|
|
83
|
-
x: crop_x,
|
|
84
|
-
y: crop_y,
|
|
85
|
-
width: crop_width,
|
|
86
|
-
height: crop_height
|
|
87
|
-
}
|
|
74
|
+
crop_options = { x: crop_x, y: crop_y, width: crop_width, height: crop_height }
|
|
75
|
+
add_keep_option(crop_options, options[:keep])
|
|
88
76
|
|
|
89
|
-
# Add keep parameter for smartcrop (JPT compatibility)
|
|
90
|
-
if options[:keep] && %w[attention entropy center
|
|
91
|
-
centre].include?(options[:keep].to_s)
|
|
92
|
-
crop_options[:keep] = options[:keep]
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
# Execute crop with calculated dimensions
|
|
96
77
|
@provider.crop(nil, crop_options)
|
|
97
78
|
end
|
|
98
79
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
require "fastimage"
|
|
102
|
-
original_width, original_height = FastImage.size(input_path)
|
|
103
|
-
|
|
104
|
-
raise ArgumentError, "Unable to determine original image dimensions" unless original_width && original_height
|
|
80
|
+
def add_keep_option(crop_options, keep)
|
|
81
|
+
return unless keep && %w[attention entropy center centre].include?(keep.to_s)
|
|
105
82
|
|
|
106
|
-
[
|
|
83
|
+
crop_options[:keep] = keep
|
|
107
84
|
end
|
|
108
85
|
|
|
109
86
|
# Parse dimension (supports pixels and percentages)
|
|
@@ -13,31 +13,12 @@ module JekyllImgFlow
|
|
|
13
13
|
opacity = options[:opacity]
|
|
14
14
|
raise ArgumentError, "Opacity parameter is required" if opacity.nil?
|
|
15
15
|
|
|
16
|
-
validated_opacity = validate_opacity(opacity)
|
|
16
|
+
validated_opacity = validate_opacity(opacity, min: 0.01, max: 0.99)
|
|
17
17
|
|
|
18
18
|
@provider.alpha_opacity = validated_opacity
|
|
19
19
|
@provider.execute(input_path, output_path)
|
|
20
20
|
output_path
|
|
21
21
|
end
|
|
22
|
-
|
|
23
|
-
private
|
|
24
|
-
|
|
25
|
-
def validate_opacity(value)
|
|
26
|
-
# Check if value is numeric before converting
|
|
27
|
-
raise ArgumentError, "Invalid opacity: '#{value}'. Must be between 0.01 and 0.99." unless valid_numeric?(value)
|
|
28
|
-
|
|
29
|
-
opacity = value.to_f
|
|
30
|
-
raise ArgumentError, "Invalid opacity: '#{value}'. Must be between 0.01 and 0.99." unless opacity.between?(0.01, 0.99)
|
|
31
|
-
|
|
32
|
-
opacity
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def valid_numeric?(value)
|
|
36
|
-
Float(value)
|
|
37
|
-
true
|
|
38
|
-
rescue ArgumentError, TypeError
|
|
39
|
-
false
|
|
40
|
-
end
|
|
41
22
|
end
|
|
42
23
|
end
|
|
43
24
|
end
|
|
@@ -38,15 +38,6 @@ module JekyllImgFlow
|
|
|
38
38
|
|
|
39
39
|
quality
|
|
40
40
|
end
|
|
41
|
-
|
|
42
|
-
def validate_quality(value)
|
|
43
|
-
return @default_quality if value.nil?
|
|
44
|
-
|
|
45
|
-
quality = value.to_i
|
|
46
|
-
raise ArgumentError, "Invalid quality: '#{value}'. Must be between 1 and 100." unless quality.between?(1, 100)
|
|
47
|
-
|
|
48
|
-
quality
|
|
49
|
-
end
|
|
50
41
|
end
|
|
51
42
|
end
|
|
52
43
|
end
|
|
@@ -15,17 +15,6 @@ module JekyllImgFlow
|
|
|
15
15
|
@provider.quality = quality
|
|
16
16
|
@provider.execute(input_path, output_path)
|
|
17
17
|
end
|
|
18
|
-
|
|
19
|
-
private
|
|
20
|
-
|
|
21
|
-
def validate_quality(value)
|
|
22
|
-
return @default_quality if value.nil?
|
|
23
|
-
|
|
24
|
-
quality = value.to_i
|
|
25
|
-
raise ArgumentError, "Invalid quality: '#{value}'. Must be between 1 and 100." unless quality.between?(1, 100)
|
|
26
|
-
|
|
27
|
-
quality
|
|
28
|
-
end
|
|
29
18
|
end
|
|
30
19
|
end
|
|
31
20
|
end
|
|
@@ -13,23 +13,13 @@ module JekyllImgFlow
|
|
|
13
13
|
width = validate_positive_integer(options[:width], "width")
|
|
14
14
|
height = validate_positive_integer(options[:height], "height")
|
|
15
15
|
|
|
16
|
-
# At least one dimension must be specified
|
|
17
16
|
raise ArgumentError, "Either width or height must be specified for resize operation." if width.nil? && height.nil?
|
|
18
17
|
|
|
19
18
|
# Get original dimensions
|
|
20
19
|
original_width, original_height = get_image_dimensions(input_path)
|
|
21
20
|
|
|
22
21
|
# Calculate missing dimension to maintain aspect ratio ONLY when one dimension is missing
|
|
23
|
-
|
|
24
|
-
# Only height provided - calculate width to maintain aspect ratio
|
|
25
|
-
aspect_ratio = original_width.to_f / original_height
|
|
26
|
-
width = (height * aspect_ratio).round
|
|
27
|
-
elsif height.nil? && width
|
|
28
|
-
# Only width provided - calculate height to maintain aspect ratio
|
|
29
|
-
aspect_ratio = original_height.to_f / original_width
|
|
30
|
-
height = (width * aspect_ratio).round
|
|
31
|
-
end
|
|
32
|
-
# If both width and height are provided, use them as-is (allows aspect ratio changes)
|
|
22
|
+
width, height = calculate_missing_dimension(width, height, original_width, original_height)
|
|
33
23
|
|
|
34
24
|
# Calculate scale factors for provider
|
|
35
25
|
scale_x = width && original_width ? width.to_f / original_width : nil
|
|
@@ -54,6 +44,23 @@ module JekyllImgFlow
|
|
|
54
44
|
result
|
|
55
45
|
end
|
|
56
46
|
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def calculate_missing_dimension(width, height, original_width, original_height)
|
|
50
|
+
if width.nil? && height
|
|
51
|
+
# Only height provided - calculate width to maintain aspect ratio
|
|
52
|
+
aspect_ratio = original_width.to_f / original_height
|
|
53
|
+
[(height * aspect_ratio).round, height]
|
|
54
|
+
elsif height.nil? && width
|
|
55
|
+
# Only width provided - calculate height to maintain aspect ratio
|
|
56
|
+
aspect_ratio = original_height.to_f / original_width
|
|
57
|
+
[width, (width * aspect_ratio).round]
|
|
58
|
+
else
|
|
59
|
+
# If both width and height are provided, use them as-is (allows aspect ratio changes)
|
|
60
|
+
[width, height]
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
57
64
|
# validate_positive_integer inherited from BaseTag
|
|
58
65
|
end
|
|
59
66
|
end
|
|
@@ -36,23 +36,6 @@ module JekyllImgFlow
|
|
|
36
36
|
else position.to_s
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
|
-
|
|
40
|
-
def validate_opacity(value)
|
|
41
|
-
# Check if value is numeric before converting
|
|
42
|
-
raise ArgumentError, "Invalid opacity: '#{value}'. Must be between 0.0 and 1.0." unless valid_numeric?(value)
|
|
43
|
-
|
|
44
|
-
opacity = value.to_f
|
|
45
|
-
raise ArgumentError, "Invalid opacity: '#{value}'. Must be between 0.0 and 1.0." unless opacity.between?(0.0, 1.0)
|
|
46
|
-
|
|
47
|
-
opacity
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def valid_numeric?(value)
|
|
51
|
-
Float(value)
|
|
52
|
-
true
|
|
53
|
-
rescue ArgumentError, TypeError
|
|
54
|
-
false
|
|
55
|
-
end
|
|
56
39
|
end
|
|
57
40
|
end
|
|
58
41
|
end
|