/
preview.clj
109 lines (97 loc) · 4.81 KB
/
preview.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
(ns lupapiste-commons.preview
(:require [taoensso.timbre :refer [debugf warnf]]
[clojure.java.io :as io])
(:import (org.apache.pdfbox.pdmodel PDDocument)
(org.apache.pdfbox.io MemoryUsageSetting)
(org.apache.pdfbox.tools.imageio ImageIOUtil)
(java.awt.image BufferedImage)
(java.awt RenderingHints)
(java.io ByteArrayOutputStream ByteArrayInputStream FileInputStream InputStream)
(javax.imageio ImageIO)
(org.apache.pdfbox.rendering PDFRenderer)))
(def rez 600.0)
; The aspect ratio should be about sqrt(2) or less, i.e. A paper series aspect ratio is OK
(def min-aspect 0.7)
(def max-aspect 1.45)
(defn- buffered-image-to-input-stream
"Converts BufferedImage inputStream"
[^BufferedImage image]
(let [output (ByteArrayOutputStream.)]
(ImageIOUtil/writeImage image "jpg" output 72 0.6)
(ByteArrayInputStream. (.toByteArray output))))
(defn- size-ok? [^BufferedImage image]
(if (and (< (.getWidth image) 45000) (< (.getHeight image) 45000))
true
(do
(warnf "Image size (%d x %d) is too big for preview [byte array length exceeds MAX_INTEGER]" (.getWidth image) (.getHeight image))
false)))
(defn- must-crop? [width height]
(not (< min-aspect (/ width height) max-aspect)))
(defn- crop-amount [x y]
(if (< (/ x y) min-aspect)
(int (- y (/ x min-aspect)))
0))
(defn- ^BufferedImage scale-image
"Crops and scales BufferedImage to predefined resolution"
[^BufferedImage image]
(when (size-ok? image)
(let [original-width (.getWidth image)
original-height (.getHeight image)
crop-x (crop-amount original-height original-width)
crop-y (crop-amount original-width original-height)
cropped-width (- original-width crop-x)
cropped-height (- original-height crop-y)
scale (min (/ rez cropped-width) (/ rez cropped-height))
width (* scale cropped-width)
height (* scale cropped-height)
new-image (BufferedImage. width height BufferedImage/TYPE_INT_RGB)]
(debugf "scale-image rez: %s x %s, crop: %s x %s, scale: %s" original-width original-height crop-x crop-y scale)
(doto (.createGraphics new-image)
(.setRenderingHint RenderingHints/KEY_INTERPOLATION, RenderingHints/VALUE_INTERPOLATION_BICUBIC)
(.setRenderingHint RenderingHints/KEY_RENDERING, RenderingHints/VALUE_RENDER_QUALITY)
(.setRenderingHint RenderingHints/KEY_ANTIALIASING, RenderingHints/VALUE_ANTIALIAS_ON)
(.drawImage image, 0, 0, width, height, crop-x, crop-y, original-width, original-height, nil)
(.dispose))
new-image)))
(defn- ^BufferedImage pdf-to-buffered-image
"Converts first page of the PDF to BufferedImage"
[pdf-input]
(let [^InputStream input (if (string? pdf-input)
(FileInputStream. ^String pdf-input)
pdf-input)]
(with-open [document (PDDocument/load input (MemoryUsageSetting/setupMixed (* 100 1024 1024)))]
(let [crop-box (-> (.getPage document 0) (.getCropBox))
original-width (.getWidth crop-box)
original-height (.getHeight crop-box)
; If the image is too wide / high, we have to crop it to a more manageable aspect ratio
; and because the PDFRenderer does not directly support this, we first render in 2 x target resolution
; and then scale the image down to the final target size (with cropping)
crop? (must-crop? original-width original-height)
target-rez (if crop? (* 2 rez) rez)
scale (->> (max original-width original-height) (/ target-rez) float)]
(debugf "scale for pdf preview: %s" scale)
(cond-> (-> (PDFRenderer. document) (.renderImage 0 scale))
crop? scale-image)))))
(defn- ^BufferedImage raster-to-buffered-image
"Converts Raster image to BufferedImage"
[input]
(-> (ImageIO/read ^InputStream (io/input-stream input))
scale-image))
(defn converter [content-type]
(cond
(= "application/pdf" content-type) pdf-to-buffered-image
(re-matches (re-pattern "(image/(gif|jpeg|png|tiff))") content-type) raster-to-buffered-image))
(defn- ^BufferedImage to-buffered-image
"Tries to read content to image by JAI or apache.pdfbox. Returns nil on fail"
[content content-type]
(try
(when-let [op (converter content-type)]
(op content))
(catch Exception e (warnf "preview to-buffered-image was unable to read content of a %s file: %s" content-type e))))
(defn create-preview
"Tries to create preview image IF content type can be processed to image by JAI or apache.pdfbox. Returns nil on fail"
[content content-type]
(some-> (to-buffered-image content content-type)
buffered-image-to-input-stream))
(defn placeholder-image-is []
(io/input-stream (io/resource "no-preview-available.jpg")))