SmartPDF - lightweight pdf viewer app for rosapps
[reactos.git] / rosapps / smartpdf / poppler / poppler / PDFDoc.cc
1 //========================================================================
2 //
3 // PDFDoc.cc
4 //
5 // Copyright 1996-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <config.h>
10
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14
15 #include <locale.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <stddef.h>
19 #include <string.h>
20 #ifdef WIN32
21 # include <windows.h>
22 #endif
23 #include "goo/GooString.h"
24 #include "poppler-config.h"
25 #include "GlobalParams.h"
26 #include "Page.h"
27 #include "Catalog.h"
28 #include "Stream.h"
29 #include "XRef.h"
30 #include "Link.h"
31 #include "OutputDev.h"
32 #include "Error.h"
33 #include "ErrorCodes.h"
34 #include "Lexer.h"
35 #include "Parser.h"
36 #include "SecurityHandler.h"
37 #ifndef DISABLE_OUTLINE
38 #include "Outline.h"
39 #endif
40 #include "PDFDoc.h"
41 #include "UGooString.h"
42
43 //------------------------------------------------------------------------
44
45 #define headerSearchSize 1024 // read this many bytes at beginning of
46 // file to look for '%PDF'
47
48 //------------------------------------------------------------------------
49 // PDFDoc
50 //------------------------------------------------------------------------
51
52 PDFDoc::PDFDoc(GooString *fileNameA, GooString *ownerPassword,
53 GooString *userPassword, void *guiDataA) {
54 Object obj;
55 GooString *fileName1, *fileName2;
56
57 ok = gFalse;
58 errCode = errNone;
59
60 guiData = guiDataA;
61
62 file = NULL;
63 str = NULL;
64 xref = NULL;
65 catalog = NULL;
66 links = NULL;
67 #ifndef DISABLE_OUTLINE
68 outline = NULL;
69 #endif
70
71 fileName = fileNameA;
72 fileName1 = fileName;
73
74
75 // try to open file
76 fileName2 = NULL;
77 #ifdef VMS
78 if (!(file = fopen(fileName1->getCString(), "rb", "ctx=stm"))) {
79 error(-1, "Couldn't open file '%s'", fileName1->getCString());
80 errCode = errOpenFile;
81 return;
82 }
83 #else
84 if (!(file = fopen(fileName1->getCString(), "rb"))) {
85 fileName2 = fileName->copy();
86 fileName2->lowerCase();
87 if (!(file = fopen(fileName2->getCString(), "rb"))) {
88 fileName2->upperCase();
89 if (!(file = fopen(fileName2->getCString(), "rb"))) {
90 error(-1, "Couldn't open file '%s'", fileName->getCString());
91 delete fileName2;
92 errCode = errOpenFile;
93 return;
94 }
95 }
96 delete fileName2;
97 }
98 #endif
99
100 // create stream
101 obj.initNull();
102 str = new FileStream(file, 0, gFalse, 0, &obj);
103
104 ok = setup(ownerPassword, userPassword);
105 }
106
107 #ifdef WIN32
108 PDFDoc::PDFDoc(wchar_t *fileNameA, int fileNameLen, GooString *ownerPassword,
109 GooString *userPassword, void *guiDataA) {
110 OSVERSIONINFO version;
111 wchar_t fileName2[_MAX_PATH + 1];
112 Object obj;
113 int i;
114
115 ok = gFalse;
116 errCode = errNone;
117
118 guiData = guiDataA;
119
120 file = NULL;
121 str = NULL;
122 xref = NULL;
123 catalog = NULL;
124 links = NULL;
125 #ifndef DISABLE_OUTLINE
126 outline = NULL;
127 #endif
128
129 //~ file name should be stored in Unicode (?)
130 fileName = new GooString();
131 for (i = 0; i < fileNameLen; ++i) {
132 fileName->append((char)fileNameA[i]);
133 }
134
135 // zero-terminate the file name string
136 for (i = 0; i < fileNameLen && i < _MAX_PATH; ++i) {
137 fileName2[i] = fileNameA[i];
138 }
139 fileName2[i] = 0;
140
141 // try to open file
142 // NB: _wfopen is only available in NT
143 version.dwOSVersionInfoSize = sizeof(version);
144 GetVersionEx(&version);
145 if (version.dwPlatformId == VER_PLATFORM_WIN32_NT) {
146 file = _wfopen(fileName2, L"rb");
147 } else {
148 file = fopen(fileName->getCString(), "rb");
149 }
150 if (!file) {
151 error(-1, "Couldn't open file '%s'", fileName->getCString());
152 errCode = errOpenFile;
153 return;
154 }
155
156 // create stream
157 obj.initNull();
158 str = new FileStream(file, 0, gFalse, 0, &obj);
159
160 ok = setup(ownerPassword, userPassword);
161 }
162 #endif
163
164 PDFDoc::PDFDoc(BaseStream *strA, GooString *ownerPassword,
165 GooString *userPassword, void *guiDataA) {
166 ok = gFalse;
167 errCode = errNone;
168 guiData = guiDataA;
169 fileName = NULL;
170 file = NULL;
171 str = strA;
172 xref = NULL;
173 catalog = NULL;
174 links = NULL;
175 #ifndef DISABLE_OUTLINE
176 outline = NULL;
177 #endif
178 ok = setup(ownerPassword, userPassword);
179 }
180
181 GBool PDFDoc::setup(GooString *ownerPassword, GooString *userPassword) {
182 str->reset();
183
184 // check footer
185 if (!checkFooter()) return gFalse;
186
187 // check header
188 checkHeader();
189
190 // read xref table
191 xref = new XRef(str);
192 if (!xref->isOk()) {
193 error(-1, "Couldn't read xref table");
194 errCode = xref->getErrorCode();
195 return gFalse;
196 }
197
198 // check for encryption
199 if (!checkEncryption(ownerPassword, userPassword)) {
200 errCode = errEncrypted;
201 return gFalse;
202 }
203
204 // read catalog
205 catalog = new Catalog(xref);
206 if (!catalog->isOk()) {
207 error(-1, "Couldn't read page catalog");
208 errCode = errBadCatalog;
209 return gFalse;
210 }
211
212 #ifndef DISABLE_OUTLINE
213 // read outline
214 outline = new Outline(catalog->getOutline(), xref);
215 #endif
216
217 // done
218 return gTrue;
219 }
220
221 PDFDoc::~PDFDoc() {
222 #ifndef DISABLE_OUTLINE
223 if (outline) {
224 delete outline;
225 }
226 #endif
227 if (catalog) {
228 delete catalog;
229 }
230 if (xref) {
231 delete xref;
232 }
233 if (str) {
234 delete str;
235 }
236 if (file) {
237 fclose(file);
238 }
239 if (fileName) {
240 delete fileName;
241 }
242 if (links) {
243 delete links;
244 }
245 }
246
247
248 // Check for a %%EOF at the end of this stream
249 GBool PDFDoc::checkFooter() {
250 // we look in the last 1024 chars because Adobe does the same
251 char *eof = new char[1025];
252 int pos = str->getPos();
253 str->setPos(1024, -1);
254 int i, ch;
255 for (i = 0; i < 1024; i++)
256 {
257 ch = str->getChar();
258 if (ch == EOF)
259 break;
260 eof[i] = ch;
261 }
262 eof[i] = '\0';
263
264 bool found = false;
265 for (i = i - 5; i >= 0; i--) {
266 if (strncmp (&eof[i], "%%EOF", 5) == 0) {
267 found = true;
268 break;
269 }
270 }
271 if (!found)
272 {
273 error(-1, "Document has not the mandatory ending %%EOF");
274 errCode = errDamaged;
275 delete[] eof;
276 return gFalse;
277 }
278 delete[] eof;
279 str->setPos(pos);
280 return gTrue;
281 }
282
283 // Check for a PDF header on this stream. Skip past some garbage
284 // if necessary.
285 void PDFDoc::checkHeader() {
286 char hdrBuf[headerSearchSize+1];
287 char *p;
288 int i;
289
290 pdfVersion = 0;
291 for (i = 0; i < headerSearchSize; ++i) {
292 hdrBuf[i] = str->getChar();
293 }
294 hdrBuf[headerSearchSize] = '\0';
295 for (i = 0; i < headerSearchSize - 5; ++i) {
296 if (!strncmp(&hdrBuf[i], "%PDF-", 5)) {
297 break;
298 }
299 }
300 if (i >= headerSearchSize - 5) {
301 error(-1, "May not be a PDF file (continuing anyway)");
302 return;
303 }
304 str->moveStart(i);
305 if (!(p = strtok(&hdrBuf[i+5], " \t\n\r"))) {
306 error(-1, "May not be a PDF file (continuing anyway)");
307 return;
308 }
309 {
310 char *theLocale = setlocale(LC_NUMERIC, "C");
311 pdfVersion = atof(p);
312 setlocale(LC_NUMERIC, theLocale);
313 }
314 // We don't do the version check. Don't add it back in.
315 }
316
317 GBool PDFDoc::checkEncryption(GooString *ownerPassword, GooString *userPassword) {
318 Object encrypt;
319 GBool encrypted;
320 SecurityHandler *secHdlr;
321 GBool ret;
322
323 xref->getTrailerDict()->dictLookup("Encrypt", &encrypt);
324 if ((encrypted = encrypt.isDict())) {
325 if ((secHdlr = SecurityHandler::make(this, &encrypt))) {
326 if (secHdlr->checkEncryption(ownerPassword, userPassword)) {
327 // authorization succeeded
328 xref->setEncryption(secHdlr->getPermissionFlags(),
329 secHdlr->getOwnerPasswordOk(),
330 secHdlr->getFileKey(),
331 secHdlr->getFileKeyLength(),
332 secHdlr->getEncVersion(),
333 secHdlr->getEncRevision());
334 ret = gTrue;
335 } else {
336 // authorization failed
337 ret = gFalse;
338 }
339 delete secHdlr;
340 } else {
341 // couldn't find the matching security handler
342 ret = gFalse;
343 }
344 } else {
345 // document is not encrypted
346 ret = gTrue;
347 }
348 encrypt.free();
349 return ret;
350 }
351
352 void PDFDoc::displayPage(OutputDev *out, int page, double hDPI, double vDPI,
353 int rotate, GBool useMediaBox, GBool crop, GBool doLinks,
354 GBool (*abortCheckCbk)(void *data),
355 void *abortCheckCbkData,
356 GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
357 void *annotDisplayDecideCbkData) {
358 Page *p;
359
360 if (globalParams->getPrintCommands()) {
361 printf("***** page %d *****\n", page);
362 }
363 p = catalog->getPage(page);
364 if (doLinks) {
365 if (links) {
366 delete links;
367 }
368 getLinks(p);
369 p->display(out, hDPI, vDPI, rotate, useMediaBox, crop, links, catalog,
370 abortCheckCbk, abortCheckCbkData,
371 annotDisplayDecideCbk, annotDisplayDecideCbkData);
372 } else {
373 p->display(out, hDPI, vDPI, rotate, useMediaBox, crop, NULL, catalog,
374 abortCheckCbk, abortCheckCbkData,
375 annotDisplayDecideCbk, annotDisplayDecideCbkData);
376 }
377 }
378
379 void PDFDoc::displayPages(OutputDev *out, int firstPage, int lastPage,
380 double hDPI, double vDPI, int rotate, GBool useMediaBox,
381 GBool crop, GBool doLinks,
382 GBool (*abortCheckCbk)(void *data),
383 void *abortCheckCbkData,
384 GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
385 void *annotDisplayDecideCbkData) {
386 int page;
387
388 for (page = firstPage; page <= lastPage; ++page) {
389 displayPage(out, page, hDPI, vDPI, rotate, useMediaBox, crop, doLinks,
390 abortCheckCbk, abortCheckCbkData,
391 annotDisplayDecideCbk, annotDisplayDecideCbkData);
392 }
393 }
394
395 void PDFDoc::displayPageSlice(OutputDev *out, int page,
396 double hDPI, double vDPI,
397 int rotate, GBool useMediaBox, GBool crop, GBool doLinks,
398 int sliceX, int sliceY, int sliceW, int sliceH,
399 GBool (*abortCheckCbk)(void *data),
400 void *abortCheckCbkData,
401 GBool (*annotDisplayDecideCbk)(Annot *annot, void *user_data),
402 void *annotDisplayDecideCbkData) {
403 Page *p;
404
405 p = catalog->getPage(page);
406 if (doLinks)
407 {
408 if (links) {
409 delete links;
410 }
411 getLinks(p);
412 p->displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop,
413 sliceX, sliceY, sliceW, sliceH,
414 links, catalog,
415 abortCheckCbk, abortCheckCbkData,
416 annotDisplayDecideCbk, annotDisplayDecideCbkData);
417 } else {
418 p->displaySlice(out, hDPI, vDPI, rotate, useMediaBox, crop,
419 sliceX, sliceY, sliceW, sliceH,
420 NULL, catalog,
421 abortCheckCbk, abortCheckCbkData,
422 annotDisplayDecideCbk, annotDisplayDecideCbkData);
423 }
424 }
425
426 Links *PDFDoc::takeLinks() {
427 Links *ret;
428
429 ret = links;
430 links = NULL;
431 return ret;
432 }
433
434 GBool PDFDoc::isLinearized() {
435 Parser *parser;
436 Object obj1, obj2, obj3, obj4, obj5;
437 GBool lin;
438
439 lin = gFalse;
440 obj1.initNull();
441 parser = new Parser(xref,
442 new Lexer(xref,
443 str->makeSubStream(str->getStart(), gFalse, 0, &obj1)));
444 parser->getObj(&obj1);
445 parser->getObj(&obj2);
446 parser->getObj(&obj3);
447 parser->getObj(&obj4);
448 if (obj1.isInt() && obj2.isInt() && obj3.isCmd("obj") &&
449 obj4.isDict()) {
450 obj4.dictLookup("Linearized", &obj5);
451 if (obj5.isNum() && obj5.getNum() > 0) {
452 lin = gTrue;
453 }
454 obj5.free();
455 }
456 obj4.free();
457 obj3.free();
458 obj2.free();
459 obj1.free();
460 delete parser;
461 return lin;
462 }
463
464 GBool PDFDoc::saveAs(GooString *name) {
465 FILE *f;
466 int c;
467
468 if (!(f = fopen(name->getCString(), "wb"))) {
469 error(-1, "Couldn't open file '%s'", name->getCString());
470 return gFalse;
471 }
472 str->reset();
473 while ((c = str->getChar()) != EOF) {
474 fputc(c, f);
475 }
476 str->close();
477 fclose(f);
478 return gTrue;
479 }
480
481 void PDFDoc::getLinks(Page *page) {
482 Object obj;
483
484 links = new Links(page->getAnnots(&obj), catalog->getBaseURI());
485 obj.free();
486 }