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