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
fugetn(FILE * p,size_t n)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 *
opendvi(const char * name)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
mdvi_destroy_context(DviContext * dvi)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 *
mdvi_init_context(const char * file)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);
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
tracker_extract_get_metadata(TrackerExtractInfo * info)215 tracker_extract_get_metadata (TrackerExtractInfo *info)
216 {
217 	TrackerResource *resource;
218 	GFile *file;
219 	gchar *filename;
220 	DviContext *context;
221 
222 	file = tracker_extract_info_get_file (info);
223 	filename = g_file_get_path (file);
224 
225 	context = mdvi_init_context (filename);
226 
227 	if (context == NULL) {
228 		g_warning ("Could not open dvi file '%s'\n", filename);
229 		g_free (filename);
230 		return FALSE;
231 	}
232 
233 	resource = tracker_resource_new (NULL);
234 
235 	tracker_resource_add_uri (resource, "rdf:type", "nfo:PaginatedTextDocument");
236 
237 	tracker_resource_set_int64 (resource, "nfo:pageCount", context->npages);
238 
239 	if (context->fileid) {
240 		tracker_resource_set_string (resource, "nie:comment", context->fileid);
241 	}
242 
243 	mdvi_destroy_context (context);
244 
245 	tracker_extract_info_set_resource (info, resource);
246 	g_object_unref (resource);
247 
248 	return TRUE;
249 }
250