]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/pdftotext.cc
2de03e360c28c66f03c0d96db29ee9ae3475a96f
[evince.git] / pdf / xpdf / pdftotext.cc
1 //========================================================================
2 //
3 // pdftotext.cc
4 //
5 // Copyright 1997-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <aconf.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <stddef.h>
13 #include <string.h>
14 #include "parseargs.h"
15 #include "GString.h"
16 #include "gmem.h"
17 #include "GlobalParams.h"
18 #include "Object.h"
19 #include "Stream.h"
20 #include "Array.h"
21 #include "Dict.h"
22 #include "XRef.h"
23 #include "Catalog.h"
24 #include "Page.h"
25 #include "PDFDoc.h"
26 #include "TextOutputDev.h"
27 #include "CharTypes.h"
28 #include "UnicodeMap.h"
29 #include "Error.h"
30 #include "config.h"
31
32 static void printInfoString(FILE *f, Dict *infoDict, char *key,
33                             char *text1, char *text2, UnicodeMap *uMap);
34 static void printInfoDate(FILE *f, Dict *infoDict, char *key, char *fmt);
35
36 static int firstPage = 1;
37 static int lastPage = 0;
38 static GBool physLayout = gFalse;
39 static GBool rawOrder = gFalse;
40 static GBool htmlMeta = gFalse;
41 static char textEncName[128] = "";
42 static char textEOL[16] = "";
43 static GBool noPageBreaks = gFalse;
44 static char ownerPassword[33] = "\001";
45 static char userPassword[33] = "\001";
46 static GBool quiet = gFalse;
47 static char cfgFileName[256] = "";
48 static GBool printVersion = gFalse;
49 static GBool printHelp = gFalse;
50
51 static ArgDesc argDesc[] = {
52   {"-f",       argInt,      &firstPage,     0,
53    "first page to convert"},
54   {"-l",       argInt,      &lastPage,      0,
55    "last page to convert"},
56   {"-layout",  argFlag,     &physLayout,    0,
57    "maintain original physical layout"},
58   {"-raw",     argFlag,     &rawOrder,      0,
59    "keep strings in content stream order"},
60   {"-htmlmeta", argFlag,   &htmlMeta,       0,
61    "generate a simple HTML file, including the meta information"},
62   {"-enc",     argString,   textEncName,    sizeof(textEncName),
63    "output text encoding name"},
64   {"-eol",     argString,   textEOL,        sizeof(textEOL),
65    "output end-of-line convention (unix, dos, or mac)"},
66   {"-nopgbrk", argFlag,     &noPageBreaks,  0,
67    "don't insert page breaks between pages"},
68   {"-opw",     argString,   ownerPassword,  sizeof(ownerPassword),
69    "owner password (for encrypted files)"},
70   {"-upw",     argString,   userPassword,   sizeof(userPassword),
71    "user password (for encrypted files)"},
72   {"-q",       argFlag,     &quiet,         0,
73    "don't print any messages or errors"},
74   {"-cfg",     argString,   cfgFileName,    sizeof(cfgFileName),
75    "configuration file to use in place of .xpdfrc"},
76   {"-v",       argFlag,     &printVersion,  0,
77    "print copyright and version info"},
78   {"-h",       argFlag,     &printHelp,     0,
79    "print usage information"},
80   {"-help",    argFlag,     &printHelp,     0,
81    "print usage information"},
82   {"--help",   argFlag,     &printHelp,     0,
83    "print usage information"},
84   {"-?",       argFlag,     &printHelp,     0,
85    "print usage information"},
86   {NULL}
87 };
88
89 int main(int argc, char *argv[]) {
90   PDFDoc *doc;
91   GString *fileName;
92   GString *textFileName;
93   GString *ownerPW, *userPW;
94   TextOutputDev *textOut;
95   FILE *f;
96   UnicodeMap *uMap;
97   Object info;
98   GBool ok;
99   char *p;
100   int exitCode;
101
102   exitCode = 99;
103
104   // parse args
105   ok = parseArgs(argDesc, &argc, argv);
106   if (!ok || argc < 2 || argc > 3 || printVersion || printHelp) {
107     fprintf(stderr, "pdftotext version %s\n", xpdfVersion);
108     fprintf(stderr, "%s\n", xpdfCopyright);
109     if (!printVersion) {
110       printUsage("pdftotext", "<PDF-file> [<text-file>]", argDesc);
111     }
112     goto err0;
113   }
114   fileName = new GString(argv[1]);
115
116   // read config file
117   globalParams = new GlobalParams(cfgFileName);
118   if (textEncName[0]) {
119     globalParams->setTextEncoding(textEncName);
120   }
121   if (textEOL[0]) {
122     if (!globalParams->setTextEOL(textEOL)) {
123       fprintf(stderr, "Bad '-eol' value on command line\n");
124     }
125   }
126   if (noPageBreaks) {
127     globalParams->setTextPageBreaks(gFalse);
128   }
129   if (quiet) {
130     globalParams->setErrQuiet(quiet);
131   }
132
133   // get mapping to output encoding
134   if (!(uMap = globalParams->getTextEncoding())) {
135     error(-1, "Couldn't get text encoding");
136     delete fileName;
137     goto err1;
138   }
139
140   // open PDF file
141   if (ownerPassword[0] != '\001') {
142     ownerPW = new GString(ownerPassword);
143   } else {
144     ownerPW = NULL;
145   }
146   if (userPassword[0] != '\001') {
147     userPW = new GString(userPassword);
148   } else {
149     userPW = NULL;
150   }
151   doc = new PDFDoc(fileName, ownerPW, userPW);
152   if (userPW) {
153     delete userPW;
154   }
155   if (ownerPW) {
156     delete ownerPW;
157   }
158   if (!doc->isOk()) {
159     exitCode = 1;
160     goto err2;
161   }
162
163   // check for copy permission
164   if (!doc->okToCopy()) {
165     error(-1, "Copying of text from this document is not allowed.");
166     exitCode = 3;
167     goto err2;
168   }
169
170   // construct text file name
171   if (argc == 3) {
172     textFileName = new GString(argv[2]);
173   } else {
174     p = fileName->getCString() + fileName->getLength() - 4;
175     if (!strcmp(p, ".pdf") || !strcmp(p, ".PDF")) {
176       textFileName = new GString(fileName->getCString(),
177                                  fileName->getLength() - 4);
178     } else {
179       textFileName = fileName->copy();
180     }
181     textFileName->append(htmlMeta ? ".html" : ".txt");
182   }
183
184   // get page range
185   if (firstPage < 1) {
186     firstPage = 1;
187   }
188   if (lastPage < 1 || lastPage > doc->getNumPages()) {
189     lastPage = doc->getNumPages();
190   }
191
192   // write HTML header
193   if (htmlMeta) {
194     if (!textFileName->cmp("-")) {
195       f = stdout;
196     } else {
197       if (!(f = fopen(textFileName->getCString(), "wb"))) {
198         error(-1, "Couldn't open text file '%s'", textFileName->getCString());
199         exitCode = 2;
200         goto err3;
201       }
202     }
203     fputs("<html>\n", f);
204     fputs("<head>\n", f);
205     doc->getDocInfo(&info);
206     if (info.isDict()) {
207       printInfoString(f, info.getDict(), "Title", "<title>", "</title>\n",
208                       uMap);
209       printInfoString(f, info.getDict(), "Subject",
210                       "<meta name=\"Subject\" content=\"", "\">\n", uMap);
211       printInfoString(f, info.getDict(), "Keywords",
212                       "<meta name=\"Keywords\" content=\"", "\">\n", uMap);
213       printInfoString(f, info.getDict(), "Author",
214                       "<meta name=\"Author\" content=\"", "\">\n", uMap);
215       printInfoString(f, info.getDict(), "Creator",
216                       "<meta name=\"Creator\" content=\"", "\">\n", uMap);
217       printInfoString(f, info.getDict(), "Producer",
218                       "<meta name=\"Producer\" content=\"", "\">\n", uMap);
219       printInfoDate(f, info.getDict(), "CreationDate",
220                     "<meta name=\"CreationDate\" content=\"\">\n");
221       printInfoDate(f, info.getDict(), "LastModifiedDate",
222                     "<meta name=\"ModDate\" content=\"\">\n");
223     }
224     info.free();
225     fputs("</head>\n", f);
226     fputs("<body>\n", f);
227     fputs("<pre>\n", f);
228     if (f != stdout) {
229       fclose(f);
230     }
231   }
232
233   // write text file
234   textOut = new TextOutputDev(textFileName->getCString(),
235                               physLayout, rawOrder, htmlMeta);
236   if (textOut->isOk()) {
237     doc->displayPages(textOut, firstPage, lastPage, 72, 72, 0, gTrue, gFalse);
238   } else {
239     delete textOut;
240     exitCode = 2;
241     goto err3;
242   }
243   delete textOut;
244
245   // write end of HTML file
246   if (htmlMeta) {
247     if (!textFileName->cmp("-")) {
248       f = stdout;
249     } else {
250       if (!(f = fopen(textFileName->getCString(), "ab"))) {
251         error(-1, "Couldn't open text file '%s'", textFileName->getCString());
252         exitCode = 2;
253         goto err3;
254       }
255     }
256     fputs("</pre>\n", f);
257     fputs("</body>\n", f);
258     fputs("</html>\n", f);
259     if (f != stdout) {
260       fclose(f);
261     }
262   }
263
264   exitCode = 0;
265
266   // clean up
267  err3:
268   delete textFileName;
269  err2:
270   delete doc;
271   uMap->decRefCnt();
272  err1:
273   delete globalParams;
274  err0:
275
276   // check for memory leaks
277   Object::memCheck(stderr);
278   gMemReport(stderr);
279
280   return exitCode;
281 }
282
283 static void printInfoString(FILE *f, Dict *infoDict, char *key,
284                             char *text1, char *text2, UnicodeMap *uMap) {
285   Object obj;
286   GString *s1;
287   GBool isUnicode;
288   Unicode u;
289   char buf[8];
290   int i, n;
291
292   if (infoDict->lookup(key, &obj)->isString()) {
293     fputs(text1, f);
294     s1 = obj.getString();
295     if ((s1->getChar(0) & 0xff) == 0xfe &&
296         (s1->getChar(1) & 0xff) == 0xff) {
297       isUnicode = gTrue;
298       i = 2;
299     } else {
300       isUnicode = gFalse;
301       i = 0;
302     }
303     while (i < obj.getString()->getLength()) {
304       if (isUnicode) {
305         u = ((s1->getChar(i) & 0xff) << 8) |
306             (s1->getChar(i+1) & 0xff);
307         i += 2;
308       } else {
309         u = s1->getChar(i) & 0xff;
310         ++i;
311       }
312       n = uMap->mapUnicode(u, buf, sizeof(buf));
313       fwrite(buf, 1, n, f);
314     }
315     fputs(text2, f);
316   }
317   obj.free();
318 }
319
320 static void printInfoDate(FILE *f, Dict *infoDict, char *key, char *fmt) {
321   Object obj;
322   char *s;
323
324   if (infoDict->lookup(key, &obj)->isString()) {
325     s = obj.getString()->getCString();
326     if (s[0] == 'D' && s[1] == ':') {
327       s += 2;
328     }
329     fprintf(f, fmt, s);
330   }
331   obj.free();
332 }