canvas-resize-rails 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d585ef9707a4b802805b8cb2ce6c2d346e498299
4
+ data.tar.gz: 79dd6fbb1d91a603b2491ef195dfa401dd9773b7
5
+ SHA512:
6
+ metadata.gz: fcedfc79ca4e762e9268ad8b87a4034649e3d73ffe5a39256d47e50b1321a58c6201c4e70b3bc379d753c687a08d85a83509a6ca78ae7b64db103ada4cc5886b
7
+ data.tar.gz: a570c4b9f7f1f164d0c73a00255a7bfc265fb73af1a932f3c2598753f0afc43f065d55261ce9dfcd8fa620cbe314217750fb4fed5fbecf8323b296bd301c10d8
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in canvas-resize-rails.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 maedana
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,45 @@
1
+ # Canvas::Resize::Rails
2
+
3
+ [canvasResize](https://github.com/gokercebeci/canvasResize)をRails+carrierwaveで簡単に使えるようにする
4
+
5
+ ## インストール
6
+
7
+ Gemファイルに以下を追加
8
+
9
+ ```ruby
10
+ gem 'canvas-resize-rails'
11
+ ```
12
+
13
+ application.jsに以下を追加
14
+
15
+ ```javascript
16
+ //= require canvas-resize-sprockets
17
+ ```
18
+
19
+ ## 使い方
20
+
21
+ carrierwaveのmodelをProfile, 対象のカラム名をimageとする場合、対象のmodelに以下のように設定
22
+
23
+ ```ruby
24
+ mount_uploader :image, ImageUploader
25
+ canvas_resize :image
26
+ ```
27
+
28
+ formで例えば以下のようになっている場合、
29
+
30
+ ```haml
31
+ = simple_form_for(@profile) do |f|
32
+ = f.input :image, as: :file, input_html: { class: 'resize_trigger' }
33
+ ```
34
+
35
+ jsで以下のようにする。
36
+
37
+ ```coffee
38
+ $("input.resize_trigger").prepareToResizeForAll()
39
+ ```
40
+
41
+ これでファイルに画像を設定するタイミングで自動的に以下の処理が行われる
42
+ * profile[image_base64]というhidden_fieldにbase64でエンコードされた画像が設定される
43
+ * profile[image_file_name]というhidden_fieldにファイル名が設定される
44
+
45
+ この状態でsubmitするとリサイズ済みのファイルがcarrierwaveを通じて保存される
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,3 @@
1
+ //= require jquery.canvasResize
2
+ //= require jquery.exif
3
+ //= require canvas-resize
@@ -0,0 +1,40 @@
1
+ $ ->
2
+ jQuery.fn.extend
3
+ prepareToResizeForAll: ->
4
+ isImageFile = (file) ->
5
+ return (/image/i).test(file.type)
6
+
7
+ processFile = (file, base64FieldName, fileNameFieldName, form) ->
8
+ # Ref) http://lealog.hateblo.jp/entry/2013/03/21/212608
9
+ # TODO 外からoptionを渡せるようにする
10
+ $.canvasResize file,
11
+ width: 800
12
+ height: 0
13
+ crop: false
14
+ quality: 80
15
+ callback: (data, width, height) ->
16
+ resizedData = "<input type='hidden' name='#{base64FieldName}' value='#{data}'>"
17
+ resizedFileName = "<input type='hidden' name='#{fileNameFieldName}' value='#{file.name}'>"
18
+ $(form).append(resizedData)
19
+ $(form).append(resizedFileName)
20
+
21
+ $(this).change (e) ->
22
+ unless window.File and window.FileReader and window.FileList and window.Blob
23
+ console.log "The File APIs are not fully supported in this browser."
24
+ return false
25
+
26
+ modelName = $(this).attr('id').split('_')[0]
27
+ columnName = $(this).attr('id').split('_')[1]
28
+ fileField = e.target
29
+ form = $(fileField).closest('form')[0]
30
+
31
+ # ファイル再選択時に不要なhiddenを削除
32
+ $(form).find("input[name*=#{columnName}_base64\\]]").remove()
33
+ $(form).find("input[name*=#{columnName}_file_name\\]]").remove()
34
+
35
+ file = fileField.files[0]
36
+ if isImageFile(file)
37
+ base64FieldName = "#{modelName}[#{columnName}_base64]"
38
+ fileNameFieldName = "#{modelName}[#{columnName}_file_name]"
39
+ processFile file, base64FieldName, fileNameFieldName, form
40
+
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'canvas/resize/rails/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "canvas-resize-rails"
8
+ spec.version = Canvas::Resize::Rails::VERSION
9
+ spec.authors = ["maedana"]
10
+ spec.email = ["maeda.na@gmail.com"]
11
+ spec.summary = %q{Use canvasResize with Rails and carrierwave}
12
+ spec.description = %q{This gem provides canvasResize driver for your Rails Application}
13
+ spec.homepage = "https://github.com/maedana/canvas-resize-rails"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_dependency 'activesupport', '~>3.2'
24
+ spec.add_dependency 'activerecord', '~>3.2'
25
+ end
@@ -0,0 +1 @@
1
+ require 'canvas/resize/rails'
@@ -0,0 +1,16 @@
1
+ require 'active_record'
2
+ require 'canvas/resize/models/base64_decodable'
3
+
4
+ module Canvas
5
+ module Resize
6
+ module Model
7
+ module ActiveRecord
8
+ def canvas_resize column
9
+ include ::Canvas::Resize::Model::Base64Decodable
10
+ before_validation :decode_base64_image
11
+ data_field_prefix column
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,59 @@
1
+ require 'active_support'
2
+ module Canvas
3
+ module Resize
4
+ module Model
5
+ module Base64Decodable
6
+ extend ::ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ def data_field_prefix(prefix)
10
+ class_attribute :_data_field_prefix
11
+ self._data_field_prefix = prefix
12
+
13
+ sym_base64 = "#{self._data_field_prefix}_base64".to_sym
14
+ attr_accessible sym_base64
15
+ attr_accessor sym_base64
16
+
17
+ sym_file_name = "#{self._data_field_prefix}_file_name".to_sym
18
+ attr_accessible sym_file_name
19
+ attr_accessor sym_file_name
20
+ end
21
+ end
22
+
23
+ def data_field_prefix
24
+ self.class._data_field_prefix.to_s
25
+ end
26
+
27
+ def base64_field
28
+ self.send("#{data_field_prefix}_base64")
29
+ end
30
+
31
+ def file_name_field
32
+ self.send("#{data_field_prefix}_file_name")
33
+ end
34
+
35
+ def data_field=(data)
36
+ self.send("#{data_field_prefix}=", data)
37
+ end
38
+
39
+ def blank_base64_data?
40
+ base64_field.blank? || file_name_field.blank?
41
+ end
42
+
43
+ def decode_base64_image
44
+ unless blank_base64_data?
45
+ # base64_field should be like "data:image/jpeg;base64,/9j/4AAQ..."
46
+ splitted_data = base64_field.split(/[:;,]/, 4)
47
+ decoded_data = Base64.decode64(splitted_data.last)
48
+
49
+ self.data_field = StringIO.new(decoded_data).tap do |data|
50
+ data.class_eval{ attr_accessor :original_filename, :content_type }
51
+ data.content_type = splitted_data.second
52
+ data.original_filename = File.basename(file_name_field)
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,10 @@
1
+ require 'canvas/resize/rails/engine'
2
+ require 'canvas/resize/rails/railtie'
3
+ require "canvas/resize/rails/version"
4
+
5
+ module Canvas
6
+ module Resize
7
+ module Rails
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ module Canvas
2
+ module Resize
3
+ module Rails
4
+ class Engine < ::Rails::Engine
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,17 @@
1
+ require 'canvas/resize/models/active_record'
2
+
3
+ module Canvas
4
+ module Resize
5
+ module Rails
6
+ require 'rails'
7
+ class Railtie < ::Rails::Railtie
8
+ initializer 'canvas-resize-rails.insert_into_active_record' do
9
+ ActiveSupport.on_load :active_record do
10
+ ::ActiveRecord::Base.extend ::Canvas::Resize::Model::ActiveRecord
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,7 @@
1
+ module Canvas
2
+ module Resize
3
+ module Rails
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,313 @@
1
+ /*
2
+ * jQuery canvasResize plugin
3
+ *
4
+ * Version: 1.2.0
5
+ * Date (d/m/y): 02/10/12
6
+ * Update (d/m/y): 14/05/13
7
+ * Original author: @gokercebeci
8
+ * Licensed under the MIT license
9
+ * - This plugin working with jquery.exif.js
10
+ * (It's under the MPL License http://www.nihilogic.dk/licenses/mpl-license.txt)
11
+ * Demo: http://ios6-image-resize.gokercebeci.com/
12
+ *
13
+ * - I fixed iOS6 Safari's image file rendering issue for large size image (over mega-pixel)
14
+ * using few functions from https://github.com/stomita/ios-imagefile-megapixel
15
+ * (detectSubsampling, )
16
+ * And fixed orientation issue by edited http://blog.nihilogic.dk/2008/05/jquery-exif-data-plugin.html
17
+ * Thanks, Shinichi Tomita and Jacob Seidelin
18
+ */
19
+
20
+ (function($) {
21
+ var pluginName = 'canvasResize',
22
+ methods = {
23
+ newsize: function(w, h, W, H, C) {
24
+ var c = C ? 'h' : '';
25
+ if ((W && w > W) || (H && h > H)) {
26
+ var r = w / h;
27
+ if ((r >= 1 || H === 0) && W && !C) {
28
+ w = W;
29
+ h = (W / r) >> 0;
30
+ } else if (C && r <= (W / H)) {
31
+ w = W;
32
+ h = (W / r) >> 0;
33
+ c = 'w';
34
+ } else {
35
+ w = (H * r) >> 0;
36
+ h = H;
37
+ }
38
+ }
39
+ return {
40
+ 'width': w,
41
+ 'height': h,
42
+ 'cropped': c
43
+ };
44
+ },
45
+ dataURLtoBlob: function(data) {
46
+ var mimeString = data.split(',')[0].split(':')[1].split(';')[0];
47
+ var byteString = atob(data.split(',')[1]);
48
+ var ab = new ArrayBuffer(byteString.length);
49
+ var ia = new Uint8Array(ab);
50
+ for (var i = 0; i < byteString.length; i++) {
51
+ ia[i] = byteString.charCodeAt(i);
52
+ }
53
+ var bb = (window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder);
54
+ if (bb) {
55
+ // console.log('BlobBuilder');
56
+ bb = new (window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder)();
57
+ bb.append(ab);
58
+ return bb.getBlob(mimeString);
59
+ } else {
60
+ // console.log('Blob');
61
+ bb = new Blob([ab], {
62
+ 'type': (mimeString)
63
+ });
64
+ return bb;
65
+ }
66
+ },
67
+ /**
68
+ * Detect subsampling in loaded image.
69
+ * In iOS, larger images than 2M pixels may be subsampled in rendering.
70
+ */
71
+ detectSubsampling: function(img) {
72
+ var iw = img.width, ih = img.height;
73
+ if (iw * ih > 1048576) { // subsampling may happen over megapixel image
74
+ var canvas = document.createElement('canvas');
75
+ canvas.width = canvas.height = 1;
76
+ var ctx = canvas.getContext('2d');
77
+ ctx.drawImage(img, -iw + 1, 0);
78
+ // subsampled image becomes half smaller in rendering size.
79
+ // check alpha channel value to confirm image is covering edge pixel or not.
80
+ // if alpha value is 0 image is not covering, hence subsampled.
81
+ return ctx.getImageData(0, 0, 1, 1).data[3] === 0;
82
+ } else {
83
+ return false;
84
+ }
85
+ },
86
+ /**
87
+ * Update the orientation according to the specified rotation angle
88
+ */
89
+ rotate: function(orientation, angle) {
90
+ var o = {
91
+ // nothing
92
+ 1: {90: 6, 180: 3, 270: 8},
93
+ // horizontal flip
94
+ 2: {90: 7, 180: 4, 270: 5},
95
+ // 180 rotate left
96
+ 3: {90: 8, 180: 1, 270: 6},
97
+ // vertical flip
98
+ 4: {90: 5, 180: 2, 270: 7},
99
+ // vertical flip + 90 rotate right
100
+ 5: {90: 2, 180: 7, 270: 4},
101
+ // 90 rotate right
102
+ 6: {90: 3, 180: 8, 270: 1},
103
+ // horizontal flip + 90 rotate right
104
+ 7: {90: 4, 180: 5, 270: 2},
105
+ // 90 rotate left
106
+ 8: {90: 1, 180: 6, 270: 3}
107
+ };
108
+ return o[orientation][angle] ? o[orientation][angle] : orientation;
109
+ },
110
+ /**
111
+ * Transform canvas coordination according to specified frame size and orientation
112
+ * Orientation value is from EXIF tag
113
+ */
114
+ transformCoordinate: function(canvas, width, height, orientation) {
115
+ //console.log(width, height);
116
+ switch (orientation) {
117
+ case 5:
118
+ case 6:
119
+ case 7:
120
+ case 8:
121
+ canvas.width = height;
122
+ canvas.height = width;
123
+ break;
124
+ default:
125
+ canvas.width = width;
126
+ canvas.height = height;
127
+ }
128
+ var ctx = canvas.getContext('2d');
129
+ switch (orientation) {
130
+ case 1:
131
+ // nothing
132
+ break;
133
+ case 2:
134
+ // horizontal flip
135
+ ctx.translate(width, 0);
136
+ ctx.scale(-1, 1);
137
+ break;
138
+ case 3:
139
+ // 180 rotate left
140
+ ctx.translate(width, height);
141
+ ctx.rotate(Math.PI);
142
+ break;
143
+ case 4:
144
+ // vertical flip
145
+ ctx.translate(0, height);
146
+ ctx.scale(1, -1);
147
+ break;
148
+ case 5:
149
+ // vertical flip + 90 rotate right
150
+ ctx.rotate(0.5 * Math.PI);
151
+ ctx.scale(1, -1);
152
+ break;
153
+ case 6:
154
+ // 90 rotate right
155
+ ctx.rotate(0.5 * Math.PI);
156
+ ctx.translate(0, -height);
157
+ break;
158
+ case 7:
159
+ // horizontal flip + 90 rotate right
160
+ ctx.rotate(0.5 * Math.PI);
161
+ ctx.translate(width, -height);
162
+ ctx.scale(-1, 1);
163
+ break;
164
+ case 8:
165
+ // 90 rotate left
166
+ ctx.rotate(-0.5 * Math.PI);
167
+ ctx.translate(-width, 0);
168
+ break;
169
+ default:
170
+ break;
171
+ }
172
+ },
173
+ /**
174
+ * Detecting vertical squash in loaded image.
175
+ * Fixes a bug which squash image vertically while drawing into canvas for some images.
176
+ */
177
+ detectVerticalSquash: function(img, iw, ih) {
178
+ var canvas = document.createElement('canvas');
179
+ canvas.width = 1;
180
+ canvas.height = ih;
181
+ var ctx = canvas.getContext('2d');
182
+ ctx.drawImage(img, 0, 0);
183
+ var data = ctx.getImageData(0, 0, 1, ih).data;
184
+ // search image edge pixel position in case it is squashed vertically.
185
+ var sy = 0;
186
+ var ey = ih;
187
+ var py = ih;
188
+ while (py > sy) {
189
+ var alpha = data[(py - 1) * 4 + 3];
190
+ if (alpha === 0) {
191
+ ey = py;
192
+ } else {
193
+ sy = py;
194
+ }
195
+ py = (ey + sy) >> 1;
196
+ }
197
+ var ratio = py / ih;
198
+ return ratio === 0 ? 1 : ratio;
199
+ },
200
+ callback: function(d) {
201
+ return d;
202
+ }
203
+ },
204
+ defaults = {
205
+ width: 300,
206
+ height: 0,
207
+ crop: false,
208
+ quality: 80,
209
+ 'callback': methods.callback
210
+ };
211
+ function Plugin(file, options) {
212
+ this.file = file;
213
+ this.options = $.extend({}, defaults, options);
214
+ this._defaults = defaults;
215
+ this._name = pluginName;
216
+ this.init();
217
+ }
218
+ Plugin.prototype = {
219
+ init: function() {
220
+ //this.options.init(this);
221
+ var $this = this;
222
+ var file = this.file;
223
+
224
+ var reader = new FileReader();
225
+ reader.onloadend = function(e) {
226
+ var dataURL = e.target.result;
227
+ var img = new Image();
228
+ img.onload = function(e) {
229
+ // Read Orientation Data in EXIF
230
+ $(img).exifLoadFromDataURL(function() {
231
+ var orientation = $(img).exif('Orientation')[0] || 1;
232
+ orientation = methods.rotate(orientation, $this.options.rotate);
233
+
234
+ // CW or CCW ? replace width and height
235
+ var size = (orientation >= 5 && orientation <= 8)
236
+ ? methods.newsize(img.height, img.width, $this.options.width, $this.options.height, $this.options.crop)
237
+ : methods.newsize(img.width, img.height, $this.options.width, $this.options.height, $this.options.crop);
238
+
239
+ var iw = img.width, ih = img.height;
240
+ var width = size.width, height = size.height;
241
+
242
+ //console.log(iw, ih, size.width, size.height, orientation);
243
+
244
+ var canvas = document.createElement("canvas");
245
+ var ctx = canvas.getContext("2d");
246
+ ctx.save();
247
+ methods.transformCoordinate(canvas, width, height, orientation);
248
+
249
+ // over image size
250
+ if (methods.detectSubsampling(img)) {
251
+ iw /= 2;
252
+ ih /= 2;
253
+ }
254
+ var d = 1024; // size of tiling canvas
255
+ var tmpCanvas = document.createElement('canvas');
256
+ tmpCanvas.width = tmpCanvas.height = d;
257
+ var tmpCtx = tmpCanvas.getContext('2d');
258
+ var vertSquashRatio = methods.detectVerticalSquash(img, iw, ih);
259
+ var sy = 0;
260
+ while (sy < ih) {
261
+ var sh = sy + d > ih ? ih - sy : d;
262
+ var sx = 0;
263
+ while (sx < iw) {
264
+ var sw = sx + d > iw ? iw - sx : d;
265
+ tmpCtx.clearRect(0, 0, d, d);
266
+ tmpCtx.drawImage(img, -sx, -sy);
267
+ var dx = Math.floor(sx * width / iw);
268
+ var dw = Math.ceil(sw * width / iw);
269
+ var dy = Math.floor(sy * height / ih / vertSquashRatio);
270
+ var dh = Math.ceil(sh * height / ih / vertSquashRatio);
271
+ ctx.drawImage(tmpCanvas, 0, 0, sw, sh, dx, dy, dw, dh);
272
+ sx += d;
273
+ }
274
+ sy += d;
275
+ }
276
+ ctx.restore();
277
+ tmpCanvas = tmpCtx = null;
278
+
279
+ // if cropped or rotated width and height data replacing issue
280
+ var newcanvas = document.createElement('canvas');
281
+ newcanvas.width = size.cropped === 'h' ? height : width;
282
+ newcanvas.height = size.cropped === 'w' ? width : height;
283
+ var x = size.cropped === 'h' ? (height - width) * .5 : 0;
284
+ var y = size.cropped === 'w' ? (width - height) * .5 : 0;
285
+ newctx = newcanvas.getContext('2d');
286
+ newctx.drawImage(canvas, x, y, width, height);
287
+
288
+ if (file.type === "image/png") {
289
+ var data = newcanvas.toDataURL(file.type);
290
+ } else {
291
+ var data = newcanvas.toDataURL("image/jpeg", ($this.options.quality * .01));
292
+ }
293
+
294
+ // CALLBACK
295
+ $this.options.callback(data, width, height);
296
+
297
+ });
298
+ };
299
+ img.src = dataURL;
300
+ // =====================================================
301
+ };
302
+ reader.readAsDataURL(file);
303
+
304
+ }
305
+ };
306
+ $[pluginName] = function(file, options) {
307
+ if (typeof file === 'string')
308
+ return methods[file](options);
309
+ else
310
+ new Plugin(file, options);
311
+ };
312
+
313
+ })(jQuery);