package org.jpedal.examples.images;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;

import javax.imageio.ImageIO;

import org.jpedal.PdfDecoder;
import org.jpedal.color.ColorSpaces;
import org.jpedal.exception.PdfException;
import org.jpedal.utils.LogWriter;
import org.jpedal.io.ColorSpaceConvertor;
import org.jpedal.io.JAIHelper;
import org.jpedal.constants.JPedalSettings;
import org.jpedal.constants.PageInfo;

public class HiResThumbnailExtractor {

    private static boolean debug = false;

    PdfDecoder decoder = new PdfDecoder(true);

    /**correct separator for OS */
    final static String separator = System.getProperty"file.separator" );

    //only used if between 0 and 1
    private float JPEGcompression=-1f;

    public HiResThumbnailExtractor() {}

    public static void main(String[] argsthrows Exception {

                 /*
            * Change these variables
            */
        String fileType="";
        String pdfFile="";

        if(args!=null && args.length>1){

            pdfFile = args[0];
            fileType= args[1];

            if(pdfFile.toLowerCase().endsWith(".pdf"&& (fileType.equals("jpg"|| fileType.equals("jpeg"|| fileType.equals("png"|| fileType.equals("tiff"|| fileType.equals("tif"))){
                new HiResThumbnailExtractor(fileType, pdfFile);
            }else{

                File testDir=new File(pdfFile);

                if(testDir.isDirectory()){

                    /**
                     * get list of files and check directory
                     */

                    String[] files = null;
                    File inputFiles = null;

                    /**make sure name ends with a deliminator for correct path later*/
                    if (!pdfFile.endsWith(separator))
                        pdfFile = pdfFile + separator;

                    try {
                        inputFiles = new File(pdfFile);

                        files = inputFiles.list();
                    catch (Exception ee) {
                        LogWriter.writeLog("Exception trying to access file " + ee.getMessage());
                    }

                    /**now work through all pdf files*/
                    long fileCount = files.length;

                    for (int i = 0; i < fileCount; i++) {

                        if (files[i].toLowerCase().endsWith(".pdf"))
                            new HiResThumbnailExtractor(fileType,pdfFile + files[i]);
                    }
                }else
                    System.out.println("The file to be processed has to be a pdf and the output filetype can only be jpg,png or tiff");
            }
        }else{
            System.out.println("Not enough arguments passed in! Usage: \"C:\\examples\\1.pdf\" \"jpg\"");
        }
    }

    HiResThumbnailExtractor(String fileType, String pdfFilethrows Exception {

        long startTime=System.currentTimeMillis();

        String outputPath = pdfFile.substring(0, pdfFile.toLowerCase().indexOf(".pdf")) + separator;
        File outputPathFile = new File(outputPath);
        if (!outputPathFile.exists() || !outputPathFile.isDirectory()) {
            if (!outputPathFile.mkdirs()) {
                if(debug)
                    System.err.println("Can't create directory " + outputPath);
            }
        }

        final PdfDecoder decoder = new PdfDecoder(true);

        //mappings for non-embedded fonts to use
        PdfDecoder.setFontReplacements(decoder);

        decoder.openPdfFile(pdfFile);

        Map mapValues = new HashMap();

        /** USEFUL OPTIONS*/
        //do not scale above this figure
        mapValues.put(JPedalSettings.EXTRACT_AT_BEST_QUALITY_MAXSCALING, new Integer(2));


        //alternatively secify a page size (aspect ratio preserved so will do best fit)
        //set a page size (JPedal will put best fit to this)
        mapValues.put(JPedalSettings.EXTRACT_AT_PAGE_SIZE, new String[]{"2000","1600"});

        //which takes priority (default is false)
        mapValues.put(JPedalSettings.PAGE_SIZE_OVERRIDES_IMAGE, Boolean.TRUE);

         /**/

        /** used to debug issue
         mapValues.clear();
         mapValues.put(JPedalSettings.EXTRACT_AT_PAGE_SIZE,new String[]{"2000","1600"});
         mapValues.put(JPedalSettings.IMAGE_HIRES,Boolean.TRUE);

         mapValues.put(JPedalSettings.IGNORE_FORMS_ON_PRINT,new int[]{1,2});
         /**/
        PdfDecoder.modifyJPedalParameters(mapValues);

        if(debug)
            System.out.println("pdf : " + pdfFile);

        try{
            
            /**
             * allow output to multiple images with different values on each
             *
             * Note we REMOVE shapes as it is a new feature and we do not want to break existing functions
             */
            String separation=System.getProperty("org.jpedal.separation");
            if(separation!=null){

                Object[] sepValues=new Object[]{new Integer(7),"",Boolean.FALSE}//default of normal
                if(separation.equals("all")){
                    sepValues=new Object[]{new Integer(PdfDecoder.RENDERIMAGES),"image_and_shapes",Boolean.FALSE,
                            new Integer(PdfDecoder.RENDERIMAGES+PdfDecoder.REMOVE_RENDERSHAPES),"image_without_shapes",Boolean.FALSE,
                            new Integer(PdfDecoder.RENDERTEXT),"text_and_shapes",Boolean.TRUE,
                            new Integer(7),"all",Boolean.FALSE,
                            new Integer(PdfDecoder.RENDERTEXT+PdfDecoder.REMOVE_RENDERSHAPES),"text_without_shapes",Boolean.TRUE
                    };
                }

                int sepCount =sepValues.length;
                for(int seps=0;seps<sepCount;seps=seps+3){

                    decoder.setRenderMode(((Integer)sepValues[seps]).intValue());
                    extractPageAsImage(fileType, outputPath, decoder,"_"+sepValues[seps+1],((Boolean)sepValues[seps+2]).booleanValue())//boolean makes last transparent so we can see white text
                }

            }else //just get the page
                extractPageAsImage(fileType, outputPath, decoder,"",false);

        finally {

            decoder.closePdfFile();
        }
        System.out.println("time="+(System.currentTimeMillis()-startTime)/1000);


    }

    private void extractPageAsImage(String fileType, String outputPath, PdfDecoder decoder, String prefix, boolean isTransparentthrows PdfException, IOException {


        int start=1, end=decoder.getPageCount();

        BufferedImage[] multiPages = new BufferedImage[(end-start)];

        String multiPageFlag=System.getProperty("org.jpedal.multipage_tiff");
        boolean isSingleOutputFile=multiPageFlag!=null && multiPageFlag.toLowerCase().equals("true");

        //allow user to specify value
        String rawJPEGComp=System.getProperty("org.jpedal.compression_jpeg");
        if(rawJPEGComp!=null){
            try{
                JPEGcompression=Float.parseFloat(rawJPEGComp);
            }catch(Exception e){
                e.printStackTrace();
            }
            if(JPEGcompression<|| JPEGcompression>1)
                throw new RuntimeException("Invalid value for JPEG compression - must be between 0 and 1");

        }

        String tiffFlag=System.getProperty("org.jpedal.compress_tiff");
        String jpgFlag=System.getProperty("org.jpedal.jpeg_dpi");
        boolean compressTiffs = tiffFlag!=null && tiffFlag.toLowerCase().equals("true");


        for (int pageNo = start; pageNo < end+1; pageNo++) {

            if(debug)
                System.out.println("page : " + pageNo);


            
            /**
             * example1 - ask JPedal to return from decoding if file takes too long (time is in millis)
             * will reset after exit so call for each page
             */
            //decoder.setPageDecodeStatus(DecodeStatus.Timeout,new Integer(20) );
            /**
             * example2 thread which will ask JPedal to time out and return from decoding
             * will reset after exit so call for each page
             */
            /**
             Thread a=new Thread(){
             public void run() {

             while(true){
             //simulate 2 second delay
             try {
             Thread.sleep(2000);
             } catch (InterruptedException e) {
             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
             }

             //tell JPedal to exit asap
             decoder.setPageDecodeStatus(DecodeStatus.Timeout, Boolean.TRUE);

             }
             }
             };

             //simulate a second thread
             a.start();

             //see code after line decoder.getPageAsHiRes(pageNo); for tracking whether JPedal timed out and returned
             /**/

            /**
             * If you are using decoder.getPageAsHiRes() after passing additional parameters into JPedal using the static method
             * PdfDecoder.modifyJPedalParameters(), then getPageAsHiRes() wont necessarily be thread safe.  If you want to use
             * getPageAsHiRes() and pass in additional parameters, in a thread safe mannor, please use the method
             * getPageAsHiRes(int pageIndex, Map params) or getPageAsHiRes(int pageIndex, Map params, boolean isTransparent) and
             * pass the additional parameters in directly to the getPageAsHiRes() method without calling PdfDecoder.modifyJPedalParameters()
             * first.
             *
             * Please see http://www.jpedal.org/support_egHiRes.php for more details on how to use HiResThumbnailExtraction.
             */
            BufferedImage imageToSave = decoder.getPageAsHiRes(pageNo,null,isTransparent);

            //show status flag
            /**
             if(decoder.getPageDecodeStatus(DecodeStatus.Timeout))
             System.out.println("Timeout on decoding");
             else
             System.out.println("Done");
             /**/

            decoder.flushObjectValues(true);

            //System.out.println("w="+imageToSave.getWidth()+" h="+imageToSave.getHeight());
            //image needs to be sRGB for JPEG
            if(fileType.equals("jpg"))
                imageToSave = ColorSpaceConvertor.convertToRGB(imageToSave);


            String outputFileName="";
            if(isSingleOutputFile)
                outputFileName = outputPath+ "allPages"+prefix+"." + fileType;
            else
                outputFileName = outputPath + "page" + pageNo +prefix + "." + fileType;

            File file = new File(outputFileName);

            
            //if just gray we can reduce memory usage by converting image to Grayscale

            /**
             * see what Colorspaces used and reduce image if appropriate
             * (only does Gray at present)
             *
             * Can return null value if not sure
             */
            Iterator colorspacesUsed=decoder.getPageInfo(PageInfo.COLORSPACES);

            int nextID;
            boolean isGrayOnly=colorspacesUsed!=null//assume true and disprove

            while(colorspacesUsed!=null && colorspacesUsed.hasNext()){
                nextID=((Integer)(colorspacesUsed.next())).intValue();

                if(nextID!= ColorSpaces.DeviceGray && nextID!=ColorSpaces.CalGray)
                    isGrayOnly=false;
            }

            //draw onto GRAY image to reduce colour depth
            //(converts ARGB to gray)
            if(isGrayOnly){
                BufferedImage image_to_save2=new BufferedImage(imageToSave.getWidth(),imageToSave.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
                image_to_save2.getGraphics().drawImage(imageToSave,0,0,null);
                imageToSave = image_to_save2;
            }


            //put image in array if multi-images
            if(isSingleOutputFile)
                multiPages[pageNo-start= imageToSave;

            if (imageToSave != null) {

                /**BufferedImage does not support any dpi concept. A higher dpi can be created
                 * using JAI to convert to a higher dpi image*/

                //shrink the page to 50% with graphics2D transformation
                //- add your own parameters as needed
                //you may want to replace null with a hints object if you
                //want to fine tune quality.

                /** example 1 biliniear scaling
                 AffineTransform scale = new AffineTransform();
                 scale.scale(.5, .5); //50% as a decimal
                 AffineTransformOp scalingOp =new AffineTransformOp(scale, null);
                 image_to_save =scalingOp.filter(image_to_save, null);

                 */



                if(JAIHelper.isJAIused())
                    JAIHelper.confirmJAIOnClasspath();

                if(JAIHelper.isJAIused() && fileType.startsWith("tif")){

                    com.sun.media.jai.codec.TIFFEncodeParam params = new com.sun.media.jai.codec.TIFFEncodeParam();

                    if(compressTiffs)
                        params.setCompression(com.sun.media.jai.codec.TIFFEncodeParam.COMPRESSION_PACKBITS);

                    if(!isSingleOutputFile){
                        FileOutputStream os = new FileOutputStream(outputFileName);

                        javax.media.jai.JAI.create("encode", imageToSave, os, "TIFF", params);
                    }else if(isSingleOutputFile && pageNo == end){
                        OutputStream out = new FileOutputStream(outputFileName);
                        com.sun.media.jai.codec.ImageEncoder encoder = com.sun.media.jai.codec.ImageCodec.createImageEncoder("TIFF", out, params);
                        Vector vector = new Vector();
                        vector.addAll(Arrays.asList(multiPages).subList(1, multiPages.length));

                        params.setExtraImages(vector.iterator());

                        encoder.encode(multiPages[0]);
                        out.close();
                    }
                }else if(isSingleOutputFile){
                    //non-JAI
                else if ((jpgFlag != null || rawJPEGComp!=null&& fileType.startsWith("jp"&& JAIHelper.isJAIused()) {

                    com.sun.image.codec.jpeg.JPEGImageEncoder jpegEncoder = com.sun.image.codec.jpeg.JPEGCodec.createJPEGEncoder(new FileOutputStream(outputFileName));
                    com.sun.image.codec.jpeg.JPEGEncodeParam jpegEncodeParam = jpegEncoder.getDefaultJPEGEncodeParam(imageToSave);

                    if (jpgFlag != null){

                        int dpi = 96;

                        try {
                            dpi = Integer.parseInt(jpgFlag);
                        catch (Exception e) {
                            e.printStackTrace();
                        }

                        jpegEncodeParam.setDensityUnit(com.sun.image.codec.jpeg.JPEGEncodeParam.DENSITY_UNIT_DOTS_INCH);
                        jpegEncodeParam.setXDensity(dpi);
                        jpegEncodeParam.setYDensity(dpi);

                    }

                    if(JPEGcompression>=&& JPEGcompression<=1f){
                        jpegEncodeParam.setQuality(JPEGcompression,false);
                        System.out.println("xx");
                    }

                    jpegEncoder.encode(imageToSave, jpegEncodeParam);
                else {

                    ImageIO.write(imageToSave,fileType,file);
                }
                //if you just want to save the image, use something like
                //javax.imageio.ImageIO.write((java.awt.image.RenderedImage)image_to_save,"png",new java.io.FileOutputStream(output_dir + page + image_name+".png"));

            }

            imageToSave.flush();
            imageToSave=null;

            if(debug){
                System.out.println("Created : " + outputFileName);

            }
        }
    }
}