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 | |
package net.sourceforge.pebble.decorator; |
34 | |
|
35 | |
import java.awt.RenderingHints; |
36 | |
import java.awt.geom.AffineTransform; |
37 | |
import java.awt.image.AffineTransformOp; |
38 | |
import java.awt.image.BufferedImage; |
39 | |
import java.io.File; |
40 | |
import java.io.IOException; |
41 | |
import java.util.HashMap; |
42 | |
import java.util.Iterator; |
43 | |
import java.util.Map; |
44 | |
import net.sourceforge.pebble.domain.BlogEntry; |
45 | |
import net.sourceforge.pebble.domain.StaticPage; |
46 | |
import net.sourceforge.pebble.api.decorator.ContentDecoratorContext; |
47 | |
import java.util.regex.Matcher; |
48 | |
import java.util.regex.Pattern; |
49 | |
import javax.imageio.IIOImage; |
50 | |
import javax.imageio.ImageIO; |
51 | |
import javax.imageio.ImageWriteParam; |
52 | |
import javax.imageio.ImageWriter; |
53 | |
import javax.imageio.stream.FileImageOutputStream; |
54 | |
import net.sourceforge.pebble.PluginProperties; |
55 | |
import net.sourceforge.pebble.domain.Blog; |
56 | |
|
57 | |
|
58 | |
|
59 | |
|
60 | |
|
61 | 0 | public class ThumbnailDecorator extends ContentDecoratorSupport { |
62 | |
|
63 | |
|
64 | |
|
65 | |
|
66 | |
|
67 | |
|
68 | |
|
69 | |
@Override |
70 | |
public void decorate(ContentDecoratorContext context, BlogEntry blogEntry) { |
71 | 0 | Blog blog = blogEntry.getBlog(); |
72 | 0 | blogEntry.setBody(replaceTags(blogEntry.getBody(), blog)); |
73 | 0 | blogEntry.setExcerpt(replaceTags(blogEntry.getExcerpt(), blog)); |
74 | |
|
75 | 0 | } |
76 | |
|
77 | |
|
78 | |
|
79 | |
|
80 | |
|
81 | |
|
82 | |
|
83 | |
@Override |
84 | |
public void decorate(ContentDecoratorContext context, |
85 | |
StaticPage staticPage) { |
86 | 0 | staticPage.setBody(replaceTags(staticPage.getContent(), |
87 | |
staticPage.getBlog())); |
88 | 0 | } |
89 | |
|
90 | |
|
91 | |
|
92 | |
|
93 | |
|
94 | |
|
95 | |
|
96 | |
private String replaceTags(String content, Blog blog) { |
97 | |
|
98 | |
|
99 | 0 | if (content == null || content.length() == 0) { |
100 | 0 | return ""; |
101 | |
} |
102 | |
|
103 | |
|
104 | 0 | int defaultThumbSize = 200; |
105 | 0 | int defaultPopupSize = 640; |
106 | 0 | PluginProperties props = blog.getPluginProperties(); |
107 | 0 | if (props.hasProperty(thumbSizeProp)) { |
108 | |
try { |
109 | 0 | defaultThumbSize = |
110 | |
Integer.parseInt(props.getProperty(thumbSizeProp)); |
111 | 0 | } catch (NumberFormatException nfe) { |
112 | |
|
113 | 0 | } |
114 | |
} |
115 | 0 | if (props.hasProperty(popupSizeProp)) { |
116 | |
try { |
117 | 0 | defaultPopupSize = |
118 | |
Integer.parseInt(props.getProperty(popupSizeProp)); |
119 | 0 | } catch (NumberFormatException nfe) { |
120 | |
|
121 | 0 | } |
122 | |
} |
123 | |
|
124 | |
|
125 | 0 | Matcher tagM = tagRE.matcher(content); |
126 | 0 | StringBuffer sb = new StringBuffer(content.length()); |
127 | 0 | while (tagM.find()) { |
128 | 0 | StringBuilder repl = new StringBuilder(); |
129 | 0 | Matcher attrM = attrRE.matcher(tagM.group(1)); |
130 | 0 | String src = null; |
131 | 0 | String alt = ""; |
132 | 0 | int thumbSize = defaultThumbSize; |
133 | 0 | int popupSize = defaultPopupSize; |
134 | |
|
135 | |
|
136 | 0 | while (attrM.find()) { |
137 | 0 | String an = attrM.group(1); |
138 | 0 | String av = attrM.group(2); |
139 | 0 | if (an.equalsIgnoreCase("src")) { |
140 | 0 | src = av; |
141 | 0 | } else if (an.equalsIgnoreCase("alt")) { |
142 | 0 | alt = av; |
143 | 0 | } else if (an.equalsIgnoreCase("thumbSize")) { |
144 | |
try { |
145 | 0 | thumbSize = Integer.parseInt(av); |
146 | 0 | } catch (NumberFormatException e) { |
147 | 0 | tagM.appendReplacement(sb, String.format( |
148 | |
"<!-- ERROR: invalid thumbnail thumbSize \"%s\" -->", |
149 | |
av)); |
150 | 0 | } |
151 | 0 | } else if (an.equalsIgnoreCase("popupSize")) { |
152 | |
try { |
153 | 0 | popupSize = Integer.parseInt(av); |
154 | 0 | } catch (NumberFormatException e) { |
155 | 0 | tagM.appendReplacement(sb, String.format( |
156 | |
"<!-- ERROR: invalid thumbnail popupSize \"%s\" -->", |
157 | |
av)); |
158 | 0 | } |
159 | |
} |
160 | 0 | } |
161 | |
|
162 | |
|
163 | 0 | if (src == null) { |
164 | 0 | tagM.appendReplacement(sb, String.format( |
165 | |
"<!-- ERROR: missing thumbnail src -->")); |
166 | |
} else { |
167 | 0 | tagM.appendReplacement(sb, |
168 | |
renderTag(new File(src), thumbSize, popupSize, alt, blog)); |
169 | |
} |
170 | 0 | } |
171 | 0 | tagM.appendTail(sb); |
172 | 0 | return sb.toString(); |
173 | |
} |
174 | |
|
175 | |
|
176 | |
|
177 | |
|
178 | |
|
179 | |
|
180 | |
|
181 | |
|
182 | |
|
183 | |
|
184 | |
|
185 | |
private String renderTag(File src, int thumbSize, int popupSize, |
186 | |
String alt, Blog blog) { |
187 | |
|
188 | |
|
189 | 0 | File img = new File(blog.getRoot(), src.getPath()); |
190 | 0 | if (! img.isFile()) { |
191 | 0 | return String.format( |
192 | |
"<!-- ERROR: invalid thumbnail src \"%s\" -->", src); |
193 | |
} |
194 | |
|
195 | |
|
196 | 0 | File thumbDir = new File(img.getParent(), "thumbnails"); |
197 | 0 | if (!thumbDir.isDirectory()) { |
198 | 0 | if (!thumbDir.mkdir()) { |
199 | 0 | return String.format( |
200 | |
"<!-- ERROR: can't create thumbnail directory -->", |
201 | |
thumbDir.toString()); |
202 | |
} |
203 | |
} |
204 | |
|
205 | |
|
206 | 0 | File thumb = new File(thumbDir, img.getName()); |
207 | 0 | if (!thumb.isFile() || thumb.lastModified() < img.lastModified()) { |
208 | 0 | if (! createThumbnail(img, thumb, thumbSize)) { |
209 | 0 | return String.format( |
210 | |
"<!-- ERROR: can't create thumbnail \"%s\" -->", thumb); |
211 | |
} |
212 | |
} |
213 | |
|
214 | |
|
215 | 0 | File tsrc = new File(new File(src.getParent(), "thumbnails"), |
216 | |
src.getName()); |
217 | 0 | return String.format("<a href=\"%1$s\" onclick=\"window.open(" + |
218 | |
"'%1$s','popup','width=%4$d,height=%4$d,toolbar=no,directories=no," + |
219 | |
"location=no,menubar=no,status=no'); return false\" " + |
220 | |
"class=\"thumbnailLink\"><img src=\"%2$s\" alt=\"%3$s\" " + |
221 | |
"class=\"thumbnailImage\"/></a>", |
222 | |
src, tsrc, alt, popupSize); |
223 | |
} |
224 | |
|
225 | |
|
226 | |
|
227 | |
|
228 | |
|
229 | |
|
230 | |
|
231 | |
|
232 | |
public boolean createThumbnail(File imgFile, File thumbFile, |
233 | |
int thumbSize) { |
234 | |
|
235 | 0 | String suffix = imgFile.getName(); |
236 | 0 | int dot = suffix.lastIndexOf('.'); |
237 | 0 | if (dot < 1) { |
238 | 0 | return false; |
239 | |
} |
240 | 0 | suffix = suffix.substring(dot + 1); |
241 | |
|
242 | |
try { |
243 | |
|
244 | 0 | BufferedImage img = ImageIO.read(imgFile); |
245 | 0 | if (img == null) { |
246 | 0 | return false; |
247 | |
} |
248 | |
|
249 | |
|
250 | 0 | int ih = img.getHeight(); |
251 | 0 | int iw = img.getWidth(); |
252 | |
int thumbH, thumbW; |
253 | |
float scale; |
254 | 0 | if (iw > ih) { |
255 | 0 | scale = (float) thumbSize / (float) iw; |
256 | 0 | thumbW = thumbSize; |
257 | 0 | thumbH = Math.round(ih * scale); |
258 | |
} else { |
259 | 0 | scale = (float) thumbSize / (float) ih; |
260 | 0 | thumbH = thumbSize; |
261 | 0 | thumbW = Math.round(iw * scale); |
262 | |
} |
263 | |
|
264 | |
|
265 | 0 | BufferedImage thumb = |
266 | |
new BufferedImage(thumbW, thumbH, img.getType()); |
267 | 0 | AffineTransform at = |
268 | |
AffineTransform.getScaleInstance(scale, scale); |
269 | 0 | AffineTransformOp ato = new AffineTransformOp(at, renderHints); |
270 | 0 | ato.filter(img, thumb); |
271 | |
|
272 | |
|
273 | 0 | Iterator<ImageWriter> iwi = |
274 | |
ImageIO.getImageWritersBySuffix(suffix); |
275 | 0 | if (! iwi.hasNext()) { |
276 | 0 | return false; |
277 | |
} |
278 | 0 | ImageWriter thumbWriter = iwi.next(); |
279 | 0 | ImageWriteParam iwp = thumbWriter.getDefaultWriteParam(); |
280 | 0 | if (iwp.canWriteCompressed()) { |
281 | 0 | String ct[] = iwp.getCompressionTypes(); |
282 | 0 | if (ct != null) { |
283 | 0 | iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); |
284 | 0 | iwp.setCompressionType(ct[0]); |
285 | 0 | iwp.setCompressionQuality(0.8F); |
286 | |
} |
287 | |
} |
288 | 0 | FileImageOutputStream thumbOut = |
289 | |
new FileImageOutputStream(thumbFile); |
290 | 0 | thumbWriter.setOutput(thumbOut); |
291 | 0 | thumbWriter.write(null, new IIOImage(thumb, null, null), iwp); |
292 | 0 | thumbWriter.dispose(); |
293 | 0 | thumbOut.close(); |
294 | |
|
295 | 0 | } catch (IOException ex) { |
296 | 0 | return false; |
297 | 0 | } |
298 | 0 | return true; |
299 | |
} |
300 | |
|
301 | |
|
302 | |
private static final String thumbSizeProp = "Thumbnail.thumbnailSize"; |
303 | |
|
304 | |
private static final String popupSizeProp = "Thumbnail.popupSize"; |
305 | |
|
306 | 0 | private static Pattern tagRE = Pattern.compile( |
307 | |
"<thumbnail\\s+(.+?)\\s*/>", |
308 | |
Pattern.CASE_INSENSITIVE | Pattern.DOTALL); |
309 | |
|
310 | 0 | private static Pattern attrRE = Pattern.compile( |
311 | |
"([\\w_-]+)\\s*=\\s*\"([^\"]+)\"", |
312 | |
Pattern.CASE_INSENSITIVE | Pattern.DOTALL); |
313 | |
|
314 | |
private static final RenderingHints renderHints; |
315 | |
|
316 | |
|
317 | |
static { |
318 | 0 | Map<RenderingHints.Key, Object> hintMap = |
319 | |
new HashMap<RenderingHints.Key, Object>(); |
320 | 0 | hintMap.put(RenderingHints.KEY_ALPHA_INTERPOLATION, |
321 | |
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); |
322 | 0 | hintMap.put(RenderingHints.KEY_ANTIALIASING, |
323 | |
RenderingHints.VALUE_ANTIALIAS_ON); |
324 | 0 | hintMap.put(RenderingHints.KEY_COLOR_RENDERING, |
325 | |
RenderingHints.VALUE_COLOR_RENDER_QUALITY); |
326 | 0 | hintMap.put(RenderingHints.KEY_DITHERING, |
327 | |
RenderingHints.VALUE_DITHER_DISABLE); |
328 | 0 | hintMap.put(RenderingHints.KEY_INTERPOLATION, |
329 | |
RenderingHints.VALUE_INTERPOLATION_BILINEAR); |
330 | 0 | hintMap.put(RenderingHints.KEY_ALPHA_INTERPOLATION, |
331 | |
RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); |
332 | 0 | hintMap.put(RenderingHints.KEY_COLOR_RENDERING, |
333 | |
RenderingHints.VALUE_COLOR_RENDER_QUALITY); |
334 | 0 | renderHints = new RenderingHints(hintMap); |
335 | 0 | } |
336 | |
} |