Created
June 10, 2013 11:05
-
-
Save archagon/5748013 to your computer and use it in GitHub Desktop.
A quick bit of code to generate PDFs from scans, using the iText open-source library.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.IOException; | |
import java.io.FileOutputStream; | |
import java.io.File; | |
import java.io.FilenameFilter; | |
import java.util.Map; | |
import java.util.HashMap; | |
import com.itextpdf.text.Document; | |
import com.itextpdf.text.DocumentException; | |
import com.itextpdf.text.Image; | |
import com.itextpdf.text.Rectangle; | |
import com.itextpdf.text.pdf.PdfWriter; | |
import com.itextpdf.text.pdf.PdfTemplate; | |
import com.itextpdf.text.pdf.PdfContentByte; | |
// As with all my Gists, this was done quick & dirty for completing the task at hand. | |
public class PDFGenSimple | |
{ | |
public enum ImageType | |
{ | |
PNG, // I might be wrong, but it looks like PDFs support JPG natively, but not PNG | |
JPG | |
} | |
public enum PageLocation | |
{ | |
None, // default for full page scans | |
Left, | |
Right | |
} | |
// these get initialized on startup | |
protected String inputDirectory; | |
protected String baseInputFilename; | |
protected String outputFilename; | |
protected ImageType imageType; | |
protected boolean sideBySide; | |
// these get initialized when CreatePDF is called; I won't bother to throw exceptions if accessed before then | |
protected Document document; | |
protected PdfWriter writer; | |
protected Map<String, Image> imageDictionary; | |
public PDFGenSimple(String inputDirectory, String baseInputFilename, String outputFilename, ImageType imageType, boolean sideBySide) | |
{ | |
this.inputDirectory = inputDirectory; | |
this.baseInputFilename = baseInputFilename; | |
this.outputFilename = outputFilename; | |
this.imageType = imageType; | |
this.sideBySide = sideBySide; | |
} | |
public void CreatePDF() throws IOException, DocumentException | |
{ | |
this.imageDictionary = new HashMap<String, Image>(); | |
// PDF setup | |
this.document = new Document(); | |
this.writer = PdfWriter.getInstance(this.document, new FileOutputStream(this.outputFilename)); | |
this.document.open(); | |
System.out.println("Beginning file processing..."); | |
File dir = new File(this.inputDirectory); | |
if (dir.isDirectory()) | |
{ | |
final String extension = "." + this.imageType.toString().toLowerCase(); | |
// get a list of all the PNGs in the given directory | |
File files[] = dir.listFiles(new FilenameFilter() | |
{ | |
@Override | |
public boolean accept(File dir, String name) | |
{ | |
return name.toLowerCase().endsWith(extension); | |
} | |
}); | |
int numPages = files.length; | |
System.out.println("Number of pages: " + numPages); | |
// for use with sideBySide | |
File tempPage = null; | |
for(int i = 1; i <= numPages; i++) | |
{ | |
final String filename = GetBaseFilenameFromPageNumber(i, numPages) + extension; | |
// try to find the file with the page name provided | |
File pageFile[] = dir.listFiles(new FilenameFilter() | |
{ | |
@Override | |
public boolean accept(File dir, String name) | |
{ | |
return name.toLowerCase().equals(filename.toLowerCase()); | |
} | |
}); | |
// make sure the results have exactly one file | |
if (pageFile.length == 1) | |
{ | |
System.out.println("Processing " + filename + "..."); | |
} | |
else | |
{ | |
System.err.println("Count for " + filename + " is incorrect: " + pageFile.length); | |
continue; | |
} | |
if (this.sideBySide) | |
{ | |
if (i == 1) | |
{ | |
// front cover | |
AddPageToDocument(pageFile[0].getAbsolutePath(), PageLocation.Right); | |
} | |
else if (i == numPages) | |
{ | |
// back cover | |
AddPageToDocument(pageFile[0].getAbsolutePath(), PageLocation.Left); | |
} | |
else | |
{ | |
// two pages at a time for the rest | |
if (tempPage == null) | |
{ | |
tempPage = pageFile[0]; | |
} | |
else | |
{ | |
AddPagesToDocument(tempPage.getAbsolutePath(), pageFile[0].getAbsolutePath(), PageLocation.Left, PageLocation.Right); | |
tempPage = null; | |
} | |
} | |
} | |
else | |
{ | |
AddPageToDocument(pageFile[0].getAbsolutePath(), (i % 2 == 0 ? PageLocation.Left : PageLocation.Right)); | |
} | |
} | |
} | |
System.out.println("File processing ended!"); | |
this.document.close(); | |
} | |
protected void AddPageToDocument(String imageFilename, PageLocation pageLocation) throws IOException, DocumentException | |
{ | |
Image img = GetImageForFilename(imageFilename); | |
SetupNewPage(img, false); | |
AddImageToCurrentPage(img, pageLocation, PageLocation.None, false); | |
} | |
protected void AddPagesToDocument(String imageFilename1, String imageFilename2, PageLocation pageLocation1, PageLocation pageLocation2) throws IOException, DocumentException | |
{ | |
Image img1 = GetImageForFilename(imageFilename1); | |
Image img2 = GetImageForFilename(imageFilename2); | |
SetupNewPage(img1, true); //assuming img1 and img2 are the same size | |
AddImageToCurrentPage(img1, pageLocation1, PageLocation.Left, true); | |
AddImageToCurrentPage(img2, pageLocation2, PageLocation.Right, true); | |
} | |
protected void SetupNewPage(Image image, boolean sideBySide) | |
{ | |
float width; | |
if (sideBySide) | |
{ | |
width = GetSinglePageWidth(image) * 2; | |
} | |
else | |
{ | |
width = GetSinglePageWidth(image); | |
} | |
this.document.setPageSize(new Rectangle(0, 0, width, image.getHeight())); | |
this.document.newPage(); | |
} | |
// outputPageLocation is ignored if sideBySide is false. | |
protected void AddImageToCurrentPage(Image image, PageLocation inputPageLocation, PageLocation outputPageLocation, boolean sideBySide) throws DocumentException | |
{ | |
image.setAbsolutePosition(0, 0); | |
float outputX = 0; | |
if (sideBySide && outputPageLocation == PageLocation.Right) | |
{ | |
outputX = GetSinglePageWidth(image); | |
} | |
// this stuff allows you to "crop" images (which really means that a certain part of the image, which is included in full, is rendered) | |
PdfContentByte contentByte = this.writer.getDirectContent(); | |
PdfTemplate template = contentByte.createTemplate(GetSinglePageWidth(image), image.getHeight()); | |
template.addImage(image); | |
contentByte.addTemplate(template, outputX, 0); | |
} | |
protected float GetSinglePageWidth(Image image) | |
{ | |
return image.getWidth(); | |
} | |
// Caching the images ensures that each scanned double-page of a zine gets added to the PDF only once, halving the file size. | |
protected Image GetImageForFilename(String filename) throws IOException, DocumentException | |
{ | |
if (!this.imageDictionary.containsKey(filename)) | |
{ | |
Image image = Image.getInstance(filename); | |
this.imageDictionary.put(filename, image); | |
} | |
return this.imageDictionary.get(filename); | |
} | |
// Pages are 1-indexed. Assumes default Apple scan naming format: BaseFilename, BaseFilename 1, BaseFilename 2, etc. | |
protected String GetBaseFilenameFromPageNumber(int pageNumber, int maxPages) | |
{ | |
int actualPage = pageNumber - 1; | |
if (actualPage == 0) | |
{ | |
return this.baseInputFilename; | |
} | |
else | |
{ | |
return this.baseInputFilename + " " + actualPage; | |
} | |
} | |
public static void main(String[] args) throws IOException, DocumentException | |
{ | |
PDFGen pdfGen = new PDFGen( | |
"/Directory/To/Scanned/Pages", | |
"ScannedPage", | |
"/Directory/To/Scanned/Pages/Output.pdf", | |
ImageType.PNG, | |
true); | |
pdfGen.CreatePDF(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment