1
/* Copyright Krzysztof Kowalczyk 2006-2007
2
Copyright Hib Eris <hib@hiberis.nl> 2008
5
A tool to stress-test poppler rendering and measure rendering times for
6
very simplistic performance measuring.
9
* make it work with cairo output as well
10
* print more info about document like e.g. enumarate images,
11
streams, compression, encryption, password-protection. Each should have
12
a command-line arguments to turn it on/off
13
* never over-write file given as -out argument (optionally, provide -force
14
option to force writing the -out file). It's way too easy too lose results
19
// this sucks but I don't know any other way
20
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
29
// Define COPY_FILE if you want the file to be copied to a local disk first
30
// before it's tested. This is desired if a file is on a slow drive.
31
// Currently copying only works on Windows.
32
// Not enabled by default.
50
#include "ErrorCodes.h"
51
#include "goo/GooString.h"
52
#include "goo/GooList.h"
53
#include "goo/GooTimer.h"
54
#include "GlobalParams.h"
55
#include "splash/SplashBitmap.h"
56
#include "Object.h" /* must be included before SplashOutputDev.h because of sloppiness in SplashOutputDev.h */
57
#include "SplashOutputDev.h"
58
#include "TextOutputDev.h"
63
#define strdup _strdup
64
#define strcasecmp _stricmp
67
#define dimof(X) (sizeof(X)/sizeof((X)[0]))
69
#define INVALID_PAGE_NO -1
71
/* Those must be implemented in order to provide preview during execution.
72
They can be no-ops. An implementation for windows is in
73
perf-test-preview-win.cc
75
extern void PreviewBitmapInit(void);
76
extern void PreviewBitmapDestroy(void);
77
extern void PreviewBitmapSplash(SplashBitmap *bmpSplash);
79
class PdfEnginePoppler {
84
const char *fileName(void) const { return _fileName; };
86
void setFileName(const char *fileName) {
88
_fileName = (char*)strdup(fileName);
91
int pageCount(void) const { return _pageCount; }
93
bool load(const char *fileName);
94
SplashBitmap *renderBitmap(int pageNo, double zoomReal, int rotation);
96
SplashOutputDev * outputDevice();
102
SplashOutputDev * _outputDev;
105
typedef struct StrList {
106
struct StrList *next;
110
/* List of all command-line arguments that are not switches.
113
- names of a file with a list of PDF files
114
- names of directories with PDF files
116
static StrList *gArgsListRoot = NULL;
118
/* Names of all command-line switches we recognize */
119
#define TIMINGS_ARG "-timings"
120
#define RESOLUTION_ARG "-resolution"
121
#define RECURSIVE_ARG "-recursive"
122
#define OUT_ARG "-out"
123
#define PREVIEW_ARG "-preview"
124
#define SLOW_PREVIEW_ARG "-slowpreview"
125
#define LOAD_ONLY_ARG "-loadonly"
126
#define PAGE_ARG "-page"
127
#define TEXT_ARG "-text"
129
/* Should we record timings? True if -timings command-line argument was given. */
130
static bool gfTimings = false;
132
/* If true, we use render each page at resolution 'gResolutionX'/'gResolutionY'.
133
If false, we render each page at its native resolution.
134
True if -resolution NxM command-line argument was given. */
135
static bool gfForceResolution = false;
136
static int gResolutionX = 0;
137
static int gResolutionY = 0;
138
/* If NULL, we output the log info to stdout. If not NULL, should be a name
139
of the file to which we output log info.
140
Controled by -out command-line argument. */
141
static char * gOutFileName = NULL;
142
/* FILE * correspondig to gOutFileName or stdout if gOutFileName is NULL or
144
static FILE * gOutFile = NULL;
145
/* FILE * correspondig to gOutFileName or stderr if gOutFileName is NULL or
147
static FILE * gErrFile = NULL;
149
/* If True and a directory is given as a command-line argument, we'll process
150
pdf files in sub-directories as well.
151
Controlled by -recursive command-line argument */
152
static bool gfRecursive = false;
154
/* If true, preview rendered image. To make sure that they're being rendered correctly. */
155
static bool gfPreview = false;
157
/* 1 second (1000 milliseconds) */
158
#define SLOW_PREVIEW_TIME 1000
160
/* If true, preview rendered image in a slow mode i.e. delay displaying for
161
SLOW_PREVIEW_TIME. This is so that a human has enough time to see if the
162
PDF renders ok. In release mode on fast processor pages take only ~100-200 ms
163
to render and they go away too quickly to be inspected by a human. */
164
static bool gfSlowPreview = false;
166
/* If true, we only dump the text, not render */
167
static bool gfTextOnly = false;
169
#define PAGE_NO_NOT_GIVEN -1
171
/* If equals PAGE_NO_NOT_GIVEN, we're in default mode where we render all pages.
172
If different, will only render this page */
173
static int gPageNo = PAGE_NO_NOT_GIVEN;
174
/* If true, will only load the file, not render any pages. Mostly for
175
profiling load time */
176
static bool gfLoadOnly = false;
178
#define PDF_FILE_DPI 72
180
#define MAX_FILENAME_SIZE 1024
183
#define DOS_NEWLINE "\x0d\x0a"
184
/* Mac is single 0xd */
185
#define MAC_NEWLINE "\x0d"
186
/* Unix is single 0xa (10) */
187
#define UNIX_NEWLINE "\x0a"
188
#define UNIX_NEWLINE_C 0xa
191
#define DIR_SEP_CHAR '\\'
192
#define DIR_SEP_STR "\\"
194
#define DIR_SEP_CHAR '/'
195
#define DIR_SEP_STR "/"
198
void memzero(void *data, size_t len)
200
memset(data, 0, len);
203
void *zmalloc(size_t len)
205
void *data = malloc(len);
211
/* Concatenate 4 strings. Any string can be NULL.
212
Caller needs to free() memory. */
213
char *str_cat4(const char *str1, const char *str2, const char *str3, const char *str4)
223
str1_len = strlen(str1);
225
str2_len = strlen(str2);
227
str3_len = strlen(str3);
229
str4_len = strlen(str4);
231
str = (char*)zmalloc(str1_len + str2_len + str3_len + str4_len + 1);
237
memcpy(tmp, str1, str1_len);
241
memcpy(tmp, str2, str2_len);
245
memcpy(tmp, str3, str3_len);
249
memcpy(tmp, str4, str1_len);
254
char *str_dup(const char *str)
256
return str_cat4(str, NULL, NULL, NULL);
259
bool str_eq(const char *str1, const char *str2)
265
if (0 == strcmp(str1, str2))
270
bool str_ieq(const char *str1, const char *str2)
276
if (0 == strcasecmp(str1, str2))
281
bool str_endswith(const char *txt, const char *end)
289
txt_len = strlen(txt);
290
end_len = strlen(end);
291
if (end_len > txt_len)
293
if (str_eq(txt+txt_len-end_len, end))
298
/* TODO: probably should move to some other file and change name to
299
sleep_milliseconds */
300
void sleep_milliseconds(int milliseconds)
303
Sleep((DWORD)milliseconds);
307
secs = milliseconds / 1000;
308
nanosecs = (milliseconds - (secs * 1000)) * 1000;
309
tv.tv_sec = (time_t) secs;
310
tv.tv_nsec = (long) nanosecs;
313
int rval = nanosleep(&tv, &tv);
315
/* Completed the entire sleep time; all done. */
317
else if (errno == EINTR)
318
/* Interrupted by a signal. Try again. */
321
/* Some other error; bail out. */
329
void strcpy_s(char* dst, size_t dst_size, const char* src)
331
size_t src_size = strlen(src) + 1;
332
if (src_size <= dst_size)
333
memcpy(dst, src, src_size);
336
memcpy(dst, src, dst_size);
342
void strcat_s(char *dst, size_t dst_size, const char* src)
344
size_t dst_len = strlen(dst);
345
if (dst_len >= dst_size) {
350
strcpy_s(dst+dst_len, dst_size - dst_len, src);
354
static SplashColorMode gSplashColorMode = splashModeBGR8;
356
static SplashColor splashColRed;
357
static SplashColor splashColGreen;
358
static SplashColor splashColBlue;
359
static SplashColor splashColWhite;
360
static SplashColor splashColBlack;
362
#define SPLASH_COL_RED_PTR (SplashColorPtr)&(splashColRed[0])
363
#define SPLASH_COL_GREEN_PTR (SplashColorPtr)&(splashColGreen[0])
364
#define SPLASH_COL_BLUE_PTR (SplashColorPtr)&(splashColBlue[0])
365
#define SPLASH_COL_WHITE_PTR (SplashColorPtr)&(splashColWhite[0])
366
#define SPLASH_COL_BLACK_PTR (SplashColorPtr)&(splashColBlack[0])
368
static SplashColorPtr gBgColor = SPLASH_COL_WHITE_PTR;
370
static void splashColorSet(SplashColorPtr col, Guchar red, Guchar green, Guchar blue, Guchar alpha)
372
switch (gSplashColorMode)
390
void SplashColorsInit(void)
392
splashColorSet(SPLASH_COL_RED_PTR, 0xff, 0, 0, 0);
393
splashColorSet(SPLASH_COL_GREEN_PTR, 0, 0xff, 0, 0);
394
splashColorSet(SPLASH_COL_BLUE_PTR, 0, 0, 0xff, 0);
395
splashColorSet(SPLASH_COL_BLACK_PTR, 0, 0, 0, 0);
396
splashColorSet(SPLASH_COL_WHITE_PTR, 0xff, 0xff, 0xff, 0);
399
PdfEnginePoppler::PdfEnginePoppler() :
401
, _pageCount(INVALID_PAGE_NO)
407
PdfEnginePoppler::~PdfEnginePoppler()
414
bool PdfEnginePoppler::load(const char *fileName)
416
setFileName(fileName);
417
/* note: don't delete fileNameStr since PDFDoc takes ownership and deletes them itself */
418
GooString *fileNameStr = new GooString(fileName);
419
if (!fileNameStr) return false;
421
_pdfDoc = new PDFDoc(fileNameStr, NULL, NULL, (void*)NULL);
422
if (!_pdfDoc->isOk()) {
425
_pageCount = _pdfDoc->getNumPages();
429
SplashOutputDev * PdfEnginePoppler::outputDevice() {
431
GBool bitmapTopDown = gTrue;
432
_outputDev = new SplashOutputDev(gSplashColorMode, 4, gFalse, gBgColor, bitmapTopDown);
434
_outputDev->startDoc(_pdfDoc->getXRef());
439
SplashBitmap *PdfEnginePoppler::renderBitmap(int pageNo, double zoomReal, int rotation)
441
assert(outputDevice());
442
if (!outputDevice()) return NULL;
444
double hDPI = (double)PDF_FILE_DPI * zoomReal * 0.01;
445
double vDPI = (double)PDF_FILE_DPI * zoomReal * 0.01;
446
GBool useMediaBox = gFalse;
448
GBool doLinks = gTrue;
449
_pdfDoc->displayPage(_outputDev, pageNo, hDPI, vDPI, rotation, useMediaBox,
450
crop, doLinks, NULL, NULL);
452
SplashBitmap* bmp = _outputDev->takeBitmap();
456
struct FindFileState {
457
char path[MAX_FILENAME_SIZE];
458
char dirpath[MAX_FILENAME_SIZE]; /* current dir path */
459
char pattern[MAX_FILENAME_SIZE]; /* search pattern */
462
WIN32_FIND_DATA fileinfo;
471
#include <sys/timeb.h>
474
__inline char *getcwd(char *buffer, int maxlen)
476
return _getcwd(buffer, maxlen);
479
int fnmatch(const char *pattern, const char *string, int flags)
482
const char *star_pos = strchr(pattern, '*');
484
return strcmp(pattern, string) != 0;
486
prefix_len = (int)(star_pos-pattern);
490
if (0 == _strnicmp(pattern, string, prefix_len))
501
/* on windows to query dirs we need foo\* to get files in this directory.
502
foo\ always fails and foo will return just info about foo directory,
503
not files in this directory */
504
static void win_correct_path_for_FindFirstFile(char *path, int path_max_len)
506
int path_len = strlen(path);
507
if (path_len >= path_max_len-4)
509
if (DIR_SEP_CHAR != path[path_len])
510
path[path_len++] = DIR_SEP_CHAR;
511
path[path_len++] = '*';
516
FindFileState *find_file_open(const char *path, const char *pattern)
520
s = (FindFileState*)malloc(sizeof(FindFileState));
523
strcpy_s(s->path, sizeof(s->path), path);
524
strcpy_s(s->dirpath, sizeof(s->path), path);
526
win_correct_path_for_FindFirstFile(s->path, sizeof(s->path));
528
strcpy_s(s->pattern, sizeof(s->pattern), pattern);
531
s->dir = INVALID_HANDLE_VALUE;
538
#if 0 /* re-enable if we #define USE_OWN_GET_AUTH_DATA */
539
void *StandardSecurityHandler::getAuthData()
545
char *makepath(char *buf, int buf_size, const char *path,
546
const char *filename)
548
strcpy_s(buf, buf_size, path);
549
int len = strlen(path);
550
if (len > 0 && path[len - 1] != DIR_SEP_CHAR && len + 1 < buf_size) {
551
buf[len++] = DIR_SEP_CHAR;
554
strcat_s(buf, buf_size, filename);
559
static int skip_matching_file(const char *filename)
561
if (0 == strcmp(".", filename))
563
if (0 == strcmp("..", filename))
569
int find_file_next(FindFileState *s, char *filename, int filename_size_max)
573
if (INVALID_HANDLE_VALUE == s->dir) {
574
s->dir = FindFirstFile(s->path, &(s->fileinfo));
575
if (INVALID_HANDLE_VALUE == s->dir)
581
fFound = FindNextFile(s->dir, &(s->fileinfo));
585
if (skip_matching_file(s->fileinfo.cFileName))
587
if (0 == fnmatch(s->pattern, s->fileinfo.cFileName, 0) ) {
588
makepath(filename, filename_size_max, s->dirpath, s->fileinfo.cFileName);
593
struct dirent *dirent;
601
dirent = readdir(s->dir);
602
if (dirent == NULL) {
611
/* CG: get_str(&p, s->dirpath, sizeof(s->dirpath), ":") */
613
while (*p != ':' && *p != '\0') {
614
if ((q - s->dirpath) < (int)sizeof(s->dirpath) - 1)
622
s->dir = opendir(s->dirpath);
626
if (fnmatch(s->pattern, dirent->d_name, 0) == 0) {
627
makepath(filename, filename_size_max,
628
s->dirpath, dirent->d_name);
636
void find_file_close(FindFileState *s)
639
if (INVALID_HANDLE_VALUE != s->dir)
648
int StrList_Len(StrList **root)
663
int StrList_InsertAndOwn(StrList **root, char *txt)
670
el = (StrList*)malloc(sizeof(StrList));
679
int StrList_Insert(StrList **root, char *txt)
686
txtDup = str_dup(txt);
690
if (!StrList_InsertAndOwn(root, txtDup)) {
697
StrList* StrList_RemoveHead(StrList **root)
712
void StrList_FreeElement(StrList *el)
716
free((void*)el->str);
720
void StrList_Destroy(StrList **root)
730
StrList_FreeElement(cur);
737
void OutputDebugString(const char *txt)
741
#define _snprintf snprintf
742
#define _vsnprintf vsnprintf
745
void my_error(int pos, char *msg, va_list args) {
747
char buf[4096], *p = buf;
749
// NB: this can be called before the globalParams object is created
750
if (globalParams && globalParams->getErrQuiet()) {
755
p += _snprintf(p, sizeof(buf)-1, "Error (%d): ", pos);
757
OutputDebugString(p);
759
OutputDebugString("Error: ");
763
p += _vsnprintf(p, sizeof(buf) - 1, msg, args);
764
while ( p > buf && isspace(p[-1]) )
769
OutputDebugString(buf);
772
p += _snprintf(p, sizeof(buf)-1, "Error (%d): ", pos);
774
OutputDebugString(buf);
776
fprintf(gErrFile, buf);
778
OutputDebugString("Error: ");
780
fprintf(gErrFile, "Error: ");
786
p += _vsnprintf(p, sizeof(buf) - 3, msg, args);
787
while ( p > buf && isspace(p[-1]) )
792
OutputDebugString(buf);
794
fprintf(gErrFile, buf);
799
void LogInfo(char *fmt, ...)
802
char buf[4096], *p = buf;
806
p += _vsnprintf(p, sizeof(buf) - 1, fmt, args);
808
fprintf(gOutFile, "%s", buf);
813
static void PrintUsageAndExit(int argc, char **argv)
815
printf("Usage: pdftest [-preview|-slowpreview] [-loadonly] [-timings] [-text] [-resolution NxM] [-recursive] [-page N] [-out out.txt] pdf-files-to-process\n");
816
for (int i=0; i < argc; i++) {
817
printf("i=%d, '%s'\n", i, argv[i]);
822
static bool ShowPreview(void)
824
if (gfPreview || gfSlowPreview)
829
static void RenderPdfAsText(const char *fileName)
831
GooString * fileNameStr = NULL;
832
PDFDoc * pdfDoc = NULL;
833
GooString * txt = NULL;
841
LogInfo("started: %s\n", fileName);
843
TextOutputDev * textOut = new TextOutputDev(NULL, gTrue, gFalse, gFalse);
844
if (!textOut->isOk()) {
850
/* note: don't delete fileNameStr since PDFDoc takes ownership and deletes them itself */
851
fileNameStr = new GooString(fileName);
855
pdfDoc = new PDFDoc(fileNameStr, NULL, NULL, NULL);
856
if (!pdfDoc->isOk()) {
857
error(-1, "RenderPdfFile(): failed to open PDF file %s\n", fileName);
862
timeInMs = msTimer.getElapsed();
863
LogInfo("load: %.2f ms\n", timeInMs);
865
pageCount = pdfDoc->getNumPages();
866
LogInfo("page count: %d\n", pageCount);
868
for (int curPage = 1; curPage <= pageCount; curPage++) {
869
if ((gPageNo != PAGE_NO_NOT_GIVEN) && (gPageNo != curPage))
874
GBool useMediaBox = gFalse;
876
GBool doLinks = gFalse;
877
pdfDoc->displayPage(textOut, curPage, 72, 72, rotate, useMediaBox, crop, doLinks);
878
txt = textOut->getText(0.0, 0.0, 10000.0, 10000.0);
880
timeInMs = msTimer.getElapsed();
882
LogInfo("page %d: %.2f ms\n", curPage, timeInMs);
883
printf("%s\n", txt->getCString());
889
LogInfo("finished: %s\n", fileName);
895
#define POPPLER_TMP_NAME "c:\\poppler_tmp.pdf"
897
#define POPPLER_TMP_NAME "/tmp/poppler_tmp.pdf"
900
static void RenderPdf(const char *fileName)
902
const char * fileNameSplash = NULL;
903
PdfEnginePoppler * engineSplash = NULL;
908
// TODO: fails if file already exists and has read-only attribute
909
CopyFile(fileName, POPPLER_TMP_NAME, false);
910
fileNameSplash = POPPLER_TMP_NAME;
912
fileNameSplash = fileName;
914
LogInfo("started: %s\n", fileName);
916
engineSplash = new PdfEnginePoppler();
919
if (!engineSplash->load(fileNameSplash)) {
920
LogInfo("failed to load splash\n");
924
timeInMs = msTimer.getElapsed();
925
LogInfo("load splash: %.2f ms\n", timeInMs);
926
pageCount = engineSplash->pageCount();
928
LogInfo("page count: %d\n", pageCount);
932
for (int curPage = 1; curPage <= pageCount; curPage++) {
933
if ((gPageNo != PAGE_NO_NOT_GIVEN) && (gPageNo != curPage))
936
SplashBitmap *bmpSplash = NULL;
939
bmpSplash = engineSplash->renderBitmap(curPage, 100.0, 0);
941
double timeInMs = msTimer.getElapsed();
944
LogInfo("page splash %d: failed to render\n", curPage);
946
LogInfo("page splash %d (%dx%d): %.2f ms\n", curPage, bmpSplash->getWidth(), bmpSplash->getHeight(), timeInMs);
950
PreviewBitmapSplash(bmpSplash);
952
sleep_milliseconds(SLOW_PREVIEW_TIME);
958
LogInfo("finished: %s\n", fileName);
961
static void RenderFile(const char *fileName)
964
RenderPdfAsText(fileName);
971
static bool ParseInteger(const char *start, const char *end, int *intOut)
977
assert(start && end && intOut);
978
assert(end >= start);
979
if (!start || !end || !intOut || (start > end))
986
/* do nothing, we allow whitespace */
987
} else if (!isdigit(*tmp))
989
numBuf[digitsCount] = *tmp;
991
if (digitsCount == dimof(numBuf)-3) /* -3 to be safe */
995
if (0 == digitsCount)
997
numBuf[digitsCount] = 0;
998
*intOut = atoi(numBuf);
1002
/* Given 'resolutionString' in format NxM (e.g. "100x200"), parse the string and put N
1003
into 'resolutionXOut' and M into 'resolutionYOut'.
1004
Return false if there was an error (e.g. string is not in the right format */
1005
static bool ParseResolutionString(const char *resolutionString, int *resolutionXOut, int *resolutionYOut)
1007
const char * posOfX;
1009
assert(resolutionString);
1010
assert(resolutionXOut);
1011
assert(resolutionYOut);
1012
if (!resolutionString || !resolutionXOut || !resolutionYOut)
1014
*resolutionXOut = 0;
1015
*resolutionYOut = 0;
1016
posOfX = strchr(resolutionString, 'X');
1018
posOfX = strchr(resolutionString, 'x');
1021
if (posOfX == resolutionString)
1023
if (!ParseInteger(resolutionString, posOfX-1, resolutionXOut))
1025
if (!ParseInteger(posOfX+1, resolutionString+strlen(resolutionString)-1, resolutionYOut))
1030
static void ParseCommandLine(int argc, char **argv)
1035
PrintUsageAndExit(argc, argv);
1037
for (int i=1; i < argc; i++) {
1040
if ('-' == arg[0]) {
1041
if (str_ieq(arg, TIMINGS_ARG)) {
1043
} else if (str_ieq(arg, RESOLUTION_ARG)) {
1046
PrintUsageAndExit(argc, argv); /* expect a file name after that */
1047
if (!ParseResolutionString(argv[i], &gResolutionX, &gResolutionY))
1048
PrintUsageAndExit(argc, argv);
1049
gfForceResolution = true;
1050
} else if (str_ieq(arg, RECURSIVE_ARG)) {
1052
} else if (str_ieq(arg, OUT_ARG)) {
1053
/* expect a file name after that */
1056
PrintUsageAndExit(argc, argv);
1057
gOutFileName = str_dup(argv[i]);
1058
} else if (str_ieq(arg, PREVIEW_ARG)) {
1060
} else if (str_ieq(arg, TEXT_ARG)) {
1062
} else if (str_ieq(arg, SLOW_PREVIEW_ARG)) {
1063
gfSlowPreview = true;
1064
} else if (str_ieq(arg, LOAD_ONLY_ARG)) {
1066
} else if (str_ieq(arg, PAGE_ARG)) {
1067
/* expect an integer after that */
1070
PrintUsageAndExit(argc, argv);
1071
gPageNo = atoi(argv[i]);
1073
PrintUsageAndExit(argc, argv);
1075
/* unknown option */
1076
PrintUsageAndExit(argc, argv);
1079
/* we assume that this is not an option hence it must be
1080
a name of PDF/directory/file with PDF names */
1081
StrList_Insert(&gArgsListRoot, arg);
1087
void RenderFileList(char *pdfFileList)
1090
char *dataNormalized = NULL;
1094
assert(pdfFileList);
1097
data = file_read_all(pdfFileList, &fileSize);
1099
error(-1, "couldn't load file '%s'", pdfFileList);
1102
dataNormalized = str_normalize_newline(data, UNIX_NEWLINE);
1103
if (!dataNormalized) {
1104
error(-1, "couldn't normalize data of file '%s'", pdfFileList);
1108
pdfFileName = str_split_iter(&dataNormalized, UNIX_NEWLINE_C);
1111
str_strip_ws_both(pdfFileName);
1112
if (str_empty(pdfFileName)) {
1113
free((void*)pdfFileName);
1116
RenderFile(pdfFileName);
1117
free((void*)pdfFileName);
1120
free((void*)dataNormalized);
1126
#include <sys/types.h>
1127
#include <sys/stat.h>
1129
bool IsDirectoryName(char *path)
1134
result = _stat(path, &buf );
1138
if (buf.st_mode & _S_IFDIR)
1144
bool IsFileName(char *path)
1149
result = _stat(path, &buf );
1153
if (buf.st_mode & _S_IFREG)
1159
bool IsDirectoryName(char *path)
1161
/* TODO: implement me */
1165
bool IsFileName(char *path)
1167
/* TODO: implement me */
1172
bool IsPdfFileName(char *path)
1174
if (str_endswith(path, ".pdf"))
1179
static void RenderDirectory(char *path)
1181
FindFileState * ffs;
1182
char filename[MAX_FILENAME_SIZE];
1183
StrList * dirList = NULL;
1186
StrList_Insert(&dirList, path);
1188
while (0 != StrList_Len(&dirList)) {
1189
el = StrList_RemoveHead(&dirList);
1190
ffs = find_file_open(el->str, "*");
1191
while (!find_file_next(ffs, filename, sizeof(filename))) {
1192
if (IsDirectoryName(filename)) {
1194
StrList_Insert(&dirList, filename);
1196
} else if (IsFileName(filename)) {
1197
if (IsPdfFileName(filename)) {
1198
RenderFile(filename);
1202
find_file_close(ffs);
1203
StrList_FreeElement(el);
1205
StrList_Destroy(&dirList);
1208
/* Render 'cmdLineArg', which can be:
1211
- name of text file with names of PDF files
1213
static void RenderCmdLineArg(char *cmdLineArg)
1218
if (IsDirectoryName(cmdLineArg)) {
1219
RenderDirectory(cmdLineArg);
1220
} else if (IsFileName(cmdLineArg)) {
1221
if (IsPdfFileName(cmdLineArg))
1222
RenderFile(cmdLineArg);
1225
RenderFileList(cmdLineArg);
1228
error(-1, "unexpected argument '%s'", cmdLineArg);
1232
int main(int argc, char **argv)
1234
setErrorFunction(my_error);
1235
ParseCommandLine(argc, argv);
1236
if (0 == StrList_Len(&gArgsListRoot))
1237
PrintUsageAndExit(argc, argv);
1238
assert(gArgsListRoot);
1241
globalParams = new GlobalParams();
1244
globalParams->setErrQuiet(gFalse);
1245
globalParams->setBaseDir("");
1247
FILE * outFile = NULL;
1249
outFile = fopen(gOutFileName, "wb");
1251
printf("failed to open -out file %s\n", gOutFileName);
1264
PreviewBitmapInit();
1266
StrList * curr = gArgsListRoot;
1268
RenderCmdLineArg(curr->str);
1273
PreviewBitmapDestroy();
1274
StrList_Destroy(&gArgsListRoot);
1275
delete globalParams;