/*
//
// 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.
//
// Project: - In-memory file system used in a multithreaded workflow.
//
// Demonstrates usage of in-memory file system for a simple workflow
// in a multithreaded context.
//
// The sample shows multiple threads creating PDF files simultaneously. The
// individual PDF files are then merged into one and written to a disk file
// by the main thread.
//
// PDFL Init and Term must be performed for each thread
//
// Steps:
//
// MainProc:
// * Initialize PDF Library
// * Read in text from an ASCII file as the source for PDF file creation
// * Spawn a few threads and pass each a chunk of text for one-page PDF creation
// * When all threads are done with creation of PDF files, merge them into one and write to disk
// * Terminate PDF Library
//
// ThreadProc:
// * Initialize PDF Library
// * Create a new doc with one-page content from the text passed in
// * Terminate PDF Library
//
// Note:
// Use of the input file "input.txt" in the "ExampleFiles" folder of this sample is required.
//
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sstream>
#ifdef WIN32
#include <direct.h>
#include <process.h>
#endif
#include "PDFInit.h"
#include "
PERCalls.h"
#include "
PEWCalls.h"
#include "PSFCalls.h"
#include "PagePDECntCalls.h"
#include "MyPDFLibUtils.h"
#include "Threads.h"
#ifdef MAC_PLATFORM
#include "macUtils.h"
#endif
const int sizeThreadPool = 3; // maximum number of simultaneous worker threads
const int numJobs = 10; // number of jobs for worker threads
const int fontSize = 12; // point size of font used for content text
const float indentation = 0.75; // used to roughly control word spacing
const int charsPerLine = 55; // make sure words don't run over page margin
const int maxLineSize = 3072; // text buffer size per line read from input file
// data structure stroing per-job information for use by worker threads
typedef struct {
unsigned JobID;
char text[maxLineSize];
} JobData;
// forward declaration of functions
void MainProc( void );
ThreadFuncReturnType ThreadProc(ThreadFuncArgType threadParams );
void CreatePDFProc( JobData* JobData );
void MergeDocsProc( JobData* JobData );
void AddText(
PDEContent pdeContent, char * text,
ASSize_t x,
ASSize_t y);
char inputFile[256], outputPath[256];
void MainProc( void )
{
JobData JobData[numJobs];
ThreadHandle threadHandles[sizeThreadPool] = {0};
ThreadHandle retVal;
FILE *sourceFile =
NULL;
int taskID;
#ifdef WIN32
DWORD startTime, elapsedTime;
#else
time_t startTime, elapsedTime;
#endif
// Open up the source text file
if ((sourceFile = fopen(inputFile, "r" )) ==
NULL)
{
printf( "Failed to open source file %s.\n" , inputFile);
return ;
}
// Read in text from source file into buffer to be passed to threads as content text
for ( int i=0; i<numJobs; i++)
{
JobData[i].JobID = i;
if (fgets(JobData[i].text, maxLineSize, sourceFile) ==
NULL)
{
if (feof(sourceFile) != 0)
printf( "End-Of-File reached.\n" );
else {
printf( "[fgets]:Error reading from sourec file.\n" );
return ;
}
}
}
#ifdef WIN32
startTime = GetTickCount();
#else
time(&startTime);
#endif
taskID = 0;
srand(( unsigned )time(
NULL)*1000);
// limit number of active worker threads
for ( int j=0; j<numJobs; j++)
{
// wait for a thread to complete if the threads in the pool are used up
if (j >= sizeThreadPool)
{
int myId;
myId = rand()%3;
if (waitThread(threadHandles[myId]))
destroyThread(threadHandles[myId]);
taskID = myId;
printf( "Thread%d terminated.\n" , taskID);
}
// Spawn sizeThreadPool number of threads, passing each the chunk of text to create PDF docs with
if (createThread(ThreadProc, ( void *) &JobData[j], &retVal))
printf( "Thread%d created successfully.\n" , taskID);
if (taskID < sizeThreadPool)
threadHandles[taskID] = retVal;
taskID++;
}
printf( "\nWaiting on threads to terminate..\n" );
int m;
for (m=0; m<sizeThreadPool; m++)
{
if (waitThread(threadHandles[m]))
destroyThread(threadHandles[m]);
else
break ;
}
if (m == sizeThreadPool)
printf( "\nAll threads terminated successfully.\n" );
fclose(sourceFile);
// use memory-based temporary file
ASFileSys memFileSys =
ASGetRamFileSys();
ASSetTempFileSys(memFileSys);
// merge individual PDFs
MergeDocsProc(JobData);
#ifdef WIN32
elapsedTime = GetTickCount() - startTime;
printf( "\nWorkflow turnaround time: %d.%03d seconds\n\n" , elapsedTime/1000, elapsedTime%1000);
#else
elapsedTime = time(
NULL) - startTime;
printf( "\nWorkflow turnaround time: %ld seconds\n\n" , elapsedTime);
#endif
}
ThreadFuncReturnType ThreadProc( ThreadFuncArgType threadParams )
{
INIT_AUTO_POOL(autoReleasePool); /* Required only on MAC platform */
JobData* threadInfo;
int err;
if ( threadParams ==
NULL )
#ifdef WIN32
return 1;
#else
return
NULL;
#endif
threadInfo = (JobData*) threadParams;
err = MyPDFLInit(); // initialize the PDFLib
if (err != 0) // check for error after initialization
{
fprintf(stderr, "Initialization error. See \"AcroErr.h\" for more info.\n" );
fprintf(stderr, "Error system: %d\nError Severity: %d\nError Code: %d\n" ,
ErrGetSystem(err),
ErrGetSeverity(err),
ErrGetCode(err));
#ifdef WIN32
return 1;
#else
return
NULL;
#endif
}
// Use in-memory file system for temporary files
ASFileSys memFileSys =
ASGetRamFileSys();
ASSetTempFileSys(memFileSys);
CreatePDFProc( threadInfo );
MyPDFLTerm();
RELEASE_AUTO_POOL(autoReleasePool); /* Required only on MAC platform */
return 0;
}
void CreatePDFProc( JobData* JobData )
{
ASFixedRect mediaBox; // dimensions of page
PDDoc pdDoc; // reference to a PDF document
PDPage pdPage; // reference to a page in doc
PDEContent pdeContent; // container for page content
char * str; // reference to each word read
char buf[256] = "" ;
char outFile[256] = "" ; // output file name
char * strPtr =
NULL;
ASSize_t charCount, xPos, yPos; // character count per line and location to add text on page
PDDocSaveParamsRec saveParams;
memset(&saveParams, 0, sizeof (
PDDocSaveParamsRec));
DURING
pdDoc =
PDDocCreate(); // create new document
mediaBox.
left =
fixedZero; // dimensions of page
mediaBox.
top =
Int16ToFixed(11*72); // letter-size
mediaBox.
right =
Int16ToFixed(8.5*72);
mediaBox.
bottom =
fixedZero;
// create page with those dimensions and insert as first page
pdPage =
PDDocCreatePage(pdDoc,
PDBeforeFirstPage, mediaBox);
// Acquire PDEContent container for page
pdeContent = PDPageAcquirePDEContent(pdPage,
NULL);
strPtr = JobData->text;
charCount = xPos = yPos = 0;
std::stringstream stream(strPtr);
std::string token;
// Read one word at a time to add to the content object
while (stream>>token)
{
str = ( char *) token.c_str();
AddText(pdeContent, str, xPos, yPos );
// estimate text str position on a page
strPtr += strlen(str) + 1;
charCount += strlen(str) + 1;
// add roughly charsPerLine characters on a line
if (charCount >= charsPerLine)
{
charCount = 0;
xPos = 0;
yPos++;
}
else
xPos += strlen(str) + 1;
}
// set the PDEContent for the page
PDPageSetPDEContent(pdPage,
NULL);
// save document to a file
sprintf_safe( outFile, sizeof (outFile), "%sout_%d.pdf" , outputPath, JobData->JobID );
printf( "Job %d: Saving PDDoc as %s\n" , JobData->JobID, outFile );
saveParams.
size = sizeof (
PDDocSaveParamsRec);
saveParams.
saveFlags =
PDSaveFull |
PDSaveCollectGarbage;
#ifdef MAC_PLATFORM
saveParams.
newPath = GetMacPath(outFile);
#else
saveParams.
newPath =
ASFileSysCreatePathName (
ASGetDefaultFileSys(),
ASAtomFromString( "Cstring" ), outFile,
NULL);
#endif
saveParams.
fileSys =
ASGetDefaultFileSys();
PDDocSaveWithParams(pdDoc, &saveParams);
HANDLER
ASGetErrorString(
ERRORCODE, buf, sizeof (buf));
fprintf(stderr, "Error code: %d, Error Message: %s\n" ,
ERRORCODE, buf);
END_HANDLER
// remember to release all objects that were created
if (pdPage)
{
PDPageReleasePDEContent(pdPage,
NULL);
PDPageRelease(pdPage);
}
if (pdDoc)
PDDocClose(pdDoc);
if (saveParams.
newPath)
ASFileSysReleasePath(saveParams.
fileSys, saveParams.
newPath);
}
void MergeDocsProc(JobData* JobData)
{
char buf[256], inFile[128], outFile[128];
PDDoc inPDDoc, outPDDoc;
PDDocOpenParamsRec openParams;
PDDocSaveParamsRec saveParams;
memset(&openParams, 0, sizeof (
PDDocOpenParamsRec));
memset(&saveParams, 0, sizeof (
PDDocSaveParamsRec));
outPDDoc =
PDDocCreate();
DURING
// Insert first page of each document into the output document
for ( int i=0; i<numJobs; i++)
{
sprintf_safe(inFile, sizeof (inFile), "%sout_%d.pdf" , outputPath, JobData[i].JobID);
memset(&openParams, 0, sizeof (
PDDocOpenParamsRec));
openParams.
size = sizeof (
PDDocOpenParamsRec);
#ifdef MAC_PLATFORM
openParams.
fileName = GetMacPath(inFile);
#else
openParams.
fileName =
ASFileSysCreatePathName (
ASGetDefaultFileSys(),
ASAtomFromString( "Cstring" ), inFile,
NULL);
#endif
openParams.
fileSys =
ASGetDefaultFileSys();
inPDDoc =
PDDocOpenWithParams(&openParams);
PDDocInsertPages(outPDDoc,
PDLastPage,
inPDDoc, 0, 1,
PDInsertAll,
0, 0, 0, 0
);
PDDocClose(inPDDoc);
ASFileSysReleasePath(openParams.
fileSys, openParams.
fileName);
inPDDoc =
NULL;
openParams.
fileName =
NULL;
}
// save the merged document
saveParams.
size = sizeof (
PDDocSaveParamsRec);
saveParams.
saveFlags =
PDSaveFull |
PDSaveCollectGarbage;
sprintf_safe(outFile, sizeof (outFile), "%sMerged.pdf" , outputPath);
printf( "Output file=%s\n" , outFile);
#ifdef MAC_PLATFORM
saveParams.
newPath = GetMacPath(outFile);
#else
saveParams.
newPath =
ASFileSysCreatePathName (
ASGetDefaultFileSys(),
ASAtomFromString( "Cstring" ), outFile,
NULL);
#endif
saveParams.
fileSys =
ASGetDefaultFileSys();
PDDocSaveWithParams(outPDDoc, &saveParams);
HANDLER
ASGetErrorString(
ERRORCODE, buf, sizeof (buf));
fprintf(stderr, "Error code: %d, Error Message: %s\n" ,
ERRORCODE, buf);
END_HANDLER
if (outPDDoc)
PDDocClose(outPDDoc);
if (saveParams.
newPath)
ASFileSysReleasePath(saveParams.
fileSys, saveParams.
newPath);
if (inPDDoc)
PDDocClose(inPDDoc);
if (openParams.
fileName)
ASFileSysReleasePath(openParams.
fileSys, openParams.
fileName);
}
void AddText(
PDEContent pdeContent, char * text,
ASSize_t x,
ASSize_t y)
{
PDEFont pdeFont; // reference to a font used on a page
PDEFontAttrs pdeFontAttrs; // font attributes
PDEText pdeText; // container for text
ASDoubleMatrix textMatrix; // transformation matrix for text
PDEColorSpace pdeColorSpace; // ColorSpace
PDEGraphicState gState; // graphic state to apply to operation
//
// Acquire Font, Add Text, Insert Into Page Content Container
//
memset(&pdeFontAttrs, 0, sizeof (pdeFontAttrs));
pdeFontAttrs.
name =
ASAtomFromString( "CourierStd" );
pdeFontAttrs.
type =
ASAtomFromString( "Type1" );
PDSysFont sysFont;
sysFont =
PDFindSysFont(&pdeFontAttrs, sizeof (
PDEFontAttrs), 0);
PDSysFontGetAttrs(sysFont, &pdeFontAttrs, sizeof (pdeFontAttrs));
pdeFont =
PDEFontCreateFromSysFont(sysFont,
kPDEFontDoNotEmbed);
//
// The following code sets up the default Graphics state.
// We do this so that we can free the PDEColorSpace objects
//
pdeColorSpace =
PDEColorSpaceCreateFromName(
ASAtomFromString( "DeviceGray" ));
memset(&gState, 0, sizeof (
PDEGraphicState));
gState.
strokeColorSpec.
space = gState.
fillColorSpec.
space = pdeColorSpace;
gState.
miterLimit =
fixedTen;
gState.
flatness =
fixedOne;
gState.
lineWidth =
fixedOne;
memset(&textMatrix, 0, sizeof (textMatrix)); // clear structure
textMatrix.
a = fontSize; // set font width and height
textMatrix.
d = fontSize; // to 12 point size
textMatrix.
h = indentation*72 + x*8; // x,y coordinate on page
textMatrix.
v = 11*72 - indentation*72 - y*15;
pdeText =
PDETextCreate(); // create new text run
PDETextAddEx(pdeText, // text container to add to
kPDETextRun, // kPDETextRun, kPDETextChar
0, // index
(
Uns8 *)text, // text to add
strlen(text), // length of text
pdeFont, // font to apply to text
&gState, sizeof (gState), // graphic state to apply to text
NULL, 0, // text state and size of structure
&textMatrix, // transformation matrix for text
NULL); // stroke matrix
// insert text into page content
PDEContentAddElem(pdeContent,
kPDEAfterLast, (
PDEElement) pdeText);
PDERelease((
PDEObject) pdeColorSpace);
PDERelease((
PDEObject) pdeFont);
PDERelease((
PDEObject) pdeText);
}
int main( int argc, char *argv[], char *envp[])
{
if (argc != 3)
{
printf( "Usage: %s InputFile OutputPath\n" , argv[0]);
printf( "Use absolute paths for input file and output path.\n" );
printf( "Use of the input file \"input.txt\" in the \"ExampleFiles\" folder of this sample is required.\n" );
return 0;
}
strcpy_safe(inputFile, sizeof (inputFile), argv[1]);
strcpy_safe(outputPath, sizeof (outputPath), argv[2]);
#ifdef WIN32
//An issue was found out on Windows
//that an output path of "someoutpath/" ( as user input ) becomes someoutpath".
//So, we'll check if outputPath is ended with a ", and correct it if so.
ASSize_t l=strlen(outputPath);
if (outputPath[l-1]== '\"' )
outputPath[l-1]= '\\' ;
// add a / to the end of outputPath if there is not one.
if (outputPath[l-1]!= '\\' && outputPath[l-1]!= '/' ) {
outputPath[l]= '/' ;
outputPath[l+1]=
NULL;
}
#endif
INIT_AUTO_POOL(autoReleasePool); /* Required only on MAC platform */
int err = MyPDFLInit(); /* initialize the PDFLib */
if (err != 0) /* check for error after initialization */
{
fprintf(stderr, "Initialization error. See \"AcroErr.h\" for more info.\n" );
fprintf(stderr, "Error system: %d\nError Severity: %d\nError Code: %d\n" ,
ErrGetSystem(err),
ErrGetSeverity(err),
ErrGetCode(err));
}
else
{
MainProc();
MyPDFLTerm(); /* terminate the PDFLib */
}
RELEASE_AUTO_POOL(autoReleasePool); /* Required only on MAC platform */
return 0;
}