/*
//
// ADOBE SYSTEMS INCORPORATED
// Copyright (C) 2000-2007 Adobe Systems Incorporated
// All rights reserved.
//
// NOTICE: Adobe permits you to use, modify, and distribute this file
// in accordance with the terms of the Adobe license agreement
// accompanying it. If you have received this file from a source other
// than Adobe, then your use, modification, or distribution of it
// requires the prior written permission of Adobe.
//
// Project: Re-encoding embedded image XObject with JPXDecode filter
// 
// This sample demonstrates the process of re-encoding PDF embedded 
// image XObjects with the JPEG2000 encoding standard via the use of
// PDFEdit and COS layer APIs.   
//
// The output with re-encoded image XObject is saved in the project
// folder as "out.pdf"
// 
*/

#ifdef WIN_PLATFORM
#include <sys/types.h>
#include <sys/stat.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

#include "CosCalls.h"
#include "PERCalls.h"
#include "PEWCalls.h"
#include "PagePDECntCalls.h"
#include "MyPDFLibUtils.h"

#ifdef MAC_PLATFORM                                                                   
#include "MacUtils.h"
#endif

// 
// Switch the INPUT macro definition to cottage.pdf to invoke the code path
// that handles Indexed color space used in the flate-encoded source image.
//

#define INPUT     "../sources/ducky.pdf"
//#define INPUT "../sources/cottage.pdf"



PDEImage   GetJPXEncodedImage(PDDoc  dstDoc,  PDDoc  srcDoc);

void   releaseMemory(PDPage  pdPage,  ASStm  asStm);

PDEImage   GetJPXEncodedImage(PDDoc  dstDoc,  PDDoc  srcDoc)
{
                PDEImage  desImage =  NULL;                              // PDEImage object to be created
                PDEImageAttrs  srcImageAttrs;        // source image attributes                         
                PDEColorSpace  srcColorSpace;        // source image color model and values   
                ASDoubleMatrix  srcMatrix;                                              // source image transformation matrix
                ASErrorCode  errCode = 0;

                PDPage  srcPage =  NULL;
                ASStm  srcStm =  NULL;

DURING
                // Get source image from input file
               srcPage =  PDDocAcquirePage(srcDoc, 0);
                PDEContent  srcContent =  PDPageAcquirePDEContent(srcPage, 0);
                int  numElems =  PDEContentGetNumElems(srcContent);
               
                PDEElement  srcElem =  NULL;
                PDEImage  srcImage =  NULL;
                ASBool  found =  false ;

                //
                // Enumerate the content elements to find an image XObject.
                // For demonstration purpose, we deal only with the first image XObject found
                //

                for  ( int  i = 0; i<numElems; i++)
               {
                               srcElem =  PDEContentGetElem(srcContent, i);

                                // Make sure we are getting an image XObject
                                if  (PDEObjectGetType((PDEObject)srcElem) ==  kPDEImage  
                                               &&  PDEImageIsCosObj((PDEImage)srcElem))
                               {
                                               srcImage = (PDEImage)srcElem;
                                               found =  true ;
                                                break ;
                               }
               }
               
                if  (!found) 
               {
                               printf( "No image XObjects found on the first page of the input document.\n \
                                               Try another document.\n" );
                                releaseMemory(srcPage, srcStm);
                                E_RETURN(NULL);
               }

                // Get the decoded data stream from the image XObject
               srcStm =  PDEImageGetDataStm(srcImage, 0);

                // Get source image attributes, color space information, and transformation matrix
                PDEImageGetAttrs(srcImage, &srcImageAttrs,  sizeof (PDEImageAttrs));
               srcColorSpace =  PDEImageGetColorSpace(srcImage);
                PDEElementGetMatrixEx((PDEElement)srcImage, &srcMatrix);

                PDEFilterArray  filterArray;                          // Structure to specify filter information for data streams
                PDEFilterSpec  filterSpec;                              // Structure to store filter elements in a filter array
                CosDoc  cosDoc;
                CosObj  jpxParams;                                                              // Dictionay of parameters passed to the JPX encoder
                               
               cosDoc =  PDDocGetCosDoc(dstDoc);
               jpxParams =  CosNewDict( cosDoc,  false , 15);

                // width and height of image in pixels
                CosDictPut(jpxParams,  ASAtomFromString( "Width" ),  CosNewInteger( cosDoc,  false , srcImageAttrs.width));     
                CosDictPut(jpxParams,  ASAtomFromString( "Height" ),  CosNewInteger( cosDoc,  false , srcImageAttrs.height)); 
               
                // width and height of tiles to use in compression; 256 x 256 is typical
                CosDictPut(jpxParams,  ASAtomFromString( "TileWidth" ),  CosNewInteger( cosDoc,  false , 256));           
                CosDictPut(jpxParams,  ASAtomFromString( "TileHeight" ),  CosNewInteger( cosDoc,  false , 256));         

                // Should the decorrelation transform be used or not. For best results this is set to true for
                // RGB, for LAB false, and for CMYK true may yield better compression.
                CosDictPut(jpxParams,  ASAtomFromString( "ColorTransform" ),  CosNewBoolean( cosDoc,  false ,  false ));
       
                // Quality is a value from 1 to 100.
                CosDictPut(jpxParams,  ASAtomFromString( "Quality" ),  CosNewInteger( cosDoc,  false , 75));

                // If lossless compression is specified, the Quality number is ignored.
                CosDictPut(jpxParams,  ASAtomFromString( "EncodeLosslessly" ),  CosNewBoolean( cosDoc,  false ,  false ));
       
                // The number of compression resolutions to place in the data. 
                // 5 gives good compression, and good decompression performance.
                CosDictPut(jpxParams,  ASAtomFromString( "ResProgressionSize" ),  CosNewInteger( cosDoc,  false , 5));
       
                //
                // The image samples may be indices into a color look-up table
                // rather than direct color components. This is signified by a Palette 
                // entry in the jpxParams. 
                //
                if  (PDEColorSpaceGetName(srcColorSpace) ==  ASAtomFromString( "Indexed" ))
               {
                                // Here we deal with DeviceRGB base color space
                                if  (PDEColorSpaceGetBase(srcColorSpace) ==  ASAtomFromString( "DeviceRGB" ))
                               {
                                                // Get the number of entries in the color look-up table
                                                ASInt32  sizeLUT =  PDEColorSpaceGetHiVal(srcColorSpace) + 1;
                                                ASInt32  baseNumComps =  PDEColorSpaceGetBaseNumComps(srcColorSpace);
                                               
                                                // Obtain the color look-up table
                                                ASUns8* bufLUT = (ASUns8*)ASmalloc(sizeLUT * baseNumComps);
                                                PDEColorSpaceGetCTable  (srcColorSpace, bufLUT);

                                                //
                                                // The Palette entry is an array. The first entry is an integer giving 
                                                // the size of the look-up table in terms of the number of entries. 
                                                // The second Palette entry is also an integer and gives the bit depth 
                                                // of the entries generated by the table. The remaining entries in 
                                                // Palette are strings giving the bytes making up the look-up table. 
                                                // There is one string for each component in the color space on which 
                                                // The palette is based. This may be any of the color spaces described next.
                                                //

                                                // Decomposing the color table entries (in the pattern of
                                                // RGB...RGB...RGB...) into RRR..., GGG..., and BBB... strings 
                                                ASUns8* rCompStr = (ASUns8*)ASmalloc(sizeLUT);
                                                ASUns8* gCompStr = (ASUns8*)ASmalloc(sizeLUT);
                                                ASUns8* bCompStr = (ASUns8*)ASmalloc(sizeLUT);

                                                for  ( int  i=0, j=0; i<sizeLUT; i++, j+=baseNumComps)
                                               {
                                                               rCompStr[i] = bufLUT[j];
                                                               gCompStr[i] = bufLUT[j+1];
                                                               bCompStr[i] = bufLUT[j+2];
                                               }
                                                ASfree(bufLUT);

                                                // Composing the Palette array
                                                CosObj  palette =  CosNewArray( cosDoc,  false , baseNumComps + 2);

                                                CosArrayPut( palette, 0,  CosNewInteger( cosDoc,  false , sizeLUT));
                                                CosArrayPut( palette, 1,  CosNewInteger( cosDoc,  false ,   srcImageAttrs.bitsPerComponent));
                                               
                                                CosObj  rComp =  CosNewString(cosDoc,  false , ( char *)rCompStr, sizeLUT);
                                                CosObj  gComp =  CosNewString(cosDoc,  false , ( char *)gCompStr, sizeLUT);
                                                CosObj  bComp =  CosNewString(cosDoc,  false , ( char *)bCompStr, sizeLUT);
                                                CosArrayPut( palette, 2, rComp);
                                                CosArrayPut( palette, 3, gComp);
                                                CosArrayPut( palette, 4, bComp);
                                               
                                                ASfree(rCompStr);
                                                ASfree(gCompStr);
                                                ASfree(bCompStr);

                                                CosDictPut( jpxParams,  ASAtomFromString( "Palette" ), palette);

                                                // These two entries, BitsPerComponent and Colors, describe the image samples 
                                                // to the JPX compressor. Colors is the number of color components.

                                                // In the case of indexed color space, Colors has a value of 1 indicating that 
                                                // there is only one channel of data (the indices). 
                                                // BitsPerComponent gives the number of components in the indices 
                                                // - either 8 or 16. The look-up table may contain up to 1024 entries.
                                                CosDictPut(jpxParams,  ASAtomFromString( "BitsPerComponent" ),  CosNewInteger( cosDoc,  false , srcImageAttrs.bitsPerComponent));
                                                CosDictPut(jpxParams,  ASAtomFromString( "Colors" ),  CosNewInteger( cosDoc,  false , 1));
               
                                                //
                                                // The supported JPEG2000 enumerated color space values:
                                                //
                        // 12 - CMYK
                        // 14 - LAB
                        // 16 - sRGB
                        // 17 - grayscale
                        // 18 - sYCC
                        // 20 - e-sRGB
                        // 21 - ROMMRGB
                        // 24 - e-sYCC
                                                //

                                                // only one color spec being provided here
                                                CosObj  colorSpecs =  CosNewArray( cosDoc,  false , 1); 
                                                CosObj  aColorSpec =  CosNewDict( cosDoc,  false , 1);
                                                CosArrayPut( colorSpecs, 0, aColorSpec);

                                                // The Method key specifies the base color space of the Indexed color space
                                                CosDictPut( aColorSpec,  ASAtomFromString( "Method" ),  CosNewInteger( cosDoc,  false , 16)); 

                                                CosDictPut( jpxParams,  ASAtomFromString( "ColorSpace" ), colorSpecs);
                               }  else  {
                                               printf( "We don't deal with Indexed color spaces with base color space other than /DeviceRGB here.\n" );
                                                releaseMemory(srcPage, srcStm);
                                                E_RETURN(NULL);
                               }
               }  else  {
                                //
                                // We handle non-indexed color spaces here
                                //

                                // These two entries, BitsPerComponent and Colors, describe the image samples 
                                // to the JPX compressor. Colors is the number of color components.

                                CosDictPut(jpxParams,  ASAtomFromString( "BitsPerComponent" ),  CosNewInteger( cosDoc,  false , srcImageAttrs.bitsPerComponent));
                                CosDictPut(jpxParams,  ASAtomFromString( "Colors" ),  CosNewInteger( cosDoc,  false ,  PDEColorSpaceGetNumComps(srcColorSpace)));

                                CosObj  colorSpecs =  CosNewArray( cosDoc,  false , 1); 
                                CosObj  aColorSpec =  CosNewDict( cosDoc,  false , 1);
                                CosArrayPut( colorSpecs, 0, aColorSpec);
                               
                                //
                                // We specify 16 for the /Method key for DeviceRGB source image color space.
                                // For other source color space, refer to the color space values listed above.
                                // For instance, for source image in CMYK color space, specify 12 instead.
                                //

                                CosDictPut( aColorSpec,  ASAtomFromString( "Method" ),  CosNewInteger( cosDoc,  false , 16)); 
                                CosDictPut( jpxParams,  ASAtomFromString( "ColorSpace" ), colorSpecs);
               }

                // Fill in values for the PDEFilterSpec object
               memset (&filterSpec, 0,  sizeof  (PDEFilterSpec));
               filterSpec.padding  = 0;
               filterSpec.encodeParms  = jpxParams;
               filterSpec.decodeParms  =  CosNewNull();
               filterSpec.name  =  ASAtomFromString  ( "JPXDecode" );
               
                // Fill in values for the PDEFilterArray object
               filterArray.numFilters  = 1;
               filterArray.spec[0] = filterSpec;

                // Create the destination image XObject by passing in source image data stream,
                // image attributes, and JPXDecode filter information
               desImage =  PDEImageCreateEx(&srcImageAttrs,  sizeof (srcImageAttrs),
                               &srcMatrix, 0, srcColorSpace,  NULL, &filterArray, srcStm,  NULL, 0);

                CosObj        obj;
                PDEImageGetCosObj( desImage, &obj);

                // For JPX images this dictionary entries is optional; 
                // Remove so that the color space specified in the JPEG2000 
                // data is used
                CosDictRemove( obj,  ASAtomFromString( "ColorSpace" ));                 
                // For JPX images this is never consulted; remove it
                CosDictRemove( obj,  ASAtomFromString( "BitsPerComponent" ));

HANDLER
               errCode =  ERRORCODE;
END_HANDLER

                // Do some clean-up before returning the PDEImage object
                releaseMemory(srcPage, srcStm);

                if (errCode)
                                ASRaise(errCode);

                return  desImage;
}

void   releaseMemory(PDPage  pdPage,  ASStm  asStm)
{
                if (asStm)              ASStmClose(asStm);
                if (pdPage)
               {
                                PDPageReleasePDEContent(pdPage,  NULL);
                                PDPageRelease(pdPage);
               }
}

void   MainProc( void );
void   MainProc( void )
{
                ASBool  b;
                PDDoc  pdDoc =  NULL;                                          // reference to the output PDF document
                PDDoc  sourceDoc =  NULL;                  // reference to the input PDF document
                PDPage  pdPage =  NULL;                      // reference to the page object in pdDoc
                PDEContent  pdeContent;                    // container for page content
                ASFixedRect  mediaBox;                      // dimensions of page
                PDEImage  pdeImage =  NULL;              // reference to the re-encoded image object
                ASErrorCode  errCode = 0;

DURING
                // Create new document
               pdDoc =  PDDocCreate();                                                                                   

                // Set the media box of the new document pages. 
               mediaBox.left      =  fixedZero;                           
               mediaBox.top        =  Int16ToFixed(11*72);         
               mediaBox.right    =  Int16ToFixed(8.5*72);
               mediaBox.bottom  =  fixedZero;

                // Create page with the mediabox and insert as first page
               pdPage =  PDDocCreatePage(pdDoc,  PDBeforeFirstPage, mediaBox);
               sourceDoc =  MyPDDocOpen(INPUT);

                // Acquire PDEContent container for page
               pdeContent =  PDPageAcquirePDEContent(pdPage,  NULL);         

                // Calls the helper function to re-encode the source image data
               pdeImage =  GetJPXEncodedImage(pdDoc, sourceDoc);

                if (pdeImage)
               {
                                // insert image into page content
                                PDEContentAddElem(pdeContent,  kPDEAfterLast, (PDEElement)pdeImage);

                                // Set the PDEContent for the page
                               b =  PDPageSetPDEContent(pdPage, 0);
                                PDERelease((PDEObject)pdeImage);
               
                // save document to a file
#if !MAC_ENV
                                ASPathName  path =  ASFileSysCreatePathName  (ASGetDefaultFileSys(), 
                                                                                                                ASAtomFromString( "Cstring" ),  "out.pdf" ,  NULL);
                                PDDocSave(pdDoc,  PDSaveFull  |  PDSaveLinearized, path,  ASGetDefaultFileSys(), 0, 0);
                                ASFileSysReleasePath(ASGetDefaultFileSys(), path);
#else
                                ASPathName  macPath =  GetMacPath( "out.pdf" );
                                PDDocSave(pdDoc,  PDSaveFull  |  PDSaveLinearized, macPath,  ASGetDefaultFileSys(),  NULLNULL);
                                ASFileSysReleasePath(ASGetDefaultFileSys(),macPath);
#endif
               }

HANDLER
               errCode =  ERRORCODE;
END_HANDLER
               
                DisplayErrorAndWarnings(sourceDoc, errCode);
                releaseMemory(pdPage,  NULL);
                if (pdDoc)              PDDocClose(pdDoc);
                if (sourceDoc)      PDDocClose(sourceDoc);
}

#define INCLUDE_MYPDFLIBAPP_CPP 1
#include "MyPDFLibApp.cpp"
#undef INCLUDE_MYPDFLIBAPP_CPP