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