Support for PDF (1st page)
This commit is contained in:
parent
c46cd2e3fe
commit
6f674599da
3 changed files with 124 additions and 36 deletions
11
pom.xml
11
pom.xml
|
|
@ -237,6 +237,17 @@
|
||||||
<version>3.9.4</version>
|
<version>3.9.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.pdfbox</groupId>
|
||||||
|
<artifactId>pdfbox</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.pdfbox</groupId>
|
||||||
|
<artifactId>pdfbox-io</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-server</artifactId>
|
<artifactId>jetty-server</artifactId>
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,7 @@ import java.io.IOException;
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.imageio.ImageReadParam;
|
import javax.imageio.ImageReadParam;
|
||||||
|
|
@ -19,6 +15,11 @@ import javax.imageio.stream.ImageInputStream;
|
||||||
|
|
||||||
import com.drew.imaging.ImageMetadataReader;
|
import com.drew.imaging.ImageMetadataReader;
|
||||||
import com.drew.imaging.ImageProcessingException;
|
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.db.DirectoryProps;
|
||||||
import org.forkalsrud.album.video.MovieMetadataGenerator;
|
import org.forkalsrud.album.video.MovieMetadataGenerator;
|
||||||
import org.slf4j.Logger;
|
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")) {
|
} else if (name.endsWith(".mov") || name.endsWith(".MOV") || name.endsWith(".mp4") || name.endsWith(".m4v") || name.endsWith(".avi")) {
|
||||||
Map<String, String> p = movieMetadataGenerator.generateVideoProperties(f);
|
Map<String, String> p = movieMetadataGenerator.generateVideoProperties(f);
|
||||||
addPropsForFile(props, f, p);
|
addPropsForFile(props, f, p);
|
||||||
|
} else if (name.endsWith(".pdf")) {
|
||||||
|
Map<String, String> p = generatePdfProperties(f);
|
||||||
|
addPropsForFile(props, f, p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -229,4 +233,34 @@ public class DirectoryMetadataGenerator {
|
||||||
iis.close();
|
iis.close();
|
||||||
return new Dimension(img.getWidth(), img.getHeight());
|
return new Dimension(img.getWidth(), img.getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private Map<String, String> generatePdfProperties(File file) throws IOException {
|
||||||
|
|
||||||
|
HashMap<String, String> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,15 @@ import javax.imageio.ImageWriteParam;
|
||||||
import javax.imageio.ImageWriter;
|
import javax.imageio.ImageWriter;
|
||||||
import javax.imageio.stream.ImageOutputStream;
|
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.Dimension;
|
||||||
import org.forkalsrud.album.exif.Thumbnail;
|
import org.forkalsrud.album.exif.Thumbnail;
|
||||||
|
|
||||||
|
|
@ -85,7 +94,10 @@ public class PictureScaler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CachedImage call() throws Exception {
|
public CachedImage call() throws Exception {
|
||||||
try {
|
try {
|
||||||
|
if (file.getName().endsWith(".pdf")) {
|
||||||
|
return renderPdf(file, thumbnail, size);
|
||||||
|
}
|
||||||
return scalePictureReally(file, thumbnail, size);
|
return scalePictureReally(file, thumbnail, size);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("sadness: " + file.getAbsolutePath(), e);
|
log.error("sadness: " + file.getAbsolutePath(), e);
|
||||||
|
|
@ -151,11 +163,11 @@ public class PictureScaler {
|
||||||
* @param size
|
* @param size
|
||||||
* @throws IOException
|
* @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 orig = thumbnail.getSize();
|
||||||
Dimension outd = orig.scale(size);
|
Dimension outd = orig.scale(size);
|
||||||
|
|
||||||
/* In order to make the quality as good as possible we follow the advice from
|
/* 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
|
* 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
|
* 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.
|
// on pictures with each orientation.
|
||||||
//
|
//
|
||||||
// 1 2 3 4 5 6 7 8
|
// 1 2 3 4 5 6 7 8
|
||||||
//
|
//
|
||||||
// 888888 888888 88 88 8888888888 88 88 8888888888
|
// 888888 888888 88 88 8888888888 88 88 8888888888
|
||||||
// 88 88 88 88 88 88 88 88 88 88 88 88
|
// 88 88 88 88 88 88 88 88 88 88 88 88
|
||||||
// 8888 8888 8888 8888 88 8888888888 8888888888 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.
|
// 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[] flipX = new double[]{ +1, -1, -1, +1, -1, +1, +1, -1 };
|
||||||
double[] flipY = 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;
|
int idx = thumbnail.getOrientation() - 1;
|
||||||
|
|
||||||
// Recalculate scale after sub-sampling was applied
|
// Recalculate scale after sub-sampling was applied
|
||||||
double scale;
|
double scale;
|
||||||
AffineTransform xform = new AffineTransform();
|
AffineTransform xform = new AffineTransform();
|
||||||
boolean rotate = idx >= 4;
|
boolean rotate = idx >= 4;
|
||||||
|
|
||||||
xform.translate(intermediate.getWidth() / 2d, intermediate.getHeight() / 2d);
|
xform.translate(intermediate.getWidth() / 2d, intermediate.getHeight() / 2d);
|
||||||
if (rotate) {
|
if (rotate) {
|
||||||
xform.rotate(Math.PI / 2);
|
xform.rotate(Math.PI / 2);
|
||||||
scale = (double)intermediate.getHeight() / img.getWidth();
|
scale = (double) intermediate.getHeight() / img.getWidth();
|
||||||
} else {
|
} else {
|
||||||
scale = (double)intermediate.getWidth() / img.getWidth();
|
scale = (double) intermediate.getWidth() / img.getWidth();
|
||||||
}
|
}
|
||||||
xform.scale(scale, scale);
|
xform.scale(scale, scale);
|
||||||
xform.scale(flipX[idx], flipY[idx]);
|
xform.scale(flipX[idx], flipY[idx]);
|
||||||
xform.translate(-img.getWidth() / 2d, -img.getHeight() / 2d);
|
xform.translate(-img.getWidth() / 2d, -img.getHeight() / 2d);
|
||||||
|
|
||||||
int imgType = img.getType() > 0 ? img.getType() : BufferedImage.TYPE_INT_RGB;
|
int imgType = img.getType() > 0 ? img.getType() : BufferedImage.TYPE_INT_RGB;
|
||||||
BufferedImage buf2 = new BufferedImage(intermediate.getWidth(), intermediate.getHeight(), imgType);
|
BufferedImage buf2 = new BufferedImage(intermediate.getWidth(), intermediate.getHeight(), imgType);
|
||||||
Graphics2D g2 = buf2.createGraphics();
|
Graphics2D g2 = buf2.createGraphics();
|
||||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||||
|
|
||||||
g2.transform(xform);
|
|
||||||
g2.drawImage(img, 0, 0, null);
|
|
||||||
img = null;
|
|
||||||
|
|
||||||
while (intermediate.getWidth() > outd.getWidth()) {
|
g2.transform(xform);
|
||||||
|
g2.drawImage(img, 0, 0, null);
|
||||||
BufferedImage buf3 = buf2;
|
img = null;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterator<ImageWriter> 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<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");
|
||||||
ImageWriter writer = writers.next();
|
ImageWriter writer = writers.next();
|
||||||
ImageWriteParam writeParam = writer.getDefaultWriteParam();
|
ImageWriteParam writeParam = writer.getDefaultWriteParam();
|
||||||
writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||||
|
|
@ -241,10 +257,37 @@ public class PictureScaler {
|
||||||
ios.flush();
|
ios.flush();
|
||||||
|
|
||||||
CachedImage cimg = new CachedImage();
|
CachedImage cimg = new CachedImage();
|
||||||
cimg.lastModified = file.lastModified();
|
cimg.lastModified = lastModifiedTs;
|
||||||
cimg.mimeType = "image/jpeg";
|
cimg.mimeType = "image/jpeg";
|
||||||
cimg.bits = bits.toByteArray();
|
cimg.bits = bits.toByteArray();
|
||||||
return cimg;
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue