atd-attachment_fu 1.0.20080507
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.
- data/CHANGELOG +35 -0
- data/README +186 -0
- data/Rakefile +22 -0
- data/amazon_s3.yml.tpl +14 -0
- data/attachment_fu.gemspec +79 -0
- data/lib/geometry.rb +93 -0
- data/lib/technoweenie/attachment_fu.rb +497 -0
- data/lib/technoweenie/attachment_fu/backends/db_file_backend.rb +39 -0
- data/lib/technoweenie/attachment_fu/backends/file_system_backend.rb +101 -0
- data/lib/technoweenie/attachment_fu/backends/s3_backend.rb +303 -0
- data/lib/technoweenie/attachment_fu/processors/core_image_processor.rb +59 -0
- data/lib/technoweenie/attachment_fu/processors/gd2_processor.rb +54 -0
- data/lib/technoweenie/attachment_fu/processors/image_science_processor.rb +61 -0
- data/lib/technoweenie/attachment_fu/processors/mini_magick_processor.rb +132 -0
- data/lib/technoweenie/attachment_fu/processors/rmagick_processor.rb +54 -0
- data/rails/init.rb +16 -0
- data/test/backends/db_file_test.rb +16 -0
- data/test/backends/file_system_test.rb +80 -0
- data/test/backends/remote/s3_test.rb +119 -0
- data/test/base_attachment_tests.rb +77 -0
- data/test/basic_test.rb +71 -0
- data/test/database.yml +18 -0
- data/test/extra_attachment_test.rb +86 -0
- data/test/fixtures/attachment.rb +183 -0
- data/test/fixtures/files/fake/rails.png +0 -0
- data/test/fixtures/files/foo.txt +1 -0
- data/test/fixtures/files/rails.png +0 -0
- data/test/geometry_test.rb +108 -0
- data/test/processors/core_image_test.rb +37 -0
- data/test/processors/gd2_test.rb +31 -0
- data/test/processors/image_science_test.rb +31 -0
- data/test/processors/mini_magick_test.rb +103 -0
- data/test/processors/rmagick_test.rb +255 -0
- data/test/schema.rb +109 -0
- data/test/test_helper.rb +150 -0
- data/test/validation_test.rb +55 -0
- data/vendor/red_artisan/core_image/filters/color.rb +27 -0
- data/vendor/red_artisan/core_image/filters/effects.rb +31 -0
- data/vendor/red_artisan/core_image/filters/perspective.rb +25 -0
- data/vendor/red_artisan/core_image/filters/quality.rb +25 -0
- data/vendor/red_artisan/core_image/filters/scale.rb +47 -0
- data/vendor/red_artisan/core_image/filters/watermark.rb +32 -0
- data/vendor/red_artisan/core_image/processor.rb +123 -0
- metadata +116 -0
data/test/schema.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 0) do
|
2
|
+
create_table :attachments, :force => true do |t|
|
3
|
+
t.column :db_file_id, :integer
|
4
|
+
t.column :parent_id, :integer
|
5
|
+
t.column :thumbnail, :string
|
6
|
+
t.column :filename, :string, :limit => 255
|
7
|
+
t.column :content_type, :string, :limit => 255
|
8
|
+
t.column :size, :integer
|
9
|
+
t.column :width, :integer
|
10
|
+
t.column :height, :integer
|
11
|
+
t.column :aspect_ratio, :float
|
12
|
+
end
|
13
|
+
|
14
|
+
create_table :file_attachments, :force => true do |t|
|
15
|
+
t.column :parent_id, :integer
|
16
|
+
t.column :thumbnail, :string
|
17
|
+
t.column :filename, :string, :limit => 255
|
18
|
+
t.column :content_type, :string, :limit => 255
|
19
|
+
t.column :size, :integer
|
20
|
+
t.column :width, :integer
|
21
|
+
t.column :height, :integer
|
22
|
+
t.column :type, :string
|
23
|
+
t.column :aspect_ratio, :float
|
24
|
+
end
|
25
|
+
|
26
|
+
create_table :gd2_attachments, :force => true do |t|
|
27
|
+
t.column :parent_id, :integer
|
28
|
+
t.column :thumbnail, :string
|
29
|
+
t.column :filename, :string, :limit => 255
|
30
|
+
t.column :content_type, :string, :limit => 255
|
31
|
+
t.column :size, :integer
|
32
|
+
t.column :width, :integer
|
33
|
+
t.column :height, :integer
|
34
|
+
t.column :type, :string
|
35
|
+
end
|
36
|
+
|
37
|
+
create_table :image_science_attachments, :force => true do |t|
|
38
|
+
t.column :parent_id, :integer
|
39
|
+
t.column :thumbnail, :string
|
40
|
+
t.column :filename, :string, :limit => 255
|
41
|
+
t.column :content_type, :string, :limit => 255
|
42
|
+
t.column :size, :integer
|
43
|
+
t.column :width, :integer
|
44
|
+
t.column :height, :integer
|
45
|
+
t.column :type, :string
|
46
|
+
end
|
47
|
+
|
48
|
+
create_table :core_image_attachments, :force => true do |t|
|
49
|
+
t.column :parent_id, :integer
|
50
|
+
t.column :thumbnail, :string
|
51
|
+
t.column :filename, :string, :limit => 255
|
52
|
+
t.column :content_type, :string, :limit => 255
|
53
|
+
t.column :size, :integer
|
54
|
+
t.column :width, :integer
|
55
|
+
t.column :height, :integer
|
56
|
+
t.column :type, :string
|
57
|
+
end
|
58
|
+
|
59
|
+
create_table :mini_magick_attachments, :force => true do |t|
|
60
|
+
t.column :parent_id, :integer
|
61
|
+
t.column :thumbnail, :string
|
62
|
+
t.column :filename, :string, :limit => 255
|
63
|
+
t.column :content_type, :string, :limit => 255
|
64
|
+
t.column :size, :integer
|
65
|
+
t.column :width, :integer
|
66
|
+
t.column :height, :integer
|
67
|
+
t.column :type, :string
|
68
|
+
end
|
69
|
+
|
70
|
+
create_table :mini_magick_attachments, :force => true do |t|
|
71
|
+
t.column :parent_id, :integer
|
72
|
+
t.column :thumbnail, :string
|
73
|
+
t.column :filename, :string, :limit => 255
|
74
|
+
t.column :content_type, :string, :limit => 255
|
75
|
+
t.column :size, :integer
|
76
|
+
t.column :width, :integer
|
77
|
+
t.column :height, :integer
|
78
|
+
t.column :type, :string
|
79
|
+
end
|
80
|
+
|
81
|
+
create_table :orphan_attachments, :force => true do |t|
|
82
|
+
t.column :db_file_id, :integer
|
83
|
+
t.column :filename, :string, :limit => 255
|
84
|
+
t.column :content_type, :string, :limit => 255
|
85
|
+
t.column :size, :integer
|
86
|
+
end
|
87
|
+
|
88
|
+
create_table :minimal_attachments, :force => true do |t|
|
89
|
+
t.column :size, :integer
|
90
|
+
t.column :content_type, :string, :limit => 255
|
91
|
+
t.column :spare_data, :string
|
92
|
+
end
|
93
|
+
|
94
|
+
create_table :db_files, :force => true do |t|
|
95
|
+
t.column :data, :binary
|
96
|
+
end
|
97
|
+
|
98
|
+
create_table :s3_attachments, :force => true do |t|
|
99
|
+
t.column :parent_id, :integer
|
100
|
+
t.column :thumbnail, :string
|
101
|
+
t.column :filename, :string, :limit => 255
|
102
|
+
t.column :content_type, :string, :limit => 255
|
103
|
+
t.column :size, :integer
|
104
|
+
t.column :width, :integer
|
105
|
+
t.column :height, :integer
|
106
|
+
t.column :type, :string
|
107
|
+
t.column :aspect_ratio, :float
|
108
|
+
end
|
109
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
ENV['RAILS_ENV'] = 'test'
|
4
|
+
ENV['RAILS_ROOT'] ||= File.dirname(__FILE__) + '/../../../..'
|
5
|
+
|
6
|
+
require 'test/unit'
|
7
|
+
require File.expand_path(File.join(ENV['RAILS_ROOT'], 'config/environment.rb'))
|
8
|
+
require 'active_record/fixtures'
|
9
|
+
require 'action_controller/test_process'
|
10
|
+
|
11
|
+
config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
|
12
|
+
ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
|
13
|
+
|
14
|
+
db_adapter = ENV['DB']
|
15
|
+
|
16
|
+
# no db passed, try one of these fine config-free DBs before bombing.
|
17
|
+
db_adapter ||=
|
18
|
+
begin
|
19
|
+
require 'rubygems'
|
20
|
+
require 'sqlite'
|
21
|
+
'sqlite'
|
22
|
+
rescue MissingSourceFile
|
23
|
+
begin
|
24
|
+
require 'sqlite3'
|
25
|
+
'sqlite3'
|
26
|
+
rescue MissingSourceFile
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
if db_adapter.nil?
|
31
|
+
raise "No DB Adapter selected. Pass the DB= option to pick one, or install Sqlite or Sqlite3."
|
32
|
+
end
|
33
|
+
|
34
|
+
ActiveRecord::Base.establish_connection(config[db_adapter])
|
35
|
+
|
36
|
+
load(File.dirname(__FILE__) + "/schema.rb")
|
37
|
+
|
38
|
+
Test::Unit::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures"
|
39
|
+
$LOAD_PATH.unshift(Test::Unit::TestCase.fixture_path)
|
40
|
+
|
41
|
+
class Test::Unit::TestCase #:nodoc:
|
42
|
+
include ActionController::TestProcess
|
43
|
+
def create_fixtures(*table_names)
|
44
|
+
if block_given?
|
45
|
+
Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) { yield }
|
46
|
+
else
|
47
|
+
Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def setup
|
52
|
+
Attachment.saves = 0
|
53
|
+
DbFile.transaction { [Attachment, FileAttachment, OrphanAttachment, MinimalAttachment, DbFile].each { |klass| klass.delete_all } }
|
54
|
+
attachment_model self.class.attachment_model
|
55
|
+
end
|
56
|
+
|
57
|
+
def teardown
|
58
|
+
FileUtils.rm_rf File.join(File.dirname(__FILE__), 'files')
|
59
|
+
end
|
60
|
+
|
61
|
+
self.use_transactional_fixtures = true
|
62
|
+
self.use_instantiated_fixtures = false
|
63
|
+
|
64
|
+
def self.attachment_model(klass = nil)
|
65
|
+
@attachment_model = klass if klass
|
66
|
+
@attachment_model
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.test_against_class(test_method, klass, subclass = false)
|
70
|
+
define_method("#{test_method}_on_#{:sub if subclass}class") do
|
71
|
+
klass = Class.new(klass) if subclass
|
72
|
+
attachment_model klass
|
73
|
+
send test_method, klass
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.test_against_subclass(test_method, klass)
|
78
|
+
test_against_class test_method, klass, true
|
79
|
+
end
|
80
|
+
|
81
|
+
protected
|
82
|
+
def upload_file(options = {})
|
83
|
+
use_temp_file options[:filename] do |file|
|
84
|
+
att = attachment_model.create :uploaded_data => fixture_file_upload(file, options[:content_type] || 'image/png')
|
85
|
+
att.reload unless att.new_record?
|
86
|
+
return att
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def upload_merb_file(options = {})
|
91
|
+
use_temp_file options[:filename] do |file|
|
92
|
+
att = attachment_model.create :uploaded_data => {"size" => file.size, "content_type" => options[:content_type] || 'image/png', "filename" => file, 'tempfile' => fixture_file_upload(file, options[:content_type] || 'image/png')}
|
93
|
+
att.reload unless att.new_record?
|
94
|
+
return att
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def use_temp_file(fixture_filename)
|
99
|
+
temp_path = File.join('/tmp', File.basename(fixture_filename))
|
100
|
+
FileUtils.mkdir_p File.join(fixture_path, 'tmp')
|
101
|
+
FileUtils.cp File.join(fixture_path, fixture_filename), File.join(fixture_path, temp_path)
|
102
|
+
yield temp_path
|
103
|
+
ensure
|
104
|
+
FileUtils.rm_rf File.join(fixture_path, 'tmp')
|
105
|
+
end
|
106
|
+
|
107
|
+
def assert_created(num = 1)
|
108
|
+
assert_difference attachment_model.base_class, :count, num do
|
109
|
+
if attachment_model.included_modules.include? DbFile
|
110
|
+
assert_difference DbFile, :count, num do
|
111
|
+
yield
|
112
|
+
end
|
113
|
+
else
|
114
|
+
yield
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def assert_not_created
|
120
|
+
assert_created(0) { yield }
|
121
|
+
end
|
122
|
+
|
123
|
+
def should_reject_by_size_with(klass)
|
124
|
+
attachment_model klass
|
125
|
+
assert_not_created do
|
126
|
+
attachment = upload_file :filename => '/files/rails.png'
|
127
|
+
assert attachment.new_record?
|
128
|
+
assert attachment.errors.on(:size)
|
129
|
+
assert_nil attachment.db_file if attachment.respond_to?(:db_file)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def assert_difference(object, method = nil, difference = 1)
|
134
|
+
initial_value = object.send(method)
|
135
|
+
yield
|
136
|
+
assert_equal initial_value + difference, object.send(method)
|
137
|
+
end
|
138
|
+
|
139
|
+
def assert_no_difference(object, method, &block)
|
140
|
+
assert_difference object, method, 0, &block
|
141
|
+
end
|
142
|
+
|
143
|
+
def attachment_model(klass = nil)
|
144
|
+
@attachment_model = klass if klass
|
145
|
+
@attachment_model
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
require File.join(File.dirname(__FILE__), 'fixtures/attachment')
|
150
|
+
require File.join(File.dirname(__FILE__), 'base_attachment_tests')
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
2
|
+
|
3
|
+
class ValidationTest < Test::Unit::TestCase
|
4
|
+
def test_should_invalidate_big_files
|
5
|
+
@attachment = SmallAttachment.new
|
6
|
+
assert !@attachment.valid?
|
7
|
+
assert @attachment.errors.on(:size)
|
8
|
+
|
9
|
+
@attachment.size = 2000
|
10
|
+
assert !@attachment.valid?
|
11
|
+
assert @attachment.errors.on(:size), @attachment.errors.full_messages.to_sentence
|
12
|
+
|
13
|
+
@attachment.size = 1000
|
14
|
+
assert !@attachment.valid?
|
15
|
+
assert_nil @attachment.errors.on(:size)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_should_invalidate_small_files
|
19
|
+
@attachment = BigAttachment.new
|
20
|
+
assert !@attachment.valid?
|
21
|
+
assert @attachment.errors.on(:size)
|
22
|
+
|
23
|
+
@attachment.size = 2000
|
24
|
+
assert !@attachment.valid?
|
25
|
+
assert @attachment.errors.on(:size), @attachment.errors.full_messages.to_sentence
|
26
|
+
|
27
|
+
@attachment.size = 1.megabyte
|
28
|
+
assert !@attachment.valid?
|
29
|
+
assert_nil @attachment.errors.on(:size)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_should_validate_content_type
|
33
|
+
@attachment = PdfAttachment.new
|
34
|
+
assert !@attachment.valid?
|
35
|
+
assert @attachment.errors.on(:content_type)
|
36
|
+
|
37
|
+
@attachment.content_type = 'foo'
|
38
|
+
assert !@attachment.valid?
|
39
|
+
assert @attachment.errors.on(:content_type)
|
40
|
+
|
41
|
+
@attachment.content_type = 'pdf'
|
42
|
+
assert !@attachment.valid?
|
43
|
+
assert_nil @attachment.errors.on(:content_type)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_should_require_filename
|
47
|
+
@attachment = Attachment.new
|
48
|
+
assert !@attachment.valid?
|
49
|
+
assert @attachment.errors.on(:filename)
|
50
|
+
|
51
|
+
@attachment.filename = 'foo'
|
52
|
+
assert !@attachment.valid?
|
53
|
+
assert_nil @attachment.errors.on(:filename)
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module RedArtisan
|
2
|
+
module CoreImage
|
3
|
+
module Filters
|
4
|
+
module Color
|
5
|
+
|
6
|
+
def greyscale(color = nil, intensity = 1.00)
|
7
|
+
create_core_image_context(@original.extent.size.width, @original.extent.size.height)
|
8
|
+
|
9
|
+
color = OSX::CIColor.colorWithString("1.0 1.0 1.0 1.0") unless color
|
10
|
+
|
11
|
+
@original.color_monochrome :inputColor => color, :inputIntensity => intensity do |greyscale|
|
12
|
+
@target = greyscale
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def sepia(intensity = 1.00)
|
17
|
+
create_core_image_context(@original.extent.size.width, @original.extent.size.height)
|
18
|
+
|
19
|
+
@original.sepia_tone :inputIntensity => intensity do |sepia|
|
20
|
+
@target = sepia
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module RedArtisan
|
2
|
+
module CoreImage
|
3
|
+
module Filters
|
4
|
+
module Effects
|
5
|
+
|
6
|
+
def spotlight(position, points_at, brightness, concentration, color)
|
7
|
+
create_core_image_context(@original.extent.size.width, @original.extent.size.height)
|
8
|
+
|
9
|
+
@original.spot_light :inputLightPosition => vector3(*position), :inputLightPointsAt => vector3(*points_at),
|
10
|
+
:inputBrightness => brightness, :inputConcentration => concentration, :inputColor => color do |spot|
|
11
|
+
@target = spot
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def edges(intensity = 1.00)
|
16
|
+
create_core_image_context(@original.extent.size.width, @original.extent.size.height)
|
17
|
+
|
18
|
+
@original.edges :inputIntensity => intensity do |edged|
|
19
|
+
@target = edged
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def vector3(x, y, w)
|
26
|
+
OSX::CIVector.vectorWithX_Y_Z(x, y, w)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RedArtisan
|
2
|
+
module CoreImage
|
3
|
+
module Filters
|
4
|
+
module Perspective
|
5
|
+
|
6
|
+
def perspective(top_left, top_right, bottom_left, bottom_right)
|
7
|
+
create_core_image_context(@original.extent.size.width, @original.extent.size.height)
|
8
|
+
|
9
|
+
@original.perspective_transform :inputTopLeft => top_left, :inputTopRight => top_right, :inputBottomLeft => bottom_left, :inputBottomRight => bottom_right do |transformed|
|
10
|
+
@target = transformed
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def perspective_tiled(top_left, top_right, bottom_left, bottom_right)
|
15
|
+
create_core_image_context(@original.extent.size.width, @original.extent.size.height)
|
16
|
+
|
17
|
+
@original.perspective_tile :inputTopLeft => top_left, :inputTopRight => top_right, :inputBottomLeft => bottom_left, :inputBottomRight => bottom_right do |tiled|
|
18
|
+
@target = tiled
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module RedArtisan
|
2
|
+
module CoreImage
|
3
|
+
module Filters
|
4
|
+
module Quality
|
5
|
+
|
6
|
+
def reduce_noise(level = 0.02, sharpness = 0.4)
|
7
|
+
create_core_image_context(@original.extent.size.width, @original.extent.size.height)
|
8
|
+
|
9
|
+
@original.noise_reduction :inputNoiseLevel => level, :inputSharpness => sharpness do |noise_reduced|
|
10
|
+
@target = noise_reduced
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def adjust_exposure(input_ev = 0.5)
|
15
|
+
create_core_image_context(@original.extent.size.width, @original.extent.size.height)
|
16
|
+
|
17
|
+
@original.exposure_adjust :inputEV => input_ev do |adjusted|
|
18
|
+
@target = adjusted
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module RedArtisan
|
2
|
+
module CoreImage
|
3
|
+
module Filters
|
4
|
+
module Scale
|
5
|
+
|
6
|
+
def resize(width, height)
|
7
|
+
create_core_image_context(width, height)
|
8
|
+
|
9
|
+
scale_x, scale_y = scale(width, height)
|
10
|
+
|
11
|
+
@original.affine_clamp :inputTransform => OSX::NSAffineTransform.transform do |clamped|
|
12
|
+
clamped.lanczos_scale_transform :inputScale => scale_x > scale_y ? scale_x : scale_y, :inputAspectRatio => scale_x / scale_y do |scaled|
|
13
|
+
scaled.crop :inputRectangle => vector(0, 0, width, height) do |cropped|
|
14
|
+
@target = cropped
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def thumbnail(width, height)
|
21
|
+
create_core_image_context(width, height)
|
22
|
+
|
23
|
+
transform = OSX::NSAffineTransform.transform
|
24
|
+
transform.scaleXBy_yBy *scale(width, height)
|
25
|
+
|
26
|
+
@original.affine_transform :inputTransform => transform do |scaled|
|
27
|
+
@target = scaled
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def fit(size)
|
32
|
+
original_size = @original.extent.size
|
33
|
+
scale = size.to_f / (original_size.width > original_size.height ? original_size.width : original_size.height)
|
34
|
+
resize (original_size.width * scale).to_i, (original_size.height * scale).to_i
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def scale(width, height)
|
40
|
+
original_size = @original.extent.size
|
41
|
+
return width.to_f / original_size.width.to_f, height.to_f / original_size.height.to_f
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|