How to combine multiple multi-page tif files into a single tif

twilbrand picture twilbrand · Jul 12, 2011 · Viewed 15.7k times · Source

I am trying to take multiple multi-page .tif files and combine them into a single multi-page tif file.

I found some code in this question, but it only seems to take the first page of each individual .tif file and create the new multi-page .tif with those first pages.

Is there a small change I'm not seeing that would cause this same code to grab every page from the source .tif files and put them all into the combined .tif?

To clarify, I would like the source files:

  • SourceA.tif (3 pages)
  • SourceB.tif (4 pages)
  • SourceC.tif (1 page)

to be combined into

  • combined.tif (8 pages)

I would also like to be able to specify a resolution and compression of the .tif, but I'm not sure if JAI supports that and it's not a necessity for a correct answer.

The code from the referenced question, modified by me to load all the .tif files in a directory, is below for easy answering:

public static void main(String[] args) {
        String inputDir = "C:\\tifSources";
        File sourceDirectory = new File(inputDir);
        File file[] = sourceDirectory.listFiles();
        int numImages = file.length;

        BufferedImage image[] = new BufferedImage[numImages];

        try
        {
            for (int i = 0; i < numImages; i++)
            {
                SeekableStream ss = new FileSeekableStream(file[i]);
                ImageDecoder decoder = ImageCodec.createImageDecoder("tiff", ss, null);
                PlanarImage op = new NullOpImage(decoder.decodeAsRenderedImage(0), null, null, OpImage.OP_IO_BOUND);
                image[i] = op.getAsBufferedImage();
            }

            TIFFEncodeParam params = new TIFFEncodeParam();
            OutputStream out = new FileOutputStream(inputDir + "\\combined.tif"); 
            ImageEncoder encoder = ImageCodec.createImageEncoder("tiff", out, params);
            List<BufferedImage> imageList = new ArrayList<BufferedImage>();   
            for (int i = 0; i < numImages; i++)
            {
                imageList.add(image[i]); 
            }
            params.setExtraImages(imageList.iterator()); 
            encoder.encode(image[0]); 
            out.close();
        }
        catch (Exception e)
        {
            System.out.println("Exception " + e);
        }
    }

Answer

twilbrand picture twilbrand · Jul 12, 2011

I knew I was just missing some little part about iterating over the pages in a single .tif, I just wasn't sure where it was.

More searching on the internet led me to find that rather than doing:

PlanarImage op = new NullOpImage(decoder.decodeAsRenderedImage(0), null, null, OpImage.OP_IO_BOUND);

I wanted to iterate over every page in the current document with something like:

int numPages = decoder.getNumPages();
for(int j = 0; j < numPages; j++)
{
     PlanarImage op = new NullOpImage(decoder.decodeAsRenderedImage(j), null, null, OpImage.OP_IO_BOUND);
     images.add(op.getAsBufferedImage());
}

This adds every page of every .tif into the images List. One final trap was that the final call to

encoder.encode(images.get(0));

Would cause the first page to be in the new .tif twice, so I added an intermediate loop and List population that doesn't add the first page in the call to:

params.setExtraImages(imageList.iterator());

which keeps the first page out of the "ExtraImages" and it gets added with the call to encode.

Final updated code is:

public static void main(String[] args) {
        String inputDir = "C:\\tifSources";
        File faxSource = new File(inputDir);
        File file[] = faxSource.listFiles();
        System.out.println("files are " + Arrays.toString(file));
        int numImages = file.length;

        List<BufferedImage> images = new ArrayList<BufferedImage>();

        try
        {
            for (int i = 0; i < numImages; i++)
            {
                SeekableStream ss = new FileSeekableStream(file[i]);
                ImageDecoder decoder = ImageCodec.createImageDecoder("tiff", ss, null);

                int numPages = decoder.getNumPages();
                for(int j = 0; j < numPages; j++)
                {
                    PlanarImage op = new NullOpImage(decoder.decodeAsRenderedImage(j), null, null, OpImage.OP_IO_BOUND);
                    images.add(op.getAsBufferedImage());
                }
            }

            TIFFEncodeParam params = new TIFFEncodeParam();
            OutputStream out = new FileOutputStream(inputDir + "\\combined.tif"); 
            ImageEncoder encoder = ImageCodec.createImageEncoder("tiff", out, params);
            List<BufferedImage> imageList = new ArrayList<BufferedImage>();   
            for (int i = 1; i < images.size(); i++)
            {
                imageList.add(images.get(i)); 
            }
            params.setExtraImages(imageList.iterator()); 
            encoder.encode(images.get(0));
            out.close();
        }
        catch (Exception e)
        {
            System.out.println("Exception " + e);
        }
    }