diff --git a/app/javascript/application.js b/app/javascript/application.js index 56bf7c6..887a338 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -3,9 +3,23 @@ import "trix" import "@rails/actiontext" import { start } from "@rails/activestorage" +import piexif from "piexifjs" start() +function arrayBufferToBinaryString(buf) { + var bytes = new Uint8Array(buf); + var str = ""; + for (var i = 0; i < bytes.length; i++) str += String.fromCharCode(bytes[i]); + return str; +} + +function binaryStringToBlob(str) { + var bytes = new Uint8Array(str.length); + for (var i = 0; i < str.length; i++) bytes[i] = str.charCodeAt(i); + return new Blob([bytes], { type: "image/jpeg" }); +} + function compressToJpeg(file) { return new Promise(function (resolve) { var MAX = 1920, QUALITY = 0.82; @@ -22,9 +36,30 @@ function compressToJpeg(file) { var canvas = document.createElement("canvas"); canvas.width = w; canvas.height = h; canvas.getContext("2d").drawImage(img, 0, 0, w, h); - canvas.toBlob(function (blob) { if (!blob) { resolve(file); return; } + canvas.toBlob(function (blob) { + if (!blob) { resolve(file); return; } var name = file.name.replace(/\.[^.]+$/, ".jpg"); - resolve(new File([blob], name, { type: "image/jpeg", lastModified: Date.now() })); + var isJpeg = file.type === "image/jpeg" || file.type === "image/jpg"; + if (!isJpeg) { + resolve(new File([blob], name, { type: "image/jpeg", lastModified: Date.now() })); + return; + } + // Preserve EXIF from the original JPEG. Canvas drawImage already applies + // visual orientation, so reset the orientation tag to 1 (upright) to + // avoid double-rotation in viewers that honour EXIF. + Promise.all([file.arrayBuffer(), blob.arrayBuffer()]).then(function(bufs) { + try { + var srcStr = arrayBufferToBinaryString(bufs[0]); + var destStr = arrayBufferToBinaryString(bufs[1]); + var exifObj = piexif.load(srcStr); + if (exifObj["0th"]) exifObj["0th"][piexif.ImageIFD.Orientation] = 1; + var exifBytes = piexif.dump(exifObj); + var finalStr = piexif.insert(exifBytes, destStr); + resolve(new File([binaryStringToBlob(finalStr)], name, { type: "image/jpeg", lastModified: Date.now() })); + } catch (_) { + resolve(new File([blob], name, { type: "image/jpeg", lastModified: Date.now() })); + } + }); }, "image/jpeg", QUALITY); }; img.onerror = function () { resolve(file); }; diff --git a/app/views/active_storage/blobs/_blob.html.erb b/app/views/active_storage/blobs/_blob.html.erb index 49ba357..2aba822 100644 --- a/app/views/active_storage/blobs/_blob.html.erb +++ b/app/views/active_storage/blobs/_blob.html.erb @@ -1,6 +1,6 @@
attachment--<%= blob.filename.extension %>"> <% if blob.representable? %> - <%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ]) %> + <%= image_tag blob.representation(resize_to_limit: local_assigns[:in_gallery] ? [ 800, 600 ] : [ 1024, 768 ], saver: { strip: false }) %> <% end %>
diff --git a/app/views/admin/check_ins/index.html.erb b/app/views/admin/check_ins/index.html.erb index 546fab7..d07e84e 100644 --- a/app/views/admin/check_ins/index.html.erb +++ b/app/views/admin/check_ins/index.html.erb @@ -46,7 +46,7 @@
<% if ci.image.attached? %> - <%= image_tag ci.image.variant(resize_to_limit: [80, 80]), + <%= image_tag ci.image.variant(resize_to_limit: [80, 80], saver: { strip: false }), class: "w-20 h-20 object-cover border-2 border-black" %> <% else %>
diff --git a/app/views/admin/check_ins/show.html.erb b/app/views/admin/check_ins/show.html.erb index 382e8b1..485f5ee 100644 --- a/app/views/admin/check_ins/show.html.erb +++ b/app/views/admin/check_ins/show.html.erb @@ -43,7 +43,7 @@
<% if @check_in.image.attached? %> - <%= image_tag @check_in.image.variant(resize_to_limit: [800, 800]), + <%= image_tag @check_in.image.variant(resize_to_limit: [800, 800], saver: { strip: false }), class: "max-w-full border-2 border-black" %>
<%= link_to "Download original", url_for(@check_in.image), diff --git a/app/views/admin/submissions/_table.html.erb b/app/views/admin/submissions/_table.html.erb index 60775f5..f5524d4 100644 --- a/app/views/admin/submissions/_table.html.erb +++ b/app/views/admin/submissions/_table.html.erb @@ -77,7 +77,7 @@ <% blob = sub.media.blob %> <% if blob.content_type.start_with?("image/") %> <%= link_to url_for(sub.media), target: "_blank", rel: "noopener" do %> - <%= image_tag sub.media.variant(resize_to_limit: [64, 64]), + <%= image_tag sub.media.variant(resize_to_limit: [64, 64], saver: { strip: false }), class: "w-16 h-16 object-cover border-2 border-black" %> <% end %> <% elsif blob.content_type.start_with?("video/") %> diff --git a/app/views/admin/submissions/index.html.erb b/app/views/admin/submissions/index.html.erb index b348390..ee6484a 100644 --- a/app/views/admin/submissions/index.html.erb +++ b/app/views/admin/submissions/index.html.erb @@ -108,7 +108,7 @@ <% if blob.content_type.start_with?("image/") %>
- <%= image_tag submission.media.variant(resize_to_limit: [160, 160]), + <%= image_tag submission.media.variant(resize_to_limit: [160, 160], saver: { strip: false }), class: "w-40 h-40 object-cover border-2 border-black" %>