mirror of
https://github.com/penpot/penpot.git
synced 2025-03-13 16:21:57 -05:00
Refactor thumbnails generation.
- Start use temporal file instead of pipe because im4java seems does not work properly with it (it constantly generates `-` file on he *cwd*) - Move many impl of ratpack file types from old storage vendor package to catacumba code. - Add spec for thumbnails configuration.
This commit is contained in:
parent
618ce12fd8
commit
e6602ac68b
3 changed files with 90 additions and 68 deletions
|
@ -27,7 +27,8 @@
|
|||
|
||||
(def +thumbnail-options+ {:src :path
|
||||
:dst :thumbnail
|
||||
:size [300 110]
|
||||
:width 300
|
||||
:height 100
|
||||
:quality 92
|
||||
:format "webp"})
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2016-2017 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.images
|
||||
"Image postprocessing."
|
||||
|
@ -13,41 +13,81 @@
|
|||
[datoteka.proto :as pt]
|
||||
[uxbox.util.spec :as us]
|
||||
[uxbox.media :as media]
|
||||
[uxbox.util.images :as images]
|
||||
[uxbox.util.data :refer (dissoc-in)]))
|
||||
[uxbox.util.data :refer (dissoc-in)])
|
||||
(:import java.io.InputStream
|
||||
java.io.ByteArrayInputStream
|
||||
ratpack.form.UploadedFile
|
||||
ratpack.http.TypedData
|
||||
org.im4java.core.IMOperation
|
||||
org.im4java.core.ConvertCmd))
|
||||
|
||||
;; FIXME: add spec for thumbnail config
|
||||
;; --- Thumbnails Generation
|
||||
|
||||
(s/def ::width integer?)
|
||||
(s/def ::height integer?)
|
||||
(s/def ::quality #(< 0 % 101))
|
||||
(s/def ::format #{"jpg" "webp"})
|
||||
(s/def ::thumbnail-opts
|
||||
(s/keys :opt-un [::format ::quality ::width ::height]))
|
||||
|
||||
;; Related info on how thumbnails generation
|
||||
;; http://www.imagemagick.org/Usage/thumbnails/
|
||||
|
||||
(defn generate-thumbnail
|
||||
([input] (generate-thumbnail input nil))
|
||||
([input {:keys [size quality format width height]
|
||||
:or {format "jpg"
|
||||
quality 92
|
||||
width 200
|
||||
height 200}
|
||||
:as opts}]
|
||||
{:pre [(us/valid? ::thumbnail-opts opts)
|
||||
(fs/path? input)]}
|
||||
(let [tmp (fs/create-tempfile :suffix (str "." format))
|
||||
opr (doto (IMOperation.)
|
||||
(.addImage)
|
||||
(.autoOrient)
|
||||
(.resize (int width) (int height) "^")
|
||||
(.quality (double quality))
|
||||
(.addImage))]
|
||||
(doto (ConvertCmd.)
|
||||
(.run opr (into-array (map str [input tmp]))))
|
||||
(let [thumbnail-data (fs/slurp-bytes tmp)]
|
||||
(fs/delete tmp)
|
||||
(ByteArrayInputStream. thumbnail-data)))))
|
||||
|
||||
(defn make-thumbnail
|
||||
[path {:keys [size format quality] :as cfg}]
|
||||
(let [parent (fs/parent path)
|
||||
[filename ext] (fs/split-ext path)
|
||||
|
||||
suffix-parts [(nth size 0) (nth size 1) quality format]
|
||||
final-name (apply str filename "-" (interpose "." suffix-parts))
|
||||
final-path (fs/path parent final-name)
|
||||
[input {:keys [width height format quality] :as opts}]
|
||||
{:pre [(us/valid? ::thumbnail-opts opts)
|
||||
(or (string? input)
|
||||
(fs/path input))]}
|
||||
(let [parent (fs/parent input)
|
||||
[filename ext] (fs/split-ext input)
|
||||
|
||||
suffix (->> [width height quality format]
|
||||
(interpose ".")
|
||||
(apply str))
|
||||
thumbnail-path (fs/path parent (str filename "-" suffix))
|
||||
images-storage media/images-storage
|
||||
thumbs-storage media/thumbnails-storage]
|
||||
(if @(st/exists? thumbs-storage final-path)
|
||||
(str (st/public-url thumbs-storage final-path))
|
||||
(if @(st/exists? images-storage path)
|
||||
(let [datapath @(st/lookup images-storage path)
|
||||
content (images/thumbnail datapath cfg)
|
||||
path @(st/save thumbs-storage final-path content)]
|
||||
(if @(st/exists? thumbs-storage thumbnail-path)
|
||||
(str (st/public-url thumbs-storage thumbnail-path))
|
||||
(if @(st/exists? images-storage input)
|
||||
(let [datapath @(st/lookup images-storage input)
|
||||
thumbnail (generate-thumbnail datapath opts)
|
||||
path @(st/save thumbs-storage thumbnail-path thumbnail)]
|
||||
(str (st/public-url thumbs-storage path)))
|
||||
nil))))
|
||||
|
||||
(defn populate-thumbnail
|
||||
[entry {:keys [src dst] :as cfg}]
|
||||
(assert (map? entry) "`entry` should be map")
|
||||
|
||||
[entry {:keys [src dst] :as opts}]
|
||||
{:pre [(map? entry)]}
|
||||
(let [src (if (vector? src) src [src])
|
||||
dst (if (vector? dst) dst [dst])
|
||||
src (get-in entry src)]
|
||||
(if (empty? src)
|
||||
entry
|
||||
(assoc-in entry dst (make-thumbnail src cfg)))))
|
||||
(assoc-in entry dst (make-thumbnail src opts)))))
|
||||
|
||||
(defn populate-thumbnails
|
||||
[entry & settings]
|
||||
|
@ -55,8 +95,8 @@
|
|||
|
||||
(defn populate-urls
|
||||
[entry storage src dst]
|
||||
(assert (map? entry) "`entry` should be map")
|
||||
(assert (st/storage? storage) "`storage` should be a valid storage instance.")
|
||||
{:pre [(map? entry)
|
||||
(st/storage? storage)]}
|
||||
(let [src (if (vector? src) src [src])
|
||||
dst (if (vector? dst) dst [dst])
|
||||
value (get-in entry src)]
|
||||
|
@ -66,3 +106,28 @@
|
|||
(-> entry
|
||||
(dissoc-in src)
|
||||
(assoc-in dst url))))))
|
||||
|
||||
;; --- Impl
|
||||
|
||||
(extend-type UploadedFile
|
||||
pt/IPath
|
||||
(-path [this]
|
||||
(pt/-path (.getFileName ^UploadedFile this))))
|
||||
|
||||
(extend-type TypedData
|
||||
pt/IContent
|
||||
(-input-stream [this]
|
||||
(.getInputStream this))
|
||||
|
||||
io/IOFactory
|
||||
(make-reader [td opts]
|
||||
(let [^InputStream is (.getInputStream td)]
|
||||
(io/make-reader is opts)))
|
||||
(make-writer [path opts]
|
||||
(throw (UnsupportedOperationException. "read only object")))
|
||||
(make-input-stream [td opts]
|
||||
(let [^InputStream is (.getInputStream td)]
|
||||
(io/make-input-stream is opts)))
|
||||
(make-output-stream [path opts]
|
||||
(throw (UnsupportedOperationException. "read only object"))))
|
||||
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.util.images
|
||||
"Images transformation utils."
|
||||
(:require [clojure.java.io :as io])
|
||||
(:import org.im4java.core.IMOperation
|
||||
org.im4java.core.ConvertCmd
|
||||
org.im4java.process.Pipe
|
||||
java.io.ByteArrayInputStream
|
||||
java.io.ByteArrayOutputStream))
|
||||
|
||||
;; Related info on how thumbnails generation
|
||||
;; http://www.imagemagick.org/Usage/thumbnails/
|
||||
|
||||
(defn thumbnail
|
||||
([input] (thumbnail input nil))
|
||||
([input {:keys [size quality format]
|
||||
:or {format "jpg"
|
||||
quality 92
|
||||
size [200 200]}
|
||||
:as opts}]
|
||||
{:pre [(vector? size)]}
|
||||
(with-open [out (ByteArrayOutputStream.)
|
||||
in (io/input-stream input)]
|
||||
(let [[width height] size
|
||||
pipe (Pipe. in out)
|
||||
op (doto (IMOperation.)
|
||||
(.addRawArgs ^java.util.List ["-"])
|
||||
(.autoOrient)
|
||||
;; (.thumbnail (int width) (int height) "^")
|
||||
;; (.gravity "center")
|
||||
;; (.extent (int width) (int height))
|
||||
(.resize (int width) (int height) "^")
|
||||
(.quality (double quality))
|
||||
(.addRawArgs ^java.util.List [(str format ":-")]))
|
||||
cmd (doto (ConvertCmd.)
|
||||
(.setInputProvider pipe)
|
||||
(.setOutputConsumer pipe))]
|
||||
(.run cmd op (make-array Object 0))
|
||||
(ByteArrayInputStream. (.toByteArray out))))))
|
Loading…
Add table
Reference in a new issue