1 /*
2 * Copyright (C) 2012, Red Hat, Inc.
3 *
4 * Code adapted from evince/backend/dvi/mdvi-lib/dviread.c
5 * Copyright (C) 2000, Matias Atria
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 #include <stdio.h>
24 #include <fcntl.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include <glib.h>
29 #include <gmodule.h>
30
31 #include <libtracker-extract/tracker-extract.h>
32
33 #define __PROTO(x) x
34 extern gulong fugetn __PROTO((FILE *, size_t));
35
36 #define fgetbyte(p) ((unsigned)getc(p))
37 #define fuget4(p) fugetn((p), 4)
38 #define fuget3(p) fugetn((p), 3)
39 #define fuget2(p) fugetn((p), 2)
40 #define fuget1(p) fgetbyte(p)
41
42
43 #define DVI_ID 2
44 #define DVI_TRAILER 223
45 #define DVI_PRE 247
46 #define DVI_POST 248
47
48 typedef struct {
49 char *filename; /* name of the DVI file */
50 FILE *in; /* from here we read */
51 char *fileid; /* from preamble */
52 int npages; /* number of pages */
53 int depth; /* recursion depth */
54 gint32 num; /* numerator */
55 gint32 den; /* denominator */
56 gint32 dvimag; /* original magnification */
57 int dvi_page_w; /* unscaled page width */
58 int dvi_page_h; /* unscaled page height */
59 int stacksize; /* stack depth */
60 } DviContext;
61
62 gulong
63 fugetn (FILE *p, size_t n)
64 {
65 gulong v;
66
67 v = fgetbyte(p);
68 while (--n > 0) {
69 v = (v << 8) | fgetbyte(p);
70 }
71
72 return v;
73 }
74
75 static char *
76 opendvi (const char *name)
77 {
78 int len;
79
80 len = strlen (name);
81
82 /* if file ends with .dvi and it exists, that's it */
83 if (len >= 4 && g_strcmp0 (name + len - 4, ".dvi") == 0) {
84 g_debug ("Opening filename:'%s'", name);
85
86 if (access (name, R_OK) == 0) {
87 return g_strdup (name);
88 }
89 }
90
91 return NULL;
92 }
93
94 static void
95 mdvi_destroy_context (DviContext *dvi)
96 {
97 g_free (dvi->filename);
98 g_free (dvi->fileid);
99
100 if (dvi->in) {
101 fclose (dvi->in);
102 }
103
104 g_free (dvi);
105 }
106
107 static DviContext *
108 mdvi_init_context (const char *file)
109 {
110 FILE *p;
111 gint32 arg;
112 int op;
113 int n;
114 DviContext *dvi;
115 char *filename;
116
117 /*
118 * 1. Open the file and initialize the DVI context
119 */
120 filename = opendvi (file);
121 if (filename == NULL) {
122 return NULL;
123 }
124
125 p = fopen (filename, "rb");
126 if (p == NULL) {
127 g_free (filename);
128 return NULL;
129 }
130
131 dvi = g_new0 (DviContext, 1);
132 dvi->filename = filename;
133 dvi->in = p;
134
135 /*
136 * 2. Read the preamble, extract scaling information
137 */
138 if (fuget1 (p) != DVI_PRE) {
139 goto error;
140 }
141
142 if ((arg = fuget1 (p)) != DVI_ID) {
143 g_message ("Unsupported DVI format (version %u)", arg);
144 goto error;
145 }
146
147 /* get dimensions */
148 dvi->num = fuget4 (p);
149 dvi->den = fuget4 (p);
150 dvi->dvimag = fuget4 (p);
151
152 /* check that these numbers make sense */
153 if (!dvi->num || !dvi->den || !dvi->dvimag) {
154 goto error;
155 }
156
157 /* get the comment from the preamble */
158 n = fuget1 (p);
159 dvi->fileid = g_malloc (n + 1);
160 fread (dvi->fileid, 1, n, p);
ignoring return value of 'fread', declared with attribute warn_unused_result
(emitted by gcc)
161 dvi->fileid[n] = 0;
162 g_debug ("Preamble Comment: '%s'", dvi->fileid);
163
164 /*
165 * 3. Read postamble, extract page information (number of
166 * pages, dimensions) and stack depth.
167 */
168
169 /* jump to the end of the file */
170 if (fseek (p, (long) - 1, SEEK_END) == -1) {
171 goto error;
172 }
173
174 for (n = 0; (op = fuget1 (p)) == DVI_TRAILER; n++) {
175 if (fseek (p, (long) - 2, SEEK_CUR) < 0) {
176 break;
177 }
178 }
179
180 if (op != arg || n < 4) {
181 goto error;
182 }
183
184 /* get the pointer to postamble */
185 fseek (p, (long) - 5, SEEK_CUR);
186 arg = fuget4 (p);
187
188 /* jump to it */
189 fseek (p, (long) arg, SEEK_SET);
190 if (fuget1 (p) != DVI_POST) {
191 goto error;
192 }
193
194 fuget4 (p); /* offset */
195 if (dvi->num != fuget4 (p) ||
196 dvi->den != fuget4 (p) ||
197 dvi->dvimag != fuget4 (p)) {
198 goto error;
199 }
200 dvi->dvi_page_h = fuget4 (p);
201 dvi->dvi_page_w = fuget4 (p);
202 dvi->stacksize = fuget2 (p);
203 dvi->npages = fuget2 (p);
204
205 g_debug ("Postamble: %d pages", dvi->npages);
206
207 return dvi;
208
209 error:
210 mdvi_destroy_context (dvi);
211 return NULL;
212 }
213
214 G_MODULE_EXPORT gboolean
215 tracker_extract_get_metadata (TrackerExtractInfo *info)
216 {
217 TrackerSparqlBuilder *metadata;
218 GFile *file;
219 gchar *filename;
220 DviContext *context;
221
222 metadata = tracker_extract_info_get_metadata_builder (info);
223 file = tracker_extract_info_get_file (info);
224 filename = g_file_get_path (file);
225
226 context = mdvi_init_context (filename);
227
228 if (context == NULL) {
229 g_warning ("Could not open dvi file '%s'\n", filename);
230 g_free (filename);
231 return FALSE;
232 }
233
234 tracker_sparql_builder_predicate (metadata, "a");
235 tracker_sparql_builder_object (metadata, "nfo:PaginatedTextDocument");
236
237 tracker_sparql_builder_predicate (metadata, "nfo:pageCount");
238 tracker_sparql_builder_object_int64 (metadata, context->npages);
239
240 if (context->fileid) {
241 tracker_sparql_builder_predicate (metadata, "nie:comment");
242 tracker_sparql_builder_object_unvalidated (metadata, context->fileid);
243 }
244
245 mdvi_destroy_context (context);
246
247 return TRUE;
248 }