]> www.fi.muni.cz Git - evince.git/blob - dvi/mdvi-lib/dviread.c
Updated Canadian English translation.
[evince.git] / dvi / mdvi-lib / dviread.c
1 /*
2  * Copyright (C) 2000, Matias Atria
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdarg.h>
22 #include <string.h>
23 #include <math.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27
28 #include "mdvi.h"
29 #include "private.h"
30
31 typedef int (*DviCommand) __PROTO((DviContext *, int));
32
33 #define DVICMDDEF(x)    static int x __PROTO((DviContext *, int))
34
35 DVICMDDEF(set_char);
36 DVICMDDEF(set_rule);
37 DVICMDDEF(no_op);
38 DVICMDDEF(push);
39 DVICMDDEF(pop);
40 DVICMDDEF(move_right);
41 DVICMDDEF(move_down);
42 DVICMDDEF(move_w);
43 DVICMDDEF(move_x);
44 DVICMDDEF(move_y);
45 DVICMDDEF(move_z);
46 DVICMDDEF(sel_font);
47 DVICMDDEF(sel_fontn);
48 DVICMDDEF(special);
49 DVICMDDEF(def_font);
50 DVICMDDEF(undefined);
51 DVICMDDEF(unexpected);
52
53 static const DviCommand dvi_commands[] = {
54         set_char, set_char, set_char, set_char, 
55         set_char, set_char, set_char, set_char, 
56         set_char, set_char, set_char, set_char, 
57         set_char, set_char, set_char, set_char, 
58         set_char, set_char, set_char, set_char, 
59         set_char, set_char, set_char, set_char, 
60         set_char, set_char, set_char, set_char, 
61         set_char, set_char, set_char, set_char, 
62         set_char, set_char, set_char, set_char, 
63         set_char, set_char, set_char, set_char, 
64         set_char, set_char, set_char, set_char, 
65         set_char, set_char, set_char, set_char, 
66         set_char, set_char, set_char, set_char, 
67         set_char, set_char, set_char, set_char, 
68         set_char, set_char, set_char, set_char, 
69         set_char, set_char, set_char, set_char, 
70         set_char, set_char, set_char, set_char, 
71         set_char, set_char, set_char, set_char, 
72         set_char, set_char, set_char, set_char, 
73         set_char, set_char, set_char, set_char, 
74         set_char, set_char, set_char, set_char, 
75         set_char, set_char, set_char, set_char, 
76         set_char, set_char, set_char, set_char, 
77         set_char, set_char, set_char, set_char, 
78         set_char, set_char, set_char, set_char, 
79         set_char, set_char, set_char, set_char, 
80         set_char, set_char, set_char, set_char, 
81         set_char, set_char, set_char, set_char, 
82         set_char, set_char, set_char, set_char, 
83         set_char, set_char, set_char, set_char, 
84         set_char, set_char, set_char, set_char, 
85         set_char, set_char, set_char, set_char, /* 0 - 127 */
86         set_char, set_char, set_char, set_char, /* 128 - 131 */
87         set_rule,                               /* 132 */
88         set_char, set_char, set_char, set_char, /* 133 - 136 */
89         set_rule,                               /* 137 */
90         no_op,                                  /* 138 */
91         unexpected,                             /* 139 (BOP) */
92         unexpected,                             /* 140 (EOP) */
93         push,                                   /* 141 */
94         pop,                                    /* 142 */
95         move_right, move_right, move_right, move_right, /* 143 - 146 */
96         move_w, move_w, move_w, move_w, move_w, /* 147 - 151 */
97         move_x, move_x, move_x, move_x, move_x, /* 152 - 156 */
98         move_down, move_down, move_down, move_down,     /* 157 - 160 */
99         move_y, move_y, move_y, move_y, move_y, /* 161 - 165 */
100         move_z, move_z, move_z, move_z, move_z, /* 166 - 170 */
101         sel_font, sel_font, sel_font, sel_font,
102         sel_font, sel_font, sel_font, sel_font,
103         sel_font, sel_font, sel_font, sel_font,
104         sel_font, sel_font, sel_font, sel_font,
105         sel_font, sel_font, sel_font, sel_font,
106         sel_font, sel_font, sel_font, sel_font,
107         sel_font, sel_font, sel_font, sel_font,
108         sel_font, sel_font, sel_font, sel_font,
109         sel_font, sel_font, sel_font, sel_font,
110         sel_font, sel_font, sel_font, sel_font,
111         sel_font, sel_font, sel_font, sel_font,
112         sel_font, sel_font, sel_font, sel_font,
113         sel_font, sel_font, sel_font, sel_font,
114         sel_font, sel_font, sel_font, sel_font,
115         sel_font, sel_font, sel_font, sel_font,
116         sel_font, sel_font, sel_font, sel_font, /* 171 - 234 */
117         sel_fontn, sel_fontn, sel_fontn, sel_fontn,     /* 235 - 238 */
118         special, special, special, special,     /* 239 - 242 */
119         def_font, def_font, def_font, def_font, /* 243 - 246 */
120         unexpected,                             /* 247 (PRE) */
121         unexpected,                             /* 248 (POST) */
122         unexpected,                             /* 249 (POST_POST) */
123         undefined, undefined, undefined, 
124         undefined, undefined, undefined         /* 250 - 255 */
125 };
126
127 #define DVI_BUFLEN      4096
128
129 static int      mdvi_run_macro(DviContext *dvi, Uchar *macro, size_t len);
130
131 static void dummy_draw_glyph(DviContext *dvi, DviFontChar *ch, int x, int y)
132 {
133 }
134
135 static void dummy_draw_rule(DviContext *dvi, int x, int y, Uint w, Uint h, int f)
136 {
137 }
138
139 static int dummy_alloc_colors(void *a, Ulong *b, int c, Ulong d, Ulong e, double f, int g)
140 {
141         return -1;
142 }
143
144 static void *dummy_create_image(void *a, Uint b, Uint c, Uint d)
145 {
146         return NULL;
147 }
148
149 static void dummy_free_image(void *a)
150 {
151 }
152
153 static void dummy_dev_destroy(void *a)
154 {
155 }
156
157 static void dummy_dev_putpixel(void *a, int x, int y, Ulong c)
158 {
159 }
160
161 static void dummy_dev_refresh(DviContext *a, void *b)
162 {
163 }
164
165 static void dummy_dev_set_color(void *a, Ulong b, Ulong c)
166 {
167 }
168
169 /* functions to report errors */
170 static void dvierr(DviContext *dvi, const char *format, ...)
171 {
172         va_list ap;
173         
174         va_start(ap, format);
175         fprintf(stderr, "%s[%d]: Error: ",
176                 dvi->filename, dvi->currpage);
177         vfprintf(stderr, format, ap);
178         va_end(ap);
179 }
180
181 static void dviwarn(DviContext *dvi, const char *format, ...)
182 {
183         va_list ap;
184         
185         fprintf(stderr, "%s[%d]: Warning: ",
186                 dvi->filename, dvi->currpage);
187         va_start(ap, format);
188         vfprintf(stderr, format, ap);
189         va_end(ap);
190 }
191
192 #define NEEDBYTES(d,n) \
193         ((d)->buffer.pos + (n) > (d)->buffer.length)
194
195 static int get_bytes(DviContext *dvi, size_t n)
196 {       
197         /* 
198          * caller wants to read `n' bytes from dvi->buffer + dvi->pos.
199          * Make sure there is enough data to satisfy the request 
200          */
201         if(NEEDBYTES(dvi, n)) {
202                 size_t  required;
203                 int     newlen;
204                 
205                 if(dvi->buffer.frozen || dvi->in == NULL || feof(dvi->in)) {
206                         /* this is EOF */
207                         dviwarn(dvi, _("unexpected EOF\n"));
208                         return -1;
209                 }
210                 /* get more data */
211                 if(dvi->buffer.data == NULL) {
212                         /* first allocation */
213                         dvi->buffer.size = Max(DVI_BUFLEN, n);
214                         dvi->buffer.data = (Uchar *)xmalloc(dvi->buffer.size);
215                         dvi->buffer.length = 0;
216                         dvi->buffer.frozen = 0;
217                 } else if(dvi->buffer.pos < dvi->buffer.length) {
218                         /* move whatever we want to keep */
219                         dvi->buffer.length -= dvi->buffer.pos;
220                         memmove(dvi->buffer.data,
221                                 dvi->buffer.data + dvi->buffer.pos,
222                                 dvi->buffer.length);
223                 } else {
224                         /* we can discard all the data in this buffer */
225                         dvi->buffer.length = 0;
226                 }
227
228                 required = n - dvi->buffer.length;
229                 if(required > dvi->buffer.size - dvi->buffer.length) {
230                         /* need to allocate more memory */
231                         dvi->buffer.size = dvi->buffer.length + required + 128;
232                         dvi->buffer.data = (Uchar *)xresize(dvi->buffer.data,
233                                 char, dvi->buffer.size);
234                 }
235                 /* now read into the buffer */
236                 newlen = fread(dvi->buffer.data + dvi->buffer.length,
237                         1, dvi->buffer.size - dvi->buffer.length, dvi->in);
238                 if(newlen == -1) {
239                         error("%s: %s\n", dvi->filename, strerror(errno));
240                         return -1;
241                 }
242                 dvi->buffer.length += newlen;
243                 dvi->buffer.pos = 0;
244         }
245         return 0;
246 }
247
248 /* only relative forward seeks are supported by this function */
249 static int dskip(DviContext *dvi, long offset)
250 {
251         ASSERT(offset > 0);
252         
253         if(NEEDBYTES(dvi, offset) && get_bytes(dvi, offset) == -1)
254                 return -1;
255         dvi->buffer.pos += offset;
256         return 0;
257 }
258
259 /* DVI I/O functions (note: here `n' must be <= 4) */
260 static long dsgetn(DviContext *dvi, size_t n)
261 {
262         long    val;
263
264         if(NEEDBYTES(dvi, n) && get_bytes(dvi, n) == -1)
265                 return -1;
266         val = msgetn(dvi->buffer.data + dvi->buffer.pos, n);
267         dvi->buffer.pos += n;
268         return val;
269 }
270
271 static int dread(DviContext *dvi, char *buffer, size_t len)
272 {
273         if(NEEDBYTES(dvi, len) && get_bytes(dvi, len) == -1)
274                 return -1;
275         memcpy(buffer, dvi->buffer.data + dvi->buffer.pos, len);
276         dvi->buffer.pos += len;
277         return 0;
278 }
279
280 static long dugetn(DviContext *dvi, size_t n)
281 {
282         long    val;
283
284         if(NEEDBYTES(dvi, n) && get_bytes(dvi, n) == -1)        
285                 return -1;      
286         val = mugetn(dvi->buffer.data + dvi->buffer.pos, n);
287         dvi->buffer.pos += n;
288         return val;
289 }
290
291 static long dtell(DviContext *dvi)
292 {
293         return dvi->depth ? 
294                 dvi->buffer.pos : 
295                 ftell(dvi->in) - dvi->buffer.length + dvi->buffer.pos;
296 }
297
298 static void dreset(DviContext *dvi)
299 {
300         if(!dvi->buffer.frozen && dvi->buffer.data)
301                 xfree(dvi->buffer.data);
302         dvi->buffer.data = NULL;
303         dvi->buffer.size = 0;
304         dvi->buffer.length = 0;
305         dvi->buffer.pos = 0;
306 }
307
308 #define dsget1(d)       dsgetn((d), 1)
309 #define dsget2(d)       dsgetn((d), 2)
310 #define dsget3(d)       dsgetn((d), 3)
311 #define dsget4(d)       dsgetn((d), 4)
312 #define duget1(d)       dugetn((d), 1)
313 #define duget2(d)       dugetn((d), 2)
314 #define duget3(d)       dugetn((d), 3)
315 #define duget4(d)       dugetn((d), 4)
316
317 #ifndef NODEBUG
318 static void dviprint(DviContext *dvi, const char *command, int sub, const char *fmt, ...)
319 {
320         int     i;
321         va_list ap;
322         
323         printf("%s: ", dvi->filename);
324         for(i = 0; i < dvi->depth; i++)
325                 printf("  ");
326         printf("%4lu: %s", dtell(dvi), command);
327         if(sub >= 0) printf("%d", sub);
328         if(*fmt) printf(": ");
329         va_start(ap, fmt);
330         vprintf(fmt, ap);
331         va_end(ap);
332 }
333 #define SHOWCMD(x)      \
334         if(_mdvi_debug_mask & DBG_OPCODE) do { dviprint x; } while(0)
335 #else
336 #define SHOWCMD(x)      do { } while(0)
337 #endif
338
339 int     mdvi_find_tex_page(DviContext *dvi, int tex_page)
340 {
341         int     i;
342         
343         for(i = 0; i < dvi->npages; i++)
344                 if(dvi->pagemap[i][1] == tex_page)
345                         return i;
346         return -1;
347 }
348
349 /* page sorting functions */
350 static int sort_up(const void *p1, const void *p2)
351 {
352         return ((long *)p1)[1] - ((long *)p2)[1];
353 }
354 static int sort_down(const void *p1, const void *p2)
355 {
356         return ((long *)p2)[1] - ((long *)p1)[1];
357 }
358 static int sort_random(const void *p1, const void *p2)
359 {
360         return (random() % 1) ? -1 : 1;
361 }
362 static int sort_dvi_up(const void *p1, const void *p2)
363 {
364         return ((long *)p1)[0] - ((long *)p2)[0];
365 }
366 static int sort_dvi_down(const void *p1, const void *p2)
367 {
368         return ((long *)p1)[0] - ((long *)p2)[0];
369 }
370
371 void    mdvi_sort_pages(DviContext *dvi, DviPageSort type)
372 {
373         int     (*sortfunc) __PROTO((const void *, const void *));
374         
375         switch(type) {
376         case MDVI_PAGE_SORT_UP:
377                 sortfunc = sort_up;
378                 break;
379         case MDVI_PAGE_SORT_DOWN:
380                 sortfunc = sort_down;
381                 break;
382         case MDVI_PAGE_SORT_RANDOM:
383                 sortfunc = sort_random;
384                 break;
385         case MDVI_PAGE_SORT_DVI_UP:
386                 sortfunc = sort_dvi_up;
387                 break;
388         case MDVI_PAGE_SORT_DVI_DOWN:
389                 sortfunc = sort_dvi_down;
390                 break;
391         case MDVI_PAGE_SORT_NONE:
392         default:
393                 sortfunc = NULL;
394                 break;
395         }
396         
397         if(sortfunc)
398                 qsort(dvi->pagemap, dvi->npages, sizeof(PageNum), sortfunc);
399 }
400
401 static DviFontRef *define_font(DviContext *dvi, int op)
402 {
403         Int32   arg;
404         Int32   scale;
405         Int32   dsize;
406         Int32   checksum;
407         int     hdpi;
408         int     vdpi;
409         int     n;
410         char    *name;
411         DviFontRef *ref;
412         
413         arg = dugetn(dvi, op - DVI_FNT_DEF1 + 1);
414         checksum = duget4(dvi);
415         scale = duget4(dvi);
416         dsize = duget4(dvi);
417         hdpi = FROUND(dvi->params.mag * dvi->params.dpi * scale / dsize);
418         vdpi = FROUND(dvi->params.mag * dvi->params.vdpi * scale / dsize);
419         n = duget1(dvi) + duget1(dvi);
420         name = xmalloc(n + 1);
421         dread(dvi, name, n);
422         name[n] = 0;
423         DEBUG((DBG_FONTS, "requesting font %d = `%s' at %.1fpt (%dx%d dpi)\n",
424                 arg, name, (double)scale / (dvi->params.tfm_conv * 0x100000),
425                 hdpi, vdpi));
426         ref = font_reference(&dvi->params, arg, name, checksum, hdpi, vdpi, scale);
427         if(ref == NULL) {
428                 error(_("could not load font `%s'\n"), name);
429                 xfree(name);
430                 return NULL;
431         }
432         xfree(name);
433         return ref;
434 }
435
436 static char *opendvi(const char *name)
437 {
438         int     len;
439         char    *file;
440         
441         len = strlen(name);
442         /* if file ends with .dvi and it exists, that's it */
443         if(len >= 4 && STREQ(name+len-4, ".dvi")) {
444                 DEBUG((DBG_DVI|DBG_FILES, "opendvi: Trying `%s'\n", name));
445                 if(access(name, R_OK) == 0)
446                         return xstrdup(name);
447         }
448                 
449         /* try appending .dvi */
450         file = xmalloc(len + 5);
451         strcpy(file, name);
452         strcpy(file+len, ".dvi");
453         DEBUG((DBG_DVI|DBG_FILES, "opendvi: Trying `%s'\n", file));
454         if(access(file, R_OK) == 0)
455                 return file;
456         /* try the given name */
457         file[len] = 0;
458         DEBUG((DBG_DVI|DBG_FILES, "opendvi: Trying `%s'\n", file));
459         if(access(file, R_OK) == 0)
460                 return file;
461         xfree(file);
462         return NULL;
463 }
464
465 int     mdvi_reload(DviContext *dvi, DviParams *np)
466 {
467         DviContext *newdvi;
468         DviParams  *pars;
469         
470         /* close our file */
471         if(dvi->in) {
472                 fclose(dvi->in);
473                 dvi->in = NULL;
474         }
475
476         pars = np ? np : &dvi->params;
477         DEBUG((DBG_DVI, "%s: reloading\n", dvi->filename));     
478         
479         /* load it again */
480         newdvi = mdvi_init_context(pars, dvi->pagesel, dvi->filename);
481         if(newdvi == NULL) {
482                 warning(_("could not reload `%s'\n"), dvi->filename);
483                 return -1;
484         }
485
486         /* drop all our font references */
487         font_drop_chain(dvi->fonts);
488         /* destroy our font map */
489         if(dvi->fontmap)
490                 xfree(dvi->fontmap);
491         dvi->currfont = NULL;
492
493         /* and use the ones we just loaded */
494         dvi->fonts = newdvi->fonts;
495         dvi->fontmap = newdvi->fontmap;
496         dvi->nfonts = newdvi->nfonts;
497
498         /* copy the new information */
499         dvi->params = newdvi->params;
500         dvi->num = newdvi->num;
501         dvi->den = newdvi->den;
502         dvi->dvimag = newdvi->dvimag;
503         dvi->dviconv = newdvi->dviconv;
504         dvi->dvivconv = newdvi->dvivconv;
505         dvi->modtime = newdvi->modtime;
506
507         if(dvi->fileid) xfree(dvi->fileid);
508         dvi->fileid = newdvi->fileid;
509                 
510         dvi->dvi_page_w = newdvi->dvi_page_w;
511         dvi->dvi_page_h = newdvi->dvi_page_h;
512
513         xfree(dvi->pagemap);
514         dvi->pagemap = newdvi->pagemap;
515         dvi->npages = newdvi->npages;
516         if(dvi->currpage > dvi->npages-1)
517                 dvi->currpage = 0;
518                 
519         xfree(dvi->stack);
520         dvi->stack = newdvi->stack;
521         dvi->stacksize = newdvi->stacksize;
522
523         /* remove fonts that are not being used anymore */
524         font_free_unused(&dvi->device);
525                 
526         xfree(newdvi->filename);                
527         xfree(newdvi);
528
529         DEBUG((DBG_DVI, "%s: reload successful\n", dvi->filename));
530         if(dvi->device.refresh)
531                 dvi->device.refresh(dvi, dvi->device.device_data);
532         
533         return 0;
534 }
535
536 /* function to change parameters ia DVI context 
537  * The DVI context is modified ONLY if this function is successful */
538 int mdvi_configure(DviContext *dvi, DviParamCode option, ...)
539 {
540         va_list ap;
541         int     reset_all;
542         int     reset_font;
543         DviParams np;
544         
545         va_start(ap, option);
546
547         reset_font = 0;
548         reset_all  = 0;
549         np = dvi->params; /* structure copy */  
550         while(option != MDVI_PARAM_LAST) {
551                 switch(option) {
552                 case MDVI_SET_DPI:
553                         np.dpi = np.vdpi = va_arg(ap, Uint);
554                         reset_all = 1;
555                         break;
556                 case MDVI_SET_XDPI:
557                         np.dpi = va_arg(ap, Uint);
558                         reset_all = 1;
559                         break;
560                 case MDVI_SET_YDPI:
561                         np.vdpi = va_arg(ap, Uint);
562                         break;
563                 case MDVI_SET_SHRINK:
564                         np.hshrink = np.vshrink = va_arg(ap, Uint);
565                         reset_font = MDVI_FONTSEL_GREY|MDVI_FONTSEL_BITMAP;
566                         break;
567                 case MDVI_SET_XSHRINK:
568                         np.hshrink = va_arg(ap, Uint);
569                         reset_font = MDVI_FONTSEL_GREY|MDVI_FONTSEL_BITMAP;
570                         break;
571                 case MDVI_SET_YSHRINK:
572                         np.vshrink = va_arg(ap, Uint);
573                         reset_font = MDVI_FONTSEL_GREY|MDVI_FONTSEL_BITMAP;
574                         break;
575                 case MDVI_SET_ORIENTATION:
576                         np.orientation = va_arg(ap, DviOrientation);
577                         reset_font = MDVI_FONTSEL_GLYPH;
578                         break;
579                 case MDVI_SET_GAMMA:
580                         np.gamma = va_arg(ap, double);
581                         if(np.pixels) {
582                                 xfree(np.pixels);
583                                 np.pixels = 0;
584                                 np.npixels = 0;
585                         }
586                         reset_font = MDVI_FONTSEL_GREY;
587                         break;
588                 case MDVI_SET_DENSITY:
589                         np.density = va_arg(ap, Uint);
590                         reset_font = MDVI_FONTSEL_BITMAP;
591                         break;
592                 case MDVI_SET_MAGNIFICATION:
593                         np.mag = va_arg(ap, double);
594                         reset_all = 1;
595                         break;
596                 case MDVI_SET_DRIFT:
597                         np.hdrift = np.vdrift = va_arg(ap, int);
598                         break;
599                 case MDVI_SET_HDRIFT:
600                         np.hdrift = va_arg(ap, int);
601                         break;
602                 case MDVI_SET_VDRIFT:
603                         np.vdrift = va_arg(ap, int);
604                         break;
605                 case MDVI_SET_FOREGROUND:
606                         np.fg = va_arg(ap, Ulong);
607                         reset_font = MDVI_FONTSEL_GREY;
608                         break;
609                 case MDVI_SET_BACKGROUND:
610                         np.bg = va_arg(ap, Ulong);
611                         reset_font = MDVI_FONTSEL_GREY;
612                         break;
613                 default:
614                         break;
615                 }
616                 option = va_arg(ap, DviParamCode);
617         }
618         va_end(ap);
619         
620         /* check that all values make sense */
621         if(np.dpi <= 0 || np.vdpi <= 0)
622                 return -1;
623         if(np.mag <= 0.0)
624                 return -1;
625         if(np.density < 0)
626                 return -1;
627         if(np.hshrink < 1 || np.vshrink < 1)
628                 return -1;
629         if(np.hdrift < 0 || np.vdrift < 0)
630                 return -1;      
631         if(np.fg == np.bg)
632                 return -1;
633
634         /* 
635          * If the dpi or the magnification change, we basically have to reload
636          * the DVI file again from scratch.
637          */
638
639         if(reset_all)
640                 return (mdvi_reload(dvi, &np) == 0);
641
642         if(np.hshrink != dvi->params.hshrink) {
643                 np.conv = dvi->dviconv;
644                 if(np.hshrink)
645                         np.conv /= np.hshrink;
646         }
647         if(np.vshrink != dvi->params.vshrink) {
648                 np.vconv = dvi->dvivconv;
649                 if(np.vshrink)
650                         np.vconv /= np.vshrink;
651         }
652
653         if(reset_font) {
654                 font_reset_chain_glyphs(&dvi->device, dvi->fonts, reset_font);
655         }
656         dvi->params = np;       
657         if((reset_font & MDVI_FONTSEL_GLYPH) && dvi->device.refresh) {
658                 dvi->device.refresh(dvi, dvi->device.device_data);
659                 return 0;
660         }
661
662         return 1;
663 }
664 /*
665  * Read the initial data from the DVI file. If something is wrong with the 
666  * file, we just spit out an error message and refuse to load the file, 
667  * without giving any details. This makes sense because DVI files are ok
668  * 99.99% of the time, and dvitype(1) can be used to check the other 0.01%.
669  */
670 DviContext *mdvi_init_context(DviParams *par, DviPageSpec *spec, const char *file)
671 {
672         FILE    *p;
673         Int32   arg;
674         int     op;
675         long    offset;
676         int     n;
677         DviContext *dvi;
678         char    *filename;
679         int     pagecount;
680
681         /*
682          * 1. Open the file and initialize the DVI context
683          */     
684
685         filename = opendvi(file);
686         if(filename == NULL) {
687                 perror(file);
688                 return NULL;
689         }
690         p = fopen(filename, "r");
691         if(p == NULL) {
692                 perror(file);
693                 xfree(filename);
694                 return NULL;
695         }
696         dvi = xalloc(DviContext);
697         memzero(dvi, sizeof(DviContext));
698         dvi->pagemap = NULL;
699         dvi->filename = filename;
700         dvi->stack = NULL;
701         dvi->modtime = get_mtime(fileno(p));    
702         dvi->buffer.data = NULL;
703         dvi->pagesel = spec;
704         dvi->in = p; /* now we can use the dget*() functions */
705
706         /* 
707          * 2. Read the preamble, extract scaling information, and 
708          *    setup the DVI parameters.
709          */
710
711         if(fuget1(p) != DVI_PRE)
712                 goto bad_dvi;
713         if((arg = fuget1(p)) != DVI_ID) {
714                 error(_("%s: unsupported DVI format (version %u)\n"),
715                         file, arg);
716                 goto error; /* jump to the end of this routine, 
717                              * where we handle errors */
718         }
719         /* get dimensions */
720         dvi->num = fuget4(p);
721         dvi->den = fuget4(p);
722         dvi->dvimag = fuget4(p);
723
724         /* check that these numbers make sense */
725         if(!dvi->num || !dvi->den || !dvi->dvimag)
726                 goto bad_dvi;
727         
728         dvi->params.mag = 
729                 (par->mag > 0 ? par->mag : (double)dvi->dvimag / 1000.0);
730         dvi->params.hdrift  = par->hdrift;
731         dvi->params.vdrift  = par->vdrift;
732         dvi->params.dpi     = par->dpi ? par->dpi : MDVI_DPI;
733         dvi->params.vdpi    = par->vdpi ? par->vdpi : par->dpi;
734         dvi->params.hshrink = par->hshrink;
735         dvi->params.vshrink = par->vshrink;
736         dvi->params.density = par->density;
737         dvi->params.gamma   = par->gamma;
738         dvi->params.conv    = (double)dvi->num / dvi->den;
739         dvi->params.conv   *= (dvi->params.dpi / 254000.0) * dvi->params.mag;
740         dvi->params.vconv   = (double)dvi->num / dvi->den;
741         dvi->params.vconv  *= (dvi->params.vdpi / 254000.0) * dvi->params.mag;
742         dvi->params.tfm_conv = (25400000.0 / dvi->num) *
743                                 ((double)dvi->den / 473628672) / 16.0;
744         dvi->params.flags = par->flags;
745         dvi->params.orientation = par->orientation;
746         dvi->params.fg = par->fg;
747         dvi->params.bg = par->bg;
748         dvi->params.pixels = NULL;
749         dvi->params.npixels = 0;
750
751         /* initialize colors */
752         dvi->curr_fg = par->fg;
753         dvi->curr_bg = par->bg;
754         dvi->color_stack = NULL;
755         dvi->color_top = 0;
756         dvi->color_size = 0;
757
758         /* pixel conversion factors */
759         dvi->dviconv = dvi->params.conv;
760         dvi->dvivconv = dvi->params.vconv;
761         if(dvi->params.hshrink)
762                 dvi->params.conv /= dvi->params.hshrink;
763         if(dvi->params.vshrink)
764                 dvi->params.vconv /= dvi->params.vshrink;
765
766         /* get the comment from the preamble */
767         n = fuget1(p);
768         dvi->fileid = xmalloc(n + 1);
769         fread(dvi->fileid, 1, n, p);
770         dvi->fileid[n] = 0;
771         DEBUG((DBG_DVI, "%s: %s\n", filename, dvi->fileid));
772         
773         /*
774          * 3. Read postamble, extract page information (number of
775          *    pages, dimensions) and stack depth.
776          */
777
778         /* jump to the end of the file */
779         if(fseek(p, (long)-1, SEEK_END) == -1)
780                 goto error;     
781         for(n = 0; (op = fuget1(p)) == DVI_TRAILER; n++)
782                 if(fseek(p, (long)-2, SEEK_CUR) < 0)
783                         break;
784         if(op != arg || n < 4)
785                 goto bad_dvi;
786         /* get the pointer to postamble */
787         fseek(p, (long)-5, SEEK_CUR);
788         arg = fuget4(p);
789         /* jump to it */
790         fseek(p, (long)arg, SEEK_SET);
791         if(fuget1(p) != DVI_POST)
792                 goto bad_dvi;
793         offset = fuget4(p);
794         if(dvi->num != fuget4(p) || dvi->den != fuget4(p) ||
795            dvi->dvimag != fuget4(p))
796                 goto bad_dvi;
797         dvi->dvi_page_h = fuget4(p);
798         dvi->dvi_page_w = fuget4(p);
799         dvi->stacksize = fuget2(p);
800         dvi->npages = fuget2(p);
801         DEBUG((DBG_DVI, "%s: from postamble: stack depth %d, %d page%s\n",
802                 filename, dvi->stacksize, dvi->npages, dvi->npages > 1 ? "s" : ""));
803         
804         /*
805          * 4. Process font definitions.
806          */
807
808         /* process font definitions */
809         dvi->nfonts = 0;
810         dvi->fontmap = NULL;
811         /* 
812          * CAREFUL: here we need to use the dvi->buffer, but it might leave the
813          * the file cursor in the wrong position after reading fonts (because of
814          * buffering). It's ok, though, because after the font definitions we read
815          * the page offsets, and we fseek() to the relevant part of the file with
816          * SEEK_SET. Nothing is read after the page offsets.
817          */
818         while((op = duget1(dvi)) != DVI_POST_POST) {
819                 DviFontRef *ref;
820                 
821                 if(op == DVI_NOOP)
822                         continue;
823                 else if(op < DVI_FNT_DEF1 || op > DVI_FNT_DEF4)
824                         goto error;
825                 ref = define_font(dvi, op);
826                 if(ref == NULL)
827                         goto error;
828                 ref->next = dvi->fonts;
829                 dvi->fonts = ref;
830                 dvi->nfonts++;
831         }
832         /* we don't need the buffer anymore */
833         dreset(dvi);
834         
835         if(op != DVI_POST_POST)
836                 goto bad_dvi;
837         font_finish_definitions(dvi);
838         DEBUG((DBG_DVI, "%s: %d font%s required by this job\n",
839                 filename, dvi->nfonts, dvi->nfonts > 1 ? "s" : ""));
840         dvi->findref = font_find_mapped;
841
842         /*
843          * 5. Build the page map.
844          */
845
846         dvi->pagemap = xnalloc(PageNum, dvi->npages);
847         memzero(dvi->pagemap, sizeof(PageNum) * dvi->npages);
848                 
849         n = dvi->npages - 1;
850         pagecount = n;
851         while(offset != -1) {
852                 int     i;
853                 PageNum page;
854
855                 fseek(p, offset, SEEK_SET);             
856                 op = fuget1(p);
857                 if(op != DVI_BOP || n < 0)
858                         goto bad_dvi;
859                 for(i = 1; i <= 10; i++)
860                         page[i] = fsget4(p);
861                 page[0] = offset;
862                 offset = (long)fuget4(p);
863                 /* check if the page is selected */
864                 if(spec && mdvi_page_selected(spec, page, n) == 0) {
865                         DEBUG((DBG_DVI, "Page %d (%ld.%ld.%ld.%ld.%ld.%ld.%ld.%ld.%ld.%ld) ignored by request\n",
866                         n, page[1], page[2], page[3], page[4], page[5],
867                            page[6], page[7], page[8], page[9], page[10]));
868                 } else {
869                         memcpy(&dvi->pagemap[pagecount], page, sizeof(PageNum));
870                         pagecount--;
871                 }
872                 n--;
873         }
874         pagecount++;
875         if(pagecount >= dvi->npages) {
876                 error(_("no pages selected\n"));
877                 goto error;
878         }
879         if(pagecount) {
880                 DEBUG((DBG_DVI, "%d of %d pages selected\n",
881                         dvi->npages - pagecount, dvi->npages));
882                 dvi->npages -= pagecount;
883                 memmove(dvi->pagemap, &dvi->pagemap[pagecount], 
884                         dvi->npages * sizeof(PageNum));
885         }
886
887         /*
888          * 6. Setup stack, initialize device functions
889          */
890
891         dvi->curr_layer = 0;
892         dvi->stack = xnalloc(DviState, dvi->stacksize + 8);
893
894         dvi->device.draw_glyph   = dummy_draw_glyph;
895         dvi->device.draw_rule    = dummy_draw_rule;
896         dvi->device.alloc_colors = dummy_alloc_colors;
897         dvi->device.create_image = dummy_create_image;
898         dvi->device.free_image   = dummy_free_image;
899         dvi->device.dev_destroy  = dummy_dev_destroy;
900         dvi->device.put_pixel    = dummy_dev_putpixel;
901         dvi->device.refresh      = dummy_dev_refresh;
902         dvi->device.set_color    = dummy_dev_set_color;
903         dvi->device.device_data  = NULL;
904
905         /* initialize associations */
906         mdvi_hash_init(&dvi->assoc);
907
908         DEBUG((DBG_DVI, "%s read successfully\n", filename));
909         return dvi;
910
911 bad_dvi:
912         error(_("%s: File corrupted, or not a DVI file\n"), file);
913 error:
914         /* if we came from the font definitions, this will be non-trivial */
915         dreset(dvi);
916         mdvi_destroy_context(dvi);
917         return NULL;
918 }
919
920 void    mdvi_destroy_context(DviContext *dvi)
921 {
922         if(dvi->device.dev_destroy)
923                 dvi->device.dev_destroy(dvi->device.device_data);
924         /* release all fonts */
925         if(dvi->fonts) {
926                 font_drop_chain(dvi->fonts);
927                 font_free_unused(&dvi->device);
928         }
929         if(dvi->fontmap)
930                 xfree(dvi->fontmap);
931         if(dvi->filename)
932                 xfree(dvi->filename);
933         if(dvi->stack)
934                 xfree(dvi->stack);
935         if(dvi->pagemap)
936                 xfree(dvi->pagemap);
937         if(dvi->fileid)
938                 xfree(dvi->fileid);
939         if(dvi->in)
940                 fclose(dvi->in);
941         if(dvi->buffer.data && !dvi->buffer.frozen)
942                 xfree(dvi->buffer.data);
943         if(dvi->color_stack)
944                 xfree(dvi->color_stack);
945         mdvi_assoc_flush(dvi);
946         
947         xfree(dvi);
948 }
949
950 void    mdvi_setpage(DviContext *dvi, int pageno)
951 {
952         if(pageno < 0)
953                 pageno = 0;
954         if(pageno > dvi->npages-1)
955                 pageno = dvi->npages - 1;
956         dvi->currpage = pageno;
957 }
958
959 static int      mdvi_run_macro(DviContext *dvi, Uchar *macro, size_t len)
960 {
961         DviFontRef *curr, *fonts;
962         DviBuffer saved_buffer;
963         FILE    *saved_file;
964         int     opcode;
965         int     oldtop;
966
967         dvi->depth++;   
968         push(dvi, DVI_PUSH);
969         dvi->pos.w = 0;
970         dvi->pos.x = 0;
971         dvi->pos.y = 0;
972         dvi->pos.z = 0;
973         
974         /* save our state */
975         curr = dvi->currfont;
976         fonts = dvi->fonts;
977         saved_buffer = dvi->buffer;
978         saved_file = dvi->in;
979         dvi->currfont = curr->ref->subfonts;
980         dvi->fonts = curr->ref->subfonts;
981         dvi->buffer.data = macro;
982         dvi->buffer.pos = 0;
983         dvi->buffer.length = len;
984         dvi->buffer.frozen = 1;
985         dvi->in = NULL;
986         oldtop = dvi->stacktop;
987
988         /* execute commands */
989         while((opcode = duget1(dvi)) != DVI_EOP) {
990                 if(dvi_commands[opcode](dvi, opcode) < 0)
991                         break;
992         }
993         if(opcode != DVI_EOP)
994                 dviwarn(dvi, _("%s: vf macro had errors\n"), 
995                         curr->ref->fontname);
996         if(dvi->stacktop != oldtop)
997                 dviwarn(dvi, _("%s: stack not empty after vf macro\n"),
998                         curr->ref->fontname);
999
1000         /* restore things */
1001         pop(dvi, DVI_POP);
1002         dvi->currfont = curr;
1003         dvi->fonts = fonts;
1004         dvi->buffer = saved_buffer;
1005         dvi->in = saved_file;
1006         dvi->depth--;
1007
1008         return (opcode != DVI_EOP ? -1 : 0);
1009 }
1010
1011 int     mdvi_dopage(DviContext *dvi, int pageno)
1012 {
1013         int     op;
1014         int     ppi;
1015         int     reloaded = 0;
1016
1017 again:  
1018         if(dvi->in == NULL) {
1019                 /* try reopening the file */
1020                 dvi->in = fopen(dvi->filename, "r");
1021                 if(dvi->in == NULL) {
1022                         warning(_("%s: could not reopen file (%s)\n"),
1023                                 dvi->filename,
1024                                 strerror(errno));
1025                         return -1;
1026                 }
1027                 DEBUG((DBG_FILES, "reopen(%s) -> Ok\n", dvi->filename));
1028         }
1029         
1030         /* check if we need to reload the file */
1031         if(!reloaded && get_mtime(fileno(dvi->in)) > dvi->modtime) {
1032                 mdvi_reload(dvi, &dvi->params);
1033                 /* we have to reopen the file, again */
1034                 reloaded = 1;
1035                 goto again;
1036         }
1037         
1038         if(pageno < 0 || pageno > dvi->npages-1) {
1039                 error(_("%s: page %d out of range\n"),
1040                         dvi->filename, pageno);
1041                 return -1;
1042         }
1043         
1044         fseek(dvi->in, (long)dvi->pagemap[pageno][0], SEEK_SET);
1045         if((op = fuget1(dvi->in)) != DVI_BOP) {
1046                 error(_("%s: bad offset at page %d\n"),
1047                         dvi->filename, pageno+1);
1048                 return -1;
1049         }
1050         
1051         /* skip bop */
1052         fseek(dvi->in, (long)44, SEEK_CUR);
1053
1054         /* reset state */
1055         dvi->currfont = NULL;
1056         memzero(&dvi->pos, sizeof(DviState));
1057         dvi->stacktop = 0;
1058         dvi->currpage = pageno;
1059         dvi->curr_layer = 0;
1060         
1061         if(dvi->buffer.data && !dvi->buffer.frozen)
1062                 xfree(dvi->buffer.data);
1063
1064         /* reset our buffer */
1065         dvi->buffer.data   = NULL;
1066         dvi->buffer.length = 0;
1067         dvi->buffer.pos    = 0;
1068         dvi->buffer.frozen = 0;
1069
1070 #if 0 /* make colors survive page breaks */
1071         /* reset color stack */
1072         mdvi_reset_color(dvi);
1073 #endif
1074                 
1075         /* set max horizontal and vertical drift (from dvips) */
1076         if(dvi->params.hdrift < 0) {
1077                 ppi = dvi->params.dpi / dvi->params.hshrink; /* shrunk pixels per inch */
1078                 if(ppi < 600)
1079                         dvi->params.hdrift = ppi / 100;
1080                 else if(ppi < 1200)
1081                         dvi->params.hdrift = ppi / 200;
1082                 else
1083                         dvi->params.hdrift = ppi / 400;
1084         }
1085         if(dvi->params.vdrift < 0) {
1086                 ppi = dvi->params.vdpi / dvi->params.vshrink; /* shrunk pixels per inch */
1087                 if(ppi < 600)
1088                         dvi->params.vdrift = ppi / 100;
1089                 else if(ppi < 1200)
1090                         dvi->params.vdrift = ppi / 200;
1091                 else
1092                         dvi->params.vdrift = ppi / 400;
1093         }
1094
1095         dvi->params.thinsp   = FROUND(0.025 * dvi->params.dpi / dvi->params.conv);
1096         dvi->params.vsmallsp = FROUND(0.025 * dvi->params.vdpi / dvi->params.vconv);
1097                 
1098         /* execute all the commands in the page */
1099         while((op = duget1(dvi)) != DVI_EOP) {
1100                 if(dvi_commands[op](dvi, op) < 0)
1101                         break;
1102         }
1103         
1104         fflush(stdout);
1105         fflush(stderr);
1106         if(op != DVI_EOP)
1107                 return -1;
1108         if(dvi->stacktop)
1109                 dviwarn(dvi, _("stack not empty at end of page\n"));
1110         return 0;
1111 }
1112
1113 static int inline move_vertical(DviContext *dvi, int amount)
1114 {
1115         int     rvv;
1116         
1117         dvi->pos.v += amount;
1118         rvv = vpixel_round(dvi, dvi->pos.v);
1119         if(!dvi->params.vdrift)
1120                 return rvv;
1121         if(amount > dvi->params.vsmallsp || amount <= -dvi->params.vsmallsp)
1122                 return rvv;
1123         else {
1124                 int     newvv;
1125                 
1126                 newvv = dvi->pos.vv + vpixel_round(dvi, amount);
1127                 if(rvv - newvv > dvi->params.vdrift)
1128                         return rvv - dvi->params.vdrift;
1129                 else if(newvv - rvv > dvi->params.vdrift)
1130                         return rvv + dvi->params.vdrift;
1131                 else
1132                         return newvv;
1133         }       
1134 }
1135
1136 static int inline move_horizontal(DviContext *dvi, int amount)
1137 {
1138         int     rhh;
1139         
1140         dvi->pos.h += amount;
1141         rhh = pixel_round(dvi, dvi->pos.h);
1142         if(!dvi->params.hdrift)
1143                 return rhh;
1144         else if(amount > dvi->params.thinsp || amount <= -6 * dvi->params.thinsp)
1145                 return rhh;
1146         else {
1147                 int     newhh;
1148                 
1149                 newhh = dvi->pos.hh + pixel_round(dvi, amount);
1150                 if(rhh - newhh > dvi->params.hdrift)
1151                         return rhh - dvi->params.hdrift;
1152                 else if(newhh - rhh > dvi->params.hdrift)
1153                         return rhh + dvi->params.hdrift;
1154                 else
1155                         return newhh;
1156         }       
1157 }
1158
1159 static void inline fix_after_horizontal(DviContext *dvi)
1160 {
1161         int     rhh;
1162
1163         rhh = pixel_round(dvi, dvi->pos.h);
1164         if(!dvi->params.hdrift)
1165                 dvi->pos.hh = rhh;
1166         else if(rhh - dvi->pos.hh > dvi->params.hdrift)
1167                 dvi->pos.hh = rhh - dvi->params.hdrift;
1168         else if(dvi->pos.hh - rhh > dvi->params.hdrift)
1169                 dvi->pos.hh = rhh + dvi->params.hdrift;
1170 }
1171
1172 /* commands */
1173
1174 #define DBGSUM(a,b,c) \
1175         (a), (b) > 0 ? '+' : '-', \
1176         (b) > 0 ? (b) : -(b), (c)
1177
1178 /* 
1179  * The only commands that actually draw something are:
1180  *   set_char, set_rule
1181  */
1182
1183 static void draw_box(DviContext *dvi, DviFontChar *ch)
1184 {
1185         DviGlyph *glyph = NULL;
1186         int     x, y, w, h;
1187                 
1188         if(!MDVI_GLYPH_UNSET(ch->shrunk.data))
1189                 glyph = &ch->shrunk;
1190         else if(!MDVI_GLYPH_UNSET(ch->grey.data))
1191                 glyph = &ch->grey;
1192         else if(!MDVI_GLYPH_UNSET(ch->glyph.data))
1193                 glyph = &ch->glyph;
1194         if(glyph == NULL)
1195                 return;
1196         x = glyph->x;
1197         y = glyph->y;
1198         w = glyph->w;
1199         h = glyph->h;
1200         /* this is bad -- we have to undo the orientation */
1201         switch(dvi->params.orientation) {
1202         case MDVI_ORIENT_TBLR:
1203                 break;
1204         case MDVI_ORIENT_TBRL:
1205                 x = w - x;
1206                 break;
1207         case MDVI_ORIENT_BTLR:
1208                 y = h - y;
1209                 break;
1210         case MDVI_ORIENT_BTRL:
1211                 x = w - x;
1212                 y = h - y;
1213                 break;
1214         case MDVI_ORIENT_RP90:
1215                 SWAPINT(w, h);
1216                 SWAPINT(x, y);
1217                 x = w - x;
1218                 break;
1219         case MDVI_ORIENT_RM90:
1220                 SWAPINT(w, h);
1221                 SWAPINT(x, y);
1222                 y = h - y;
1223                 break;
1224         case MDVI_ORIENT_IRP90:
1225                 SWAPINT(w, h);
1226                 SWAPINT(x, y);
1227                 break;
1228         case MDVI_ORIENT_IRM90:
1229                 SWAPINT(w, h);
1230                 SWAPINT(x, y);
1231                 x = w - x;
1232                 y = h - y;
1233                 break;
1234         }
1235                 
1236         dvi->device.draw_rule(dvi,
1237                 dvi->pos.hh - x, dvi->pos.vv - y, w, h, 1);
1238 }
1239
1240 int     set_char(DviContext *dvi, int opcode)
1241 {
1242         int     num;
1243         int     h;
1244         int     hh;
1245         DviFontChar *ch;
1246         DviFont *font;
1247         
1248         if(opcode < 128)
1249                 num = opcode;
1250         else
1251                 num = dugetn(dvi, opcode - DVI_SET1 + 1);
1252         if(dvi->currfont == NULL) {
1253                 dvierr(dvi, _("no default font set yet\n"));
1254                 return -1;
1255         }
1256         font = dvi->currfont->ref;
1257         ch = font_get_glyph(dvi, font, num);
1258         if(ch == NULL || ch->missing) {
1259                 /* try to display something anyway */
1260                 ch = FONTCHAR(font, num);
1261                 if(!glyph_present(ch)) {
1262                         dviwarn(dvi, 
1263                         _("requested character %d does not exist in `%s'\n"), 
1264                                 num, font->fontname);
1265                         return 0;
1266                 }
1267                 draw_box(dvi, ch);
1268         } else if(dvi->curr_layer <= dvi->params.layer) {
1269                 if(ISVIRTUAL(font))
1270                         mdvi_run_macro(dvi, (Uchar *)font->private + 
1271                                 ch->offset, ch->width);
1272                 else if(ch->width && ch->height)
1273                         dvi->device.draw_glyph(dvi, ch, 
1274                                 dvi->pos.hh, dvi->pos.vv);
1275         }
1276         if(opcode >= DVI_PUT1 && opcode <= DVI_PUT4) {
1277                 SHOWCMD((dvi, "putchar", opcode - DVI_PUT1 + 1,
1278                         "char %d (%s)\n",
1279                         num, dvi->currfont->ref->fontname));
1280         } else {
1281                 h = dvi->pos.h + ch->tfmwidth;
1282                 hh = dvi->pos.hh + pixel_round(dvi, ch->tfmwidth);
1283                 SHOWCMD((dvi, "setchar", num, "(%d,%d) h:=%d%c%d=%d, hh:=%d (%s)\n",
1284                         dvi->pos.hh, dvi->pos.vv,
1285                         DBGSUM(dvi->pos.h, ch->tfmwidth, h), hh,
1286                         font->fontname));
1287                 dvi->pos.h  = h;
1288                 dvi->pos.hh = hh;
1289                 fix_after_horizontal(dvi);
1290         }
1291         
1292         return 0;
1293 }
1294
1295 int     set_rule(DviContext *dvi, int opcode)
1296 {
1297         Int32   a, b;
1298         int     h, w;
1299         
1300         a = dsget4(dvi);
1301         b = dsget4(dvi); w = rule_round(dvi, b);
1302         if(a > 0 && b > 0) {
1303                 h = vrule_round(dvi, a); 
1304                 SHOWCMD((dvi, opcode == DVI_SET_RULE ? "setrule" : "putrule", -1,
1305                         "width %d, height %d (%dx%d pixels)\n",
1306                         b, a, w, h));
1307                 /* the `draw' functions expect the origin to be at the top left
1308                  * corner of the rule, not the bottom left, as in DVI files */
1309                 if(dvi->curr_layer <= dvi->params.layer)
1310                         dvi->device.draw_rule(dvi,
1311                                 dvi->pos.hh, dvi->pos.vv - h + 1, w, h, 1);
1312         } else { 
1313                 SHOWCMD((dvi, opcode == DVI_SET_RULE ? "setrule" : "putrule", -1,
1314                         "(moving left only, by %d)\n", b));
1315         }
1316                         
1317         if(opcode == DVI_SET_RULE) {
1318                 dvi->pos.h  += b;
1319                 dvi->pos.hh += w;
1320                 fix_after_horizontal(dvi);
1321         }
1322         return 0;
1323 }
1324
1325 int     no_op(DviContext *dvi, int opcode)
1326 {
1327         SHOWCMD((dvi, "noop", -1, ""));
1328         return 0;
1329 }
1330
1331 int     push(DviContext *dvi, int opcode)
1332 {
1333         if(dvi->stacktop == dvi->stacksize) {
1334                 if(!dvi->depth)
1335                         dviwarn(dvi, _("enlarging stack\n"));
1336                 dvi->stacksize += 8;
1337                 dvi->stack = xresize(dvi->stack,
1338                         DviState, dvi->stacksize);
1339         }
1340         memcpy(&dvi->stack[dvi->stacktop], &dvi->pos, sizeof(DviState));
1341         SHOWCMD((dvi, "push", -1,
1342                 "level %d: (h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=%d,vv=%d)\n",
1343                 dvi->stacktop, 
1344                 dvi->pos.h, dvi->pos.v, dvi->pos.w, dvi->pos.x,
1345                 dvi->pos.y, dvi->pos.z, dvi->pos.hh, dvi->pos.vv));
1346         dvi->stacktop++;
1347         return 0;
1348 }
1349
1350 int     pop(DviContext *dvi, int opcode)
1351 {
1352         if(dvi->stacktop == 0) {
1353                 dvierr(dvi, _("stack underflow\n"));
1354                 return -1;
1355         }
1356         memcpy(&dvi->pos, &dvi->stack[dvi->stacktop-1], sizeof(DviState));
1357         SHOWCMD((dvi, "pop", -1,
1358                 "level %d: (h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=%d,vv=%d)\n",
1359                 dvi->stacktop, 
1360                 dvi->pos.h, dvi->pos.v, dvi->pos.w, dvi->pos.x,
1361                 dvi->pos.y, dvi->pos.z, dvi->pos.hh, dvi->pos.vv));
1362         dvi->stacktop--;
1363         return 0;
1364 }
1365
1366 int     move_right(DviContext *dvi, int opcode)
1367 {
1368         Int32   arg;
1369         int     h, hh;
1370         
1371         arg = dsgetn(dvi, opcode - DVI_RIGHT1 + 1);
1372         h = dvi->pos.h;
1373         hh = move_horizontal(dvi, arg);
1374         SHOWCMD((dvi, "right", opcode - DVI_RIGHT1 + 1,
1375                 "%d h:=%d%c%d=%d, hh:=%d\n",
1376                 arg, DBGSUM(h, arg, dvi->pos.h), hh));
1377         dvi->pos.hh = hh;
1378         return 0;
1379 }
1380
1381 int     move_down(DviContext *dvi, int opcode)
1382 {
1383         Int32   arg;
1384         int     v, vv;
1385         
1386         arg = dsgetn(dvi, opcode - DVI_DOWN1 + 1);
1387         v = dvi->pos.v;
1388         vv = move_vertical(dvi, arg);
1389         SHOWCMD((dvi, "down", opcode - DVI_DOWN1 + 1,
1390                 "%d v:=%d%c%d=%d, vv:=%d\n",
1391                 arg, DBGSUM(v, arg, dvi->pos.v), vv));
1392         dvi->pos.vv = vv;
1393         return 0;
1394 }
1395
1396 int     move_w(DviContext *dvi, int opcode)
1397 {
1398         int     h, hh;
1399         
1400         if(opcode != DVI_W0)
1401                 dvi->pos.w = dsgetn(dvi, opcode - DVI_W0);
1402         h = dvi->pos.h;
1403         hh = move_horizontal(dvi, dvi->pos.w);
1404         SHOWCMD((dvi, "w", opcode - DVI_W0,
1405                 "%d h:=%d%c%d=%d, hh:=%d\n",
1406                 dvi->pos.w, DBGSUM(h, dvi->pos.w, dvi->pos.h), hh));
1407         dvi->pos.hh = hh;
1408         return 0;
1409 }
1410
1411 int     move_x(DviContext *dvi, int opcode)
1412 {
1413         int     h, hh;
1414         
1415         if(opcode != DVI_X0)
1416                 dvi->pos.x = dsgetn(dvi, opcode - DVI_X0);
1417         h = dvi->pos.h;
1418         hh = move_horizontal(dvi, dvi->pos.x);
1419         SHOWCMD((dvi, "x", opcode - DVI_X0,
1420                 "%d h:=%d%c%d=%d, hh:=%d\n", 
1421                 dvi->pos.x, DBGSUM(h, dvi->pos.x, dvi->pos.h), hh));
1422         dvi->pos.hh = hh;
1423         return 0;
1424 }
1425
1426 int     move_y(DviContext *dvi, int opcode)
1427 {
1428         int     v, vv;
1429         
1430         if(opcode != DVI_Y0)
1431                 dvi->pos.y = dsgetn(dvi, opcode - DVI_Y0);
1432         v = dvi->pos.v;
1433         vv = move_vertical(dvi, dvi->pos.y);
1434         SHOWCMD((dvi, "y", opcode - DVI_Y0,
1435                 "%d h:=%d%c%d=%d, hh:=%d\n", 
1436                 dvi->pos.y, DBGSUM(v, dvi->pos.y, dvi->pos.v), vv));
1437         dvi->pos.vv = vv;
1438         return 0;
1439 }
1440
1441 int     move_z(DviContext *dvi, int opcode)
1442 {
1443         int     v, vv;
1444
1445         if(opcode != DVI_Z0)
1446                 dvi->pos.z = dsgetn(dvi, opcode - DVI_Z0);
1447         v = dvi->pos.v;
1448         vv = move_vertical(dvi, dvi->pos.z);
1449         SHOWCMD((dvi, "z", opcode - DVI_Z0,
1450                 "%d h:=%d%c%d=%d, hh:=%d\n", 
1451                 dvi->pos.z, DBGSUM(v, dvi->pos.z, dvi->pos.v), vv));
1452         dvi->pos.vv = vv;
1453         return 0;
1454 }
1455
1456 int     sel_font(DviContext *dvi, int opcode)
1457 {
1458         DviFontRef *ref;
1459         int     ndx;
1460         
1461         ndx = opcode - DVI_FNT_NUM0;
1462         if(dvi->depth)
1463                 ref = font_find_flat(dvi, ndx);
1464         else
1465                 ref = dvi->findref(dvi, ndx);
1466         if(ref == NULL) {
1467                 dvierr(dvi, _("font %d is not defined\n"),
1468                         opcode - DVI_FNT_NUM0);
1469                 return -1;
1470         }
1471         SHOWCMD((dvi, "fntnum", opcode - DVI_FNT_NUM0,
1472                 "current font is %s\n",
1473                 ref->ref->fontname));
1474         dvi->currfont = ref;
1475         return 0;
1476 }
1477
1478 int     sel_fontn(DviContext *dvi, int opcode)
1479 {
1480         Int32   arg;
1481         DviFontRef *ref;
1482         
1483         arg = dugetn(dvi, opcode - DVI_FNT1 + 1);
1484         if(dvi->depth)
1485                 ref = font_find_flat(dvi, arg);
1486         else
1487                 ref = dvi->findref(dvi, arg);
1488         if(ref == NULL) {
1489                 dvierr(dvi, _("font %d is not defined\n"), arg);
1490                 return -1;
1491         }
1492         SHOWCMD((dvi, "fnt", opcode - DVI_FNT1 + 1,
1493                 "current font is %s (id %d)\n", 
1494                 ref->ref->fontname, arg));
1495         dvi->currfont = ref;
1496         return 0;
1497 }
1498
1499 int     special(DviContext *dvi, int opcode)
1500 {
1501         char    *s;
1502         Int32   arg;
1503         
1504         arg = dugetn(dvi, opcode - DVI_XXX1 + 1);
1505         s = xmalloc(arg + 1);
1506         dread(dvi, s, arg);
1507         s[arg] = 0;
1508         mdvi_do_special(dvi, s);
1509         SHOWCMD((dvi, "XXXX", opcode - DVI_XXX1 + 1,
1510                 "[%s]", s));
1511         xfree(s);
1512         return 0;
1513 }
1514
1515 int     def_font(DviContext *dvi, int opcode)
1516 {
1517         DviFontRef *ref;
1518         Int32   arg;
1519         
1520         arg = dugetn(dvi, opcode - DVI_FNT_DEF1 + 1);
1521         if(dvi->depth)
1522                 ref = font_find_flat(dvi, arg);
1523         else
1524                 ref = dvi->findref(dvi, arg);
1525         /* skip the rest */
1526         dskip(dvi, 12);
1527         dskip(dvi, duget1(dvi) + duget1(dvi));
1528         if(ref == NULL) {
1529                 dvierr(dvi, _("font %d is not defined in postamble\n"), arg);
1530                 return -1;
1531         }
1532         SHOWCMD((dvi, "fntdef", opcode - DVI_FNT_DEF1 + 1,
1533                 "%d -> %s (%d links)\n",
1534                 ref->fontid, ref->ref->fontname,
1535                 ref->ref->links));
1536         return 0;
1537 }
1538
1539 int     unexpected(DviContext *dvi, int opcode)
1540 {
1541         dvierr(dvi, _("unexpected opcode %d\n"), opcode);
1542         return -1;
1543 }
1544
1545 int     undefined(DviContext *dvi, int opcode)
1546 {
1547         dvierr(dvi, _("undefined opcode %d\n"), opcode);
1548         return -1;
1549 }
1550