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