~ubuntu-branches/ubuntu/vivid/emscripten/vivid

« back to all changes in this revision

Viewing changes to tests/poppler/test/perf-test.cc

  • Committer: Package Import Robot
  • Author(s): Sylvestre Ledru
  • Date: 2013-05-02 13:11:51 UTC
  • Revision ID: package-import@ubuntu.com-20130502131151-q8dvteqr1ef2x7xz
Tags: upstream-1.4.1~20130504~adb56cb
ImportĀ upstreamĀ versionĀ 1.4.1~20130504~adb56cb

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright Krzysztof Kowalczyk 2006-2007
 
2
   Copyright Hib Eris <hib@hiberis.nl> 2008
 
3
   License: GPLv2 */
 
4
/*
 
5
  A tool to stress-test poppler rendering and measure rendering times for
 
6
  very simplistic performance measuring.
 
7
 
 
8
  TODO:
 
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
 
15
     of a previous run.
 
16
*/
 
17
 
 
18
#ifdef _MSC_VER
 
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='*'\"")
 
21
#endif
 
22
 
 
23
#ifdef _WIN32
 
24
#include <windows.h>
 
25
#else
 
26
#include <strings.h>
 
27
#endif
 
28
 
 
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.
 
33
//#define COPY_FILE 1
 
34
 
 
35
#include <assert.h>
 
36
#include <config.h>
 
37
#include <stdio.h>
 
38
#include <stdarg.h>
 
39
#include <ctype.h>
 
40
#include <stdlib.h>
 
41
#include <string.h>
 
42
#include <errno.h>
 
43
#include <time.h>
 
44
 
 
45
#ifdef HAVE_DIRENT_H
 
46
#include <dirent.h>
 
47
#endif
 
48
 
 
49
#include "Error.h"
 
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"
 
59
#include "PDFDoc.h"
 
60
#include "Link.h"
 
61
 
 
62
#ifdef _MSC_VER
 
63
#define strdup _strdup
 
64
#define strcasecmp _stricmp
 
65
#endif
 
66
 
 
67
#define dimof(X)    (sizeof(X)/sizeof((X)[0]))
 
68
 
 
69
#define INVALID_PAGE_NO     -1
 
70
 
 
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
 
74
*/
 
75
extern void PreviewBitmapInit(void);
 
76
extern void PreviewBitmapDestroy(void);
 
77
extern void PreviewBitmapSplash(SplashBitmap *bmpSplash);
 
78
 
 
79
class PdfEnginePoppler {
 
80
public:
 
81
    PdfEnginePoppler();
 
82
    ~PdfEnginePoppler();
 
83
 
 
84
    const char *fileName(void) const { return _fileName; };
 
85
 
 
86
    void setFileName(const char *fileName) {
 
87
        assert(!_fileName);
 
88
        _fileName = (char*)strdup(fileName);
 
89
    }
 
90
 
 
91
    int pageCount(void) const { return _pageCount; }
 
92
 
 
93
    bool load(const char *fileName);
 
94
    SplashBitmap *renderBitmap(int pageNo, double zoomReal, int rotation);
 
95
 
 
96
    SplashOutputDev *   outputDevice();
 
97
private:
 
98
    char *              _fileName;
 
99
    int                 _pageCount;
 
100
 
 
101
    PDFDoc *            _pdfDoc;
 
102
    SplashOutputDev *   _outputDev;
 
103
};
 
104
 
 
105
typedef struct StrList {
 
106
    struct StrList *next;
 
107
    char *          str;
 
108
} StrList;
 
109
 
 
110
/* List of all command-line arguments that are not switches.
 
111
   We assume those are:
 
112
     - names of PDF files
 
113
     - names of a file with a list of PDF files
 
114
     - names of directories with PDF files
 
115
*/
 
116
static StrList *gArgsListRoot = NULL;
 
117
 
 
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"
 
128
 
 
129
/* Should we record timings? True if -timings command-line argument was given. */
 
130
static bool gfTimings = false;
 
131
 
 
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
 
143
   was invalid name */
 
144
static FILE *   gOutFile = NULL;
 
145
/* FILE * correspondig to gOutFileName or stderr if gOutFileName is NULL or
 
146
   was invalid name */
 
147
static FILE *   gErrFile = NULL;
 
148
 
 
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;
 
153
 
 
154
/* If true, preview rendered image. To make sure that they're being rendered correctly. */
 
155
static bool gfPreview = false;
 
156
 
 
157
/* 1 second (1000 milliseconds) */
 
158
#define SLOW_PREVIEW_TIME 1000
 
159
 
 
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;
 
165
 
 
166
/* If true, we only dump the text, not render */
 
167
static bool gfTextOnly = false;
 
168
 
 
169
#define PAGE_NO_NOT_GIVEN -1
 
170
 
 
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;
 
177
 
 
178
#define PDF_FILE_DPI 72
 
179
 
 
180
#define MAX_FILENAME_SIZE 1024
 
181
 
 
182
/* DOS is 0xd 0xa */
 
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
 
189
 
 
190
#ifdef _WIN32
 
191
  #define DIR_SEP_CHAR '\\'
 
192
  #define DIR_SEP_STR  "\\"
 
193
#else
 
194
  #define DIR_SEP_CHAR '/'
 
195
  #define DIR_SEP_STR  "/"
 
196
#endif
 
197
 
 
198
void memzero(void *data, size_t len)
 
199
{
 
200
    memset(data, 0, len);
 
201
}
 
202
 
 
203
void *zmalloc(size_t len)
 
204
{
 
205
    void *data = malloc(len);
 
206
    if (data)
 
207
        memzero(data, len);
 
208
    return data;
 
209
}
 
210
 
 
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)
 
214
{
 
215
    char *str;
 
216
    char *tmp;
 
217
    size_t str1_len = 0;
 
218
    size_t str2_len = 0;
 
219
    size_t str3_len = 0;
 
220
    size_t str4_len = 0;
 
221
 
 
222
    if (str1)
 
223
        str1_len = strlen(str1);
 
224
    if (str2)
 
225
        str2_len = strlen(str2);
 
226
    if (str3)
 
227
        str3_len = strlen(str3);
 
228
    if (str4)
 
229
        str4_len = strlen(str4);
 
230
 
 
231
    str = (char*)zmalloc(str1_len + str2_len + str3_len + str4_len + 1);
 
232
    if (!str)
 
233
        return NULL;
 
234
 
 
235
    tmp = str;
 
236
    if (str1) {
 
237
        memcpy(tmp, str1, str1_len);
 
238
        tmp += str1_len;
 
239
    }
 
240
    if (str2) {
 
241
        memcpy(tmp, str2, str2_len);
 
242
        tmp += str2_len;
 
243
    }
 
244
    if (str3) {
 
245
        memcpy(tmp, str3, str3_len);
 
246
        tmp += str3_len;
 
247
    }
 
248
    if (str4) {
 
249
        memcpy(tmp, str4, str1_len);
 
250
    }
 
251
    return str;
 
252
}
 
253
 
 
254
char *str_dup(const char *str)
 
255
{
 
256
    return str_cat4(str, NULL, NULL, NULL);
 
257
}
 
258
 
 
259
bool str_eq(const char *str1, const char *str2)
 
260
{
 
261
    if (!str1 && !str2)
 
262
        return true;
 
263
    if (!str1 || !str2)
 
264
        return false;
 
265
    if (0 == strcmp(str1, str2))
 
266
        return true;
 
267
    return false;
 
268
}
 
269
 
 
270
bool str_ieq(const char *str1, const char *str2)
 
271
{
 
272
    if (!str1 && !str2)
 
273
        return true;
 
274
    if (!str1 || !str2)
 
275
        return false;
 
276
    if (0 == strcasecmp(str1, str2))
 
277
        return true;
 
278
    return false;
 
279
}
 
280
 
 
281
bool str_endswith(const char *txt, const char *end)
 
282
{
 
283
    size_t end_len;
 
284
    size_t txt_len;
 
285
 
 
286
    if (!txt || !end)
 
287
        return false;
 
288
 
 
289
    txt_len = strlen(txt);
 
290
    end_len = strlen(end);
 
291
    if (end_len > txt_len)
 
292
        return false;
 
293
    if (str_eq(txt+txt_len-end_len, end))
 
294
        return true;
 
295
    return false;
 
296
}
 
297
 
 
298
/* TODO: probably should move to some other file and change name to
 
299
   sleep_milliseconds */
 
300
void sleep_milliseconds(int milliseconds)
 
301
{
 
302
#ifdef _WIN32
 
303
    Sleep((DWORD)milliseconds);
 
304
#else
 
305
    struct timespec tv;
 
306
    int             secs, nanosecs;
 
307
    secs = milliseconds / 1000;
 
308
    nanosecs = (milliseconds - (secs * 1000)) * 1000;
 
309
    tv.tv_sec = (time_t) secs;
 
310
    tv.tv_nsec = (long) nanosecs;
 
311
    while (1)
 
312
    {
 
313
        int rval = nanosleep(&tv, &tv);
 
314
        if (rval == 0)
 
315
            /* Completed the entire sleep time; all done. */
 
316
            return;
 
317
        else if (errno == EINTR)
 
318
            /* Interrupted by a signal. Try again. */
 
319
            continue;
 
320
        else
 
321
            /* Some other error; bail out. */
 
322
            return;
 
323
    }
 
324
    return;
 
325
#endif
 
326
}
 
327
 
 
328
#ifndef _MSC_VER
 
329
void strcpy_s(char* dst, size_t dst_size, const char* src)
 
330
{
 
331
    size_t src_size = strlen(src) + 1;
 
332
    if (src_size <= dst_size)
 
333
        memcpy(dst, src, src_size);
 
334
    else {
 
335
        if (dst_size > 0) {
 
336
            memcpy(dst, src, dst_size);
 
337
            dst[dst_size-1] = 0;
 
338
        }
 
339
    }
 
340
}
 
341
 
 
342
void strcat_s(char *dst, size_t dst_size, const char* src)
 
343
{
 
344
    size_t dst_len = strlen(dst);
 
345
    if (dst_len >= dst_size) {
 
346
        if (dst_size > 0)
 
347
            dst[dst_size-1] = 0;
 
348
        return;
 
349
    }
 
350
    strcpy_s(dst+dst_len, dst_size - dst_len, src);
 
351
}
 
352
#endif
 
353
 
 
354
static SplashColorMode gSplashColorMode = splashModeBGR8;
 
355
 
 
356
static SplashColor splashColRed;
 
357
static SplashColor splashColGreen;
 
358
static SplashColor splashColBlue;
 
359
static SplashColor splashColWhite;
 
360
static SplashColor splashColBlack;
 
361
 
 
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])
 
367
 
 
368
static SplashColorPtr  gBgColor = SPLASH_COL_WHITE_PTR;
 
369
 
 
370
static void splashColorSet(SplashColorPtr col, Guchar red, Guchar green, Guchar blue, Guchar alpha)
 
371
{
 
372
    switch (gSplashColorMode)
 
373
    {
 
374
        case splashModeBGR8:
 
375
            col[0] = blue;
 
376
            col[1] = green;
 
377
            col[2] = red;
 
378
            break;
 
379
        case splashModeRGB8:
 
380
            col[0] = red;
 
381
            col[1] = green;
 
382
            col[2] = blue;
 
383
            break;
 
384
        default:
 
385
            assert(0);
 
386
            break;
 
387
    }
 
388
}
 
389
 
 
390
void SplashColorsInit(void)
 
391
{
 
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);
 
397
}
 
398
 
 
399
PdfEnginePoppler::PdfEnginePoppler() : 
 
400
   _fileName(0)
 
401
   , _pageCount(INVALID_PAGE_NO) 
 
402
   , _pdfDoc(NULL)
 
403
   , _outputDev(NULL)
 
404
{
 
405
}
 
406
 
 
407
PdfEnginePoppler::~PdfEnginePoppler()
 
408
{
 
409
    free(_fileName);
 
410
    delete _outputDev;
 
411
    delete _pdfDoc;
 
412
}
 
413
 
 
414
bool PdfEnginePoppler::load(const char *fileName)
 
415
{
 
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;
 
420
 
 
421
    _pdfDoc = new PDFDoc(fileNameStr, NULL, NULL, (void*)NULL);
 
422
    if (!_pdfDoc->isOk()) {
 
423
        return false;
 
424
    }
 
425
    _pageCount = _pdfDoc->getNumPages();
 
426
    return true;
 
427
}
 
428
 
 
429
SplashOutputDev * PdfEnginePoppler::outputDevice() {
 
430
    if (!_outputDev) {
 
431
        GBool bitmapTopDown = gTrue;
 
432
        _outputDev = new SplashOutputDev(gSplashColorMode, 4, gFalse, gBgColor, bitmapTopDown);
 
433
        if (_outputDev)
 
434
            _outputDev->startDoc(_pdfDoc->getXRef());
 
435
    }
 
436
    return _outputDev;
 
437
}
 
438
 
 
439
SplashBitmap *PdfEnginePoppler::renderBitmap(int pageNo, double zoomReal, int rotation)
 
440
{
 
441
    assert(outputDevice());
 
442
    if (!outputDevice()) return NULL;
 
443
 
 
444
    double hDPI = (double)PDF_FILE_DPI * zoomReal * 0.01;
 
445
    double vDPI = (double)PDF_FILE_DPI * zoomReal * 0.01;
 
446
    GBool  useMediaBox = gFalse;
 
447
    GBool  crop        = gTrue;
 
448
    GBool  doLinks     = gTrue;
 
449
    _pdfDoc->displayPage(_outputDev, pageNo, hDPI, vDPI, rotation, useMediaBox, 
 
450
        crop, doLinks, NULL, NULL);
 
451
 
 
452
    SplashBitmap* bmp = _outputDev->takeBitmap();
 
453
    return bmp;
 
454
}
 
455
 
 
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 */
 
460
    const char *bufptr;
 
461
#ifdef _WIN32
 
462
    WIN32_FIND_DATA fileinfo;
 
463
    HANDLE dir;
 
464
#else
 
465
    DIR *dir;
 
466
#endif
 
467
};
 
468
 
 
469
#ifdef _WIN32
 
470
#include <windows.h>
 
471
#include <sys/timeb.h>
 
472
#include <direct.h>
 
473
 
 
474
__inline char *getcwd(char *buffer, int maxlen)
 
475
{
 
476
    return _getcwd(buffer, maxlen);
 
477
}
 
478
 
 
479
int fnmatch(const char *pattern, const char *string, int flags)
 
480
{
 
481
    int prefix_len;
 
482
    const char *star_pos = strchr(pattern, '*');
 
483
    if (!star_pos)
 
484
        return strcmp(pattern, string) != 0;
 
485
 
 
486
    prefix_len = (int)(star_pos-pattern);
 
487
    if (0 == prefix_len)
 
488
        return 0;
 
489
 
 
490
    if (0 == _strnicmp(pattern, string, prefix_len))
 
491
        return 0;
 
492
 
 
493
    return 1;
 
494
}
 
495
 
 
496
#else
 
497
#include <fnmatch.h>
 
498
#endif
 
499
 
 
500
#ifdef _WIN32
 
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)
 
505
{
 
506
    int path_len = strlen(path);
 
507
    if (path_len >= path_max_len-4)
 
508
        return;
 
509
    if (DIR_SEP_CHAR != path[path_len])
 
510
        path[path_len++] = DIR_SEP_CHAR;
 
511
    path[path_len++] = '*';
 
512
    path[path_len] = 0;
 
513
}
 
514
#endif
 
515
 
 
516
FindFileState *find_file_open(const char *path, const char *pattern)
 
517
{
 
518
    FindFileState *s;
 
519
 
 
520
    s = (FindFileState*)malloc(sizeof(FindFileState));
 
521
    if (!s)
 
522
        return NULL;
 
523
    strcpy_s(s->path, sizeof(s->path), path);
 
524
    strcpy_s(s->dirpath, sizeof(s->path), path);
 
525
#ifdef _WIN32
 
526
    win_correct_path_for_FindFirstFile(s->path, sizeof(s->path));
 
527
#endif
 
528
    strcpy_s(s->pattern, sizeof(s->pattern), pattern);
 
529
    s->bufptr = s->path;
 
530
#ifdef _WIN32
 
531
    s->dir = INVALID_HANDLE_VALUE;
 
532
#else
 
533
    s->dir = NULL;
 
534
#endif
 
535
    return s;
 
536
}
 
537
 
 
538
#if 0 /* re-enable if we #define USE_OWN_GET_AUTH_DATA */
 
539
void *StandardSecurityHandler::getAuthData()
 
540
{
 
541
    return NULL;
 
542
}
 
543
#endif
 
544
 
 
545
char *makepath(char *buf, int buf_size, const char *path,
 
546
               const char *filename)
 
547
{
 
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;
 
552
        buf[len] = '\0';
 
553
    }
 
554
    strcat_s(buf, buf_size, filename);
 
555
    return buf;
 
556
}
 
557
 
 
558
#ifdef _WIN32
 
559
static int skip_matching_file(const char *filename)
 
560
{
 
561
    if (0 == strcmp(".", filename))
 
562
        return 1;
 
563
    if (0 == strcmp("..", filename))
 
564
        return 1;
 
565
    return 0;
 
566
}
 
567
#endif
 
568
 
 
569
int find_file_next(FindFileState *s, char *filename, int filename_size_max)
 
570
{
 
571
#ifdef _WIN32
 
572
    int    fFound;
 
573
    if (INVALID_HANDLE_VALUE == s->dir) {
 
574
        s->dir = FindFirstFile(s->path, &(s->fileinfo));
 
575
        if (INVALID_HANDLE_VALUE == s->dir)
 
576
            return -1;
 
577
        goto CheckFile;
 
578
    }
 
579
 
 
580
    while (1) {
 
581
        fFound = FindNextFile(s->dir, &(s->fileinfo));
 
582
        if (!fFound)
 
583
            return -1;
 
584
CheckFile:
 
585
        if (skip_matching_file(s->fileinfo.cFileName))
 
586
            continue;
 
587
        if (0 == fnmatch(s->pattern, s->fileinfo.cFileName, 0) ) {
 
588
            makepath(filename, filename_size_max, s->dirpath, s->fileinfo.cFileName);
 
589
            return 0;
 
590
        }
 
591
    }
 
592
#else
 
593
    struct dirent *dirent;
 
594
    const char *p;
 
595
    char *q;
 
596
 
 
597
    if (s->dir == NULL)
 
598
        goto redo;
 
599
 
 
600
    for (;;) {
 
601
        dirent = readdir(s->dir);
 
602
        if (dirent == NULL) {
 
603
        redo:
 
604
            if (s->dir) {
 
605
                closedir(s->dir);
 
606
                s->dir = NULL;
 
607
            }
 
608
            p = s->bufptr;
 
609
            if (*p == '\0')
 
610
                return -1;
 
611
            /* CG: get_str(&p, s->dirpath, sizeof(s->dirpath), ":") */
 
612
            q = s->dirpath;
 
613
            while (*p != ':' && *p != '\0') {
 
614
                if ((q - s->dirpath) < (int)sizeof(s->dirpath) - 1)
 
615
                    *q++ = *p;
 
616
                p++;
 
617
            }
 
618
            *q = '\0';
 
619
            if (*p == ':')
 
620
                p++;
 
621
            s->bufptr = p;
 
622
            s->dir = opendir(s->dirpath);
 
623
            if (!s->dir)
 
624
                goto redo;
 
625
        } else {
 
626
            if (fnmatch(s->pattern, dirent->d_name, 0) == 0) {
 
627
                makepath(filename, filename_size_max,
 
628
                         s->dirpath, dirent->d_name);
 
629
                return 0;
 
630
            }
 
631
        }
 
632
    }
 
633
#endif
 
634
}
 
635
 
 
636
void find_file_close(FindFileState *s)
 
637
{
 
638
#ifdef _WIN32
 
639
    if (INVALID_HANDLE_VALUE != s->dir)
 
640
       FindClose(s->dir);
 
641
#else
 
642
    if (s->dir)
 
643
        closedir(s->dir);
 
644
#endif
 
645
    free(s);
 
646
}
 
647
 
 
648
int StrList_Len(StrList **root)
 
649
{
 
650
    int         len = 0;
 
651
    StrList *   cur;
 
652
    assert(root);
 
653
    if (!root)
 
654
        return 0;
 
655
    cur = *root;
 
656
    while (cur) {
 
657
        ++len;
 
658
        cur = cur->next;
 
659
    }
 
660
    return len;
 
661
}
 
662
 
 
663
int StrList_InsertAndOwn(StrList **root, char *txt)
 
664
{
 
665
    StrList *   el;
 
666
    assert(root && txt);
 
667
    if (!root || !txt)
 
668
        return false;
 
669
 
 
670
    el = (StrList*)malloc(sizeof(StrList));
 
671
    if (!el)
 
672
        return false;
 
673
    el->str = txt;
 
674
    el->next = *root;
 
675
    *root = el;
 
676
    return true;
 
677
}
 
678
 
 
679
int StrList_Insert(StrList **root, char *txt)
 
680
{
 
681
    char *txtDup;
 
682
 
 
683
    assert(root && txt);
 
684
    if (!root || !txt)
 
685
        return false;
 
686
    txtDup = str_dup(txt);
 
687
    if (!txtDup)
 
688
        return false;
 
689
 
 
690
    if (!StrList_InsertAndOwn(root, txtDup)) {
 
691
        free((void*)txtDup);
 
692
        return false;
 
693
    }
 
694
    return true;
 
695
}
 
696
 
 
697
StrList* StrList_RemoveHead(StrList **root)
 
698
{
 
699
    StrList *tmp;
 
700
    assert(root);
 
701
    if (!root)
 
702
        return NULL;
 
703
 
 
704
    if (!*root)
 
705
        return NULL;
 
706
    tmp = *root;
 
707
    *root = tmp->next;
 
708
    tmp->next = NULL;
 
709
    return tmp;
 
710
}
 
711
 
 
712
void StrList_FreeElement(StrList *el)
 
713
{
 
714
    if (!el)
 
715
        return;
 
716
    free((void*)el->str);
 
717
    free((void*)el);
 
718
}
 
719
 
 
720
void StrList_Destroy(StrList **root)
 
721
{
 
722
    StrList *   cur;
 
723
    StrList *   next;
 
724
 
 
725
    if (!root)
 
726
        return;
 
727
    cur = *root;
 
728
    while (cur) {
 
729
        next = cur->next;
 
730
        StrList_FreeElement(cur);
 
731
        cur = next;
 
732
    }
 
733
    *root = NULL;
 
734
}
 
735
 
 
736
#ifndef _WIN32
 
737
void OutputDebugString(const char *txt)
 
738
{
 
739
    /* do nothing */
 
740
}
 
741
#define _snprintf snprintf
 
742
#define _vsnprintf vsnprintf
 
743
#endif
 
744
 
 
745
void my_error(int pos, char *msg, va_list args) {
 
746
#if 0
 
747
    char        buf[4096], *p = buf;
 
748
 
 
749
    // NB: this can be called before the globalParams object is created
 
750
    if (globalParams && globalParams->getErrQuiet()) {
 
751
        return;
 
752
    }
 
753
 
 
754
    if (pos >= 0) {
 
755
        p += _snprintf(p, sizeof(buf)-1, "Error (%d): ", pos);
 
756
        *p   = '\0';
 
757
        OutputDebugString(p);
 
758
    } else {
 
759
        OutputDebugString("Error: ");
 
760
    }
 
761
 
 
762
    p = buf;
 
763
    p += _vsnprintf(p, sizeof(buf) - 1, msg, args);
 
764
    while ( p > buf  &&  isspace(p[-1]) )
 
765
            *--p = '\0';
 
766
    *p++ = '\r';
 
767
    *p++ = '\n';
 
768
    *p   = '\0';
 
769
    OutputDebugString(buf);
 
770
 
 
771
    if (pos >= 0) {
 
772
        p += _snprintf(p, sizeof(buf)-1, "Error (%d): ", pos);
 
773
        *p   = '\0';
 
774
        OutputDebugString(buf);
 
775
        if (gErrFile)
 
776
            fprintf(gErrFile, buf);
 
777
    } else {
 
778
        OutputDebugString("Error: ");
 
779
        if (gErrFile)
 
780
            fprintf(gErrFile, "Error: ");
 
781
    }
 
782
#endif
 
783
#if 0
 
784
    p = buf;
 
785
    va_start(args, msg);
 
786
    p += _vsnprintf(p, sizeof(buf) - 3, msg, args);
 
787
    while ( p > buf  &&  isspace(p[-1]) )
 
788
            *--p = '\0';
 
789
    *p++ = '\r';
 
790
    *p++ = '\n';
 
791
    *p   = '\0';
 
792
    OutputDebugString(buf);
 
793
    if (gErrFile)
 
794
        fprintf(gErrFile, buf);
 
795
    va_end(args);
 
796
#endif
 
797
}
 
798
 
 
799
void LogInfo(char *fmt, ...)
 
800
{
 
801
    va_list args;
 
802
    char        buf[4096], *p = buf;
 
803
 
 
804
    p = buf;
 
805
    va_start(args, fmt);
 
806
    p += _vsnprintf(p, sizeof(buf) - 1, fmt, args);
 
807
    *p   = '\0';
 
808
    fprintf(gOutFile, "%s", buf);
 
809
    va_end(args);
 
810
    fflush(gOutFile);
 
811
}
 
812
 
 
813
static void PrintUsageAndExit(int argc, char **argv)
 
814
{
 
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]);
 
818
    }
 
819
    exit(0);
 
820
}
 
821
 
 
822
static bool ShowPreview(void)
 
823
{
 
824
    if (gfPreview || gfSlowPreview)
 
825
        return true;
 
826
    return false;
 
827
}
 
828
 
 
829
static void RenderPdfAsText(const char *fileName)
 
830
{
 
831
    GooString *         fileNameStr = NULL;
 
832
    PDFDoc *            pdfDoc = NULL;
 
833
    GooString *         txt = NULL;
 
834
    int                 pageCount;
 
835
    double              timeInMs;
 
836
 
 
837
    assert(fileName);
 
838
    if (!fileName)
 
839
        return;
 
840
 
 
841
    LogInfo("started: %s\n", fileName);
 
842
 
 
843
    TextOutputDev * textOut = new TextOutputDev(NULL, gTrue, gFalse, gFalse);
 
844
    if (!textOut->isOk()) {
 
845
        delete textOut;
 
846
        return;
 
847
    }
 
848
 
 
849
    GooTimer msTimer;
 
850
    /* note: don't delete fileNameStr since PDFDoc takes ownership and deletes them itself */
 
851
    fileNameStr = new GooString(fileName);
 
852
    if (!fileNameStr)
 
853
        goto Exit;
 
854
 
 
855
    pdfDoc = new PDFDoc(fileNameStr, NULL, NULL, NULL);
 
856
    if (!pdfDoc->isOk()) {
 
857
        error(-1, "RenderPdfFile(): failed to open PDF file %s\n", fileName);
 
858
        goto Exit;
 
859
    }
 
860
 
 
861
    msTimer.stop();
 
862
    timeInMs = msTimer.getElapsed();
 
863
    LogInfo("load: %.2f ms\n", timeInMs);
 
864
 
 
865
    pageCount = pdfDoc->getNumPages();
 
866
    LogInfo("page count: %d\n", pageCount);
 
867
 
 
868
    for (int curPage = 1; curPage <= pageCount; curPage++) {
 
869
        if ((gPageNo != PAGE_NO_NOT_GIVEN) && (gPageNo != curPage))
 
870
            continue;
 
871
 
 
872
        msTimer.start();
 
873
        int rotate = 0;
 
874
        GBool useMediaBox = gFalse;
 
875
        GBool crop = gTrue;
 
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);
 
879
        msTimer.stop();
 
880
        timeInMs = msTimer.getElapsed();
 
881
        if (gfTimings)
 
882
            LogInfo("page %d: %.2f ms\n", curPage, timeInMs);
 
883
        printf("%s\n", txt->getCString());
 
884
        delete txt;
 
885
        txt = NULL;
 
886
    }
 
887
 
 
888
Exit:
 
889
    LogInfo("finished: %s\n", fileName);
 
890
    delete textOut;
 
891
    delete pdfDoc;
 
892
}
 
893
 
 
894
#ifdef _MSC_VER
 
895
#define POPPLER_TMP_NAME "c:\\poppler_tmp.pdf"
 
896
#else
 
897
#define POPPLER_TMP_NAME "/tmp/poppler_tmp.pdf"
 
898
#endif
 
899
 
 
900
static void RenderPdf(const char *fileName)
 
901
{
 
902
    const char *        fileNameSplash = NULL;
 
903
    PdfEnginePoppler *  engineSplash = NULL;
 
904
    int                 pageCount;
 
905
    double              timeInMs;
 
906
 
 
907
#ifdef COPY_FILE
 
908
    // TODO: fails if file already exists and has read-only attribute
 
909
    CopyFile(fileName, POPPLER_TMP_NAME, false);
 
910
    fileNameSplash = POPPLER_TMP_NAME;
 
911
#else
 
912
    fileNameSplash = fileName;
 
913
#endif
 
914
    LogInfo("started: %s\n", fileName);
 
915
 
 
916
    engineSplash = new PdfEnginePoppler();
 
917
 
 
918
    GooTimer msTimer;
 
919
    if (!engineSplash->load(fileNameSplash)) {
 
920
        LogInfo("failed to load splash\n");
 
921
        goto Error;
 
922
    }
 
923
    msTimer.stop();
 
924
    timeInMs = msTimer.getElapsed();
 
925
    LogInfo("load splash: %.2f ms\n", timeInMs);
 
926
    pageCount = engineSplash->pageCount();
 
927
 
 
928
    LogInfo("page count: %d\n", pageCount);
 
929
    if (gfLoadOnly)
 
930
        goto Error;
 
931
 
 
932
    for (int curPage = 1; curPage <= pageCount; curPage++) {
 
933
        if ((gPageNo != PAGE_NO_NOT_GIVEN) && (gPageNo != curPage))
 
934
            continue;
 
935
 
 
936
        SplashBitmap *bmpSplash = NULL;
 
937
 
 
938
        GooTimer msTimer;
 
939
        bmpSplash = engineSplash->renderBitmap(curPage, 100.0, 0);
 
940
        msTimer.stop();
 
941
        double timeInMs = msTimer.getElapsed();
 
942
        if (gfTimings) {
 
943
            if (!bmpSplash)
 
944
                LogInfo("page splash %d: failed to render\n", curPage);
 
945
            else
 
946
                LogInfo("page splash %d (%dx%d): %.2f ms\n", curPage, bmpSplash->getWidth(), bmpSplash->getHeight(), timeInMs);
 
947
        }
 
948
 
 
949
        if (ShowPreview()) {
 
950
            PreviewBitmapSplash(bmpSplash);
 
951
            if (gfSlowPreview)
 
952
                sleep_milliseconds(SLOW_PREVIEW_TIME);
 
953
        }
 
954
        delete bmpSplash;
 
955
    }
 
956
Error:
 
957
    delete engineSplash;
 
958
    LogInfo("finished: %s\n", fileName);
 
959
}
 
960
 
 
961
static void RenderFile(const char *fileName)
 
962
{
 
963
    if (gfTextOnly) {
 
964
        RenderPdfAsText(fileName);
 
965
        return;
 
966
    }
 
967
 
 
968
    RenderPdf(fileName);
 
969
}
 
970
 
 
971
static bool ParseInteger(const char *start, const char *end, int *intOut)
 
972
{
 
973
    char            numBuf[16];
 
974
    int             digitsCount;
 
975
    const char *    tmp;
 
976
 
 
977
    assert(start && end && intOut);
 
978
    assert(end >= start);
 
979
    if (!start || !end || !intOut || (start > end))
 
980
        return false;
 
981
 
 
982
    digitsCount = 0;
 
983
    tmp = start;
 
984
    while (tmp <= end) {
 
985
        if (isspace(*tmp)) {
 
986
            /* do nothing, we allow whitespace */
 
987
        } else if (!isdigit(*tmp))
 
988
            return false;
 
989
        numBuf[digitsCount] = *tmp;
 
990
        ++digitsCount;
 
991
        if (digitsCount == dimof(numBuf)-3) /* -3 to be safe */
 
992
            return false;
 
993
        ++tmp;
 
994
    }
 
995
    if (0 == digitsCount)
 
996
        return false;
 
997
    numBuf[digitsCount] = 0;
 
998
    *intOut = atoi(numBuf);
 
999
    return true;
 
1000
}
 
1001
 
 
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)
 
1006
{
 
1007
    const char *    posOfX;
 
1008
 
 
1009
    assert(resolutionString);
 
1010
    assert(resolutionXOut);
 
1011
    assert(resolutionYOut);
 
1012
    if (!resolutionString || !resolutionXOut || !resolutionYOut)
 
1013
        return false;
 
1014
    *resolutionXOut = 0;
 
1015
    *resolutionYOut = 0;
 
1016
    posOfX = strchr(resolutionString, 'X');
 
1017
    if (!posOfX)
 
1018
        posOfX = strchr(resolutionString, 'x');
 
1019
    if (!posOfX)
 
1020
        return false;
 
1021
    if (posOfX == resolutionString)
 
1022
        return false;
 
1023
    if (!ParseInteger(resolutionString, posOfX-1, resolutionXOut))
 
1024
        return false;
 
1025
    if (!ParseInteger(posOfX+1, resolutionString+strlen(resolutionString)-1, resolutionYOut))
 
1026
        return false;
 
1027
    return true;
 
1028
}
 
1029
 
 
1030
static void ParseCommandLine(int argc, char **argv)
 
1031
{
 
1032
    char *      arg;
 
1033
 
 
1034
    if (argc < 2)
 
1035
        PrintUsageAndExit(argc, argv);
 
1036
 
 
1037
    for (int i=1; i < argc; i++) {
 
1038
        arg = argv[i];
 
1039
        assert(arg);
 
1040
        if ('-' == arg[0]) {
 
1041
            if (str_ieq(arg, TIMINGS_ARG)) {
 
1042
                gfTimings = true;
 
1043
            } else if (str_ieq(arg, RESOLUTION_ARG)) {
 
1044
                ++i;
 
1045
                if (i == argc)
 
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)) {
 
1051
                gfRecursive = true;
 
1052
            } else if (str_ieq(arg, OUT_ARG)) {
 
1053
                /* expect a file name after that */
 
1054
                ++i;
 
1055
                if (i == argc)
 
1056
                    PrintUsageAndExit(argc, argv);
 
1057
                gOutFileName = str_dup(argv[i]);
 
1058
            } else if (str_ieq(arg, PREVIEW_ARG)) {
 
1059
                gfPreview = true;
 
1060
            } else if (str_ieq(arg, TEXT_ARG)) {
 
1061
                gfTextOnly = true;
 
1062
            } else if (str_ieq(arg, SLOW_PREVIEW_ARG)) {
 
1063
                gfSlowPreview = true;
 
1064
            } else if (str_ieq(arg, LOAD_ONLY_ARG)) {
 
1065
                gfLoadOnly = true;
 
1066
            } else if (str_ieq(arg, PAGE_ARG)) {
 
1067
                /* expect an integer after that */
 
1068
                ++i;
 
1069
                if (i == argc)
 
1070
                    PrintUsageAndExit(argc, argv);
 
1071
                gPageNo = atoi(argv[i]);
 
1072
                if (gPageNo < 1)
 
1073
                    PrintUsageAndExit(argc, argv);
 
1074
            } else {
 
1075
                /* unknown option */
 
1076
                PrintUsageAndExit(argc, argv);
 
1077
            }
 
1078
        } else {
 
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);
 
1082
        }
 
1083
    }
 
1084
}
 
1085
 
 
1086
#if 0
 
1087
void RenderFileList(char *pdfFileList)
 
1088
{
 
1089
    char *data = NULL;
 
1090
    char *dataNormalized = NULL;
 
1091
    char *pdfFileName;
 
1092
    uint64_t fileSize;
 
1093
 
 
1094
    assert(pdfFileList);
 
1095
    if (!pdfFileList)
 
1096
        return;
 
1097
    data = file_read_all(pdfFileList, &fileSize);
 
1098
    if (!data) {
 
1099
        error(-1, "couldn't load file '%s'", pdfFileList);
 
1100
        return;
 
1101
    }
 
1102
    dataNormalized = str_normalize_newline(data, UNIX_NEWLINE);
 
1103
    if (!dataNormalized) {
 
1104
        error(-1, "couldn't normalize data of file '%s'", pdfFileList);
 
1105
        goto Exit;
 
1106
    }
 
1107
    for (;;) {
 
1108
        pdfFileName = str_split_iter(&dataNormalized, UNIX_NEWLINE_C);
 
1109
        if (!pdfFileName)
 
1110
            break;
 
1111
        str_strip_ws_both(pdfFileName);
 
1112
        if (str_empty(pdfFileName)) {
 
1113
            free((void*)pdfFileName);
 
1114
            continue;
 
1115
        }
 
1116
        RenderFile(pdfFileName);
 
1117
        free((void*)pdfFileName);
 
1118
    }
 
1119
Exit:
 
1120
    free((void*)dataNormalized);
 
1121
    free((void*)data);
 
1122
}
 
1123
#endif
 
1124
 
 
1125
#ifdef _WIN32
 
1126
#include <sys/types.h>
 
1127
#include <sys/stat.h>
 
1128
 
 
1129
bool IsDirectoryName(char *path)
 
1130
{
 
1131
    struct _stat    buf;
 
1132
    int             result;
 
1133
 
 
1134
    result = _stat(path, &buf );
 
1135
    if (0 != result)
 
1136
        return false;
 
1137
 
 
1138
    if (buf.st_mode & _S_IFDIR)
 
1139
        return true;
 
1140
 
 
1141
    return false;
 
1142
}
 
1143
 
 
1144
bool IsFileName(char *path)
 
1145
{
 
1146
    struct _stat    buf;
 
1147
    int             result;
 
1148
 
 
1149
    result = _stat(path, &buf );
 
1150
    if (0 != result)
 
1151
        return false;
 
1152
 
 
1153
    if (buf.st_mode & _S_IFREG)
 
1154
        return true;
 
1155
 
 
1156
    return false;
 
1157
}
 
1158
#else
 
1159
bool IsDirectoryName(char *path)
 
1160
{
 
1161
    /* TODO: implement me */
 
1162
    return false;
 
1163
}
 
1164
 
 
1165
bool IsFileName(char *path)
 
1166
{
 
1167
    /* TODO: implement me */
 
1168
    return true;
 
1169
}
 
1170
#endif
 
1171
 
 
1172
bool IsPdfFileName(char *path)
 
1173
{
 
1174
    if (str_endswith(path, ".pdf"))
 
1175
        return true;
 
1176
    return false;
 
1177
}
 
1178
 
 
1179
static void RenderDirectory(char *path)
 
1180
{
 
1181
    FindFileState * ffs;
 
1182
    char            filename[MAX_FILENAME_SIZE];
 
1183
    StrList *       dirList = NULL;
 
1184
    StrList *       el;
 
1185
 
 
1186
    StrList_Insert(&dirList, path);
 
1187
 
 
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)) {
 
1193
                if (gfRecursive) {
 
1194
                    StrList_Insert(&dirList, filename);
 
1195
                }
 
1196
            } else if (IsFileName(filename)) {
 
1197
                if (IsPdfFileName(filename)) {
 
1198
                    RenderFile(filename);
 
1199
                }
 
1200
            }
 
1201
        }
 
1202
        find_file_close(ffs);
 
1203
        StrList_FreeElement(el);
 
1204
    }
 
1205
    StrList_Destroy(&dirList);
 
1206
}
 
1207
 
 
1208
/* Render 'cmdLineArg', which can be:
 
1209
   - directory name
 
1210
   - name of PDF file
 
1211
   - name of text file with names of PDF files
 
1212
*/
 
1213
static void RenderCmdLineArg(char *cmdLineArg)
 
1214
{
 
1215
    assert(cmdLineArg);
 
1216
    if (!cmdLineArg)
 
1217
        return;
 
1218
    if (IsDirectoryName(cmdLineArg)) {
 
1219
        RenderDirectory(cmdLineArg);
 
1220
    } else if (IsFileName(cmdLineArg)) {
 
1221
        if (IsPdfFileName(cmdLineArg))
 
1222
            RenderFile(cmdLineArg);
 
1223
#if 0
 
1224
        else
 
1225
            RenderFileList(cmdLineArg);
 
1226
#endif
 
1227
    } else {
 
1228
        error(-1, "unexpected argument '%s'", cmdLineArg);
 
1229
    }
 
1230
}
 
1231
 
 
1232
int main(int argc, char **argv)
 
1233
{
 
1234
    setErrorFunction(my_error);
 
1235
    ParseCommandLine(argc, argv);
 
1236
    if (0 == StrList_Len(&gArgsListRoot))
 
1237
        PrintUsageAndExit(argc, argv);
 
1238
    assert(gArgsListRoot);
 
1239
 
 
1240
    SplashColorsInit();
 
1241
    globalParams = new GlobalParams();
 
1242
    if (!globalParams)
 
1243
        return 1;
 
1244
    globalParams->setErrQuiet(gFalse);
 
1245
    globalParams->setBaseDir("");
 
1246
 
 
1247
    FILE * outFile = NULL;
 
1248
    if (gOutFileName) {
 
1249
        outFile = fopen(gOutFileName, "wb");
 
1250
        if (!outFile) {
 
1251
            printf("failed to open -out file %s\n", gOutFileName);
 
1252
            return 1;
 
1253
        }
 
1254
        gOutFile = outFile;
 
1255
    }
 
1256
    else
 
1257
        gOutFile = stdout;
 
1258
 
 
1259
    if (gOutFileName)
 
1260
        gErrFile = outFile;
 
1261
    else
 
1262
        gErrFile = stderr;
 
1263
 
 
1264
    PreviewBitmapInit();
 
1265
 
 
1266
    StrList * curr = gArgsListRoot;
 
1267
    while (curr) {
 
1268
        RenderCmdLineArg(curr->str);
 
1269
        curr = curr->next;
 
1270
    }
 
1271
    if (outFile)
 
1272
        fclose(outFile);
 
1273
    PreviewBitmapDestroy();
 
1274
    StrList_Destroy(&gArgsListRoot);
 
1275
    delete globalParams;
 
1276
    free(gOutFileName);
 
1277
    return 0;
 
1278
}
 
1279