0
Fork 0
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:
Andrey Antukh 2017-02-25 16:01:00 +01:00
parent 618ce12fd8
commit e6602ac68b
No known key found for this signature in database
GPG key ID: 4DFEBCB8316A8B95
3 changed files with 90 additions and 68 deletions

View file

@ -27,7 +27,8 @@
(def +thumbnail-options+ {:src :path
:dst :thumbnail
:size [300 110]
:width 300
:height 100
:quality 92
:format "webp"})

View file

@ -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"))))

View file

@ -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))))))