exifjs-rails 1.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0103a352b7f32ef2898d1537458434244372e34c
4
+ data.tar.gz: 618842a1e824dac042eac8a304de4a52f08ca006
5
+ SHA512:
6
+ metadata.gz: 41205f8d2db706dfa535d53437c0d5c8e51bc6fb407abf39e49913a5b0ad0d6f5232467e20ac1f81f5fe882d6680be325439cba96abc39f8d63ae1f9f68c7b29
7
+ data.tar.gz: 5a3fbfb53da2344ec02d1fa8f36aa2f07c6acff1df4178c84b021b6619cbd541d3a5f300096d6984958a620788cc36d73b0c26f3a107f4ae0fbfc67caf67784c
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in exifjs-rails.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Yamagishi Kazutoshi
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,31 @@
1
+ # Exifjs::Rails
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'exifjs-rails'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install exifjs-rails
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/[my-github-username]/exifjs-rails/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'exifjs/rails/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "exifjs-rails"
8
+ spec.version = Exifjs::Rails::VERSION
9
+ spec.authors = ["Yamagishi Kazutoshi"]
10
+ spec.email = ["ykzts@desire.sh"]
11
+ spec.summary = %q{The Exif.js JavaScript library ready to play with Rails.}
12
+ spec.description = spec.summary
13
+ spec.homepage = "https://github.com/ykzts/exifjs-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.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ end
@@ -0,0 +1 @@
1
+ require "exifjs/rails"
@@ -0,0 +1,8 @@
1
+ require "exifjs/rails/version"
2
+
3
+ module Exifjs
4
+ module Rails
5
+ end
6
+ end
7
+
8
+ require "exifjs/rails/railtie" if defined?(Rails)
@@ -0,0 +1,6 @@
1
+ module Exifjs
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Exifjs
2
+ module Rails
3
+ VERSION = "1.0.0.beta1"
4
+ end
5
+ end
@@ -0,0 +1,805 @@
1
+ (function() {
2
+
3
+ var debug = false;
4
+
5
+ var root = this;
6
+
7
+ var EXIF = function(obj) {
8
+ if (obj instanceof EXIF) return obj;
9
+ if (!(this instanceof EXIF)) return new EXIF(obj);
10
+ this.EXIFwrapped = obj;
11
+ };
12
+
13
+ if (typeof exports !== 'undefined') {
14
+ if (typeof module !== 'undefined' && module.exports) {
15
+ exports = module.exports = EXIF;
16
+ }
17
+ exports.EXIF = EXIF;
18
+ } else {
19
+ root.EXIF = EXIF;
20
+ }
21
+
22
+ var ExifTags = EXIF.Tags = {
23
+
24
+ // version tags
25
+ 0x9000 : "ExifVersion", // EXIF version
26
+ 0xA000 : "FlashpixVersion", // Flashpix format version
27
+
28
+ // colorspace tags
29
+ 0xA001 : "ColorSpace", // Color space information tag
30
+
31
+ // image configuration
32
+ 0xA002 : "PixelXDimension", // Valid width of meaningful image
33
+ 0xA003 : "PixelYDimension", // Valid height of meaningful image
34
+ 0x9101 : "ComponentsConfiguration", // Information about channels
35
+ 0x9102 : "CompressedBitsPerPixel", // Compressed bits per pixel
36
+
37
+ // user information
38
+ 0x927C : "MakerNote", // Any desired information written by the manufacturer
39
+ 0x9286 : "UserComment", // Comments by user
40
+
41
+ // related file
42
+ 0xA004 : "RelatedSoundFile", // Name of related sound file
43
+
44
+ // date and time
45
+ 0x9003 : "DateTimeOriginal", // Date and time when the original image was generated
46
+ 0x9004 : "DateTimeDigitized", // Date and time when the image was stored digitally
47
+ 0x9290 : "SubsecTime", // Fractions of seconds for DateTime
48
+ 0x9291 : "SubsecTimeOriginal", // Fractions of seconds for DateTimeOriginal
49
+ 0x9292 : "SubsecTimeDigitized", // Fractions of seconds for DateTimeDigitized
50
+
51
+ // picture-taking conditions
52
+ 0x829A : "ExposureTime", // Exposure time (in seconds)
53
+ 0x829D : "FNumber", // F number
54
+ 0x8822 : "ExposureProgram", // Exposure program
55
+ 0x8824 : "SpectralSensitivity", // Spectral sensitivity
56
+ 0x8827 : "ISOSpeedRatings", // ISO speed rating
57
+ 0x8828 : "OECF", // Optoelectric conversion factor
58
+ 0x9201 : "ShutterSpeedValue", // Shutter speed
59
+ 0x9202 : "ApertureValue", // Lens aperture
60
+ 0x9203 : "BrightnessValue", // Value of brightness
61
+ 0x9204 : "ExposureBias", // Exposure bias
62
+ 0x9205 : "MaxApertureValue", // Smallest F number of lens
63
+ 0x9206 : "SubjectDistance", // Distance to subject in meters
64
+ 0x9207 : "MeteringMode", // Metering mode
65
+ 0x9208 : "LightSource", // Kind of light source
66
+ 0x9209 : "Flash", // Flash status
67
+ 0x9214 : "SubjectArea", // Location and area of main subject
68
+ 0x920A : "FocalLength", // Focal length of the lens in mm
69
+ 0xA20B : "FlashEnergy", // Strobe energy in BCPS
70
+ 0xA20C : "SpatialFrequencyResponse", //
71
+ 0xA20E : "FocalPlaneXResolution", // Number of pixels in width direction per FocalPlaneResolutionUnit
72
+ 0xA20F : "FocalPlaneYResolution", // Number of pixels in height direction per FocalPlaneResolutionUnit
73
+ 0xA210 : "FocalPlaneResolutionUnit", // Unit for measuring FocalPlaneXResolution and FocalPlaneYResolution
74
+ 0xA214 : "SubjectLocation", // Location of subject in image
75
+ 0xA215 : "ExposureIndex", // Exposure index selected on camera
76
+ 0xA217 : "SensingMethod", // Image sensor type
77
+ 0xA300 : "FileSource", // Image source (3 == DSC)
78
+ 0xA301 : "SceneType", // Scene type (1 == directly photographed)
79
+ 0xA302 : "CFAPattern", // Color filter array geometric pattern
80
+ 0xA401 : "CustomRendered", // Special processing
81
+ 0xA402 : "ExposureMode", // Exposure mode
82
+ 0xA403 : "WhiteBalance", // 1 = auto white balance, 2 = manual
83
+ 0xA404 : "DigitalZoomRation", // Digital zoom ratio
84
+ 0xA405 : "FocalLengthIn35mmFilm", // Equivalent foacl length assuming 35mm film camera (in mm)
85
+ 0xA406 : "SceneCaptureType", // Type of scene
86
+ 0xA407 : "GainControl", // Degree of overall image gain adjustment
87
+ 0xA408 : "Contrast", // Direction of contrast processing applied by camera
88
+ 0xA409 : "Saturation", // Direction of saturation processing applied by camera
89
+ 0xA40A : "Sharpness", // Direction of sharpness processing applied by camera
90
+ 0xA40B : "DeviceSettingDescription", //
91
+ 0xA40C : "SubjectDistanceRange", // Distance to subject
92
+
93
+ // other tags
94
+ 0xA005 : "InteroperabilityIFDPointer",
95
+ 0xA420 : "ImageUniqueID" // Identifier assigned uniquely to each image
96
+ };
97
+
98
+ var TiffTags = EXIF.TiffTags = {
99
+ 0x0100 : "ImageWidth",
100
+ 0x0101 : "ImageHeight",
101
+ 0x8769 : "ExifIFDPointer",
102
+ 0x8825 : "GPSInfoIFDPointer",
103
+ 0xA005 : "InteroperabilityIFDPointer",
104
+ 0x0102 : "BitsPerSample",
105
+ 0x0103 : "Compression",
106
+ 0x0106 : "PhotometricInterpretation",
107
+ 0x0112 : "Orientation",
108
+ 0x0115 : "SamplesPerPixel",
109
+ 0x011C : "PlanarConfiguration",
110
+ 0x0212 : "YCbCrSubSampling",
111
+ 0x0213 : "YCbCrPositioning",
112
+ 0x011A : "XResolution",
113
+ 0x011B : "YResolution",
114
+ 0x0128 : "ResolutionUnit",
115
+ 0x0111 : "StripOffsets",
116
+ 0x0116 : "RowsPerStrip",
117
+ 0x0117 : "StripByteCounts",
118
+ 0x0201 : "JPEGInterchangeFormat",
119
+ 0x0202 : "JPEGInterchangeFormatLength",
120
+ 0x012D : "TransferFunction",
121
+ 0x013E : "WhitePoint",
122
+ 0x013F : "PrimaryChromaticities",
123
+ 0x0211 : "YCbCrCoefficients",
124
+ 0x0214 : "ReferenceBlackWhite",
125
+ 0x0132 : "DateTime",
126
+ 0x010E : "ImageDescription",
127
+ 0x010F : "Make",
128
+ 0x0110 : "Model",
129
+ 0x0131 : "Software",
130
+ 0x013B : "Artist",
131
+ 0x8298 : "Copyright"
132
+ };
133
+
134
+ var GPSTags = EXIF.GPSTags = {
135
+ 0x0000 : "GPSVersionID",
136
+ 0x0001 : "GPSLatitudeRef",
137
+ 0x0002 : "GPSLatitude",
138
+ 0x0003 : "GPSLongitudeRef",
139
+ 0x0004 : "GPSLongitude",
140
+ 0x0005 : "GPSAltitudeRef",
141
+ 0x0006 : "GPSAltitude",
142
+ 0x0007 : "GPSTimeStamp",
143
+ 0x0008 : "GPSSatellites",
144
+ 0x0009 : "GPSStatus",
145
+ 0x000A : "GPSMeasureMode",
146
+ 0x000B : "GPSDOP",
147
+ 0x000C : "GPSSpeedRef",
148
+ 0x000D : "GPSSpeed",
149
+ 0x000E : "GPSTrackRef",
150
+ 0x000F : "GPSTrack",
151
+ 0x0010 : "GPSImgDirectionRef",
152
+ 0x0011 : "GPSImgDirection",
153
+ 0x0012 : "GPSMapDatum",
154
+ 0x0013 : "GPSDestLatitudeRef",
155
+ 0x0014 : "GPSDestLatitude",
156
+ 0x0015 : "GPSDestLongitudeRef",
157
+ 0x0016 : "GPSDestLongitude",
158
+ 0x0017 : "GPSDestBearingRef",
159
+ 0x0018 : "GPSDestBearing",
160
+ 0x0019 : "GPSDestDistanceRef",
161
+ 0x001A : "GPSDestDistance",
162
+ 0x001B : "GPSProcessingMethod",
163
+ 0x001C : "GPSAreaInformation",
164
+ 0x001D : "GPSDateStamp",
165
+ 0x001E : "GPSDifferential"
166
+ };
167
+
168
+ var StringValues = EXIF.StringValues = {
169
+ ExposureProgram : {
170
+ 0 : "Not defined",
171
+ 1 : "Manual",
172
+ 2 : "Normal program",
173
+ 3 : "Aperture priority",
174
+ 4 : "Shutter priority",
175
+ 5 : "Creative program",
176
+ 6 : "Action program",
177
+ 7 : "Portrait mode",
178
+ 8 : "Landscape mode"
179
+ },
180
+ MeteringMode : {
181
+ 0 : "Unknown",
182
+ 1 : "Average",
183
+ 2 : "CenterWeightedAverage",
184
+ 3 : "Spot",
185
+ 4 : "MultiSpot",
186
+ 5 : "Pattern",
187
+ 6 : "Partial",
188
+ 255 : "Other"
189
+ },
190
+ LightSource : {
191
+ 0 : "Unknown",
192
+ 1 : "Daylight",
193
+ 2 : "Fluorescent",
194
+ 3 : "Tungsten (incandescent light)",
195
+ 4 : "Flash",
196
+ 9 : "Fine weather",
197
+ 10 : "Cloudy weather",
198
+ 11 : "Shade",
199
+ 12 : "Daylight fluorescent (D 5700 - 7100K)",
200
+ 13 : "Day white fluorescent (N 4600 - 5400K)",
201
+ 14 : "Cool white fluorescent (W 3900 - 4500K)",
202
+ 15 : "White fluorescent (WW 3200 - 3700K)",
203
+ 17 : "Standard light A",
204
+ 18 : "Standard light B",
205
+ 19 : "Standard light C",
206
+ 20 : "D55",
207
+ 21 : "D65",
208
+ 22 : "D75",
209
+ 23 : "D50",
210
+ 24 : "ISO studio tungsten",
211
+ 255 : "Other"
212
+ },
213
+ Flash : {
214
+ 0x0000 : "Flash did not fire",
215
+ 0x0001 : "Flash fired",
216
+ 0x0005 : "Strobe return light not detected",
217
+ 0x0007 : "Strobe return light detected",
218
+ 0x0009 : "Flash fired, compulsory flash mode",
219
+ 0x000D : "Flash fired, compulsory flash mode, return light not detected",
220
+ 0x000F : "Flash fired, compulsory flash mode, return light detected",
221
+ 0x0010 : "Flash did not fire, compulsory flash mode",
222
+ 0x0018 : "Flash did not fire, auto mode",
223
+ 0x0019 : "Flash fired, auto mode",
224
+ 0x001D : "Flash fired, auto mode, return light not detected",
225
+ 0x001F : "Flash fired, auto mode, return light detected",
226
+ 0x0020 : "No flash function",
227
+ 0x0041 : "Flash fired, red-eye reduction mode",
228
+ 0x0045 : "Flash fired, red-eye reduction mode, return light not detected",
229
+ 0x0047 : "Flash fired, red-eye reduction mode, return light detected",
230
+ 0x0049 : "Flash fired, compulsory flash mode, red-eye reduction mode",
231
+ 0x004D : "Flash fired, compulsory flash mode, red-eye reduction mode, return light not detected",
232
+ 0x004F : "Flash fired, compulsory flash mode, red-eye reduction mode, return light detected",
233
+ 0x0059 : "Flash fired, auto mode, red-eye reduction mode",
234
+ 0x005D : "Flash fired, auto mode, return light not detected, red-eye reduction mode",
235
+ 0x005F : "Flash fired, auto mode, return light detected, red-eye reduction mode"
236
+ },
237
+ SensingMethod : {
238
+ 1 : "Not defined",
239
+ 2 : "One-chip color area sensor",
240
+ 3 : "Two-chip color area sensor",
241
+ 4 : "Three-chip color area sensor",
242
+ 5 : "Color sequential area sensor",
243
+ 7 : "Trilinear sensor",
244
+ 8 : "Color sequential linear sensor"
245
+ },
246
+ SceneCaptureType : {
247
+ 0 : "Standard",
248
+ 1 : "Landscape",
249
+ 2 : "Portrait",
250
+ 3 : "Night scene"
251
+ },
252
+ SceneType : {
253
+ 1 : "Directly photographed"
254
+ },
255
+ CustomRendered : {
256
+ 0 : "Normal process",
257
+ 1 : "Custom process"
258
+ },
259
+ WhiteBalance : {
260
+ 0 : "Auto white balance",
261
+ 1 : "Manual white balance"
262
+ },
263
+ GainControl : {
264
+ 0 : "None",
265
+ 1 : "Low gain up",
266
+ 2 : "High gain up",
267
+ 3 : "Low gain down",
268
+ 4 : "High gain down"
269
+ },
270
+ Contrast : {
271
+ 0 : "Normal",
272
+ 1 : "Soft",
273
+ 2 : "Hard"
274
+ },
275
+ Saturation : {
276
+ 0 : "Normal",
277
+ 1 : "Low saturation",
278
+ 2 : "High saturation"
279
+ },
280
+ Sharpness : {
281
+ 0 : "Normal",
282
+ 1 : "Soft",
283
+ 2 : "Hard"
284
+ },
285
+ SubjectDistanceRange : {
286
+ 0 : "Unknown",
287
+ 1 : "Macro",
288
+ 2 : "Close view",
289
+ 3 : "Distant view"
290
+ },
291
+ FileSource : {
292
+ 3 : "DSC"
293
+ },
294
+
295
+ Components : {
296
+ 0 : "",
297
+ 1 : "Y",
298
+ 2 : "Cb",
299
+ 3 : "Cr",
300
+ 4 : "R",
301
+ 5 : "G",
302
+ 6 : "B"
303
+ }
304
+ };
305
+
306
+ function addEvent(element, event, handler) {
307
+ if (element.addEventListener) {
308
+ element.addEventListener(event, handler, false);
309
+ } else if (element.attachEvent) {
310
+ element.attachEvent("on" + event, handler);
311
+ }
312
+ }
313
+
314
+ function imageHasData(img) {
315
+ return !!(img.exifdata);
316
+ }
317
+
318
+
319
+ function base64ToArrayBuffer(base64, contentType) {
320
+ contentType = contentType || base64.match(/^data\:([^\;]+)\;base64,/mi)[1] || ''; // e.g. 'data:image/jpeg;base64,...' => 'image/jpeg'
321
+ base64 = base64.replace(/^data\:([^\;]+)\;base64,/gmi, '');
322
+ var binary = atob(base64);
323
+ var len = binary.length;
324
+ var buffer = new ArrayBuffer(len);
325
+ var view = new Uint8Array(buffer);
326
+ for (var i = 0; i < len; i++) {
327
+ view[i] = binary.charCodeAt(i);
328
+ }
329
+ return buffer;
330
+ }
331
+
332
+ function objectURLToBlob(url, callback) {
333
+ var http = new XMLHttpRequest();
334
+ http.open("GET", url, true);
335
+ http.responseType = "blob";
336
+ http.onload = function(e) {
337
+ if (this.status == 200 || this.status === 0) {
338
+ callback(this.response);
339
+ }
340
+ };
341
+ http.send();
342
+ }
343
+
344
+ function getImageData(img, callback) {
345
+ function handleBinaryFile(binFile) {
346
+ var data = findEXIFinJPEG(binFile);
347
+ var iptcdata = findIPTCinJPEG(binFile);
348
+ img.exifdata = data || {};
349
+ img.iptcdata = iptcdata || {};
350
+ if (callback) {
351
+ callback.call(img);
352
+ }
353
+ }
354
+
355
+ if (img.src) {
356
+ if (/^data\:/i.test(img.src)) { // Data URI
357
+ var arrayBuffer = base64ToArrayBuffer(img.src);
358
+ handleBinaryFile(arrayBuffer);
359
+
360
+ } else if (/^blob\:/i.test(img.src)) { // Object URL
361
+ var fileReader = new FileReader();
362
+ fileReader.onload = function(e) {
363
+ handleBinaryFile(e.target.result);
364
+ };
365
+ objectURLToBlob(img.src, function (blob) {
366
+ fileReader.readAsArrayBuffer(blob);
367
+ });
368
+ } else {
369
+ var http = new XMLHttpRequest();
370
+ http.onload = function() {
371
+ if (this.status == 200 || this.status === 0) {
372
+ handleBinaryFile(http.response);
373
+ } else {
374
+ throw "Could not load image";
375
+ }
376
+ http = null;
377
+ };
378
+ http.open("GET", img.src, true);
379
+ http.responseType = "arraybuffer";
380
+ http.send(null);
381
+ }
382
+ } else if (window.FileReader && (img instanceof window.Blob || img instanceof window.File)) {
383
+ var fileReader = new FileReader();
384
+ fileReader.onload = function(e) {
385
+ if (debug) console.log("Got file of length " + e.target.result.byteLength);
386
+ handleBinaryFile(e.target.result);
387
+ };
388
+
389
+ fileReader.readAsArrayBuffer(img);
390
+ }
391
+ }
392
+
393
+ function findEXIFinJPEG(file) {
394
+ var dataView = new DataView(file);
395
+
396
+ if (debug) console.log("Got file of length " + file.byteLength);
397
+ if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
398
+ if (debug) console.log("Not a valid JPEG");
399
+ return false; // not a valid jpeg
400
+ }
401
+
402
+ var offset = 2,
403
+ length = file.byteLength,
404
+ marker;
405
+
406
+ while (offset < length) {
407
+ if (dataView.getUint8(offset) != 0xFF) {
408
+ if (debug) console.log("Not a valid marker at offset " + offset + ", found: " + dataView.getUint8(offset));
409
+ return false; // not a valid marker, something is wrong
410
+ }
411
+
412
+ marker = dataView.getUint8(offset + 1);
413
+ if (debug) console.log(marker);
414
+
415
+ // we could implement handling for other markers here,
416
+ // but we're only looking for 0xFFE1 for EXIF data
417
+
418
+ if (marker == 225) {
419
+ if (debug) console.log("Found 0xFFE1 marker");
420
+
421
+ return readEXIFData(dataView, offset + 4, dataView.getUint16(offset + 2) - 2);
422
+
423
+ // offset += 2 + file.getShortAt(offset+2, true);
424
+
425
+ } else {
426
+ offset += 2 + dataView.getUint16(offset+2);
427
+ }
428
+
429
+ }
430
+
431
+ }
432
+
433
+ function findIPTCinJPEG(file) {
434
+ var dataView = new DataView(file);
435
+
436
+ if (debug) console.log("Got file of length " + file.byteLength);
437
+ if ((dataView.getUint8(0) != 0xFF) || (dataView.getUint8(1) != 0xD8)) {
438
+ if (debug) console.log("Not a valid JPEG");
439
+ return false; // not a valid jpeg
440
+ }
441
+
442
+ var offset = 2,
443
+ length = file.byteLength;
444
+
445
+
446
+ var isFieldSegmentStart = function(dataView, offset){
447
+ return (
448
+ dataView.getUint8(offset) === 0x38 &&
449
+ dataView.getUint8(offset+1) === 0x42 &&
450
+ dataView.getUint8(offset+2) === 0x49 &&
451
+ dataView.getUint8(offset+3) === 0x4D &&
452
+ dataView.getUint8(offset+4) === 0x04 &&
453
+ dataView.getUint8(offset+5) === 0x04
454
+ );
455
+ };
456
+
457
+ while (offset < length) {
458
+
459
+ if ( isFieldSegmentStart(dataView, offset )){
460
+
461
+ // Get the length of the name header (which is padded to an even number of bytes)
462
+ var nameHeaderLength = dataView.getUint8(offset+7);
463
+ if(nameHeaderLength % 2 !== 0) nameHeaderLength += 1;
464
+ // Check for pre photoshop 6 format
465
+ if(nameHeaderLength === 0) {
466
+ // Always 4
467
+ nameHeaderLength = 4;
468
+ }
469
+
470
+ var startOffset = offset + 8 + nameHeaderLength;
471
+ var sectionLength = dataView.getUint16(offset + 6 + nameHeaderLength);
472
+
473
+ return readIPTCData(file, startOffset, sectionLength);
474
+
475
+ break;
476
+
477
+ }
478
+
479
+
480
+ // Not the marker, continue searching
481
+ offset++;
482
+
483
+ }
484
+
485
+ }
486
+ var IptcFieldMap = {
487
+ 0x78 : 'caption',
488
+ 0x6E : 'credit',
489
+ 0x19 : 'keywords',
490
+ 0x37 : 'dateCreated',
491
+ 0x50 : 'byline',
492
+ 0x55 : 'bylineTitle',
493
+ 0x7A : 'captionWriter',
494
+ 0x69 : 'headline',
495
+ 0x74 : 'copyright',
496
+ 0x0F : 'category'
497
+ };
498
+ function readIPTCData(file, startOffset, sectionLength){
499
+ var dataView = new DataView(file);
500
+ var data = {};
501
+ var fieldValue, fieldName, dataSize, segmentType, segmentSize;
502
+ var segmentStartPos = startOffset;
503
+ while(segmentStartPos < startOffset+sectionLength) {
504
+ if(dataView.getUint8(segmentStartPos) === 0x1C && dataView.getUint8(segmentStartPos+1) === 0x02){
505
+ segmentType = dataView.getUint8(segmentStartPos+2);
506
+ if(segmentType in IptcFieldMap) {
507
+ dataSize = dataView.getInt16(segmentStartPos+3);
508
+ segmentSize = dataSize + 5;
509
+ fieldName = IptcFieldMap[segmentType];
510
+ fieldValue = getStringFromDB(dataView, segmentStartPos+5, dataSize);
511
+ // Check if we already stored a value with this name
512
+ if(data.hasOwnProperty(fieldName)) {
513
+ // Value already stored with this name, create multivalue field
514
+ if(data[fieldName] instanceof Array) {
515
+ data[fieldName].push(fieldValue);
516
+ }
517
+ else {
518
+ data[fieldName] = [data[fieldName], fieldValue];
519
+ }
520
+ }
521
+ else {
522
+ data[fieldName] = fieldValue;
523
+ }
524
+ }
525
+
526
+ }
527
+ segmentStartPos++;
528
+ }
529
+ return data;
530
+ }
531
+
532
+
533
+
534
+ function readTags(file, tiffStart, dirStart, strings, bigEnd) {
535
+ var entries = file.getUint16(dirStart, !bigEnd),
536
+ tags = {},
537
+ entryOffset, tag,
538
+ i;
539
+
540
+ for (i=0;i<entries;i++) {
541
+ entryOffset = dirStart + i*12 + 2;
542
+ tag = strings[file.getUint16(entryOffset, !bigEnd)];
543
+ if (!tag && debug) console.log("Unknown tag: " + file.getUint16(entryOffset, !bigEnd));
544
+ tags[tag] = readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd);
545
+ }
546
+ return tags;
547
+ }
548
+
549
+
550
+ function readTagValue(file, entryOffset, tiffStart, dirStart, bigEnd) {
551
+ var type = file.getUint16(entryOffset+2, !bigEnd),
552
+ numValues = file.getUint32(entryOffset+4, !bigEnd),
553
+ valueOffset = file.getUint32(entryOffset+8, !bigEnd) + tiffStart,
554
+ offset,
555
+ vals, val, n,
556
+ numerator, denominator;
557
+
558
+ switch (type) {
559
+ case 1: // byte, 8-bit unsigned int
560
+ case 7: // undefined, 8-bit byte, value depending on field
561
+ if (numValues == 1) {
562
+ return file.getUint8(entryOffset + 8, !bigEnd);
563
+ } else {
564
+ offset = numValues > 4 ? valueOffset : (entryOffset + 8);
565
+ vals = [];
566
+ for (n=0;n<numValues;n++) {
567
+ vals[n] = file.getUint8(offset + n);
568
+ }
569
+ return vals;
570
+ }
571
+
572
+ case 2: // ascii, 8-bit byte
573
+ offset = numValues > 4 ? valueOffset : (entryOffset + 8);
574
+ return getStringFromDB(file, offset, numValues-1);
575
+
576
+ case 3: // short, 16 bit int
577
+ if (numValues == 1) {
578
+ return file.getUint16(entryOffset + 8, !bigEnd);
579
+ } else {
580
+ offset = numValues > 2 ? valueOffset : (entryOffset + 8);
581
+ vals = [];
582
+ for (n=0;n<numValues;n++) {
583
+ vals[n] = file.getUint16(offset + 2*n, !bigEnd);
584
+ }
585
+ return vals;
586
+ }
587
+
588
+ case 4: // long, 32 bit int
589
+ if (numValues == 1) {
590
+ return file.getUint32(entryOffset + 8, !bigEnd);
591
+ } else {
592
+ vals = [];
593
+ for (n=0;n<numValues;n++) {
594
+ vals[n] = file.getUint32(valueOffset + 4*n, !bigEnd);
595
+ }
596
+ return vals;
597
+ }
598
+
599
+ case 5: // rational = two long values, first is numerator, second is denominator
600
+ if (numValues == 1) {
601
+ numerator = file.getUint32(valueOffset, !bigEnd);
602
+ denominator = file.getUint32(valueOffset+4, !bigEnd);
603
+ val = new Number(numerator / denominator);
604
+ val.numerator = numerator;
605
+ val.denominator = denominator;
606
+ return val;
607
+ } else {
608
+ vals = [];
609
+ for (n=0;n<numValues;n++) {
610
+ numerator = file.getUint32(valueOffset + 8*n, !bigEnd);
611
+ denominator = file.getUint32(valueOffset+4 + 8*n, !bigEnd);
612
+ vals[n] = new Number(numerator / denominator);
613
+ vals[n].numerator = numerator;
614
+ vals[n].denominator = denominator;
615
+ }
616
+ return vals;
617
+ }
618
+
619
+ case 9: // slong, 32 bit signed int
620
+ if (numValues == 1) {
621
+ return file.getInt32(entryOffset + 8, !bigEnd);
622
+ } else {
623
+ vals = [];
624
+ for (n=0;n<numValues;n++) {
625
+ vals[n] = file.getInt32(valueOffset + 4*n, !bigEnd);
626
+ }
627
+ return vals;
628
+ }
629
+
630
+ case 10: // signed rational, two slongs, first is numerator, second is denominator
631
+ if (numValues == 1) {
632
+ return file.getInt32(valueOffset, !bigEnd) / file.getInt32(valueOffset+4, !bigEnd);
633
+ } else {
634
+ vals = [];
635
+ for (n=0;n<numValues;n++) {
636
+ vals[n] = file.getInt32(valueOffset + 8*n, !bigEnd) / file.getInt32(valueOffset+4 + 8*n, !bigEnd);
637
+ }
638
+ return vals;
639
+ }
640
+ }
641
+ }
642
+
643
+ function getStringFromDB(buffer, start, length) {
644
+ var outstr = "";
645
+ for (n = start; n < start+length; n++) {
646
+ outstr += String.fromCharCode(buffer.getUint8(n));
647
+ }
648
+ return outstr;
649
+ }
650
+
651
+ function readEXIFData(file, start) {
652
+ if (getStringFromDB(file, start, 4) != "Exif") {
653
+ if (debug) console.log("Not valid EXIF data! " + getStringFromDB(file, start, 4));
654
+ return false;
655
+ }
656
+
657
+ var bigEnd,
658
+ tags, tag,
659
+ exifData, gpsData,
660
+ tiffOffset = start + 6;
661
+
662
+ // test for TIFF validity and endianness
663
+ if (file.getUint16(tiffOffset) == 0x4949) {
664
+ bigEnd = false;
665
+ } else if (file.getUint16(tiffOffset) == 0x4D4D) {
666
+ bigEnd = true;
667
+ } else {
668
+ if (debug) console.log("Not valid TIFF data! (no 0x4949 or 0x4D4D)");
669
+ return false;
670
+ }
671
+
672
+ if (file.getUint16(tiffOffset+2, !bigEnd) != 0x002A) {
673
+ if (debug) console.log("Not valid TIFF data! (no 0x002A)");
674
+ return false;
675
+ }
676
+
677
+ var firstIFDOffset = file.getUint32(tiffOffset+4, !bigEnd);
678
+
679
+ if (firstIFDOffset < 0x00000008) {
680
+ if (debug) console.log("Not valid TIFF data! (First offset less than 8)", file.getUint32(tiffOffset+4, !bigEnd));
681
+ return false;
682
+ }
683
+
684
+ tags = readTags(file, tiffOffset, tiffOffset + firstIFDOffset, TiffTags, bigEnd);
685
+
686
+ if (tags.ExifIFDPointer) {
687
+ exifData = readTags(file, tiffOffset, tiffOffset + tags.ExifIFDPointer, ExifTags, bigEnd);
688
+ for (tag in exifData) {
689
+ switch (tag) {
690
+ case "LightSource" :
691
+ case "Flash" :
692
+ case "MeteringMode" :
693
+ case "ExposureProgram" :
694
+ case "SensingMethod" :
695
+ case "SceneCaptureType" :
696
+ case "SceneType" :
697
+ case "CustomRendered" :
698
+ case "WhiteBalance" :
699
+ case "GainControl" :
700
+ case "Contrast" :
701
+ case "Saturation" :
702
+ case "Sharpness" :
703
+ case "SubjectDistanceRange" :
704
+ case "FileSource" :
705
+ exifData[tag] = StringValues[tag][exifData[tag]];
706
+ break;
707
+
708
+ case "ExifVersion" :
709
+ case "FlashpixVersion" :
710
+ exifData[tag] = String.fromCharCode(exifData[tag][0], exifData[tag][1], exifData[tag][2], exifData[tag][3]);
711
+ break;
712
+
713
+ case "ComponentsConfiguration" :
714
+ exifData[tag] =
715
+ StringValues.Components[exifData[tag][0]] +
716
+ StringValues.Components[exifData[tag][1]] +
717
+ StringValues.Components[exifData[tag][2]] +
718
+ StringValues.Components[exifData[tag][3]];
719
+ break;
720
+ }
721
+ tags[tag] = exifData[tag];
722
+ }
723
+ }
724
+
725
+ if (tags.GPSInfoIFDPointer) {
726
+ gpsData = readTags(file, tiffOffset, tiffOffset + tags.GPSInfoIFDPointer, GPSTags, bigEnd);
727
+ for (tag in gpsData) {
728
+ switch (tag) {
729
+ case "GPSVersionID" :
730
+ gpsData[tag] = gpsData[tag][0] +
731
+ "." + gpsData[tag][1] +
732
+ "." + gpsData[tag][2] +
733
+ "." + gpsData[tag][3];
734
+ break;
735
+ }
736
+ tags[tag] = gpsData[tag];
737
+ }
738
+ }
739
+
740
+ return tags;
741
+ }
742
+
743
+ EXIF.getData = function(img, callback) {
744
+ if ((img instanceof Image || img instanceof HTMLImageElement) && !img.complete) return false;
745
+
746
+ if (!imageHasData(img)) {
747
+ getImageData(img, callback);
748
+ } else {
749
+ if (callback) {
750
+ callback.call(img);
751
+ }
752
+ }
753
+ return true;
754
+ }
755
+
756
+ EXIF.getTag = function(img, tag) {
757
+ if (!imageHasData(img)) return;
758
+ return img.exifdata[tag];
759
+ }
760
+
761
+ EXIF.getAllTags = function(img) {
762
+ if (!imageHasData(img)) return {};
763
+ var a,
764
+ data = img.exifdata,
765
+ tags = {};
766
+ for (a in data) {
767
+ if (data.hasOwnProperty(a)) {
768
+ tags[a] = data[a];
769
+ }
770
+ }
771
+ return tags;
772
+ }
773
+
774
+ EXIF.pretty = function(img) {
775
+ if (!imageHasData(img)) return "";
776
+ var a,
777
+ data = img.exifdata,
778
+ strPretty = "";
779
+ for (a in data) {
780
+ if (data.hasOwnProperty(a)) {
781
+ if (typeof data[a] == "object") {
782
+ if (data[a] instanceof Number) {
783
+ strPretty += a + " : " + data[a] + " [" + data[a].numerator + "/" + data[a].denominator + "]\r\n";
784
+ } else {
785
+ strPretty += a + " : [" + data[a].length + " values]\r\n";
786
+ }
787
+ } else {
788
+ strPretty += a + " : " + data[a] + "\r\n";
789
+ }
790
+ }
791
+ }
792
+ return strPretty;
793
+ }
794
+
795
+ EXIF.readFromBinaryFile = function(file) {
796
+ return findEXIFinJPEG(file);
797
+ }
798
+
799
+ if (typeof define === 'function' && define.amd) {
800
+ define('exif-js', [], function() {
801
+ return EXIF;
802
+ });
803
+ }
804
+ }.call(this));
805
+
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: exifjs-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.beta1
5
+ platform: ruby
6
+ authors:
7
+ - Yamagishi Kazutoshi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: The Exif.js JavaScript library ready to play with Rails.
42
+ email:
43
+ - ykzts@desire.sh
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - Gemfile
50
+ - LICENSE.txt
51
+ - README.md
52
+ - Rakefile
53
+ - exifjs-rails.gemspec
54
+ - lib/exifjs-rails.rb
55
+ - lib/exifjs/rails.rb
56
+ - lib/exifjs/rails/railtie.rb
57
+ - lib/exifjs/rails/version.rb
58
+ - vendor/assets/javascripts/exif.js
59
+ homepage: https://github.com/ykzts/exifjs-rails
60
+ licenses:
61
+ - MIT
62
+ metadata: {}
63
+ post_install_message:
64
+ rdoc_options: []
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">"
75
+ - !ruby/object:Gem::Version
76
+ version: 1.3.1
77
+ requirements: []
78
+ rubyforge_project:
79
+ rubygems_version: 2.4.5
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: The Exif.js JavaScript library ready to play with Rails.
83
+ test_files: []