/*********************************************************************

 ADOBE SYSTEMS INCORPORATED
 Copyright (C) 2000-2003 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.

 ---------------------------------------------------------------------

 Watermark.cpp

 - Uses the PDFEdit API to add a watermark that is a transparent text
     in the center of the page.

 - Performing this operation involves:

               1. Acquiring the page that may be current page or every page.
               2. Creating a PDEFont reference.
               3. Initializing the graphics state, color space, and text matrix
                     to be associated with the text.
               4. Creating the PDEText element and adding it to the contents of
                     the page.

*********************************************************************/

// PDFL include
#ifdef MAC_ENV
#include "macUtils.h"
#else
#include <sys/types.h>
#include <sys/stat.h>
#endif

#include "PDFInit.h"
#include "CosCalls.h"
#include "CorCalls.h"
#include "ASCalls.h"
#include "PDCalls.h"
#include "PERCalls.h"
#include "PEWCalls.h"
#include "PIExcept.h"
#include "PagePDECntCalls.h"
#include "MyPDFLibUtils.h"

// system files
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>

// header files in this project
#include "Watermark.h"

#ifndef MAC_PLATFORM
const   double   pi  = 3.1415926535;
#endif

// functins in this file
static   void   CalculateTextPosition(  const   char * text,  PDEFont   font,
                                                                                int  tSize,  double  theta,  PDEGraphicState  gState,
                                                                                int * px,  int * py);

/* Serialize is called with the PDF Library already initialized. */
bool   Serialize(  const   char  *inputFileName,  const   char  *outputFileName,  const   char  *watermark )
{
                bool  retval;
                ASFileSys  asFileSys =  ASGetDefaultFileSys();

                volatile   ASPathName  asPathName   =  NULL;
                volatile   PDDoc                    pdDoc                     =  NULL;

                DURING
                                // Open the source document
#ifdef MAC_PLATFORM
                               asPathName =  GetMacPath(( char *)inputFileName);
#else
                               asPathName =  ASFileSysCreatePathName( asFileSys,
                                                                                                ASAtomFromString(  "Cstring"  ), inputFileName, 0 );
#endif
                               pdDoc =  PDDocOpen(asPathName,  NULLNULL,  true );

                                ASFileSysReleasePath(asFileSys, asPathName);

                                // Actually add the watermark to the PDDoc
                                if  (AddWatermark( pdDoc, watermark )== false )
                                               printf( "Filaed to add watermark\n\n" );

                                // Save the result with the specified output file name
                                PDSaveFlags  flags =  PDSaveFull  |  PDSaveCollectGarbage  |  PDSaveLinearized
                                                                                                               |  PDSaveBinaryOK  |  PDSaveKeepModDate;

#ifdef MAC_PLATFORM
                               asPathName =  GetMacPath(( char *)outputFileName);
#else
                               asPathName =  ASFileSysCreatePathName( asFileSys,
                                                                                                ASAtomFromString(  "Cstring"  ), outputFileName, 0 );
#endif
                                PDDocSave( pdDoc, flags, asPathName, asFileSys,  NULLNULL  );
                                ASFileSysReleasePath(asFileSys, asPathName);

                                // Done with the doc, close it.
                                PDDocClose( pdDoc );

                               retval =  true ;

                HANDLER
                                if  (asPathName !=  NULL)
                                                ASFileSysReleasePath(asFileSys, asPathName);
                                if  (pdDoc !=  NULL)
                                                PDDocClose( pdDoc );
                               retval =  false ;
                END_HANDLER

                return  retval;
}

/* AddWatermark
** ------------------------------------------------------
**
** Function to add a string of text as watermark to a PDF.
*/
bool   AddWatermark(PDDoc  pdDoc,  const   char  *text )
{
                if  (text ==  NULL)  return   false ;

                PDPage  pdPage;
                ASFixedRect  cropBox;
                char  errorMsg[256];
                ASInt32  errorCode = 0;
                PDEFont  pdeFont =  NULL;

                PDEContent   volatile  pdeContent =  NULL;
                PDEText   volatile  pdeText =  NULL;
               
                DURING

                // take the first page as base page
                int  nPageNum = 0;
                if (mPage  > 0)   nPageNum =  mPage  -1;
               pdPage =  PDDocAcquirePage  (pdDoc, nPageNum);
                PDPageGetCropBox  (pdPage, &cropBox);
                PDPageRelease(pdPage);

                // Initialize the font descriptor then create the font reference.
                // Because we're using one of the base 14 Type 1 fonts, we only
                // need to pass a small amount of information to PDEFontCreate().

                PDEFontAttrs  pdeFontAttrs;

               memset(&pdeFontAttrs, 0,  sizeof (pdeFontAttrs));
               pdeFontAttrs.name  =  ASAtomFromString( "Times-Roman" );
               pdeFontAttrs.type  =  ASAtomFromString( "Type1" );

                // Create the font reference.
               pdeFont =  PDEFontCreate(&pdeFontAttrs,  sizeof (pdeFontAttrs), 0, 255, 0,
                                                                                                                                               0,  ASAtomNull, 0, 0, 0, 0);
                HANDLER
                                ASGetErrorString( ASGetExceptionErrorCode(), errorMsg, 256 );
                                return   false ; 
                END_HANDLER

                // color arrangement
                PDEColorSpace  pdeColorSpace;

                // gray:
                if (mColor  ==  COLOR_GRAY)
                               pdeColorSpace =  PDEColorSpaceCreateFromName  (ASAtomFromString( "DeviceGray" ));
                // rgb:
                else
                               pdeColorSpace =  PDEColorSpaceCreateFromName  (ASAtomFromString( "DeviceRGB" ));

                // extensive graphics state
                PDEExtGState  pdeExtGState =  NULL;

                DURING
                                // create extensive graphics state
                               pdeExtGState =  PDEExtGStateCreateNew(PDDocGetCosDoc(pdDoc));

                                // set opacity values
                                PDEExtGStateSetOpacityFill(pdeExtGState,  FloatToASFixed(mOpacity));
                                PDEExtGStateSetOpacityStroke(pdeExtGState,  FloatToASFixed(mOpacity));

                HANDLER
                                if (pdeExtGState) {
                                                PDERelease((PDEObject) pdeExtGState);
                                               pdeExtGState =  NULL;
                               }

                END_HANDLER


                // The graphics state controls the various style properties of the text
                // including color, weight, and so forth.
                PDEGraphicState  gState;
               memset (&gState, 0,  sizeof (PDEGraphicState));
               gState.strokeColorSpec.space = gState.fillColorSpec.space = pdeColorSpace;

                // Color
                if  (mColor  ==  COLOR_RED)
                               gState.strokeColorSpec.value.color[0] = gState.fillColorSpec.value.color[0] =  FloatToASFixed(0.9);
                else   if  (mColor  ==  COLOR_GREEN)
                               gState.strokeColorSpec.value.color[1] = gState.fillColorSpec.value.color[1] =  FloatToASFixed(0.9);
                else   if  (mColor  ==  COLOR_BLUE)
                               gState.strokeColorSpec.value.color[2] = gState.fillColorSpec.value.color[2] =  FloatToASFixed(0.9);

                // other parameters
               gState.miterLimit =  fixedTen;
               gState.flatness =  fixedOne;
               gState.lineWidth =  fixedOne;
               gState.extGState = pdeExtGState;
               gState.wasSetFlags =  kPDEMiterLimitWasSet  |  kPDEFlatnessWasSet  |
                                                                                                kPDELineWidthWasSet  |  kPDEExtGStateWasSet;

                // Fill out the text matrix, which determines the point
                // size of the text and where it will is drawn on the page.

                // First, translation.

                // to put the text centered in the page, we need to
                // calculatte position of the left-bottom of the text.
                int  dx, dy;
                CalculateTextPosition( text, pdeFont,  mFontSize,  mAngle*pi/180,
                                                                                             gState,   &dx, &dy);

                // translation matrix
                ASDoubleMatrix  textMatrix;
               memset (&textMatrix, 0,  sizeof (textMatrix));
               textMatrix.a  = 1;                                                                                                              // x scale
               textMatrix.b  = 0;                                                                                                              // rotate & skew
               textMatrix.c  = 0;                                                                                                              // rotate & skew
               textMatrix.d  = 1;                                                                                                              // y scale
                ASDouble  xBox = (FixedToDouble(cropBox.right) -  FixedToDouble(cropBox.left))/2;
               textMatrix.h  =  FixedToDouble(cropBox.left) + xBox - dx;    // x translation
                ASDouble  yBox = (FixedToDouble(cropBox.top) -  FixedToDouble(cropBox.bottom))/2;
               textMatrix.v  =  FixedToDouble(cropBox.bottom) + yBox - dy;  // y translation

                // next, rotate text with angle theta
                ASDoubleMatrix  rotateMatrix;
               memset (&rotateMatrix, 0,  sizeof (rotateMatrix));
               rotateMatrix.a  = cos(pi*mAngle  / 180);
               rotateMatrix.b  = sin(pi*mAngle  / 180);
               rotateMatrix.c  = sin(-pi*mAngle  / 180);
               rotateMatrix.d  = cos(pi*mAngle  / 180);

                // multiply the above two
                ASDoubleMatrixConcat  (&rotateMatrix, &textMatrix, &rotateMatrix);

                // finally, scale
               textMatrix.a  =  mFontSize;                                              // x scale
               textMatrix.b  = 0;                                                                              // rotate & skew
               textMatrix.c  = 0;                                                                              // rotate & skew
               textMatrix.d  =  mFontSize;                                              // y scale
               textMatrix.h  = 0;                                                                              // x translation
               textMatrix.v  = 0;                                                                              // y translation

                // multiply the above two to get final matrix.
                ASDoubleMatrixConcat  (&textMatrix, &rotateMatrix, &textMatrix);

                DURING

                                // Create a new PDEText element and add a kPDETextRun object to it.
                               pdeText =  PDETextCreate();
                                PDETextAddEx  (pdeText,  kPDETextRun, 0, (Uns8  *)text, strlen( text ),
                                                                               pdeFont, &gState,  sizeof (gState),  NULL, 0, &textMatrix,  NULL);

                                // create a new container
                                PDEContainer  pdeContainer =  PDEContainerCreate(ASAtomFromString(  "Watermark"  ),  NULL,  false );
                                PDEContent      textContent =  PDEContentCreate();

                                // add the text to the container
                                PDEContentAddElem( textContent,  kPDEAfterLast, (PDEElement)pdeText );
                                PDEContainerSetContent( pdeContainer, textContent );

                                //if not all pages, just use first
                                int  nNumPage = 0;
                                if  (mPage  ==  ALL_PAGES) 
                                               nNumPage =  PDDocGetNumPages( pdDoc );

                    for  ( int  iPg=0; iPg < nNumPage; iPg++) {

                                // acquire each page
                               pdPage =  PDDocAcquirePage( pdDoc, iPg );

                                // Acquire the PDEContent associated with the page.
                               pdeContent =  PDPageAcquirePDEContent( pdPage, 0 );

                                // Insert container into page content.
                                PDEContentAddElem( pdeContent,  kPDEAfterLast, (PDEElement)pdeContainer );

                                // Insert text element into page content.
                                //PDEContentAddElem (pdeContent, kPDEAfterLast, (PDEElement)pdeText);

                                // Commit the changes to the PDEContent.
                                PDPageSetPDEContent( pdPage, 0 );
                               
                                //Release everything
                                PDPageReleasePDEContent( pdPage, 0 );
                                PDPageRelease( pdPage );

                                // Advertise that we changed the contents so the viewer redraws the
                                // page and other clients can re-acquire the page contents if needed.
                                PDPageNotifyContentsDidChange( pdPage );
                   }
                    PDERelease( (PDEObject) textContent);
                    PDERelease( (PDEObject) pdeContainer);
                               
                HANDLER
                                // Store the error code.
                               errorCode = ASGetExceptionErrorCode();

                END_HANDLER

                // Release any objects we may have created or acquired.
                // Note : PDERelease correctly handles NULL, so we don't
                // need to test for valid objects.
                PDERelease( (PDEObject) pdeText );
                PDERelease( (PDEObject) pdeColorSpace );
                PDERelease( (PDEObject) pdeFont );
                if (pdeExtGState) {
                                PDERelease((PDEObject) pdeExtGState);
                               pdeExtGState =  NULL;
               }


                if  (pdeContent)
                                PDPageReleasePDEContent( pdPage, 0 );

               
               
                // If we were thrown an error, let the user know.
                if  (errorCode) {
                                ASGetErrorString( ASGetExceptionErrorCode(), errorMsg, 256 );
               }

                return   true ; 
}



/* CalculateTextPosition
** ------------------------------------------------------
** calculate position of the text centered.
** IN: char* text, PDEFont font, int tSize, double theta,PDEGraphicState gState.
** OUT: int* px, int* py.
*/
static   void   CalculateTextPosition(  const   char * text,  PDEFont   font,
                                                                int  tSize,  double  theta,  PDEGraphicState  gState,
                                                                int * px,  int * py)
{
                // text matrix
                ASDoubleMatrix  textMatrix;
               memset( &textMatrix, 0,  sizeof (textMatrix) );

               textMatrix.a  = tSize;                                      // x scale
               textMatrix.b  = 0;                                                              // rotate & skew
               textMatrix.c  = 0;                                                              // rotate & skew
               textMatrix.d  = tSize;                                      // y scale
               textMatrix.h  = 0;                                                              // x translation
               textMatrix.v  = 0;                                                              // y translation

                // create a text
                PDEText  pdeText =  PDETextCreate();
                PDETextAddEx( pdeText,  kPDETextRun, 0, (Uns8  *)text, strlen( text ),
                                                               font, &gState,  sizeof (gState),  NULL, 0, &textMatrix,  NULL  );

                // get text bound box
                ASFixedRect  bbox;
                PDETextGetBBox( pdeText,  kPDETextRun, 0, &bbox );
                double  l =  ASFixedToFloat( bbox.left  );
                double  r =  ASFixedToFloat( bbox.right  );
                double  t =  ASFixedToFloat( bbox.top  );
                double  b =  ASFixedToFloat( bbox.bottom  );

                // calculation
        double  beta = atan2( t-b, r-l );
                double  alhpa = beta + theta;
                double  ll = sqrt( (t-b)*(t-b) + (r-l)*(r-l) );
               *px = int (0.5 * ll * cos( alhpa ) );
               *py = int (0.5 * ll * sin( alhpa ) );

                // clean
                PDERelease( (PDEObject) pdeText );
}