1 package be
.nikiroo
.utils
;
4 import java
.awt
.geom
.AffineTransform
;
5 import java
.awt
.image
.AffineTransformOp
;
6 import java
.awt
.image
.BufferedImage
;
7 import java
.io
.BufferedReader
;
9 import java
.io
.FileInputStream
;
10 import java
.io
.FileOutputStream
;
11 import java
.io
.FileReader
;
12 import java
.io
.FileWriter
;
13 import java
.io
.IOException
;
14 import java
.io
.InputStream
;
15 import java
.io
.OutputStream
;
16 import java
.util
.zip
.ZipEntry
;
17 import java
.util
.zip
.ZipOutputStream
;
19 import javax
.imageio
.ImageIO
;
22 * This class offer some utilities based around Streams.
26 public class IOUtils
{
28 * Write the data to the given {@link File}.
33 * the target {@link File}
36 * in case of I/O error
38 public static void write(InputStream in
, File target
) throws IOException
{
39 OutputStream out
= new FileOutputStream(target
);
48 * Write the data to the given {@link OutputStream}.
53 * the target {@link OutputStream}
56 * in case of I/O error
58 public static void write(InputStream in
, OutputStream out
)
60 byte buffer
[] = new byte[4069];
61 for (int len
= 0; (len
= in
.read(buffer
)) > 0;) {
62 out
.write(buffer
, 0, len
);
67 * Recursively Add a {@link File} (which can thus be a directory, too) to a
68 * {@link ZipOutputStream}.
73 * the path to prepend to the ZIP info before the actual
76 * the source {@link File} (which can be a directory)
78 * FALSE if we need to add a {@link ZipEntry} for base/target,
79 * TRUE to add it at the root of the ZIP
82 * in case of I/O error
84 public static void zip(ZipOutputStream zip
, String base
, File target
,
85 boolean targetIsRoot
) throws IOException
{
86 if (target
.isDirectory()) {
88 if (base
== null || base
.isEmpty()) {
89 base
= target
.getName();
91 base
+= "/" + target
.getName();
93 zip
.putNextEntry(new ZipEntry(base
+ "/"));
95 for (File file
: target
.listFiles()) {
96 zip(zip
, base
, file
, false);
99 if (base
== null || base
.isEmpty()) {
100 base
= target
.getName();
102 base
+= "/" + target
.getName();
104 zip
.putNextEntry(new ZipEntry(base
));
105 FileInputStream in
= new FileInputStream(target
);
107 IOUtils
.write(in
, zip
);
115 * Zip the given source into dest.
118 * the source {@link File} (which can be a directory)
120 * the destination <tt>.zip</tt> file
122 * FALSE if we need to add a {@link ZipEntry} for src, TRUE to
123 * add it at the root of the ZIP
125 * @throws IOException
126 * in case of I/O error
128 public static void zip(File src
, File dest
, boolean srcIsRoot
)
130 OutputStream out
= new FileOutputStream(dest
);
132 ZipOutputStream zip
= new ZipOutputStream(out
);
134 IOUtils
.zip(zip
, "", src
, srcIsRoot
);
144 * Write the {@link String} content to {@link File}.
147 * the directory where to write the {@link File}
149 * the {@link File} name
153 * @throws IOException
154 * in case of I/O error
156 public static void writeSmallFile(File dir
, String filename
, String content
)
162 FileWriter writerVersion
= new FileWriter(new File(dir
, filename
));
164 writerVersion
.write(content
);
166 writerVersion
.close();
171 * Read the whole {@link File} content into a {@link String}.
176 * @return the content
178 * @throws IOException
179 * in case of I/O error
181 public static String
readSmallFile(File file
) throws IOException
{
182 BufferedReader reader
= new BufferedReader(new FileReader(file
));
184 StringBuilder builder
= new StringBuilder();
185 for (String line
= reader
.readLine(); line
!= null; line
= reader
187 builder
.append(line
);
189 return builder
.toString();
196 * Recursively delete the given {@link File}, which may of course also be a
199 * Will silently continue in case of error.
202 * the target to delete
204 public static void deltree(File target
) {
205 for (File file
: target
.listFiles()) {
206 if (file
.isDirectory()) {
209 if (!file
.delete()) {
210 System
.err
.println("Cannot delete file: "
211 + file
.getAbsolutePath());
216 if (!target
.delete()) {
217 System
.err
.println("Cannot delete file: "
218 + target
.getAbsolutePath());
223 * Convert the given {@link InputStream} (which should allow calls to
224 * {@link InputStream#reset() for better perfs}) into an {@link Image}
225 * object, respecting the EXIF transformations if any.
228 * the 'resetable' {@link InputStream}
230 * @return the {@link Image} object
232 * @throws IOException
233 * in case of IO error
235 static public BufferedImage
toImage(InputStream in
) throws IOException
{
236 MarkableFileInputStream tmpIn
= null;
240 } catch (IOException e
) {
241 tmp
= File
.createTempFile("fanfic-tmp-image", ".tmp");
243 IOUtils
.write(in
, tmp
);
244 tmpIn
= new MarkableFileInputStream(new FileInputStream(tmp
));
249 orientation
= getExifTransorm(in
);
250 } catch (Exception e
) {
251 // no EXIF transform, ok
256 BufferedImage image
= ImageIO
.read(in
);
263 throw new IOException("Failed to convert input to image");
266 // Note: this code has been found on internet;
267 // thank you anonymous coder.
268 int width
= image
.getWidth();
269 int height
= image
.getHeight();
270 AffineTransform affineTransform
= new AffineTransform();
272 switch (orientation
) {
274 affineTransform
= null;
277 affineTransform
.scale(-1.0, 1.0);
278 affineTransform
.translate(-width
, 0);
280 case 3: // PI rotation
281 affineTransform
.translate(width
, height
);
282 affineTransform
.rotate(Math
.PI
);
285 affineTransform
.scale(1.0, -1.0);
286 affineTransform
.translate(0, -height
);
288 case 5: // - PI/2 and Flip X
289 affineTransform
.rotate(-Math
.PI
/ 2);
290 affineTransform
.scale(-1.0, 1.0);
292 case 6: // -PI/2 and -width
293 affineTransform
.translate(height
, 0);
294 affineTransform
.rotate(Math
.PI
/ 2);
296 case 7: // PI/2 and Flip
297 affineTransform
.scale(-1.0, 1.0);
298 affineTransform
.translate(-height
, 0);
299 affineTransform
.translate(0, width
);
300 affineTransform
.rotate(3 * Math
.PI
/ 2);
303 affineTransform
.translate(0, width
);
304 affineTransform
.rotate(3 * Math
.PI
/ 2);
307 affineTransform
= null;
311 if (affineTransform
!= null) {
312 AffineTransformOp affineTransformOp
= new AffineTransformOp(
313 affineTransform
, AffineTransformOp
.TYPE_BILINEAR
);
315 BufferedImage transformedImage
= new BufferedImage(width
, height
,
317 transformedImage
= affineTransformOp
318 .filter(image
, transformedImage
);
320 image
= transformedImage
;
333 * Return the EXIF transformation flag of this image if any.
336 * Note: this code has been found on internet; thank you anonymous coder.
340 * the data {@link InputStream}
342 * @return the transformation flag if any
344 * @throws IOException
345 * in case of IO error
347 static private int getExifTransorm(InputStream in
) throws IOException
{
348 int[] exif_data
= new int[100];
352 /* Read File head, check for JPEG SOI + Exif APP1 */
353 for (int i
= 0; i
< 4; i
++)
354 exif_data
[i
] = in
.read();
356 if (exif_data
[0] != 0xFF || exif_data
[1] != 0xD8
357 || exif_data
[2] != 0xFF || exif_data
[3] != 0xE1)
360 /* Get the marker parameter length count */
361 int length
= (in
.read() << 8 | in
.read());
363 /* Length includes itself, so must be at least 2 */
364 /* Following Exif data length must be at least 6 */
368 /* Read Exif head, check for "Exif" */
369 for (int i
= 0; i
< 6; i
++)
370 exif_data
[i
] = in
.read();
372 if (exif_data
[0] != 0x45 || exif_data
[1] != 0x78
373 || exif_data
[2] != 0x69 || exif_data
[3] != 0x66
374 || exif_data
[4] != 0 || exif_data
[5] != 0)
378 length
= length
> exif_data
.length ? exif_data
.length
: length
;
379 for (int i
= 0; i
< length
; i
++)
380 exif_data
[i
] = in
.read();
383 return -1; /* Length of an IFD entry */
385 /* Discover byte order */
386 if (exif_data
[0] == 0x49 && exif_data
[1] == 0x49)
388 else if (exif_data
[0] == 0x4D && exif_data
[1] == 0x4D)
394 if (is_motorola
== 1) {
395 if (exif_data
[2] != 0)
397 if (exif_data
[3] != 0x2A)
400 if (exif_data
[3] != 0)
402 if (exif_data
[2] != 0x2A)
406 /* Get first IFD offset (offset to IFD0) */
408 if (is_motorola
== 1) {
409 if (exif_data
[4] != 0)
411 if (exif_data
[5] != 0)
413 offset
= exif_data
[6];
415 offset
+= exif_data
[7];
417 if (exif_data
[7] != 0)
419 if (exif_data
[6] != 0)
421 offset
= exif_data
[5];
423 offset
+= exif_data
[4];
425 if (offset
> length
- 2)
426 return -1; /* check end of data segment */
428 /* Get the number of directory entries contained in this IFD */
430 if (is_motorola
== 1) {
431 number_of_tags
= exif_data
[offset
];
432 number_of_tags
<<= 8;
433 number_of_tags
+= exif_data
[offset
+ 1];
435 number_of_tags
= exif_data
[offset
+ 1];
436 number_of_tags
<<= 8;
437 number_of_tags
+= exif_data
[offset
];
439 if (number_of_tags
== 0)
443 /* Search for Orientation Tag in IFD0 */
445 if (offset
> length
- 12)
446 return -1; /* check end of data segment */
449 if (is_motorola
== 1) {
450 tagnum
= exif_data
[offset
];
452 tagnum
+= exif_data
[offset
+ 1];
454 tagnum
= exif_data
[offset
+ 1];
456 tagnum
+= exif_data
[offset
];
458 if (tagnum
== 0x0112)
459 break; /* found Orientation Tag */
460 if (--number_of_tags
== 0)
465 /* Get the Orientation value */
466 if (is_motorola
== 1) {
467 if (exif_data
[offset
+ 8] != 0)
469 set_flag
= exif_data
[offset
+ 9];
471 if (exif_data
[offset
+ 9] != 0)
473 set_flag
= exif_data
[offset
+ 8];