1 | // Jomic - a viewer for comic book archives. |
2 | // Copyright (C) 2004-2011 Thomas Aglassinger |
3 | // |
4 | // This program is free software: you can redistribute it and/or modify |
5 | // it under the terms of the GNU General Public License as published by |
6 | // the Free Software Foundation, either version 3 of the License, or |
7 | // (at your option) any later version. |
8 | // |
9 | // This program is distributed in the hope that it will be useful, |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | // GNU General Public License for more details. |
13 | // |
14 | // You should have received a copy of the GNU General Public License |
15 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | package net.sf.jomic.comic; |
17 | |
18 | import java.awt.Dimension; |
19 | import java.awt.image.RenderedImage; |
20 | import java.io.File; |
21 | import java.io.IOException; |
22 | |
23 | import net.sf.jomic.tools.BasicSettings; |
24 | import net.sf.jomic.tools.ImageInfo; |
25 | import net.sf.jomic.tools.ImageTools; |
26 | import net.sf.jomic.tools.StringTools; |
27 | |
28 | /** |
29 | * Conversion describing how to convert a comic and/or images in it. To actually convert something, |
30 | * you need a <code>ConvertComicTask</code>. |
31 | * |
32 | * @see ConversionBean |
33 | * @see ConvertComicTask |
34 | * @author Thomas Aglassinger |
35 | */ |
36 | public class Conversion extends BasicSettings |
37 | { |
38 | public static final String COMIC_FORMAT_CBZ = "cbz"; |
39 | public static final String COMIC_FORMAT_PDF = "pdf"; |
40 | public static final String IMAGE_FORMAT_JFIF = "jpeg"; |
41 | public static final String IMAGE_FORMAT_PNG = "png"; |
42 | public static final String IMAGE_FORMAT_PRESERVE = "preserve"; |
43 | static final String PROPERTY_ADD_ONLY_IMAGES = "addNonImages"; |
44 | static final String PROPERTY_COMIC_FORMAT = "comicFormat"; |
45 | static final String PROPERTY_EXCLUDE_THUMB_PAGES = "excludeThumbPages"; |
46 | static final String PROPERTY_IMAGE_FORMAT = "imageFormat"; |
47 | static final String PROPERTY_LIMIT_PAGE_SIZE = "limitPageSize"; |
48 | static final String PROPERTY_MAX_PAGE_HEIGHT = "maxPageHeight"; |
49 | static final String PROPERTY_MAX_PAGE_WIDTH = "maxPageWidth"; |
50 | static final String PROPERTY_MAX_THUMB_PAGE_HEIGHT = "maxThumbPageHeight"; |
51 | static final String PROPERTY_MAX_THUMB_PAGE_WIDTH = "maxThumbPageWidth"; |
52 | private static final boolean DEFAULT_ADD_ONLY_IMAGES = false; |
53 | private static final String DEFAULT_COMIC_FORMAT = COMIC_FORMAT_CBZ; |
54 | private static final boolean DEFAULT_EXCLUDE_THUMB_PAGES = true; |
55 | private static final String DEFAULT_IMAGE_FORMAT = IMAGE_FORMAT_PRESERVE; |
56 | private static final boolean DEFAULT_LIMIT_PAGE_SIZE = false; |
57 | private static final int DEFAULT_MAX_PAGE_HEIGHT = 1600; |
58 | private static final int DEFAULT_MAX_PAGE_WIDTH = 1200; |
59 | private static final int DEFAULT_MAX_THUMB_HEIGHT = 200; |
60 | private static final int DEFAULT_MAX_THUMB_WIDTH = 160; |
61 | private static final String[] VALID_COMIC_FORMATS = new String[]{ |
62 | COMIC_FORMAT_CBZ, COMIC_FORMAT_PDF}; |
63 | private static final String[] VALID_IMAGE_FORMATS = new String[]{ |
64 | IMAGE_FORMAT_PRESERVE, IMAGE_FORMAT_JFIF, IMAGE_FORMAT_PNG}; |
65 | |
66 | private ImageTools imageTools; |
67 | private StringTools stringTools; |
68 | |
69 | public Conversion() { |
70 | super(); |
71 | imageTools = ImageTools.instance(); |
72 | stringTools = StringTools.instance(); |
73 | |
74 | for (int i = 0; i < VALID_COMIC_FORMATS.length; i += 1) { |
75 | checkValidComicFileFormat(VALID_COMIC_FORMATS[i]); |
76 | } |
77 | for (int i = 0; i < VALID_IMAGE_FORMATS.length; i += 1) { |
78 | checkValidImageFileFormat(VALID_IMAGE_FORMATS[i]); |
79 | } |
80 | |
81 | setDefault(PROPERTY_ADD_ONLY_IMAGES, DEFAULT_ADD_ONLY_IMAGES); |
82 | setDefault(PROPERTY_COMIC_FORMAT, DEFAULT_COMIC_FORMAT); |
83 | setDefault(PROPERTY_EXCLUDE_THUMB_PAGES, DEFAULT_EXCLUDE_THUMB_PAGES); |
84 | setDefault(PROPERTY_IMAGE_FORMAT, DEFAULT_IMAGE_FORMAT); |
85 | setDefault(PROPERTY_LIMIT_PAGE_SIZE, DEFAULT_LIMIT_PAGE_SIZE); |
86 | setDefault(PROPERTY_MAX_PAGE_HEIGHT, DEFAULT_MAX_PAGE_HEIGHT); |
87 | setDefault(PROPERTY_MAX_PAGE_WIDTH, DEFAULT_MAX_PAGE_WIDTH); |
88 | setDefault(PROPERTY_MAX_THUMB_PAGE_HEIGHT, DEFAULT_MAX_THUMB_HEIGHT); |
89 | setDefault(PROPERTY_MAX_THUMB_PAGE_WIDTH, DEFAULT_MAX_THUMB_WIDTH); |
90 | } |
91 | |
92 | public Conversion(String newComicFormat) { |
93 | this(); |
94 | checkValidComicFileFormat(newComicFormat); |
95 | setComicFormat(newComicFormat); |
96 | } |
97 | |
98 | public void setAddOnlyImages(boolean newValue) { |
99 | setBooleanProperty(PROPERTY_ADD_ONLY_IMAGES, newValue); |
100 | } |
101 | |
102 | /** |
103 | * Set the format of the target comic. |
104 | * |
105 | * @param newFileFormat COMIC_FORMAT_CBZ or COMIC_FORMAT_PDF |
106 | */ |
107 | public void setComicFormat(String newFileFormat) { |
108 | checkValidComicFileFormat(newFileFormat); |
109 | setProperty(PROPERTY_COMIC_FORMAT, newFileFormat); |
110 | } |
111 | |
112 | public void setExcludeThumbPages(boolean newValue) { |
113 | setBooleanProperty(PROPERTY_EXCLUDE_THUMB_PAGES, newValue); |
114 | } |
115 | |
116 | /** |
117 | * Set the fomat of the images in the target comic. |
118 | * |
119 | * @param newImageFormat IMAGE_FORMAT_JFIF, IMAGE_FORMAT_PNG, or IMAGE_FORMAT_PRESERVE |
120 | */ |
121 | public void setImageFormat(String newImageFormat) { |
122 | checkValidImageFileFormat(newImageFormat); |
123 | setProperty(PROPERTY_IMAGE_FORMAT, newImageFormat); |
124 | } |
125 | |
126 | public void setLimitImageSize(boolean newValue) { |
127 | setBooleanProperty(PROPERTY_LIMIT_PAGE_SIZE, newValue); |
128 | } |
129 | |
130 | public void setMaxImageHeight(int maxImageHeight) { |
131 | assert maxImageHeight >= 0; |
132 | setIntProperty(PROPERTY_MAX_PAGE_HEIGHT, maxImageHeight); |
133 | } |
134 | |
135 | public void setMaxImageWidth(int maxImageWidth) { |
136 | assert maxImageWidth >= 0; |
137 | setIntProperty(PROPERTY_MAX_PAGE_WIDTH, maxImageWidth); |
138 | } |
139 | |
140 | public void setMaxThumbHeight(int maxThumbPageHeight) { |
141 | assert maxThumbPageHeight >= 0; |
142 | setIntProperty(PROPERTY_MAX_THUMB_PAGE_HEIGHT, maxThumbPageHeight); |
143 | } |
144 | |
145 | public void setMaxThumbWidth(int maxThumbPageWidth) { |
146 | assert maxThumbPageWidth >= 0; |
147 | setIntProperty(PROPERTY_MAX_THUMB_PAGE_WIDTH, maxThumbPageWidth); |
148 | } |
149 | |
150 | public String getComicFormat() { |
151 | return getProperty(PROPERTY_COMIC_FORMAT); |
152 | } |
153 | |
154 | /** |
155 | * Get the preferred file suffix for a comic according to <code>getComicFormat()</code> |
156 | * (without a leading "."). |
157 | * |
158 | * @return "cbz" or "pdf". |
159 | * @see #getComicFormat() |
160 | */ |
161 | public String getComicFormatSuffix() { |
162 | String result; |
163 | |
164 | if (getComicFormat().equals(Conversion.COMIC_FORMAT_CBZ)) { |
165 | result = "cbz"; |
166 | } else if (getComicFormat().equals(Conversion.COMIC_FORMAT_PDF)) { |
167 | result = "pdf"; |
168 | } else { |
169 | assert false : "comicFormat=" + stringTools.sourced(getComicFormat()); |
170 | result = null; |
171 | } |
172 | return result; |
173 | } |
174 | |
175 | public String getImageFormat() { |
176 | return getProperty(PROPERTY_IMAGE_FORMAT); |
177 | } |
178 | |
179 | /** |
180 | * Get target image format name taking in account the original format of the source image (in |
181 | * case <code>getImageFormat()</code> is <code>IMAGE_FORMAT_PRESERVE</code>) or the format |
182 | * selected by the user. |
183 | */ |
184 | public String getImageFormatName(String sourceImageFormatName) { |
185 | String result; |
186 | String imageFormat = getProperty(PROPERTY_IMAGE_FORMAT); |
187 | |
188 | if (imageFormat.equals(IMAGE_FORMAT_JFIF)) { |
189 | result = "jpeg"; |
190 | } else if (imageFormat.equals(IMAGE_FORMAT_PNG)) { |
191 | result = "png"; |
192 | } else if (imageFormat.equals(IMAGE_FORMAT_PRESERVE)) { |
193 | result = sourceImageFormatName; |
194 | } else { |
195 | assert false : "imageFormat = " + imageFormat; |
196 | result = null; |
197 | } |
198 | return result; |
199 | } |
200 | |
201 | public int getMaxImageHeight() { |
202 | return getIntProperty(PROPERTY_MAX_PAGE_HEIGHT); |
203 | } |
204 | |
205 | public int getMaxImageWidth() { |
206 | return getIntProperty(PROPERTY_MAX_PAGE_WIDTH); |
207 | } |
208 | |
209 | public int getMaxThumbHeight() { |
210 | return getIntProperty(PROPERTY_MAX_THUMB_PAGE_HEIGHT); |
211 | } |
212 | |
213 | public int getMaxThumbWidth() { |
214 | return getIntProperty(PROPERTY_MAX_THUMB_PAGE_WIDTH); |
215 | } |
216 | |
217 | /** |
218 | * Yield image trimmed to maximum size. Landscape images can have twice the maximum width. |
219 | * |
220 | * @see #getMaxImageHeight() |
221 | * @see #getMaxImageWidth() |
222 | * @see ImageTools#isLandscape(RenderedImage) |
223 | */ |
224 | public RenderedImage getTrimmed(RenderedImage image) { |
225 | RenderedImage result; |
226 | int trimmedWidth; |
227 | |
228 | if (imageTools.isLandscape(image)) { |
229 | trimmedWidth = 2 * getMaxImageWidth(); |
230 | } else { |
231 | trimmedWidth = getMaxImageWidth(); |
232 | } |
233 | result = imageTools.getSqueezed(image, trimmedWidth, getMaxImageHeight(), ImageTools.SCALE_FIT); |
234 | |
235 | return result; |
236 | } |
237 | |
238 | public boolean isAddOnlyImages() { |
239 | return getBooleanProperty(PROPERTY_ADD_ONLY_IMAGES); |
240 | } |
241 | |
242 | /** |
243 | * Yield <code>true</code> if file should be added to comic archives. |
244 | */ |
245 | public boolean isAddable(String imageFormat, Dimension imageSize) { |
246 | boolean result = (imageFormat != null); |
247 | |
248 | if (result && isExcludeThumbPages()) { |
249 | Dimension portraitSize = getPortraitSize(imageSize); |
250 | |
251 | result = (portraitSize.width > getMaxThumbWidth()) |
252 | && (portraitSize.height > getMaxThumbHeight()); |
253 | } |
254 | |
255 | return result; |
256 | } |
257 | |
258 | public boolean isAddable(ImageInfo imageInfo) { |
259 | return !imageInfo.hasError() && isAddable(imageInfo.getFormat(), imageInfo.getSize()); |
260 | } |
261 | |
262 | public boolean isExcludeThumbPages() { |
263 | return getBooleanProperty(PROPERTY_EXCLUDE_THUMB_PAGES); |
264 | } |
265 | |
266 | public boolean isLimitImageSize() { |
267 | return getBooleanProperty(PROPERTY_LIMIT_PAGE_SIZE); |
268 | } |
269 | |
270 | /** |
271 | * Yield the portrait size of <code>size</code> even for landscape dimensions. |
272 | */ |
273 | private Dimension getPortraitSize(Dimension size) { |
274 | Dimension result; |
275 | int width; |
276 | |
277 | if (imageTools.isLandscape(size)) { |
278 | width = size.width / 2; |
279 | } else { |
280 | width = size.width; |
281 | } |
282 | result = new Dimension(width, size.height); |
283 | return result; |
284 | } |
285 | |
286 | public void checkValidComicFileFormat(String supposedFileFormat) { |
287 | if (!stringTools.equalsAnyOf(VALID_COMIC_FORMATS, supposedFileFormat, true)) { |
288 | // TODO: proper localized error message: "file format is {0}, but must be one of: {1}" |
289 | throw new IllegalArgumentException("comicFormat=" + supposedFileFormat); |
290 | } |
291 | } |
292 | |
293 | public void checkValidImageFileFormat(String supposedFileFormat) { |
294 | if (!stringTools.equalsAnyOf(VALID_IMAGE_FORMATS, supposedFileFormat, true)) { |
295 | // TODO: proper localized error message: "file format is {0}, but must be one of: {1}" |
296 | throw new IllegalArgumentException("imageFormat=" + supposedFileFormat); |
297 | } |
298 | } |
299 | |
300 | /** |
301 | * Yield <code>true</code> if the image in <code>imageFile</code> is bigger than the conversion |
302 | * allows (provided that <code>isLimitImageSize</code> actually is enabled). |
303 | * |
304 | * @see #isLimitImageSize() |
305 | */ |
306 | public boolean needsTrim(File imageFile) |
307 | throws IOException { |
308 | boolean result = false; |
309 | |
310 | if (isLimitImageSize()) { |
311 | Dimension imageSize = imageTools.getImageDimension(imageFile); |
312 | |
313 | result = needsTrim(imageSize); |
314 | } |
315 | return result; |
316 | } |
317 | |
318 | /** |
319 | * Yield <code>true</code> if the image with size <code>imageSize</code> is bigger than the |
320 | * conversion allows (provided that <code>isLimitImageSize</code> actually is enabled). |
321 | * |
322 | * @see #isLimitImageSize() |
323 | */ |
324 | public boolean needsTrim(Dimension imageSize) { |
325 | boolean result = false; |
326 | |
327 | if (isLimitImageSize()) { |
328 | Dimension portraitSize = getPortraitSize(imageSize); |
329 | boolean widthNeedsTrim = portraitSize.width > getMaxImageWidth(); |
330 | boolean heightNeedsTrim = portraitSize.height > getMaxImageHeight(); |
331 | |
332 | result = widthNeedsTrim || heightNeedsTrim; |
333 | } |
334 | return result; |
335 | } |
336 | } |