added arts device
[swftools.git] / pdf2swf / xpdf / PDFDoc.cc
1 //========================================================================
2 //
3 // PDFDoc.cc
4 //
5 // Copyright 1996-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <aconf.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 }