1 /**
2 * Example to convert PDF to highest possible quality images3 * Also consider org.jpedal.examples.images.ConvertPagesToImages for faster if quality less important4 *5 * Useful blog article athttp://www.jpedal.org/PDFblog/2009/07/pdf-to-image-quality/
6 *7 * It can run from jar directly using the command8 *9 * java -cp libraries_needed org/jpedal/examples/images/ConvertPagesToHiResImages pdfFilepath inputValues10 *11 * where inputValues is 2 values12 *13 * First Parameter: The full path including the name and extension of the target PDF file.14 * Second Parameter: The output image file type. Choices are tif, jpg and png.15 *16 * See alsohttp://www.jpedal.org/javadoc/org/jpedal/constants/JPedalSettings.htmlfor settings to customise17 */
18
19 /**
20 * ===========================================21 * Java Pdf Extraction Decoding Access Library22 * ===========================================23 *24 * Project Info:http://www.jpedal.org
25 * (C) Copyright 1997-2012, IDRsolutions and Contributors.26 *27 * This file is part of JPedal28 *29 This source code is copyright IDRSolutions 201230
31
32 *33 * ---------------34 * ConvertPagesToHiResImages.java35 * ---------------36 */
37
38 importjava.awt.Graphics2D;39 importjava.awt.image.BufferedImage;40 importjava.io.BufferedOutputStream;41 importjava.io.File;42 importjava.io.FileOutputStream;43 importjava.io.IOException;44 importjava.io.OutputStream;45 import java.util.*;46
47 importjavax.imageio.IIOImage;48 importjavax.imageio.ImageIO;49 importjavax.imageio.ImageTypeSpecifier;50 importjavax.imageio.metadata.IIOMetadata;51 importjavax.imageio.plugins.jpeg.JPEGImageWriteParam;52 importjavax.imageio.stream.ImageOutputStream;53
54 importcom.sun.imageio.plugins.jpeg.JPEGImageWriter;55 importorg.jpedal.PdfDecoder;56 importorg.jpedal.color.ColorSpaces;57 importorg.jpedal.exception.PdfException;58 importorg.jpedal.fonts.FontMappings;59 importorg.jpedal.objects.PdfPageData;60 importorg.jpedal.utils.LogWriter;61 importorg.jpedal.io.ColorSpaceConvertor;62 importorg.jpedal.io.JAIHelper;63 importorg.jpedal.constants.JPedalSettings;64 importorg.jpedal.constants.PageInfo;65 importorg.w3c.dom.Element;66
67 public classConvertPagesToHiResImages {68
69 private static boolean debug = false;70
71 /**correct separator for OS*/
72 final static String separator = System.getProperty( "file.separator");73
74 //only used if between 0 and 1
75 private float JPEGcompression=-1f;76
77 publicConvertPagesToHiResImages() {}78
79 public static void main(String[] args) throwsException {80
81 /*
82 * Change these variables83 */
84 String fileType,pdfFile;85
86 if(args!=null && args.length>1){87
88 pdfFile = args[0];89 fileType= args[1];90
91 //open the file
92 if(pdfFile.toLowerCase().endsWith(".pdf") && (fileType.equals("jpg") || fileType.equals("jpeg") || fileType.equals("png") || fileType.equals("tiff") || fileType.equals("tif"))){93 newConvertPagesToHiResImages(fileType, pdfFile);94 }else{ //open the directory
95
96 File testDir=newFile(pdfFile);97
98 if(testDir.isDirectory()){99
100 /**
101 * get list of files and check directory102 */
103
104 String[] files = null;105 File inputFiles;106
107 /**make sure name ends with a deliminator for correct path later*/
108 if (!pdfFile.endsWith(separator))109 pdfFile = pdfFile +separator;110
111 try{112 inputFiles = newFile(pdfFile);113
114 files =inputFiles.list();115 } catch(Exception ee) {116 LogWriter.writeLog("Exception trying to access file " +ee.getMessage());117 }118
119 /**now work through all pdf files*/
120 for(String file : files) {121
122 if (file.toLowerCase().endsWith(".pdf"))123 new ConvertPagesToHiResImages(fileType, pdfFile +file);124 }125 }else
126 System.out.println("The file to be processed has to be a pdf and the output filetype can only be jpg,png or tiff");127 }128 }else{129 System.out.println("Not enough arguments passed in! Usage: \"C:\\examples\\1.pdf\" \"jpg\"");130 }131 }132
133 /**
134 * main constructor to convert PDF to img135 *@paramfileType136 *@parampdfFile137 *@throwsException138 */
139 public ConvertPagesToHiResImages(String fileType, String pdfFile) throwsException {140
141 long startTime=System.currentTimeMillis();142
143 String outputPath = pdfFile.substring(0, pdfFile.toLowerCase().indexOf(".pdf")) +separator;144 File outputPathFile = newFile(outputPath);145 if (!outputPathFile.exists() || !outputPathFile.isDirectory()) {146 if (!outputPathFile.mkdirs()) {147 if(debug)148 System.err.println("Can't create directory " +outputPath);149 }150 }151
152 //PdfDecoder object provides the conversion
153 final PdfDecoder decoder = new PdfDecoder(true);154
155 //mappings for non-embedded fonts to use156 //FontMappings.setFontReplacements();
157
158 decoder.openPdfFile(pdfFile);159
160 /**
161 * this process is very flaxible to we create a Map and pass in values to select what sort162 * of results we want. There is a choice between methods used and image size. Larger images use more163 * memory and are slower but look better164 */
165 Map mapValues = newHashMap();166
167 /**USEFUL OPTIONS*/
168 //do not scale above this figure
169 mapValues.put(JPedalSettings.EXTRACT_AT_BEST_QUALITY_MAXSCALING, 2);170
171 //alternatively secify a page size (aspect ratio preserved so will do best fit)172 //set a page size (JPedal will put best fit to this)
173 mapValues.put(JPedalSettings.EXTRACT_AT_PAGE_SIZE, new String[]{"2000","1600"});174
175 //which takes priority (default is false)
176 mapValues.put(JPedalSettings.PAGE_SIZE_OVERRIDES_IMAGE, Boolean.TRUE);177
178 PdfDecoder.modifyJPedalParameters(mapValues);179
180 if(debug)181 System.out.println("pdf : " +pdfFile);182
183 try{184 /**
185 * allow output to multiple images with different values on each186 *187 * Note we REMOVE shapes as it is a new feature and we do not want to break existing functions188 */
189 String separation=System.getProperty("org.jpedal.separation");190 if(separation!=null){191
192 Object[] sepValues=new Object[]{7,"",Boolean.FALSE}; //default of normal
193 if(separation.equals("all")){194 sepValues=new Object[]{PdfDecoder.RENDERIMAGES,"image_and_shapes",Boolean.FALSE,195 PdfDecoder.RENDERIMAGES + PdfDecoder.REMOVE_RENDERSHAPES,"image_without_shapes",Boolean.FALSE,196 PdfDecoder.RENDERTEXT,"text_and_shapes",Boolean.TRUE,197 7,"all",Boolean.FALSE,198 PdfDecoder.RENDERTEXT + PdfDecoder.REMOVE_RENDERSHAPES,"text_without_shapes",Boolean.TRUE199 };200 }201
202 int sepCount =sepValues.length;203 for(int seps=0;seps
205 decoder.setRenderMode((Integer) sepValues[seps]);206 extractPageAsImage(fileType, outputPath, decoder,"_"+sepValues[seps+1], (Boolean) sepValues[seps + 2]); //boolean makes last transparent so we can see white text
207 }208
209 }else //just get the page
210 extractPageAsImage(fileType, outputPath, decoder,"",false);211
212 } finally{213
214 decoder.closePdfFile();215 }216 System.out.println("time="+(System.currentTimeMillis()-startTime)/1000);217
218
219 }220
221 /**
222 * convenience method to get a page as a BufferedImage quickly223 * - for bulk conversion, use the other methods224 */
225 public static BufferedImage getHiresPage(int pageNo, intscaling, String pdfFile){226
227 BufferedImage imageToSave = null;228
229 final PdfDecoder decoder = new PdfDecoder(true);230
231 try{232 //mappings for non-embedded fonts to use233 //FontMappings.setFontReplacements();
234
235 decoder.openPdfFile(pdfFile);236
237 PdfPageData pageData =decoder.getPdfPageData();238 int width=scaling*pageData.getCropBoxWidth(pageNo);239 int height=scaling*pageData.getCropBoxHeight(pageNo);240
241 Map mapValues = newHashMap();242
243 /**USEFUL OPTIONS*/
244 //do not scale above this figure
245 mapValues.put(JPedalSettings.EXTRACT_AT_BEST_QUALITY_MAXSCALING, 2);246
247 //alternatively secify a page size (aspect ratio preserved so will do best fit)248 //set a page size (JPedal will put best fit to this)
249 mapValues.put(JPedalSettings.EXTRACT_AT_PAGE_SIZE, newString[]{String.valueOf(width),String.valueOf(height)});250
251 //which takes priority (default is false)
252 mapValues.put(JPedalSettings.PAGE_SIZE_OVERRIDES_IMAGE, Boolean.TRUE);253
254 PdfDecoder.modifyJPedalParameters(mapValues);255
256 imageToSave = decoder.getPageAsHiRes(pageNo,null,false);257
258 } catch(PdfException e) {259 e.printStackTrace();260 } finally{261
262 decoder.closePdfFile();263 }264 returnimageToSave;265 }266
267 /**
268 * actual conversion of a PDF page into an image269 *@paramfileType270 *@paramoutputPath271 *@paramdecoder272 *@paramprefix273 *@paramisTransparent274 *@throwsPdfException275 *@throwsIOException276 */
277 private void extractPageAsImage(String fileType, String outputPath, PdfDecoder decoder, String prefix, boolean isTransparent) throwsPdfException, IOException {278
279
280 //page range
281 int start=1, end=decoder.getPageCount();282
283 //container if the user is creating a multi-image tiff
284 BufferedImage[] multiPages = new BufferedImage[1 + (end-start)];285
286 /**
287 * set of JVM flags which allow user control on process288 */
289
290
291 //TIFF OPTIONS/
292
293 String multiPageFlag=System.getProperty("org.jpedal.multipage_tiff");294 boolean isSingleOutputFile=multiPageFlag!=null && multiPageFlag.toLowerCase().equals("true");295
296 String tiffFlag=System.getProperty("org.jpedal.compress_tiff");297 boolean compressTiffs = tiffFlag!=null && tiffFlag.toLowerCase().equals("true");298
299 //JPEG OPTIONS/300
301 //allow user to specify value
302 String rawJPEGComp=System.getProperty("org.jpedal.compression_jpeg");303 if(rawJPEGComp!=null){304 try{305 JPEGcompression=Float.parseFloat(rawJPEGComp);306 }catch(Exception e){307 e.printStackTrace();308 }309 if(JPEGcompression<0 || JPEGcompression>1)310 throw new RuntimeException("Invalid value for JPEG compression - must be between 0 and 1");311
312 }313
314 String jpgFlag=System.getProperty("org.jpedal.jpeg_dpi");315
316 ///
317
318 for (int pageNo = start; pageNo < end+1; pageNo++) {319
320 if(debug)321 System.out.println("page : " +pageNo);322
323 /**
324 * example1 - ask JPedal to return from decoding if file takes too long (time is in millis)325 * will reset after exit so call for each page326 */
327 //decoder.setPageDecodeStatus(DecodeStatus.Timeout,new Integer(20) );
328 /**
329 * example2 thread which will ask JPedal to time out and return from decoding330 * will reset after exit so call for each page331 */
332 /**
333 Thread a=new Thread(){334 public void run() {335
336 while(true){337 //simulate 2 second delay338 try {339 Thread.sleep(2000);340 } catch (InterruptedException e) {341 e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.342 }343
344 //tell JPedal to exit asap345 decoder.setPageDecodeStatus(DecodeStatus.Timeout, Boolean.TRUE);346
347 }348 }349 };350
351 //simulate a second thread352 a.start();353
354 //see code after line decoder.getPageAsHiRes(pageNo); for tracking whether JPedal timed out and returned355 /**/
356
357 /**
358 * If you are using decoder.getPageAsHiRes() after passing additional parameters into JPedal using the static method359 * PdfDecoder.modifyJPedalParameters(), then getPageAsHiRes() wont necessarily be thread safe. If you want to use360 * getPageAsHiRes() and pass in additional parameters, in a thread safe mannor, please use the method361 * getPageAsHiRes(int pageIndex, Map params) or getPageAsHiRes(int pageIndex, Map params, boolean isTransparent) and362 * pass the additional parameters in directly to the getPageAsHiRes() method without calling PdfDecoder.modifyJPedalParameters()363 * first.364 *365 * Please see org/jpedal/examples/images/ConvertPagesToImages.java.html for more details on how to use HiRes image conversion366 */
367 BufferedImage imageToSave = decoder.getPageAsHiRes(pageNo,null,isTransparent);368
369 String imageFormat = System.getProperty("org.jpedal.imageType");370 if(imageFormat!=null){371 if(isNumber(imageFormat)){372 int iFormat =Integer.parseInt(imageFormat);373 if(iFormat>-1 && iFormat<14){374 BufferedImage tempImage = newBufferedImage(imageToSave.getWidth(), imageToSave.getHeight(), iFormat);375 Graphics2D g =tempImage.createGraphics();376 g.drawImage(imageToSave, null, null);377
378 imageToSave =tempImage;379 }else{380 System.err.println("Image Type is not valid. Value should be a digit between 0 - 13 based on the BufferedImage TYPE variables.");381 }382 }else{383 System.err.println("Image Type provided is not an Integer. Value should be a digit between 0 - 13 based on the BufferedImage TYPE variables.");384 }385 }386
387 //show status flag
388 /**
389 if(decoder.getPageDecodeStatus(DecodeStatus.Timeout))390 System.out.println("Timeout on decoding");391 else392 System.out.println("Done");393 /**/
394
395 decoder.flushObjectValues(true);396
397 //System.out.println("w="+imageToSave.getWidth()+" h="+imageToSave.getHeight());398 //image needs to be sRGB for JPEG
399 if(fileType.equals("jpg"))400 imageToSave =ColorSpaceConvertor.convertToRGB(imageToSave);401
402
403 String outputFileName;404 if(isSingleOutputFile)405 outputFileName = outputPath+ "allPages"+prefix+ '.' +fileType;406 else{407 /**
408 * create a name with zeros for if more than 9 pages appears in correct order409 */
410 String pageAsString=String.valueOf(pageNo);411 String maxPageSize=String.valueOf(end);412 int padding=maxPageSize.length()-pageAsString.length();413 for(int ii=0;ii
416 outputFileName = outputPath + "page" + pageAsString +prefix + '.' +fileType;417 }418
419 //if just gray we can reduce memory usage by converting image to Grayscale
420
421 /**
422 * see what Colorspaces used and reduce image if appropriate423 * (only does Gray at present)424 *425 * Can return null value if not sure426 */
427 Iterator colorspacesUsed=decoder.getPageInfo(PageInfo.COLORSPACES);428
429 intnextID;430 boolean isGrayOnly=colorspacesUsed!=null; //assume true and disprove
431
432 while(colorspacesUsed!=null &&colorspacesUsed.hasNext()){433 nextID=(Integer) (colorspacesUsed.next());434
435 if(nextID!= ColorSpaces.DeviceGray && nextID!=ColorSpaces.CalGray)436 isGrayOnly=false;437 }438
439 //draw onto GRAY image to reduce colour depth440 //(converts ARGB to gray)
441 if(isGrayOnly){442 BufferedImage image_to_save2=newBufferedImage(imageToSave.getWidth(),imageToSave.getHeight(), BufferedImage.TYPE_BYTE_GRAY);443 image_to_save2.getGraphics().drawImage(imageToSave,0,0,null);444 imageToSave =image_to_save2;445 }446
447 //put image in array if multi-images (we save on last page in code below)
448 if(isSingleOutputFile)449 multiPages[pageNo-start] =imageToSave;450
451 //we save the image out here
452 if (imageToSave != null) {453
454 /**BufferedImage does not support any dpi concept. A higher dpi can be created455 * using JAI to convert to a higher dpi image*/
456
457 //shrink the page to 50% with graphics2D transformation458 //- add your own parameters as needed459 //you may want to replace null with a hints object if you460 //want to fine tune quality.
461
462 /**example 1 biliniear scaling463 AffineTransform scale = new AffineTransform();464 scale.scale(.5, .5); //50% as a decimal465 AffineTransformOp scalingOp =new AffineTransformOp(scale, null);466 image_to_save =scalingOp.filter(image_to_save, null);467
468 */
469
470 if(JAIHelper.isJAIused())471 JAIHelper.confirmJAIOnClasspath();472
473 if(JAIHelper.isJAIused() && fileType.startsWith("tif")){474
475 com.sun.media.jai.codec.TIFFEncodeParam params = newcom.sun.media.jai.codec.TIFFEncodeParam();476
477 if(compressTiffs)478 params.setCompression(com.sun.media.jai.codec.TIFFEncodeParam.COMPRESSION_PACKBITS);479
480 if(!isSingleOutputFile){481 BufferedOutputStream os = new BufferedOutputStream(newFileOutputStream(outputFileName));482
483 javax.media.jai.JAI.create("encode", imageToSave, os, "TIFF", params);484 }else if(isSingleOutputFile && pageNo ==end){485 OutputStream out = new BufferedOutputStream(newFileOutputStream(outputFileName));486 com.sun.media.jai.codec.ImageEncoder encoder = com.sun.media.jai.codec.ImageCodec.createImageEncoder("TIFF", out, params);487 List vector = newArrayList();488 vector.addAll(Arrays.asList(multiPages).subList(1, multiPages.length));489
490 params.setExtraImages(vector.iterator());491
492 encoder.encode(multiPages[0]);493 out.close();494 }495 }else if(isSingleOutputFile){496 //non-JAI
497 } else if ((jpgFlag != null || rawJPEGComp!=null) && fileType.startsWith("jp") &&JAIHelper.isJAIused()) {498
499 saveAsJPEG(jpgFlag, imageToSave, JPEGcompression, new BufferedOutputStream(newFileOutputStream(outputFileName)));500 } else{501
502 BufferedOutputStream bos= new BufferedOutputStream(new FileOutputStream(newFile(outputFileName)));503 ImageIO.write(imageToSave, fileType, bos);504 bos.flush();505 bos.close();506 }507 //if you just want to save the image, use something like508 //javax.imageio.ImageIO.write((java.awt.image.RenderedImage)image_to_save,"png",new java.io.FileOutputStream(output_dir + page + image_name+".png"));
509
510 }511
512 imageToSave.flush();513
514 if(debug){515 System.out.println("Created : " +outputFileName);516
517 }518 }519 }520
521 /**test to see if string or number*/
522 private static booleanisNumber(String value) {523
524 //assume true and see if proved wrong
525 boolean isNumber=true;526
527 int charCount=value.length();528 for(int i=0;i'9')){531 isNumber=false;532 i=charCount;533 }534 }535
536 returnisNumber;537 }538
539 private static void saveAsJPEG(String jpgFlag,BufferedImage image_to_save, float JPEGcompression, BufferedOutputStream fos) throwsIOException {540
541 //useful documentation athttp://docs.oracle.com/javase/7/docs/api/javax/imageio/metadata/doc-files/jpeg_metadata.html
542 //useful example program athttp://johnbokma.com/java/obtaining-image-metadata.htmlto output JPEG data543
544 //old jpeg class545 //com.sun.image.codec.jpeg.JPEGImageEncoder jpegEncoder = com.sun.image.codec.jpeg.JPEGCodec.createJPEGEncoder(fos);546 //com.sun.image.codec.jpeg.JPEGEncodeParam jpegEncodeParam = jpegEncoder.getDefaultJPEGEncodeParam(image_to_save);547
548 //Image writer
549 JPEGImageWriter imageWriter = (JPEGImageWriter) ImageIO.getImageWritersBySuffix("jpeg").next();550 ImageOutputStream ios =ImageIO.createImageOutputStream(fos);551 imageWriter.setOutput(ios);552
553 //and metadata
554 IIOMetadata imageMetaData = imageWriter.getDefaultImageMetadata(new ImageTypeSpecifier(image_to_save), null);555
556 if (jpgFlag != null){557
558 int dpi = 96;559
560 try{561 dpi =Integer.parseInt(jpgFlag);562 } catch(Exception e) {563 e.printStackTrace();564 }565
566 //old metadata567 //jpegEncodeParam.setDensityUnit(com.sun.image.codec.jpeg.JPEGEncodeParam.DENSITY_UNIT_DOTS_INCH);568 //jpegEncodeParam.setXDensity(dpi);569 //jpegEncodeParam.setYDensity(dpi);570
571 //new metadata
572 Element tree = (Element) imageMetaData.getAsTree("javax_imageio_jpeg_image_1.0");573 Element jfif = (Element)tree.getElementsByTagName("app0JFIF").item(0);574 jfif.setAttribute("Xdensity", Integer.toString(dpi));575 jfif.setAttribute("Ydensity", Integer.toString(dpi));576
577 }578
579 if(JPEGcompression>=0 && JPEGcompression<=1f){580
581 //old compression582 //jpegEncodeParam.setQuality(JPEGcompression,false);583
584 //new Compression
585 JPEGImageWriteParam jpegParams =(JPEGImageWriteParam) imageWriter.getDefaultWriteParam();586 jpegParams.setCompressionMode(JPEGImageWriteParam.MODE_EXPLICIT);587 jpegParams.setCompressionQuality(JPEGcompression);588
589 }590
591 //old write and clean592 //jpegEncoder.encode(image_to_save, jpegEncodeParam);593
594 //new Write and clean up
595 imageWriter.write(imageMetaData, new IIOImage(image_to_save, null, null), null);596 ios.close();597 imageWriter.dispose();598
599 }600 }