1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.thumbnail.impl;
17
18 import java.awt.Image;
19 import java.awt.Rectangle;
20 import java.awt.image.BufferedImage;
21 import java.io.File;
22 import java.io.IOException;
23 import java.nio.file.Files;
24 import java.nio.file.Path;
25 import java.util.Iterator;
26 import java.util.Map;
27
28 import javax.imageio.ImageIO;
29 import javax.imageio.ImageReadParam;
30 import javax.imageio.ImageReader;
31 import javax.imageio.stream.ImageInputStream;
32
33 import org.apache.logging.log4j.LogManager;
34 import org.apache.logging.log4j.Logger;
35 import org.codelibs.core.lang.StringUtil;
36 import org.codelibs.core.misc.Tuple3;
37 import org.codelibs.fess.crawler.entity.ResponseData;
38 import org.codelibs.fess.mylasta.direction.FessConfig;
39 import org.codelibs.fess.util.ComponentUtil;
40 import org.codelibs.fess.util.DocumentUtil;
41
42
43
44
45
46
47
48
49
50
51
52
53 public class HtmlTagBasedGenerator extends BaseThumbnailGenerator {
54
55 private static final Logger logger = LogManager.getLogger(HtmlTagBasedGenerator.class);
56
57
58
59
60 public HtmlTagBasedGenerator() {
61 super();
62 }
63
64
65
66
67
68 @Override
69 public void destroy() {
70 }
71
72
73
74
75
76
77
78
79 @Override
80 public Tuple3<String, String, String> createTask(final String path, final Map<String, Object> docMap) {
81 final FessConfig fessConfig = ComponentUtil.getFessConfig();
82 final String thumbnailId = DocumentUtil.getValue(docMap, fessConfig.getIndexFieldId(), String.class);
83 final Tuple3<String, String, String> task = new Tuple3<>(getName(), thumbnailId, path);
84 if (logger.isDebugEnabled()) {
85 logger.debug("Create thumbnail task: {}", task);
86 }
87 return task;
88 }
89
90
91
92
93
94
95
96
97
98
99
100
101 @Override
102 public boolean generate(final String thumbnailId, final File outputFile) {
103 if (logger.isDebugEnabled()) {
104 logger.debug("Generate Thumbnail: {}", thumbnailId);
105 }
106
107 if (outputFile.exists()) {
108 if (logger.isDebugEnabled()) {
109 logger.debug("The thumbnail file exists: {}", outputFile.getAbsolutePath());
110 }
111 return true;
112 }
113
114 final File parentFile = outputFile.getParentFile();
115 final Path parentPath = parentFile.toPath();
116 try {
117 Files.createDirectories(parentPath);
118 } catch (final IOException e) {
119 logger.warn("Failed to create parent directory: {}", parentFile.getAbsolutePath(), e);
120 return false;
121 }
122
123 return process(thumbnailId, responseData -> {
124 if (!isImageMimeType(responseData)) {
125 if (logger.isDebugEnabled()) {
126 logger.debug("Thumbnail is not image: {} : {}", thumbnailId, responseData.getUrl());
127 }
128 updateThumbnailField(thumbnailId, StringUtil.EMPTY);
129 return false;
130 }
131 boolean created = false;
132 try (ImageInputStream input = ImageIO.createImageInputStream(responseData.getResponseBody())) {
133 switch (saveImage(input, outputFile)) {
134 case OK:
135 created = true;
136 break;
137 case FAILED:
138 logger.warn("Failed to create thumbnail: {} -> {}", thumbnailId, responseData.getUrl());
139 break;
140 case INVALID_SIZE:
141 logger.info("Unmatched thumbnail size: {} -> {}", thumbnailId, responseData.getUrl());
142 break;
143 case NO_IMAGE:
144 if (logger.isDebugEnabled()) {
145 logger.debug("No thumbnail: {} -> {}", thumbnailId, responseData.getUrl());
146 }
147 break;
148 default:
149 logger.error("Unknown thumbnail result: {} -> {}", thumbnailId, responseData.getUrl());
150 break;
151 }
152 } catch (final Throwable t) {
153 logger.warn("Failed to create thumbnail: {} -> {} ({}:{})", thumbnailId, responseData.getUrl(),
154 t.getClass().getCanonicalName(), t.getMessage());
155 if (logger.isDebugEnabled()) {
156 logger.debug("Details for failed thumbnail creation.", t);
157 }
158 } finally {
159 if (!created) {
160 updateThumbnailField(thumbnailId, StringUtil.EMPTY);
161 try {
162 Files.deleteIfExists(outputFile.toPath());
163 } catch (final IOException e) {
164 logger.warn("Failed to delete output file: {}", outputFile.getAbsolutePath(), e);
165 }
166 }
167 }
168 return outputFile.exists();
169 });
170
171 }
172
173
174
175
176
177
178
179
180 protected boolean isImageMimeType(final ResponseData responseData) {
181 final String mimeType = responseData.getMimeType();
182 if (mimeType == null) {
183 return true;
184 }
185
186 return switch (mimeType) {
187 case "image/png", "image/gif", "image/jpeg", "image/bmp" -> true;
188 default -> false;
189 };
190 }
191
192
193
194
195
196
197
198
199
200
201
202
203
204 protected Result saveImage(final ImageInputStream input, final File outputFile) throws IOException {
205 final FessConfig fessConfig = ComponentUtil.getFessConfig();
206 final Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
207 if (readers.hasNext()) {
208 final ImageReader reader = readers.next();
209 try {
210 reader.setInput(input);
211 final ImageReadParam param = reader.getDefaultReadParam();
212 final int width = reader.getWidth(0);
213 final int height = reader.getHeight(0);
214 if (width <= 0 || height <= 0) {
215 return Result.NO_IMAGE;
216 }
217 if (!fessConfig.validateThumbnailSize(width, height)) {
218 return Result.INVALID_SIZE;
219 }
220 final int samplingWidth = width / fessConfig.getThumbnailHtmlImageThumbnailWidthAsInteger();
221 final int samplingHeight = height / fessConfig.getThumbnailHtmlImageThumbnailHeightAsInteger();
222 param.setSourceSubsampling(samplingWidth <= 0 ? 1 : samplingWidth, samplingHeight <= 0 ? 1 : samplingHeight, 0, 0);
223 param.setSourceRegion(new Rectangle(width, height > width ? width : height));
224 final BufferedImage image = reader.read(0, param);
225 final int thumbnailWidth = fessConfig.getThumbnailHtmlImageThumbnailWidthAsInteger();
226 final int thumbnailHeight = (int) ((height > width ? width : height)
227 * fessConfig.getThumbnailHtmlImageThumbnailWidthAsInteger().floatValue() / width);
228 final BufferedImage thumbnail = new BufferedImage(thumbnailWidth, thumbnailHeight, image.getType());
229 thumbnail.getGraphics()
230 .drawImage(image.getScaledInstance(thumbnailWidth, thumbnailHeight, Image.SCALE_AREA_AVERAGING), 0, 0,
231 thumbnailWidth, thumbnailHeight, null);
232 ImageIO.write(thumbnail, fessConfig.getThumbnailHtmlImageFormat(), outputFile);
233 image.flush();
234 return Result.OK;
235 } finally {
236 reader.dispose();
237 }
238 }
239 return Result.FAILED;
240 }
241
242
243
244
245 protected enum Result {
246
247 OK,
248
249 FAILED,
250
251 INVALID_SIZE,
252
253 NO_IMAGE;
254 }
255 }