mirror of https://github.com/logseq/logseq
Image wip
parent
cd57db1465
commit
160510a298
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,29 @@
|
|||
(ns frontend.blob)
|
||||
|
||||
(defn- decode
|
||||
"Decodes the data portion of a data url from base64"
|
||||
[[media-type data]]
|
||||
[media-type (js/atob data)])
|
||||
|
||||
(defn- uint8
|
||||
"Converts a base64 decoded data string to a Uint8Array"
|
||||
[[media-type data]]
|
||||
(->> (map #(.charCodeAt %1) data)
|
||||
js/Uint8Array.
|
||||
(vector media-type)))
|
||||
|
||||
(defn- make-blob
|
||||
"Creates a JS Blob object from a media type and a Uint8Array"
|
||||
[[media-type uint8]]
|
||||
(js/Blob. (array uint8) (js-obj "type" media-type)))
|
||||
|
||||
(defn blob
|
||||
"Converts a data-url into a JS Blob. This is useful for uploading
|
||||
image data from JavaScript."
|
||||
[data-url]
|
||||
{:pre [(string? data-url)]}
|
||||
(-> (re-find #"^data:([^;]+);base64,(.*)$" data-url)
|
||||
rest
|
||||
decode
|
||||
uint8
|
||||
make-blob))
|
|
@ -8,14 +8,15 @@
|
|||
[frontend.mixins :as mixins]
|
||||
[frontend.db :as db]
|
||||
[frontend.state :as state]
|
||||
[frontend.format.org-mode :as org]))
|
||||
[frontend.format.org-mode :as org]
|
||||
[goog.object :as gobj]
|
||||
[frontend.image :as image]))
|
||||
|
||||
(def edit-content (atom ""))
|
||||
(rum/defc editor-box <
|
||||
(mixins/event-mixin
|
||||
(fn [state]
|
||||
(let [heading (first (:rum/args state))]
|
||||
(prn "heading: " heading)
|
||||
(mixins/hide-when-esc-or-outside
|
||||
state
|
||||
nil
|
||||
|
@ -33,7 +34,30 @@
|
|||
:style {:border "none"
|
||||
:border-radius 0
|
||||
:background "transparent"
|
||||
:margin-top 12.5}})])
|
||||
:margin-top 12.5}})
|
||||
[:input
|
||||
{:id "files"
|
||||
:type "file"
|
||||
:on-change (fn [e]
|
||||
(let [files (.-files (.-target e))]
|
||||
(image/upload
|
||||
files
|
||||
(fn [file file-form-data file-name file-type]
|
||||
;; TODO: set uploading
|
||||
(.append file-form-data "name" file-name)
|
||||
(.append file-form-data file-type true)
|
||||
|
||||
;; (citrus/dispatch!
|
||||
;; :image/upload
|
||||
;; file-form-data
|
||||
;; (fn [url]
|
||||
;; (reset! uploading? false)
|
||||
;; (swap! form assoc name url)
|
||||
;; (if on-uploaded
|
||||
;; (on-uploaded form name url))))
|
||||
))))
|
||||
;; :hidden true
|
||||
}]])
|
||||
|
||||
(defn split-first [re s]
|
||||
(clojure.string/split s re 2))
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
// copied from https://stackoverflow.com/questions/7584794/accessing-jpeg-exif-rotation-data-in-javascript-on-the-client-side
|
||||
|
||||
function objectURLToBlob(url, callback) {
|
||||
var http = new XMLHttpRequest();
|
||||
http.open("GET", url, true);
|
||||
http.responseType = "blob";
|
||||
http.onload = function(e) {
|
||||
if (this.status == 200 || this.status === 0) {
|
||||
callback(this.response);
|
||||
}
|
||||
};
|
||||
http.send();
|
||||
}
|
||||
|
||||
export var getEXIFOrientation = function (img, callback) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = e => {
|
||||
var view = new DataView(e.target.result)
|
||||
|
||||
if (view.getUint16(0, false) !== 0xFFD8) {
|
||||
return callback(-2)
|
||||
}
|
||||
var length = view.byteLength
|
||||
var offset = 2
|
||||
while (offset < length) {
|
||||
var marker = view.getUint16(offset, false)
|
||||
offset += 2
|
||||
if (marker === 0xFFE1) {
|
||||
if (view.getUint32(offset += 2, false) !== 0x45786966) {
|
||||
return callback(-1)
|
||||
}
|
||||
var little = view.getUint16(offset += 6, false) === 0x4949
|
||||
offset += view.getUint32(offset + 4, little)
|
||||
var tags = view.getUint16(offset, little)
|
||||
offset += 2
|
||||
for (var i = 0; i < tags; i++) {
|
||||
if (view.getUint16(offset + (i * 12), little) === 0x0112) {
|
||||
var o = view.getUint16(offset + (i * 12) + 8, little);
|
||||
return callback(o)
|
||||
}
|
||||
}
|
||||
} else if ((marker & 0xFF00) !== 0xFF00) {
|
||||
break
|
||||
} else {
|
||||
offset += view.getUint16(offset, false)
|
||||
}
|
||||
}
|
||||
return callback(-1)
|
||||
};
|
||||
|
||||
objectURLToBlob(img.src, function (blob) {
|
||||
reader.readAsArrayBuffer(blob.slice(0, 65536));
|
||||
});
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
(ns frontend.image
|
||||
(:require [goog.object :as gobj]
|
||||
[frontend.blob :as blob]
|
||||
["/frontend/exif" :as exif]
|
||||
[frontend.util :as util]))
|
||||
|
||||
(defn reverse?
|
||||
[exif-orientation]
|
||||
(contains? #{5 6 7 8} exif-orientation))
|
||||
|
||||
(defn re-scale
|
||||
[exif-orientation width height max-width max-height]
|
||||
(let [[width height]
|
||||
(if (reverse? exif-orientation)
|
||||
[height width]
|
||||
[width height])]
|
||||
(let [ratio (/ width height)
|
||||
to-width (if (> width max-width) max-width width)
|
||||
to-height (if (> height max-height) max-height height)
|
||||
new-ratio (/ to-width to-height)]
|
||||
(let [[w h] (cond
|
||||
(> new-ratio ratio)
|
||||
[(* ratio to-height) to-height]
|
||||
|
||||
(< new-ratio ratio)
|
||||
[to-width (/ to-width ratio)]
|
||||
|
||||
:else
|
||||
[to-width to-height])]
|
||||
[(int w) (int h)]))))
|
||||
|
||||
|
||||
(defn fix-orientation
|
||||
"Given image and exif orientation, ensure the photo is displayed
|
||||
rightside up"
|
||||
[img exif-orientation cb max-width max-height]
|
||||
(let [off-canvas (js/document.createElement "canvas")
|
||||
ctx ^js (.getContext off-canvas "2d")
|
||||
width (gobj/get img "width")
|
||||
height (gobj/get img "height")
|
||||
[to-width to-height] (re-scale exif-orientation width height max-width max-height)]
|
||||
(gobj/set ctx "imageSmoothingEnabled" false)
|
||||
(set! (.-width off-canvas) to-width)
|
||||
(set! (.-height off-canvas) to-height)
|
||||
;; rotate
|
||||
(let [[width height] (if (reverse? exif-orientation)
|
||||
[to-height to-width]
|
||||
[to-width to-height])]
|
||||
(case exif-orientation
|
||||
2 (.transform ctx -1 0 0 1 width 0)
|
||||
3 (.transform ctx -1 0 0 -1 width height)
|
||||
4 (.transform ctx 1 0 0 -1 0 height)
|
||||
5 (.transform ctx 0 1 1 0 0 0)
|
||||
6 (.transform ctx 0 1 -1 0 height 0)
|
||||
7 (.transform ctx 0 -1 -1 0 height width)
|
||||
8 (.transform ctx 0 -1 1 0 0 width)
|
||||
(.transform ctx 1 0 0 1 0 0))
|
||||
(.drawImage ctx img 0 0 width height))
|
||||
(cb off-canvas)))
|
||||
|
||||
(defn get-orientation
|
||||
[img cb max-width max-height]
|
||||
(exif/getEXIFOrientation
|
||||
img
|
||||
(fn [orientation]
|
||||
(fix-orientation img orientation cb max-width max-height))))
|
||||
|
||||
(defn upload
|
||||
[files file-cb & {:keys [max-width max-height]
|
||||
:or {max-width 1920
|
||||
max-height 1080}}]
|
||||
(doseq [file (array-seq files)]
|
||||
(let [file-type (gobj/get file "type")
|
||||
file-name (str (util/year-month-day-concat) "_"
|
||||
(gobj/get file "name"))]
|
||||
(if (= 0 (.indexOf type "image/"))
|
||||
(let [img (js/Image.)]
|
||||
(set! (.-onload img)
|
||||
(fn []
|
||||
(get-orientation img
|
||||
(fn [^js off-canvas]
|
||||
(let [file-form-data ^js (js/FormData.)
|
||||
data-url (.toDataURL off-canvas)
|
||||
blob (blob/blob data-url)]
|
||||
(.append file-form-data "file" blob)
|
||||
(file-cb file file-form-data file-name file-type)))
|
||||
max-width
|
||||
max-height)))
|
||||
(set! (.-src img)
|
||||
(.createObjectURL (or (.-URL js/window)
|
||||
(.-webkitURL js/window))
|
||||
file)))))))
|
|
@ -134,3 +134,10 @@
|
|||
:year "numeric"
|
||||
:day "numeric"
|
||||
:weekday "long"})))
|
||||
|
||||
(defn year-month-day-concat
|
||||
[]
|
||||
(let [{:keys [year month day]} (get-date)
|
||||
month (if (< month 10) (str "0" month) month)
|
||||
day (if (< day 10) (str "0" day) day)]
|
||||
(str year "_" month "_" day)))
|
||||
|
|
Loading…
Reference in New Issue