diff --git a/pom.xml b/pom.xml
index 10ed6f8..507a398 100644
--- a/pom.xml
+++ b/pom.xml
@@ -237,6 +237,17 @@
3.9.4
+
+ org.apache.pdfbox
+ pdfbox
+ 3.0.0
+
+
+ org.apache.pdfbox
+ pdfbox-io
+ 3.0.0
+
+
org.eclipse.jetty
jetty-server
diff --git a/src/main/java/org/forkalsrud/album/exif/DirectoryMetadataGenerator.java b/src/main/java/org/forkalsrud/album/exif/DirectoryMetadataGenerator.java
index 13397ce..6cd2f1a 100644
--- a/src/main/java/org/forkalsrud/album/exif/DirectoryMetadataGenerator.java
+++ b/src/main/java/org/forkalsrud/album/exif/DirectoryMetadataGenerator.java
@@ -6,11 +6,7 @@ import java.io.IOException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Properties;
+import java.util.*;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
@@ -19,6 +15,11 @@ import javax.imageio.stream.ImageInputStream;
import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
+import org.apache.pdfbox.Loader;
+import org.apache.pdfbox.io.RandomAccessReadBufferedFile;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.forkalsrud.album.db.DirectoryProps;
import org.forkalsrud.album.video.MovieMetadataGenerator;
import org.slf4j.Logger;
@@ -91,6 +92,9 @@ public class DirectoryMetadataGenerator {
} else if (name.endsWith(".mov") || name.endsWith(".MOV") || name.endsWith(".mp4") || name.endsWith(".m4v") || name.endsWith(".avi")) {
Map p = movieMetadataGenerator.generateVideoProperties(f);
addPropsForFile(props, f, p);
+ } else if (name.endsWith(".pdf")) {
+ Map p = generatePdfProperties(f);
+ addPropsForFile(props, f, p);
}
}
}
@@ -229,4 +233,34 @@ public class DirectoryMetadataGenerator {
iis.close();
return new Dimension(img.getWidth(), img.getHeight());
}
+
+
+
+ private Map generatePdfProperties(File file) throws IOException {
+
+ HashMap props = new HashMap<>();
+
+ String name = file.getName();
+ props.put("etag", Integer.toHexString(name.hashCode() + Long.valueOf(file.lastModified()).hashCode()));
+
+ try (PDDocument document = Loader.loadPDF(new RandomAccessReadBufferedFile(file))) {
+
+ PDPage page = document.getPage(0);
+ PDRectangle rect = page.getMediaBox();
+
+
+ int width = Math.round(rect.getWidth());
+ int height = Math.round(rect.getHeight());
+ props.put("dimensions", new Dimension(width, height).toString());
+ props.put("orientation", "1");
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-HHmmss");
+ Calendar cal = document.getDocumentInformation().getCreationDate();
+ Date date = cal != null ? cal.getTime() : new Date(file.lastModified());
+ props.put("captureDate", sdf.format(date));
+
+ }
+ return props;
+ }
+
+
}
diff --git a/src/main/java/org/forkalsrud/album/web/PictureScaler.java b/src/main/java/org/forkalsrud/album/web/PictureScaler.java
index 48f7c7e..ee344eb 100644
--- a/src/main/java/org/forkalsrud/album/web/PictureScaler.java
+++ b/src/main/java/org/forkalsrud/album/web/PictureScaler.java
@@ -25,6 +25,15 @@ import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
+import org.apache.pdfbox.Loader;
+import org.apache.pdfbox.cos.COSDocument;
+import org.apache.pdfbox.io.RandomAccessRead;
+import org.apache.pdfbox.io.RandomAccessReadBufferedFile;
+import org.apache.pdfbox.io.RandomAccessReadMemoryMappedFile;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.common.PDRectangle;
+import org.apache.pdfbox.rendering.PDFRenderer;
import org.forkalsrud.album.exif.Dimension;
import org.forkalsrud.album.exif.Thumbnail;
@@ -85,7 +94,10 @@ public class PictureScaler {
@Override
public CachedImage call() throws Exception {
- try {
+ try {
+ if (file.getName().endsWith(".pdf")) {
+ return renderPdf(file, thumbnail, size);
+ }
return scalePictureReally(file, thumbnail, size);
} catch (Exception e) {
log.error("sadness: " + file.getAbsolutePath(), e);
@@ -151,11 +163,11 @@ public class PictureScaler {
* @param size
* @throws IOException
*/
- CachedImage scalePictureReally(File file, Thumbnail thumbnail, String size) throws IOException {
+ CachedImage scalePictureReally(File file, Thumbnail thumbnail, String size) throws IOException {
Dimension orig = thumbnail.getSize();
Dimension outd = orig.scale(size);
-
+
/* In order to make the quality as good as possible we follow the advice from
* http://today.java.net/pub/a/today/2007/04/03/perils-of-image-getscaledinstance.html
* and scale the image by a factor of 2 in intermediate steps, thereby getting a smoother
@@ -174,7 +186,7 @@ public class PictureScaler {
// on pictures with each orientation.
//
// 1 2 3 4 5 6 7 8
- //
+ //
// 888888 888888 88 88 8888888888 88 88 8888888888
// 88 88 88 88 88 88 88 88 88 88 88 88
// 8888 8888 8888 8888 88 8888888888 8888888888 88
@@ -185,46 +197,50 @@ public class PictureScaler {
// The last four are obtained by rotating 90 degrees and then flipping X and/or Y.
//
- double[] flipX = new double[] { +1, -1, -1, +1, -1, +1, +1, -1 };
- double[] flipY = new double[] { +1, +1, -1, -1, +1, +1, -1, -1 };
+ double[] flipX = new double[]{ +1, -1, -1, +1, -1, +1, +1, -1 };
+ double[] flipY = new double[]{ +1, +1, -1, -1, +1, +1, -1, -1 };
int idx = thumbnail.getOrientation() - 1;
- // Recalculate scale after sub-sampling was applied
- double scale;
- AffineTransform xform = new AffineTransform();
+ // Recalculate scale after sub-sampling was applied
+ double scale;
+ AffineTransform xform = new AffineTransform();
boolean rotate = idx >= 4;
xform.translate(intermediate.getWidth() / 2d, intermediate.getHeight() / 2d);
if (rotate) {
- xform.rotate(Math.PI / 2);
- scale = (double)intermediate.getHeight() / img.getWidth();
+ xform.rotate(Math.PI / 2);
+ scale = (double) intermediate.getHeight() / img.getWidth();
} else {
- scale = (double)intermediate.getWidth() / img.getWidth();
+ scale = (double) intermediate.getWidth() / img.getWidth();
}
xform.scale(scale, scale);
xform.scale(flipX[idx], flipY[idx]);
xform.translate(-img.getWidth() / 2d, -img.getHeight() / 2d);
- int imgType = img.getType() > 0 ? img.getType() : BufferedImage.TYPE_INT_RGB;
- BufferedImage buf2 = new BufferedImage(intermediate.getWidth(), intermediate.getHeight(), imgType);
- Graphics2D g2 = buf2.createGraphics();
- g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
-
- g2.transform(xform);
- g2.drawImage(img, 0, 0, null);
- img = null;
+ int imgType = img.getType() > 0 ? img.getType() : BufferedImage.TYPE_INT_RGB;
+ BufferedImage buf2 = new BufferedImage(intermediate.getWidth(), intermediate.getHeight(), imgType);
+ Graphics2D g2 = buf2.createGraphics();
+ g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
- while (intermediate.getWidth() > outd.getWidth()) {
-
- BufferedImage buf3 = buf2;
- intermediate = intermediate.halved();
- buf2 = new BufferedImage(intermediate.getWidth(), intermediate.getHeight(), imgType);
- g2 = buf2.createGraphics();
- g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
- g2.drawImage(buf3, 0, 0, intermediate.getWidth(), intermediate.getHeight(), null);
- }
+ g2.transform(xform);
+ g2.drawImage(img, 0, 0, null);
+ img = null;
- Iterator writers = ImageIO.getImageWritersByFormatName("jpg");
+ while (intermediate.getWidth() > outd.getWidth()) {
+
+ BufferedImage buf3 = buf2;
+ intermediate = intermediate.halved();
+ buf2 = new BufferedImage(intermediate.getWidth(), intermediate.getHeight(), imgType);
+ g2 = buf2.createGraphics();
+ g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+ g2.drawImage(buf3, 0, 0, intermediate.getWidth(), intermediate.getHeight(), null);
+ }
+ return encoded(buf2, file.lastModified());
+ }
+
+
+ private CachedImage encoded(BufferedImage buf2, long lastModifiedTs) throws IOException {
+ Iterator writers = ImageIO.getImageWritersByFormatName("jpg");
ImageWriter writer = writers.next();
ImageWriteParam writeParam = writer.getDefaultWriteParam();
writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
@@ -241,10 +257,37 @@ public class PictureScaler {
ios.flush();
CachedImage cimg = new CachedImage();
- cimg.lastModified = file.lastModified();
+ cimg.lastModified = lastModifiedTs;
cimg.mimeType = "image/jpeg";
cimg.bits = bits.toByteArray();
return cimg;
}
+
+ CachedImage renderPdf(File file, Thumbnail thumbnail, String size) throws IOException {
+
+
+ Dimension orig = thumbnail.getSize();
+ Dimension dim = orig.scale(size);
+
+
+
+ try (PDDocument document = Loader.loadPDF(new RandomAccessReadBufferedFile(file))) {
+
+ PDPage page = document.getPage(0);
+ PDRectangle rect = page.getMediaBox();
+
+
+ float width = rect.getWidth() / 72; // inches
+ float targetWidth = dim.getWidth(); // pixels
+
+ PDFRenderer renderer = new PDFRenderer(document);
+ BufferedImage image = renderer.renderImageWithDPI(0, targetWidth / width);
+
+
+ return encoded(image, file.lastModified());
+ }
+ }
+
+
}