1 /* Copyright Krzysztof Kowalczyk 2006-2007
4 A tool to stress-test poppler rendering and measure rendering times for
5 very simplistic performance measuring.
8 * print more info about document like e.g. enumarate images,
9 streams, compression, encryption, password-protection. Each should have
10 a command-line arguments to turn it on/off
11 * never over-write file given as -out argument (optionally, provide -force
12 option to force writing the -out file). It's way too easy too lose results
16 // this sucks but I don't know any other way
17 #pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
20 #include "base_util.h"
21 #include "file_util.h"
23 #include "PdfEngine.h"
25 #include "ErrorCodes.h"
26 #include "GooString.h"
28 #include "GlobalParams.h"
29 #include "SplashBitmap.h"
30 #include "Object.h" /* must be included before SplashOutputDev.h because of sloppiness in SplashOutputDev.h */
31 #include "SplashOutputDev.h"
32 #include "TextOutputDev.h"
34 #include "SecurityHandler.h"
52 extern void PreviewBitmapInit(void);
53 extern void PreviewBitmapDestroy(void);
54 extern void PreviewBitmapSplashFitz(RenderedBitmap
*bmpSplash
, RenderedBitmap
*bmpFitz
);
55 extern "C" char *GetPasswordForFile(WindowInfo
*win
, const char *fileName
);
57 char *GetPasswordForFile(WindowInfo
*win
, const char *fileName
)
62 static void PreviewBitmapFitz(RenderedBitmap
*bmpFitz
)
64 PreviewBitmapSplashFitz(NULL
, bmpFitz
);
67 static void PreviewBitmapSplash(RenderedBitmap
*bmpSplash
)
69 PreviewBitmapSplashFitz(bmpSplash
, NULL
);
72 class RGBAImageFitz
: public RGBAImage
75 RGBAImageFitz(fz_pixmap
*bmp
) {
80 samples
= bmp
->samples
;
85 virtual ~RGBAImageFitz() {}
86 virtual int Get_Width(void) const { return m_bmp
->w
; }
87 virtual int Get_Height(void) const { return m_bmp
->h
; }
88 virtual unsigned char Get_Red(unsigned int i
) { return samples
[i
*4]; }
89 virtual unsigned char Get_Green(unsigned int i
) { return samples
[i
*4+1]; }
90 virtual unsigned char Get_Blue(unsigned int i
) { return samples
[i
*4+2]; }
91 // virtual unsigned char Get_Alpha(unsigned int i) { return samples[i*4+3]; }
92 virtual unsigned char Get_Alpha(unsigned int i
) { return 0; }
93 virtual void Set(unsigned char r
, unsigned char g
, unsigned char b
, unsigned char a
, unsigned int i
) { /* no-op */ }
94 virtual unsigned int Get(int i
) const {
95 unsigned int *data
= (unsigned int *)samples
;
105 class RGBAImageSplash
: public RGBAImage
108 RGBAImageSplash(SplashBitmap
*bmp
) {
110 assert(splashModeBGR8
== m_bmp
->getMode());
111 rowSize
= m_bmp
->getRowSize();
114 virtual ~RGBAImageSplash() {}
115 virtual int Get_Width(void) const { return m_bmp
->getWidth(); }
116 virtual int Get_Height(void) const { return m_bmp
->getHeight(); }
117 virtual unsigned char Get_Red(unsigned int i
) {
121 m_bmp
->getPixel(x
, y
, p
);
122 return splashBGR8R(p
);
124 virtual unsigned char Get_Green(unsigned int i
) {
128 m_bmp
->getPixel(x
, y
, p
);
129 return splashBGR8G(p
);
131 virtual unsigned char Get_Blue(unsigned int i
) {
135 m_bmp
->getPixel(x
, y
, p
);
136 return splashBGR8B(p
);
138 virtual unsigned char Get_Alpha(unsigned int i
) { return 0; }
139 virtual void Set(unsigned char r
, unsigned char g
, unsigned char b
, unsigned char a
, unsigned int i
) { /* no-op */ }
140 virtual unsigned int Get(int i
) const {
144 m_bmp
->getPixel(x
, y
, p
);
145 return p
[0] + ((unsigned int)p
[1] >> 8) + ((unsigned int)p
[2] >> 16);
148 SplashBitmap
* m_bmp
;
152 #define PDF_FILE_DPI 72
154 #define MAX_FILENAME_SIZE 1024
156 struct FindFileState
{
157 char path
[MAX_FILENAME_SIZE
];
158 char dirpath
[MAX_FILENAME_SIZE
]; /* current dir path */
159 char pattern
[MAX_FILENAME_SIZE
]; /* search pattern */
162 WIN32_FIND_DATA fileinfo
;
171 #include <sys/timeb.h>
174 __inline
char *getcwd(char *buffer
, int maxlen
)
176 return _getcwd(buffer
, maxlen
);
179 int fnmatch(const char *pattern
, const char *string
, int flags
)
182 const char *star_pos
= strchr(pattern
, '*');
184 return strcmp(pattern
, string
) != 0;
186 prefix_len
= (int)(star_pos
-pattern
);
190 if (0 == _strnicmp(pattern
, string
, prefix_len
))
201 /* on windows to query dirs we need foo\* to get files in this directory.
202 foo\ always fails and foo will return just info about foo directory,
203 not files in this directory */
204 static void win_correct_path_for_FindFirstFile(char *path
, int path_max_len
)
206 int path_len
= strlen(path
);
207 if (path_len
>= path_max_len
-4)
209 if (DIR_SEP_CHAR
!= path
[path_len
])
210 path
[path_len
++] = DIR_SEP_CHAR
;
211 path
[path_len
++] = '*';
216 FindFileState
*find_file_open(const char *path
, const char *pattern
)
220 s
= (FindFileState
*)malloc(sizeof(FindFileState
));
223 strcpy_s(s
->path
, sizeof(s
->path
), path
);
224 strcpy_s(s
->dirpath
, sizeof(s
->path
), path
);
226 win_correct_path_for_FindFirstFile(s
->path
, sizeof(s
->path
));
228 strcpy_s(s
->pattern
, sizeof(s
->pattern
), pattern
);
231 s
->dir
= INVALID_HANDLE_VALUE
;
238 #if 0 /* re-enable if we #define USE_OWN_GET_AUTH_DATA */
239 void *StandardSecurityHandler::getAuthData()
245 char *makepath(char *buf
, int buf_size
, const char *path
,
246 const char *filename
)
248 strcpy_s(buf
, buf_size
, path
);
249 int len
= strlen(path
);
250 if (len
> 0 && path
[len
- 1] != DIR_SEP_CHAR
&& len
+ 1 < buf_size
) {
251 buf
[len
++] = DIR_SEP_CHAR
;
254 strcat_s(buf
, buf_size
, filename
);
259 static int skip_matching_file(const char *filename
)
261 if (0 == strcmp(".", filename
))
263 if (0 == strcmp("..", filename
))
269 int find_file_next(FindFileState
*s
, char *filename
, int filename_size_max
)
273 if (INVALID_HANDLE_VALUE
== s
->dir
) {
274 s
->dir
= FindFirstFile(s
->path
, &(s
->fileinfo
));
275 if (INVALID_HANDLE_VALUE
== s
->dir
)
281 fFound
= FindNextFile(s
->dir
, &(s
->fileinfo
));
285 if (skip_matching_file(s
->fileinfo
.cFileName
))
287 if (0 == fnmatch(s
->pattern
, s
->fileinfo
.cFileName
, 0) ) {
288 makepath(filename
, filename_size_max
, s
->dirpath
, s
->fileinfo
.cFileName
);
293 struct dirent
*dirent
;
301 dirent
= readdir(s
->dir
);
302 if (dirent
== NULL
) {
311 /* CG: get_str(&p, s->dirpath, sizeof(s->dirpath), ":") */
313 while (*p
!= ':' && *p
!= '\0') {
314 if ((q
- s
->dirpath
) < (int)sizeof(s
->dirpath
) - 1)
322 s
->dir
= opendir(s
->dirpath
);
326 if (fnmatch(s
->pattern
, dirent
->d_name
, 0) == 0) {
327 makepath(filename
, filename_size_max
,
328 s
->dirpath
, dirent
->d_name
);
336 void find_file_close(FindFileState
*s
)
339 if (INVALID_HANDLE_VALUE
!= s
->dir
)
348 typedef struct StrList
{
349 struct StrList
*next
;
353 /* List of all command-line arguments that are not switches.
356 - names of a file with a list of PDF files
357 - names of directories with PDF files
359 static StrList
*gArgsListRoot
= NULL
;
361 /* Names of all command-line switches we recognize */
362 #define TIMINGS_ARG "-timings"
363 #define RESOLUTION_ARG "-resolution"
364 #define RECURSIVE_ARG "-recursive"
365 #define OUT_ARG "-out"
366 #define PREVIEW_ARG "-preview"
367 #define SLOW_PREVIEW_ARG "-slowpreview"
368 #define LOAD_ONLY_ARG "-loadonly"
369 #define PAGE_ARG "-page"
370 #define DUMP_LINKS_ARG "-links"
371 #define TEXT_ARG "-text"
372 #define FITZ_ARG "-fitz"
373 #define BOTH_ARG "-both"
374 #define PDIFF_ARG "-pdiff"
376 /* Should we record timings? True if -timings command-line argument was given. */
377 static BOOL gfTimings
= FALSE
;
379 /* If true, we use render each page at resolution 'gResolutionX'/'gResolutionY'.
380 If false, we render each page at its native resolution.
381 True if -resolution NxM command-line argument was given. */
382 static BOOL gfForceResolution
= FALSE
;
383 static int gResolutionX
= 0;
384 static int gResolutionY
= 0;
385 /* If NULL, we output the log info to stdout. If not NULL, should be a name
386 of the file to which we output log info.
387 Controled by -out command-line argument. */
388 static char * gOutFileName
= NULL
;
389 /* FILE * correspondig to gOutFileName or stdout if gOutFileName is NULL or
391 static FILE * gOutFile
= NULL
;
392 /* FILE * correspondig to gOutFileName or stderr if gOutFileName is NULL or
394 static FILE * gErrFile
= NULL
;
396 /* If True and a directory is given as a command-line argument, we'll process
397 pdf files in sub-directories as well.
398 Controlled by -recursive command-line argument */
399 static BOOL gfRecursive
= FALSE
;
401 /* If true, preview rendered image. To make sure that they're being rendered correctly. */
402 static BOOL gfPreview
= FALSE
;
404 /* If true and rendering both, checks images for visual differences */
405 static BOOL gfPDiff
= FALSE
;
407 /* 1 second (1000 milliseconds) */
408 #define SLOW_PREVIEW_TIME 1000
410 /* If true, preview rendered image in a slow mode i.e. delay displaying for
411 SLOW_PREVIEW_TIME. This is so that a human has enough time to see if the
412 PDF renders ok. In release mode on fast processor pages take only ~100-200 ms
413 to render and they go away too quickly to be inspected by a human. */
414 static int gfSlowPreview
= FALSE
;
416 /* If true, we only dump the text, not render */
417 static int gfTextOnly
= FALSE
;
419 /* If true, using fitz (instead of poppler) for rendering */
420 static int gfFitzRendering
= FALSE
;
422 #define PAGE_NO_NOT_GIVEN -1
424 /* If equals PAGE_NO_NOT_GIVEN, we're in default mode where we render all pages.
425 If different, will only render this page */
426 static int gPageNo
= PAGE_NO_NOT_GIVEN
;
427 /* If true, will only load the file, not render any pages. Mostly for
428 profiling load time */
429 static BOOL gfLoadOnly
= FALSE
;
431 /* if TRUE, will dump information about links */
432 static BOOL gfLinks
= FALSE
;
434 /* if TRUE, will timer, render and preview both fitz and poppler backends */
435 static BOOL gfBoth
= FALSE
;
437 int StrList_Len(StrList
**root
)
452 int StrList_InsertAndOwn(StrList
**root
, char *txt
)
459 el
= (StrList
*)malloc(sizeof(StrList
));
468 int StrList_Insert(StrList
**root
, char *txt
)
475 txtDup
= str_dup(txt
);
479 if (!StrList_InsertAndOwn(root
, txtDup
)) {
486 StrList
* StrList_RemoveHead(StrList
**root
)
501 void StrList_FreeElement(StrList
*el
)
505 free((void*)el
->str
);
509 void StrList_Destroy(StrList
**root
)
519 StrList_FreeElement(cur
);
526 void OutputDebugString(const char *txt
)
530 #define _snprintf snprintf
531 #define _vsnprintf vsnprintf
534 void CDECL
error(int pos
, char *msg
, ...) {
536 char buf
[4096], *p
= buf
;
538 // NB: this can be called before the globalParams object is created
539 if (globalParams
&& globalParams
->getErrQuiet()) {
544 p
+= _snprintf(p
, sizeof(buf
)-1, "Error (%d): ", pos
);
546 OutputDebugString(p
);
548 OutputDebugString("Error: ");
553 p
+= _vsnprintf(p
, sizeof(buf
) - 1, msg
, args
);
554 while ( p
> buf
&& isspace(p
[-1]) )
559 OutputDebugString(buf
);
563 p
+= _snprintf(p
, sizeof(buf
)-1, "Error (%d): ", pos
);
565 OutputDebugString(buf
);
567 fprintf(gErrFile
, buf
);
569 OutputDebugString("Error: ");
571 fprintf(gErrFile
, "Error: ");
576 p
+= _vsnprintf(p
, sizeof(buf
) - 3, msg
, args
);
577 while ( p
> buf
&& isspace(p
[-1]) )
582 OutputDebugString(buf
);
584 fprintf(gErrFile
, buf
);
588 void LogInfo(char *fmt
, ...)
591 char buf
[4096], *p
= buf
;
595 p
+= _vsnprintf(p
, sizeof(buf
) - 1, fmt
, args
);
597 fprintf(gOutFile
, buf
);
602 static void printUsageAndExit(int argc
, char **argv
)
604 printf("Usage: pdftest [-preview] [-slowpreview] [-timings] [-text] [-resolution NxM] [-recursive] [-page N] [-out out.txt] pdf-files-to-process\n");
605 for (int i
=0; i
< argc
; i
++) {
606 printf("i=%d, '%s'\n", i
, argv
[i
]);
611 void *StandardSecurityHandler::getAuthData()
616 static int ShowPreview(void)
618 if (gfPreview
|| gfSlowPreview
)
623 static int DoPDiff(void)
628 static void DumpLinks(int pageNo
, PdfEngine
*engine
)
632 int linkCount
= engine
->linkCount(pageNo
);
633 for (int linkNo
= 0; linkNo
< linkCount
; ++linkNo
) {
634 const char *linkType
= engine
->linkType(pageNo
, linkNo
);
635 LogInfo("Link: page %d, type=%s\n", pageNo
, linkType
);
639 static void renderPdfAsText(const char *fileName
)
641 GooString
* fileNameStr
= NULL
;
642 PDFDoc
* pdfDoc
= NULL
;
643 GooString
* txt
= NULL
;
649 LogInfo("started: %s\n", fileName
);
651 TextOutputDev
* textOut
= new TextOutputDev(NULL
, gTrue
, gFalse
, gFalse
);
652 if (!textOut
->isOk()) {
658 /* note: don't delete fileNameStr since PDFDoc takes ownership and deletes them itself */
659 fileNameStr
= new GooString(fileName
);
663 pdfDoc
= new PDFDoc(fileNameStr
, NULL
, NULL
, NULL
);
664 if (!pdfDoc
->isOk()) {
665 error(-1, "renderPdfFile(): failed to open PDF file %s\n", fileName
);
670 double timeInMs
= msTimer
.timeInMs();
671 LogInfo("load: %.2f ms\n", timeInMs
);
673 int pageCount
= pdfDoc
->getNumPages();
674 LogInfo("page count: %d\n", pageCount
);
676 for (int curPage
= 1; curPage
<= pageCount
; curPage
++) {
677 if ((gPageNo
!= PAGE_NO_NOT_GIVEN
) && (gPageNo
!= curPage
))
682 GBool useMediaBox
= gFalse
;
684 GBool doLinks
= gFalse
;
685 pdfDoc
->displayPage(textOut
, curPage
, 72, 72, rotate
, useMediaBox
, crop
, doLinks
);
686 txt
= textOut
->getText(0.0, 0.0, 10000.0, 10000.0);
688 timeInMs
= msTimer
.timeInMs();
690 LogInfo("page %d: %.2f ms\n", curPage
, timeInMs
);
691 printf("%s\n", txt
->getCString());
697 LogInfo("finished: %s\n", fileName
);
702 extern "C" fz_error
*initfontlibs_ms(void);
704 #define FITZ_TMP_NAME "c:\\fitz_tmp.pdf"
705 #define POPPLER_TMP_NAME "c:\\poppler_tmp.pdf"
708 void SplashRender::RenderPage(int pageNo
)
710 pageDx
= (int)pdfEngine
->pdfDoc()->getPageCropWidth(pageNo
);
711 pageDy
= (int)pdfEngine
->pdfDoc()->getPageCropHeight(pageNo
);
715 if (gfForceResolution
) {
716 renderDx
= gResolutionX
;
717 renderDy
= gResolutionY
;
720 useMediaBox
= gFalse
;
725 if (pageDx
!= renderDx
)
726 scaleX
= (double)renderDx
/ (double)pageDx
;
727 if (pageDy
!= renderDy
)
728 scaleY
= (double)renderDy
/ (double)pageDy
;
729 hDPI
= (double)PDF_FILE_DPI
* scaleX
;
730 vDPI
= (double)PDF_FILE_DPI
* scaleY
;
731 pdfEngine
->pdfDoc()->displayPage(outputDevice
, pageNo
, hDPI
, vDPI
, rotate
, useMediaBox
, crop
, doLinks
);
742 static void renderPdf(const char *fileName
, RenderType renderType
)
744 const char *fileNameFitz
= NULL
;
745 PdfEngine
* engineFitz
= NULL
;
748 const char *fileNameSplash
= NULL
;
749 PdfEngine
* engineSplash
= NULL
;
752 switch (renderType
) {
754 // TODO: fails if file already exists and has read-only attribute
755 CopyFile(fileName
, FITZ_TMP_NAME
, FALSE
);
756 CopyFile(fileName
, POPPLER_TMP_NAME
, FALSE
);
757 fileNameSplash
= POPPLER_TMP_NAME
;
758 fileNameFitz
= FITZ_TMP_NAME
;
761 fileNameFitz
= fileName
;
764 fileNameSplash
= fileName
;
768 LogInfo("started: %s\n", fileName
);
772 engineFitz
= new PdfEngineFitz();
775 if (!engineFitz
->load(fileNameFitz
, NULL
)) {
776 LogInfo("failed to load fitz\n");
780 double timeInMs
= msTimer
.timeInMs();
781 LogInfo("load fitz : %.2f ms\n", timeInMs
);
782 pageCountFitz
= engineFitz
->pageCount();
785 if (fileNameSplash
) {
786 engineSplash
= new PdfEnginePoppler();
789 if (!engineSplash
->load(fileNameSplash
, NULL
)) {
790 LogInfo("failed to load splash\n");
794 double timeInMs
= msTimer
.timeInMs();
795 LogInfo("load splash: %.2f ms\n", timeInMs
);
796 pageCountSplash
= engineSplash
->pageCount();
800 switch (renderType
) {
802 pageCount
= pageCountFitz
;
803 if (pageCountSplash
< pageCount
)
804 pageCount
= pageCountSplash
;
807 pageCount
= pageCountFitz
;
810 pageCount
= pageCountSplash
;
813 LogInfo("page count: %d\n", pageCount
);
815 for (int curPage
= 1; curPage
<= pageCount
; curPage
++) {
816 if ((gPageNo
!= PAGE_NO_NOT_GIVEN
) && (gPageNo
!= curPage
))
819 RenderedBitmap
*bmpFitz
= NULL
;
820 RenderedBitmap
*bmpSplash
= NULL
;
824 bmpFitz
= engineFitz
->renderBitmap(curPage
, 100.0, 0, NULL
, NULL
);
826 double timeInMs
= msTimer
.timeInMs();
830 LogInfo("page fitz %d: failed to render\n", curPage
);
832 LogInfo("page fitz %d (%dx%d): %.2f ms\n", curPage
, bmpFitz
->dx(), bmpFitz
->dy(), timeInMs
);
834 DumpLinks(curPage
, engineFitz
);
837 if (fileNameSplash
) {
839 bmpSplash
= engineSplash
->renderBitmap(curPage
, 100.0, 0, NULL
, NULL
);
841 double timeInMs
= msTimer
.timeInMs();
844 LogInfo("page splash %d: failed to render\n", curPage
);
846 LogInfo("page splash %d (%dx%d): %.2f ms\n", curPage
, bmpSplash
->dx(), bmpSplash
->dy(), timeInMs
);
848 DumpLinks(curPage
, engineSplash
);
852 PreviewBitmapSplashFitz(bmpSplash
, bmpFitz
);
854 sleep_milliseconds(SLOW_PREVIEW_TIME
);
859 CompareArgs compareArgs
;
860 compareArgs
.ImgA
= new RGBAImageFitz(renderFitz
->image
);
861 compareArgs
.ImgB
= new RGBAImageSplash(splashBmp
);
862 compareArgs
.ImgDiff
= NULL
;
863 unsigned long pixelDiffCount
= Yee_Compare(compareArgs
);
864 LogInfo("pixels different: %d\n", (int)pixelDiffCount
);
872 LogInfo("finished: %s\n", fileName
);
875 static void renderFile(const char *fileName
)
878 /* TODO: right not rendering as text is only supported with poppler, not fitz */
879 renderPdfAsText(fileName
);
883 RenderType renderType
;
885 renderType
= renderBoth
;
887 renderType
= renderSplash
;
889 renderType
= renderFitz
;
891 renderPdf(fileName
, renderType
);
894 static int ParseInteger(const char *start
, const char *end
, int *intOut
)
900 assert(start
&& end
&& intOut
);
901 assert(end
>= start
);
902 if (!start
|| !end
|| !intOut
|| (start
> end
))
909 /* do nothing, we allow whitespace */
910 } else if (!isdigit(*tmp
))
912 numBuf
[digitsCount
] = *tmp
;
914 if (digitsCount
== dimof(numBuf
)-3) /* -3 to be safe */
918 if (0 == digitsCount
)
920 numBuf
[digitsCount
] = 0;
921 *intOut
= atoi(numBuf
);
925 /* Given 'resolutionString' in format NxM (e.g. "100x200"), parse the string and put N
926 into 'resolutionXOut' and M into 'resolutionYOut'.
927 Return FALSE if there was an error (e.g. string is not in the right format */
928 static int ParseResolutionString(const char *resolutionString
, int *resolutionXOut
, int *resolutionYOut
)
932 assert(resolutionString
);
933 assert(resolutionXOut
);
934 assert(resolutionYOut
);
935 if (!resolutionString
|| !resolutionXOut
|| !resolutionYOut
)
939 posOfX
= strchr(resolutionString
, 'X');
941 posOfX
= strchr(resolutionString
, 'x');
944 if (posOfX
== resolutionString
)
946 if (!ParseInteger(resolutionString
, posOfX
-1, resolutionXOut
))
948 if (!ParseInteger(posOfX
+1, resolutionString
+strlen(resolutionString
)-1, resolutionYOut
))
954 static void u_ParseResolutionString(void)
957 int result
, resX
, resY
;
966 { "abc", FALSE
, 0, 0},
967 { "34", FALSE
, 0, 0},
968 { "0x0", TRUE
, 0, 0},
969 { "0x1", TRUE
, 0, 1},
970 { "0xab", FALSE
, 0, 0},
971 { "1x0", TRUE
, 1, 0},
972 { "100x200", TRUE
, 100, 200},
973 { "58x58", TRUE
, 58, 58},
974 { " 58x58", TRUE
, 58, 58},
975 { "58x 58", TRUE
, 58, 58},
976 { "58x58 ", TRUE
, 58, 58},
977 { " 58 x 58 ", TRUE
, 58, 58},
978 { "34x1234a", FALSE
, 0, 0},
981 for (i
=0; NULL
!= testData
[i
].str
; i
++) {
982 str
= testData
[i
].str
;
983 result
= ParseResolutionString(str
, &resX
, &resY
);
984 assert(result
== testData
[i
].result
);
986 assert(resX
== testData
[i
].resX
);
987 assert(resY
== testData
[i
].resY
);
993 static void runAllUnitTests(void)
996 u_ParseResolutionString();
1000 static void parseCommandLine(int argc
, char **argv
)
1005 printUsageAndExit(argc
, argv
);
1007 for (int i
=1; i
< argc
; i
++) {
1010 if ('-' == arg
[0]) {
1011 if (str_ieq(arg
, TIMINGS_ARG
)) {
1013 } else if (str_ieq(arg
, RESOLUTION_ARG
)) {
1016 printUsageAndExit(argc
, argv
); /* expect a file name after that */
1017 if (!ParseResolutionString(argv
[i
], &gResolutionX
, &gResolutionY
))
1018 printUsageAndExit(argc
, argv
);
1019 gfForceResolution
= TRUE
;
1020 } else if (str_ieq(arg
, RECURSIVE_ARG
)) {
1022 } else if (str_ieq(arg
, OUT_ARG
)) {
1023 /* expect a file name after that */
1026 printUsageAndExit(argc
, argv
);
1027 gOutFileName
= str_dup(argv
[i
]);
1028 } else if (str_ieq(arg
, PREVIEW_ARG
)) {
1030 } else if (str_ieq(arg
, TEXT_ARG
)) {
1032 } else if (str_ieq(arg
, SLOW_PREVIEW_ARG
)) {
1033 gfSlowPreview
= TRUE
;
1034 } else if (str_ieq(arg
, LOAD_ONLY_ARG
)) {
1036 } else if (str_ieq(arg
, FITZ_ARG
)) {
1037 gfFitzRendering
= TRUE
;
1038 } else if (str_ieq(arg
, BOTH_ARG
)) {
1040 } else if (str_ieq(arg
, PDIFF_ARG
)) {
1042 } else if (str_ieq(arg
, PAGE_ARG
)) {
1043 /* expect an integer after that */
1046 printUsageAndExit(argc
, argv
);
1047 gPageNo
= atoi(argv
[i
]);
1049 printUsageAndExit(argc
, argv
);
1050 } else if (str_ieq(arg
, DUMP_LINKS_ARG
)) {
1053 /* unknown option */
1054 printUsageAndExit(argc
, argv
);
1057 /* we assume that this is not an option hence it must be
1058 a name of PDF/directory/file with PDF names */
1059 StrList_Insert(&gArgsListRoot
, arg
);
1064 void renderFileList(char *pdfFileList
)
1067 char *dataNormalized
= NULL
;
1071 assert(pdfFileList
);
1074 data
= file_read_all(pdfFileList
, &fileSize
);
1076 error(-1, "couldn't load file '%s'", pdfFileList
);
1079 dataNormalized
= str_normalize_newline(data
, UNIX_NEWLINE
);
1080 if (!dataNormalized
) {
1081 error(-1, "couldn't normalize data of file '%s'", pdfFileList
);
1085 pdfFileName
= str_split_iter(&dataNormalized
, UNIX_NEWLINE_C
);
1088 str_strip_ws_both(pdfFileName
);
1089 if (str_empty(pdfFileName
)) {
1090 free((void*)pdfFileName
);
1093 renderFile(pdfFileName
);
1094 free((void*)pdfFileName
);
1097 free((void*)dataNormalized
);
1102 #include <sys/types.h>
1103 #include <sys/stat.h>
1105 int IsDirectoryName(char *path
)
1110 result
= _stat(path
, &buf
);
1114 if (buf
.st_mode
& _S_IFDIR
)
1120 int IsFileName(char *path
)
1125 result
= _stat(path
, &buf
);
1129 if (buf
.st_mode
& _S_IFREG
)
1135 int IsDirectoryName(char *path
)
1137 /* TODO: implement me */
1141 int IsFileName(char *path
)
1143 /* TODO: implement me */
1148 int IsPdfFileName(char *path
)
1150 if (str_endswith(path
, ".pdf"))
1155 void renderDirectory(char *path
)
1157 FindFileState
* ffs
;
1158 char filename
[MAX_FILENAME_SIZE
];
1159 StrList
* dirList
= NULL
;
1162 StrList_Insert(&dirList
, path
);
1164 while (0 != StrList_Len(&dirList
)) {
1165 el
= StrList_RemoveHead(&dirList
);
1166 ffs
= find_file_open(el
->str
, "*");
1167 while (!find_file_next(ffs
, filename
, sizeof(filename
))) {
1168 if (IsDirectoryName(filename
)) {
1170 StrList_Insert(&dirList
, filename
);
1172 } else if (IsFileName(filename
)) {
1173 if (IsPdfFileName(filename
)) {
1174 renderFile(filename
);
1178 find_file_close(ffs
);
1179 StrList_FreeElement(el
);
1181 StrList_Destroy(&dirList
);
1184 /* Render 'cmdLineArg', which can be:
1187 - name of text file with names of PDF files
1189 static void renderCmdLineArg(char *cmdLineArg
)
1194 if (IsDirectoryName(cmdLineArg
)) {
1195 renderDirectory(cmdLineArg
);
1196 } else if (IsFileName(cmdLineArg
)) {
1197 if (IsPdfFileName(cmdLineArg
))
1198 renderFile(cmdLineArg
);
1200 renderFileList(cmdLineArg
);
1202 error(-1, "unexpected argument '%s'", cmdLineArg
);
1206 extern "C" void deinitfontlibs_ms(void);
1208 extern "C" void dump_type_stats(void);
1210 int main(int argc
, char **argv
)
1214 parseCommandLine(argc
, argv
);
1215 if (0 == StrList_Len(&gArgsListRoot
))
1216 printUsageAndExit(argc
, argv
);
1217 assert(gArgsListRoot
);
1219 void *leak
= malloc(20);
1222 globalParams
= new GlobalParams("");
1225 globalParams
->setErrQuiet(gFalse
);
1227 FILE * outFile
= NULL
;
1229 outFile
= fopen(gOutFileName
, "wb");
1231 printf("failed to open -out file %s\n", gOutFileName
);
1244 PreviewBitmapInit();
1246 StrList
* curr
= gArgsListRoot
;
1248 renderCmdLineArg(curr
->str
);
1253 PreviewBitmapDestroy();
1254 deinitfontlibs_ms();
1255 StrList_Destroy(&gArgsListRoot
);
1257 delete globalParams
;