micro_magick 0.0.6 → 0.0.7
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 +9 -9
- data/lib/micro_magick.rb +44 -31
- data/lib/micro_magick/identify_parser.rb +54 -0
- data/lib/micro_magick/image.rb +116 -0
- data/lib/micro_magick/version.rb +1 -1
- data/test/corrupt.jpg +0 -0
- data/test/identify_parser_test.rb +185 -0
- data/test/image_tests.rb +82 -0
- data/test/micro_gmagick_test.rb +5 -8
- data/test/micro_imagick_test.rb +5 -8
- data/test/micro_magick_test.rb +12 -25
- data/test/test_helper.rb +3 -0
- metadata +71 -10
- data/lib/micro_magick/convert.rb +0 -84
- data/lib/micro_magick/geometry.rb +0 -22
- data/test/geometry_test.rb +0 -10
- data/test/micro_magick_test_base.rb +0 -66
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YmJkMmI2NzI4MTk4ZDBiZTZjNmFkZDc4ZTljNWZjYzkwZTMyYzdmOQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
7
|
-
|
6
|
+
Yzk2YzEyNTk2OTEzYzkyOWI1MGE4NTc3MGNiNmY0ZWI3OThiZDI0NA==
|
7
|
+
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
OTc4OGExNDUyOGRmMWFlZjIxODk3MGQ4Njg0ODRjMGUyODBlYzNjODQ5MjIy
|
10
|
+
YmJmZGI0ZTY1NTI3Y2U4NDdkN2M1YmY3YzdmMjUyNDgzN2YwYjcxMTE0OThj
|
11
|
+
MWJlNjFlZmUxYTIxMTgyZDQ4ZWVmOWQ3MGQ4YzI5YTg0ZWFkMmY=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NmI1ZmE4OTBlOTBlMGU4MjAxYzFmYjUzNTE2NGQ1OWE1NjEwMTRhYzAyNzUx
|
14
|
+
NmI3M2EwZDJmMzZmMjM1ZGMyODFkNjhlM2I0OTg1YjZmZjBmNTgxZTdjZDlm
|
15
|
+
NTI4ZWQzYWVhMGUwMjY0MzcyMTg2ZWJiOWViOTM0MjZmMTM2ZWI=
|
data/lib/micro_magick.rb
CHANGED
@@ -1,49 +1,62 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
1
|
+
require 'micro_magick/version'
|
2
|
+
require 'micro_magick/identify_parser'
|
3
|
+
require 'micro_magick/image'
|
4
|
+
require 'open3'
|
5
5
|
|
6
6
|
module MicroMagick
|
7
7
|
|
8
|
-
|
8
|
+
Error = Class.new(StandardError)
|
9
|
+
NoSuchFile = Class.new(Error)
|
10
|
+
MissingBinaries = Class.new(Error)
|
11
|
+
ArgumentError = Class.new(Error)
|
12
|
+
CorruptImageError = Class.new(Error)
|
9
13
|
|
10
|
-
|
14
|
+
def self.use_imagemagick
|
15
|
+
@cmd_prefix = ''
|
16
|
+
end
|
11
17
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
@engine = engine
|
18
|
+
def self.use_graphicsmagick
|
19
|
+
@cmd_prefix = 'gm '
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.reset!
|
23
|
+
@cmd_prefix = nil
|
19
24
|
end
|
20
25
|
|
21
26
|
def self.cmd_prefix
|
22
|
-
@
|
23
|
-
if system(
|
24
|
-
|
25
|
-
elsif system(
|
26
|
-
|
27
|
+
@cmd_prefix ||= begin
|
28
|
+
if system('hash gm 2>&-')
|
29
|
+
'gm '
|
30
|
+
elsif system('hash convert 2>&-')
|
31
|
+
''
|
27
32
|
else
|
28
|
-
raise
|
33
|
+
raise MissingBinaries, 'Please install either GraphicsMagick or ImageMagick'
|
29
34
|
end
|
30
35
|
end
|
31
|
-
ENGINE2PREFIX[@engine]
|
32
36
|
end
|
33
37
|
|
34
|
-
def self.exec(
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
38
|
+
def self.exec(cmds, return_stdout = false)
|
39
|
+
final_cmd = cmd_prefix + cmds.join(' ')
|
40
|
+
stdout = Open3.popen3(final_cmd) do |stdin, stdout, stderr|
|
41
|
+
err = stderr.read.strip
|
42
|
+
if err.size > 0
|
43
|
+
error = if err =~ /unrecognized option/i
|
44
|
+
ArgumentError
|
45
|
+
elsif err =~ /corrupt/i
|
46
|
+
CorruptImageError
|
47
|
+
elsif err =~ /no such file or directory|unable to open/i
|
48
|
+
NoSuchFile
|
49
|
+
else
|
50
|
+
Error
|
51
|
+
end
|
52
|
+
raise error, "#{final_cmd} failed: #{err}"
|
53
|
+
end
|
54
|
+
stdout.read.strip
|
43
55
|
end
|
44
|
-
|
56
|
+
return_stdout ? stdout : final_cmd
|
57
|
+
rescue Errno::ENOENT => e
|
58
|
+
raise NoSuchFile, e.message
|
45
59
|
end
|
46
|
-
|
47
60
|
end
|
48
61
|
|
49
62
|
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module MicroMagick
|
2
|
+
class IdentifyParser
|
3
|
+
|
4
|
+
attr_reader :results
|
5
|
+
|
6
|
+
def initialize(stdout)
|
7
|
+
cleaned = stdout.encode('UTF-8', 'binary', :invalid => :replace, :undef => :replace, :replace => '')
|
8
|
+
@lines = cleaned.split("\n").select { |ea| ea =~ /\S+:/ }
|
9
|
+
if @lines.empty?
|
10
|
+
@results = {}
|
11
|
+
else
|
12
|
+
@results = parse[:image] # < remove the "image" prefix.
|
13
|
+
if (m = /(\d+)x(\d+)/.match(@results[:geometry]))
|
14
|
+
@results[:width], @results[:height] = m[1].to_i, m[2].to_i
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def [](key)
|
20
|
+
results[key_to_sym(key)]
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse
|
24
|
+
result = {}
|
25
|
+
while true
|
26
|
+
line = @lines.shift
|
27
|
+
key, value = split(line)
|
28
|
+
if @lines.first && indent(line) < indent(@lines.first)
|
29
|
+
# The current line has a sub-section.
|
30
|
+
result[key] = parse()
|
31
|
+
else
|
32
|
+
result[key] = value
|
33
|
+
end
|
34
|
+
|
35
|
+
# Next line is indented less than the current line:
|
36
|
+
break if @lines.first.nil? || indent(line) > indent(@lines.first)
|
37
|
+
end
|
38
|
+
result
|
39
|
+
end
|
40
|
+
|
41
|
+
def indent(line)
|
42
|
+
/\S/.match(line).begin(0)
|
43
|
+
end
|
44
|
+
|
45
|
+
def split(line)
|
46
|
+
k, v = line.split(':', 2).map(&:strip)
|
47
|
+
[key_to_sym(k), v]
|
48
|
+
end
|
49
|
+
|
50
|
+
def key_to_sym(key)
|
51
|
+
key.is_a?(Symbol) ? key : key.strip.gsub(/[\b\W_-]+/, '_').downcase.to_sym
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module MicroMagick
|
5
|
+
class Image
|
6
|
+
attr_reader :input_file
|
7
|
+
|
8
|
+
def initialize(input_file)
|
9
|
+
@input_file = input_file
|
10
|
+
@input_options = []
|
11
|
+
@output_options = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](key)
|
15
|
+
identify[key]
|
16
|
+
end
|
17
|
+
|
18
|
+
def width
|
19
|
+
self[:width]
|
20
|
+
end
|
21
|
+
|
22
|
+
def height
|
23
|
+
self[:height]
|
24
|
+
end
|
25
|
+
|
26
|
+
def corrupt?
|
27
|
+
identify && @corrupt
|
28
|
+
end
|
29
|
+
|
30
|
+
# Strip the image of any profiles or comments.
|
31
|
+
# Note that this re-encodes the image, so it should only be used when downsampling
|
32
|
+
# (say, for a thumbnail)
|
33
|
+
# (ImageMagick has the -strip command, but GraphicsMagick doesn't.
|
34
|
+
# It turns out that ```+profile *``` does the same thing.)
|
35
|
+
def strip
|
36
|
+
add_output_option('+profile', '*')
|
37
|
+
end
|
38
|
+
|
39
|
+
# Crop to a square, using the specified gravity.
|
40
|
+
def square_crop(gravity = 'Center')
|
41
|
+
gravity(gravity) unless gravity.nil?
|
42
|
+
d = [width, height].min
|
43
|
+
crop("#{d}x#{d}+0+0!")
|
44
|
+
end
|
45
|
+
|
46
|
+
# For normal options, like -resize or -flip, you can call .resize("32x32") or .flip().
|
47
|
+
# If you need to add an output option that starts with a '+', you can use this method.
|
48
|
+
def add_output_option(option_name, *args)
|
49
|
+
(@output_options ||= []).push(option_name)
|
50
|
+
args.each { |ea| @output_options.push(Shellwords.escape(ea.to_s)) }
|
51
|
+
|
52
|
+
# if we're a resize call, let's give the -size render hint to gm, but only when it's safe:
|
53
|
+
# * we don't have input options yet,
|
54
|
+
# * we're not cropping (because the -size will prevent the crop from working),
|
55
|
+
# * and we have dimensions in the form of NNNxNNN
|
56
|
+
if %w{-geometry -resize -sample -scale}.include?(option_name) &&
|
57
|
+
@input_options.empty? &&
|
58
|
+
!@output_options.include?('-crop')
|
59
|
+
dimensions = args.first
|
60
|
+
if dimensions.to_s =~ /\A(\d+x\d+)\z/
|
61
|
+
@input_options.push('-size', dimensions)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
# Support call chaining:
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
# Runs "convert"
|
69
|
+
# See http://www.imagemagick.org/script/convert.php
|
70
|
+
def write(output_file)
|
71
|
+
MicroMagick.exec(command('convert', output_file))
|
72
|
+
ensure
|
73
|
+
@input_options.clear
|
74
|
+
@output_options.clear
|
75
|
+
end
|
76
|
+
|
77
|
+
# Runs "mogrify"
|
78
|
+
# See http://www.imagemagick.org/script/mogrify.php
|
79
|
+
def overwrite
|
80
|
+
MicroMagick.exec(command('mogrify'))
|
81
|
+
ensure
|
82
|
+
@input_options.clear
|
83
|
+
@output_options.clear
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def method_missing(method, *args, &block)
|
89
|
+
add_output_option("-#{method.to_s}", *args)
|
90
|
+
end
|
91
|
+
|
92
|
+
def command(command_name, output_file = nil)
|
93
|
+
cmd = [command_name]
|
94
|
+
cmd.push *@input_options
|
95
|
+
cmd.push Shellwords.escape(@input_file)
|
96
|
+
cmd.push *@output_options
|
97
|
+
cmd.push(Shellwords.escape(output_file)) if output_file
|
98
|
+
cmd
|
99
|
+
end
|
100
|
+
|
101
|
+
def identify
|
102
|
+
@identify || begin
|
103
|
+
cmd = ['identify', '-verbose', Shellwords.escape(input_file)]
|
104
|
+
@identify = IdentifyParser.new(MicroMagick.exec(cmd, true)).results
|
105
|
+
@corrupt = false
|
106
|
+
rescue CorruptImageError => e
|
107
|
+
@identify = {}
|
108
|
+
@corrupt = true
|
109
|
+
end
|
110
|
+
@identify
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# Aliases to support < v0.0.6
|
115
|
+
Geometry = Convert = Image
|
116
|
+
end
|
data/lib/micro_magick/version.rb
CHANGED
data/test/corrupt.jpg
ADDED
Binary file
|
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe MicroMagick::IdentifyParser do
|
4
|
+
describe 'simple parsing functions' do
|
5
|
+
let(:ip) { MicroMagick::IdentifyParser.new('') }
|
6
|
+
it 'converts keys to expected symbols' do
|
7
|
+
ip.key_to_sym('Image').must_equal :image
|
8
|
+
ip.key_to_sym('Channel Depths').must_equal :channel_depths
|
9
|
+
ip.key_to_sym('JPEG-Quality').must_equal :jpeg_quality
|
10
|
+
ip.key_to_sym('Y Cb Cr Positioning').must_equal :y_cb_cr_positioning
|
11
|
+
ip.key_to_sym('Profile-EXIF').must_equal :profile_exif
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'determines line indent properly' do
|
15
|
+
ip.indent('Image: out.jpeg').must_equal 0
|
16
|
+
ip.indent(' Channel Depths:').must_equal 2
|
17
|
+
ip.indent(' Blue:').must_equal 4
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'with expected output' do
|
22
|
+
let(:ip) do
|
23
|
+
MicroMagick::IdentifyParser.new(<<-OUT)
|
24
|
+
Image: /tmp/input.jpg
|
25
|
+
Format: JPEG (Joint Photographic Experts Group JFIF format)
|
26
|
+
Mime type: image/jpeg
|
27
|
+
Class: DirectClass
|
28
|
+
Geometry: 1152x2048+0+0
|
29
|
+
Resolution: 72x72
|
30
|
+
Print size: 16x28.4444
|
31
|
+
Units: PixelsPerInch
|
32
|
+
Type: TrueColor
|
33
|
+
Endianess: Undefined
|
34
|
+
Colorspace: sRGB
|
35
|
+
Depth: 8-bit
|
36
|
+
Channel depth:
|
37
|
+
red: 8-bit
|
38
|
+
green: 8-bit
|
39
|
+
blue: 8-bit
|
40
|
+
Channel statistics:
|
41
|
+
Red:
|
42
|
+
min: 0 (0)
|
43
|
+
max: 255 (1)
|
44
|
+
mean: 133.986 (0.525437)
|
45
|
+
standard deviation: 54.1069 (0.212184)
|
46
|
+
kurtosis: -0.874804
|
47
|
+
skewness: -0.488666
|
48
|
+
Green:
|
49
|
+
min: 0 (0)
|
50
|
+
max: 255 (1)
|
51
|
+
mean: 119.681 (0.469337)
|
52
|
+
standard deviation: 64.5707 (0.253218)
|
53
|
+
kurtosis: -1.43104
|
54
|
+
skewness: -0.207503
|
55
|
+
Blue:
|
56
|
+
min: 0 (0)
|
57
|
+
max: 255 (1)
|
58
|
+
mean: 102.697 (0.402734)
|
59
|
+
standard deviation: 75.4283 (0.295797)
|
60
|
+
kurtosis: -1.69738
|
61
|
+
skewness: 0.0278036
|
62
|
+
Image statistics:
|
63
|
+
Overall:
|
64
|
+
min: 0 (0)
|
65
|
+
max: 255 (1)
|
66
|
+
mean: 118.788 (0.465836)
|
67
|
+
standard deviation: 65.2849 (0.256019)
|
68
|
+
kurtosis: -1.25648
|
69
|
+
skewness: -0.301872
|
70
|
+
Rendering intent: Perceptual
|
71
|
+
Gamma: 0.454545
|
72
|
+
Chromaticity:
|
73
|
+
red primary: (0.64,0.33)
|
74
|
+
green primary: (0.3,0.6)
|
75
|
+
blue primary: (0.15,0.06)
|
76
|
+
white point: (0.3127,0.329)
|
77
|
+
Background color: white
|
78
|
+
Border color: srgb(223,223,223)
|
79
|
+
Matte color: grey74
|
80
|
+
Transparent color: black
|
81
|
+
Interlace: None
|
82
|
+
Intensity: Undefined
|
83
|
+
Compose: Over
|
84
|
+
Page geometry: 1152x2048+0+0
|
85
|
+
Dispose: Undefined
|
86
|
+
Iterations: 0
|
87
|
+
Compression: JPEG
|
88
|
+
Quality: 94
|
89
|
+
Orientation: TopLeft
|
90
|
+
Properties:
|
91
|
+
date:create: 2014-02-09T08:59:26-08:00
|
92
|
+
date:modify: 2014-02-09T08:59:25-08:00
|
93
|
+
exif:ApertureValue: 228/100
|
94
|
+
exif:BrightnessValue: 105984/65536
|
95
|
+
exif:ColorSpace: 1
|
96
|
+
exif:ComponentsConfiguration: 1, 2, 3, 0
|
97
|
+
exif:Compression: 6
|
98
|
+
exif:DateTime: 2014:02:07 08:38:21
|
99
|
+
exif:DateTimeDigitized: 2014:02:07 08:38:21
|
100
|
+
exif:DateTimeOriginal: 2014:02:07 08:38:21
|
101
|
+
exif:ExifImageLength: 2048
|
102
|
+
exif:ExifImageWidth: 1152
|
103
|
+
exif:ExifOffset: 230
|
104
|
+
exif:ExifVersion: 48, 50, 50, 48
|
105
|
+
exif:ExposureBiasValue: 0/10
|
106
|
+
exif:ExposureMode: 0
|
107
|
+
exif:ExposureProgram: 2
|
108
|
+
exif:ExposureTime: 1/25
|
109
|
+
exif:Flash: 0
|
110
|
+
exif:FlashPixVersion: 48, 49, 48, 48
|
111
|
+
exif:FNumber: 220/100
|
112
|
+
exif:FocalLength: 420/100
|
113
|
+
exif:FocalLengthIn35mmFilm: 31
|
114
|
+
exif:GPSAltitude: 0/1000
|
115
|
+
exif:GPSAltitudeRef: 0
|
116
|
+
exif:GPSDateStamp: 2014:02:07
|
117
|
+
exif:GPSInfo: 722
|
118
|
+
exif:GPSLatitude: 37/1, 46/1, 341802/10000
|
119
|
+
exif:GPSLatitudeRef: N
|
120
|
+
exif:GPSLongitude: 122/1, 25/1, 6225/10000
|
121
|
+
exif:GPSLongitudeRef: W
|
122
|
+
exif:GPSProcessingMethod: ASCII
|
123
|
+
exif:GPSTimeStamp: 16/1, 38/1, 11/1
|
124
|
+
exif:GPSVersionID: 2, 2, 0, 0
|
125
|
+
exif:ImageLength: 1152
|
126
|
+
exif:ImageUniqueID: 9e6076155a3bd1ce0000000000000000
|
127
|
+
exif:ImageWidth: 2048
|
128
|
+
exif:InteroperabilityIndex: R98
|
129
|
+
exif:InteroperabilityOffset: 948
|
130
|
+
exif:InteroperabilityVersion: 48, 49, 48, 48
|
131
|
+
exif:ISOSpeedRatings: 125
|
132
|
+
exif:JPEGInterchangeFormat: 1072
|
133
|
+
exif:JPEGInterchangeFormatLength: 4729
|
134
|
+
exif:LightSource: 0
|
135
|
+
exif:Make: SAMSUNG
|
136
|
+
exif:MaxApertureValue: 228/100
|
137
|
+
exif:MeteringMode: 1
|
138
|
+
exif:Model: SCH-I545
|
139
|
+
exif:Orientation: 1
|
140
|
+
exif:ResolutionUnit: 2
|
141
|
+
exif:SceneCaptureType: 0
|
142
|
+
exif:SceneType: 1
|
143
|
+
exif:SensingMethod: 2
|
144
|
+
exif:ShutterSpeedValue: 304394/65536
|
145
|
+
exif:Software: Google
|
146
|
+
exif:thumbnail:ResolutionUnit: 2
|
147
|
+
exif:thumbnail:XResolution: 72/1
|
148
|
+
exif:thumbnail:YResolution: 72/1
|
149
|
+
exif:WhiteBalance: 0
|
150
|
+
exif:XResolution: 72/1
|
151
|
+
exif:YCbCrPositioning: 1
|
152
|
+
exif:YResolution: 72/1
|
153
|
+
jpeg:colorspace: 2
|
154
|
+
jpeg:sampling-factor: 2x2,1x1,1x1
|
155
|
+
signature: 2e969ff76481f84c85031e191ee902825bf6c2a12fb6083da57633326cde2bef
|
156
|
+
Profiles:
|
157
|
+
Profile-exif: 5808 bytes
|
158
|
+
Profile-xmp: 325 bytes
|
159
|
+
Artifacts:
|
160
|
+
filename: /Users/mrm/Downloads/20140207_083822.jpg
|
161
|
+
verbose: true
|
162
|
+
Tainted: False
|
163
|
+
Filesize: 227KB
|
164
|
+
Number pixels: 2.359M
|
165
|
+
Pixels per second: 29.49MB
|
166
|
+
User time: 0.070u
|
167
|
+
Elapsed time: 0:01.080
|
168
|
+
Version: ImageMagick 6.8.7-7 Q16 x86_64 2013-11-27 http://www.imagemagick.org
|
169
|
+
OUT
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'extracts width and depth from geometry' do
|
173
|
+
ip[:width].must_equal 1152
|
174
|
+
ip[:height].must_equal 2048
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'extracts sub-sections properly' do
|
178
|
+
ip[:channel_depth][:red].must_equal '8-bit'
|
179
|
+
ip[:channel_depth][:green].must_equal '8-bit'
|
180
|
+
ip[:channel_depth][:blue].must_equal '8-bit'
|
181
|
+
ip[:artifacts][:verbose].must_equal 'true'
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|
data/test/image_tests.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
module ImageTests
|
5
|
+
def self.included spec_class
|
6
|
+
spec_class.class_eval do
|
7
|
+
|
8
|
+
let(:img) { MicroMagick::Convert.new('test/480x270.jpg') }
|
9
|
+
|
10
|
+
let(:corrupt) { MicroMagick::Convert.new('test/corrupt.jpg') }
|
11
|
+
|
12
|
+
# By using the block format, the tempfile will be closed and deleted:
|
13
|
+
let(:outfile) { Tempfile.open(%w(out .jpg)) { |ea| ea.path } }
|
14
|
+
|
15
|
+
it 'extracts image geometry correctly' do
|
16
|
+
img.width.must_equal 270
|
17
|
+
img.height.must_equal 480
|
18
|
+
img.corrupt?.must_be_false
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'detects corrupt images properly' do
|
22
|
+
corrupt.width.must_be_nil
|
23
|
+
corrupt.height.must_be_nil
|
24
|
+
corrupt.corrupt?.must_be_true
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'resizes to cropped square' do
|
28
|
+
img.resize('64x64')
|
29
|
+
command = img.write(outfile)
|
30
|
+
command.must_equal "#{MicroMagick.cmd_prefix}convert -size 64x64 test/480x270.jpg -resize 64x64 " + Shellwords.escape(outfile)
|
31
|
+
File.exist?(outfile).must_be_true
|
32
|
+
g = MicroMagick::Geometry.new(outfile)
|
33
|
+
g.width.must_equal 64 * 270 / 480
|
34
|
+
g.height.must_equal 64
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'removes exif headers from .strip' do
|
38
|
+
img.strip
|
39
|
+
command = img.write(outfile)
|
40
|
+
command.must_equal "#{MicroMagick.cmd_prefix}convert test/480x270.jpg +profile \\* " + Shellwords.escape(outfile)
|
41
|
+
i = MicroMagick::Image.new('test/480x270.jpg')
|
42
|
+
i['Profile-EXIF'].must_be_nil
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'crops properly' do
|
46
|
+
command = img.quality(85).square_crop('North').resize('128x128').write(outfile)
|
47
|
+
command.must_equal "#{MicroMagick.cmd_prefix}convert test/480x270.jpg " +
|
48
|
+
"-quality 85 -gravity North -crop 270x270\\+0\\+0\\! -resize 128x128 " +
|
49
|
+
Shellwords.escape(outfile)
|
50
|
+
File.exist?(outfile).must_be_true
|
51
|
+
g = MicroMagick::Image.new(outfile)
|
52
|
+
g.width.must_equal 128
|
53
|
+
g.height.must_equal 128
|
54
|
+
|
55
|
+
# make sure calling previous arguments don't leak into new calls:
|
56
|
+
img.resize("64x64")
|
57
|
+
command = img.write(outfile)
|
58
|
+
command.must_equal "#{MicroMagick.cmd_prefix}convert -size 64x64 test/480x270.jpg -resize 64x64 " +
|
59
|
+
Shellwords.escape(outfile)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'raises errors from invalid parameters' do
|
63
|
+
img.boing
|
64
|
+
proc { img.write(outfile) }.must_raise MicroMagick::ArgumentError
|
65
|
+
end
|
66
|
+
|
67
|
+
let(:enoent) { MicroMagick::Image.new('nonexistant-file.jpg') }
|
68
|
+
|
69
|
+
it 'raises NoSuchFile when fetching attributes' do
|
70
|
+
proc { enoent.width }.must_raise MicroMagick::NoSuchFile
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'raises NoSuchFile from write' do
|
74
|
+
proc { enoent.overwrite }.must_raise MicroMagick::NoSuchFile
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'raises NoSuchFile from overwrite' do
|
78
|
+
proc { enoent.overwrite }.must_raise MicroMagick::NoSuchFile
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/test/micro_gmagick_test.rb
CHANGED
@@ -1,11 +1,8 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
class MicroGmagickTest < MicroMagickTestBase
|
4
|
-
|
5
|
-
def setup
|
6
|
-
MicroMagick.use(:graphicsmagick)
|
7
|
-
super
|
8
|
-
end
|
1
|
+
require 'test_helper'
|
2
|
+
require 'image_tests'
|
9
3
|
|
4
|
+
describe 'Image tests under GraphicsMagick' do
|
5
|
+
before { MicroMagick.use_graphicsmagick }
|
6
|
+
include ImageTests
|
10
7
|
end
|
11
8
|
|
data/test/micro_imagick_test.rb
CHANGED
@@ -1,11 +1,8 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
class MicroImagickTest < MicroMagickTestBase
|
4
|
-
|
5
|
-
def setup
|
6
|
-
MicroMagick.use(:imagemagick)
|
7
|
-
super
|
8
|
-
end
|
1
|
+
require 'test_helper'
|
2
|
+
require 'image_tests'
|
9
3
|
|
4
|
+
describe 'Image tests under ImageMagick' do
|
5
|
+
before { MicroMagick.use_imagemagick }
|
6
|
+
include ImageTests
|
10
7
|
end
|
11
8
|
|
data/test/micro_magick_test.rb
CHANGED
@@ -1,34 +1,21 @@
|
|
1
|
-
require
|
2
|
-
require "micro_magick"
|
1
|
+
require 'test_helper'
|
3
2
|
|
4
|
-
|
3
|
+
describe MicroMagick do
|
5
4
|
|
6
|
-
|
7
|
-
#
|
8
|
-
MicroMagick.
|
9
|
-
|
10
|
-
# eh, ok, now we should stub out system
|
11
|
-
# and verify that the next call to cmd_prefix
|
12
|
-
# calls `hash gm`?
|
13
|
-
|
14
|
-
# Meh, let's just assume that the test running machine has gm in their PATH.
|
15
|
-
assert_equal MicroMagick.cmd_prefix, "gm"
|
16
|
-
end
|
17
|
-
|
18
|
-
def test_use_invalid
|
19
|
-
assert_raise MicroMagick::InvalidArgument do
|
20
|
-
MicroMagick.use(:boink)
|
21
|
-
end
|
5
|
+
it 'uses GraphicsMagick by default' do
|
6
|
+
# Assume that the machine that's running the test has gm in it's PATH
|
7
|
+
MicroMagick.reset!
|
8
|
+
MicroMagick.cmd_prefix.must_equal 'gm '
|
22
9
|
end
|
23
10
|
|
24
|
-
|
25
|
-
MicroMagick.
|
26
|
-
|
11
|
+
it 'uses GraphicsMagick when set explicitly' do
|
12
|
+
MicroMagick.use_graphicsmagick
|
13
|
+
MicroMagick.cmd_prefix.must_equal 'gm '
|
27
14
|
end
|
28
15
|
|
29
|
-
|
30
|
-
MicroMagick.
|
31
|
-
|
16
|
+
it 'uses ImageMagick when set explicitly' do
|
17
|
+
MicroMagick.use_imagemagick
|
18
|
+
MicroMagick.cmd_prefix.must_equal ''
|
32
19
|
end
|
33
20
|
|
34
21
|
end
|
data/test/test_helper.rb
ADDED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: micro_magick
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew McEachen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2014-02-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -24,6 +24,62 @@ dependencies:
|
|
24
24
|
- - ! '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: yard
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest-great_expectations
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest-reporters
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
27
83
|
description: ''
|
28
84
|
email:
|
29
85
|
- matthew-github@mceachen.org
|
@@ -32,15 +88,17 @@ extensions: []
|
|
32
88
|
extra_rdoc_files: []
|
33
89
|
files:
|
34
90
|
- lib/micro_magick.rb
|
35
|
-
- lib/micro_magick/
|
36
|
-
- lib/micro_magick/
|
91
|
+
- lib/micro_magick/identify_parser.rb
|
92
|
+
- lib/micro_magick/image.rb
|
37
93
|
- lib/micro_magick/version.rb
|
38
94
|
- test/480x270.jpg
|
39
|
-
- test/
|
95
|
+
- test/corrupt.jpg
|
96
|
+
- test/identify_parser_test.rb
|
97
|
+
- test/image_tests.rb
|
40
98
|
- test/micro_gmagick_test.rb
|
41
99
|
- test/micro_imagick_test.rb
|
42
100
|
- test/micro_magick_test.rb
|
43
|
-
- test/
|
101
|
+
- test/test_helper.rb
|
44
102
|
homepage: https://github.com/mceachen/micro_magick
|
45
103
|
licenses: []
|
46
104
|
metadata: {}
|
@@ -60,14 +118,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
60
118
|
version: '0'
|
61
119
|
requirements: []
|
62
120
|
rubyforge_project:
|
63
|
-
rubygems_version: 2.0
|
121
|
+
rubygems_version: 2.2.0
|
64
122
|
signing_key:
|
65
123
|
specification_version: 4
|
66
|
-
summary:
|
124
|
+
summary: Simple and efficient ImageMagick/GraphicsMagick ruby wrapper
|
67
125
|
test_files:
|
68
126
|
- test/480x270.jpg
|
69
|
-
- test/
|
127
|
+
- test/corrupt.jpg
|
128
|
+
- test/identify_parser_test.rb
|
129
|
+
- test/image_tests.rb
|
70
130
|
- test/micro_gmagick_test.rb
|
71
131
|
- test/micro_imagick_test.rb
|
72
132
|
- test/micro_magick_test.rb
|
73
|
-
- test/
|
133
|
+
- test/test_helper.rb
|
134
|
+
has_rdoc:
|
data/lib/micro_magick/convert.rb
DELETED
@@ -1,84 +0,0 @@
|
|
1
|
-
require 'shellwords'
|
2
|
-
|
3
|
-
module MicroMagick
|
4
|
-
class Convert
|
5
|
-
def initialize(input_file)
|
6
|
-
@input_file = input_file
|
7
|
-
@input_options = []
|
8
|
-
@output_options = []
|
9
|
-
end
|
10
|
-
|
11
|
-
def width
|
12
|
-
geometry.width
|
13
|
-
end
|
14
|
-
|
15
|
-
def height
|
16
|
-
geometry.height
|
17
|
-
end
|
18
|
-
|
19
|
-
def geometry
|
20
|
-
@geometry ||= MicroMagick::Geometry.new(@input_file)
|
21
|
-
end
|
22
|
-
|
23
|
-
# strip the image of any profiles or comments
|
24
|
-
# (ImageMagick has the -strip command, but GraphicsMagick doesn't.
|
25
|
-
# It turns out that ```+profile *``` does the same thing.)
|
26
|
-
def strip
|
27
|
-
add_output_option("+profile", "*")
|
28
|
-
end
|
29
|
-
|
30
|
-
# Crop to a square, using the specified gravity.
|
31
|
-
def square_crop(gravity = "Center")
|
32
|
-
gravity(gravity) unless gravity.nil?
|
33
|
-
d = [width, height].min
|
34
|
-
crop("#{d}x#{d}+0+0!")
|
35
|
-
end
|
36
|
-
|
37
|
-
# For normal options, like -resize or -flip, you can call .resize("32x32") or .flip().
|
38
|
-
# If you need to add an output option that starts with a '+', you can use this method.
|
39
|
-
def add_output_option(option_name, *args)
|
40
|
-
(@output_options ||= []) << option_name
|
41
|
-
@output_options += args.collect { |ea| Shellwords.escape(ea.to_s) }
|
42
|
-
|
43
|
-
# if we're a resize call, let's give the -size render hint to gm, but only when it's safe:
|
44
|
-
# * we don't have input options yet,
|
45
|
-
# * we're not cropping (because the -size will prevent the crop from working),
|
46
|
-
# * and we have dimensions in the form of NNNxNNN
|
47
|
-
if %w{-geometry -resize -sample -scale}.include?(option_name) &&
|
48
|
-
@input_options.empty? &&
|
49
|
-
!@output_options.include?("-crop")
|
50
|
-
if (dimensions = args.first)
|
51
|
-
if dimensions =~ /^(\d+x\d+)$/
|
52
|
-
@input_options << "-size #{dimensions}"
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
|
59
|
-
# Executes `convert`, writing to output_file, and clearing all previously set input and output options.
|
60
|
-
# @return the command given to system()
|
61
|
-
def write(output_file)
|
62
|
-
@output_file = output_file
|
63
|
-
cmd = command()
|
64
|
-
MicroMagick.exec(cmd)
|
65
|
-
@input_options.clear
|
66
|
-
@output_options.clear
|
67
|
-
cmd
|
68
|
-
end
|
69
|
-
|
70
|
-
protected
|
71
|
-
|
72
|
-
def method_missing(method, *args, &block)
|
73
|
-
add_output_option("-#{method.to_s}", *args)
|
74
|
-
end
|
75
|
-
|
76
|
-
def command
|
77
|
-
([MicroMagick.cmd_prefix, "convert"] +
|
78
|
-
@input_options +
|
79
|
-
[Shellwords.escape(@input_file)] +
|
80
|
-
@output_options +
|
81
|
-
[Shellwords.escape(@output_file)]).compact.join(" ")
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'shellwords'
|
2
|
-
|
3
|
-
module MicroMagick
|
4
|
-
class Geometry
|
5
|
-
attr_reader :width, :height
|
6
|
-
|
7
|
-
def initialize(input_file)
|
8
|
-
cmd = [MicroMagick.cmd_prefix,
|
9
|
-
"identify",
|
10
|
-
"-format",
|
11
|
-
"%w:%h",
|
12
|
-
Shellwords.escape(input_file)
|
13
|
-
]
|
14
|
-
geometry = MicroMagick.exec(cmd.join(" "))
|
15
|
-
@width, @height = geometry.split(':').collect { |ea| ea.to_i }
|
16
|
-
end
|
17
|
-
|
18
|
-
def to_s
|
19
|
-
"#{width} x #{height}"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
data/test/geometry_test.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
require "test/unit"
|
2
|
-
require "shellwords"
|
3
|
-
require "micro_magick"
|
4
|
-
|
5
|
-
class MicroMagickTestBase < Test::Unit::TestCase
|
6
|
-
|
7
|
-
def setup
|
8
|
-
@img = MicroMagick::Convert.new("test/480x270.jpg")
|
9
|
-
tmp = Tempfile.new('out.jpg')
|
10
|
-
@outfile = tmp.path
|
11
|
-
tmp.close
|
12
|
-
tmp.delete
|
13
|
-
assert !File.exist?(@outfile)
|
14
|
-
end
|
15
|
-
|
16
|
-
def cmd_prefix
|
17
|
-
s = MicroMagick.cmd_prefix
|
18
|
-
s.nil? ? "" : s + " "
|
19
|
-
end
|
20
|
-
|
21
|
-
def test_resize
|
22
|
-
@img.resize("64x64")
|
23
|
-
command = @img.write(@outfile)
|
24
|
-
assert_equal "#{cmd_prefix}convert -size 64x64 test/480x270.jpg -resize 64x64 " + Shellwords.escape(@outfile), command
|
25
|
-
assert File.exist?(@outfile)
|
26
|
-
g = MicroMagick::Geometry.new(@outfile)
|
27
|
-
assert_equal (64 * (270.0/480.0)).to_i, g.width
|
28
|
-
assert_equal 64, g.height
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_strip
|
32
|
-
@img.strip
|
33
|
-
command = @img.write(@outfile)
|
34
|
-
assert_equal "#{cmd_prefix}convert test/480x270.jpg +profile \\* " + Shellwords.escape(@outfile), command
|
35
|
-
end
|
36
|
-
|
37
|
-
def test_convert_with_crop
|
38
|
-
@img.quality(85)
|
39
|
-
@img.square_crop("North")
|
40
|
-
@img.resize("128x128")
|
41
|
-
command = @img.write(@outfile)
|
42
|
-
assert_equal "#{cmd_prefix}convert test/480x270.jpg " +
|
43
|
-
"-quality 85 -gravity North -crop 270x270\\+0\\+0\\! -resize 128x128 " +
|
44
|
-
Shellwords.escape(@outfile),
|
45
|
-
command
|
46
|
-
assert File.exist?(@outfile)
|
47
|
-
g = MicroMagick::Geometry.new(@outfile)
|
48
|
-
assert_equal 128, g.width
|
49
|
-
assert_equal 128, g.height
|
50
|
-
|
51
|
-
# make sure calling previous arguments don't leak into new calls:
|
52
|
-
@img.resize("64x64")
|
53
|
-
command = @img.write(@outfile)
|
54
|
-
assert_equal "#{cmd_prefix}convert -size 64x64 test/480x270.jpg -resize 64x64 " +
|
55
|
-
Shellwords.escape(@outfile),
|
56
|
-
command
|
57
|
-
end
|
58
|
-
|
59
|
-
def test_bad_args
|
60
|
-
@img.boing
|
61
|
-
assert_raise MicroMagick::InvalidArgument do
|
62
|
-
@img.write(@outfile)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|