]> www.fi.muni.cz Git - evince.git/blob - backend/dvi/mdvi-lib/pk.c
08377e634b151b64dcb427911c6a9ce9b8d4345a
[evince.git] / backend / dvi / mdvi-lib / pk.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18
19 /*
20  * History:
21  *
22  * 11/3/2000:
23  *    - First working version
24  * 11/4/2000:
25  *    - FIXED: entirely white/black rows were missed.
26  * 11/8/2000:
27  *    - TESTED: Glyphs are rendered correctly in different byte orders.
28  *    - Made bitmap code much more efficient and compact.
29  */
30
31 #include <config.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <math.h>
37
38 #include "mdvi.h"
39 #include "private.h"
40
41 #define PK_ID      89
42 #define PK_CMD_START 240
43 #define PK_X1     240
44 #define PK_X2     241
45 #define PK_X3     242
46 #define PK_X4     243
47 #define PK_Y      244
48 #define PK_POST   245
49 #define PK_NOOP   246
50 #define PK_PRE    247
51
52 #define PK_DYN_F(x)     (((x) >> 4) & 0xf)
53 #define PK_PACKED(x)    (PK_DYN_F(x) != 14)
54
55 static int pk_load_font __PROTO((DviParams *, DviFont *));
56 static int pk_font_get_glyph __PROTO((DviParams *, DviFont *, int));
57
58 static int pk_auto_generate = 1; /* this is ON by default */
59
60 typedef struct {
61         char    currbyte;
62         char    nybpos;
63         int     dyn_f;
64 } pkread;
65
66 static char *pk_lookup __PROTO((const char *, Ushort *, Ushort *));
67 static char *pk_lookupn __PROTO((const char *, Ushort *, Ushort *));
68
69 /* only symbols exported by this file */
70 DviFontInfo pk_font_info = {
71         "PK",
72         0, /* scaling not supported natively */
73         pk_load_font,
74         pk_font_get_glyph,
75         mdvi_shrink_glyph,
76         mdvi_shrink_glyph_grey,
77         NULL,   /* free */
78         NULL,   /* reset */
79         pk_lookup,      /* lookup */
80         kpse_pk_format,
81         NULL
82 };
83
84 DviFontInfo pkn_font_info = {
85         "PKN",
86         0, /* scaling not supported natively */
87         pk_load_font,
88         pk_font_get_glyph,
89         mdvi_shrink_glyph,
90         mdvi_shrink_glyph_grey,
91         NULL,   /* free */
92         NULL,   /* reset */
93         pk_lookupn,     /* lookup */
94         kpse_pk_format,
95         NULL
96 };
97
98 static char *pk_lookup(const char *name, Ushort *hdpi, Ushort *vdpi)
99 {
100         kpse_glyph_file_type type;
101         char *filename;
102
103         if(pk_auto_generate == 0) {
104                 kpse_set_program_enabled(kpse_pk_format, 1, kpse_src_cmdline);
105                 pk_auto_generate = 1;
106         }
107         filename = kpse_find_glyph(name, Max(*hdpi, *vdpi),
108                 kpse_pk_format, &type);
109         if(filename && type.source == kpse_glyph_source_fallback) {
110                 mdvi_free(filename);
111                 filename = NULL;
112         } else if(filename) {
113                 *hdpi = *vdpi = type.dpi;
114         }
115         return filename;        
116 }
117
118 static char *pk_lookupn(const char *name, Ushort *hdpi, Ushort *vdpi)
119 {
120         kpse_glyph_file_type type;
121         char *filename;
122
123         if(pk_auto_generate) {
124                 kpse_set_program_enabled(kpse_pk_format, 0, kpse_src_cmdline);
125                 pk_auto_generate = 0;
126         }
127         filename = kpse_find_glyph(name, Max(*hdpi, *vdpi),
128                 kpse_pk_format, &type);
129         if(filename && type.source == kpse_glyph_source_fallback) {
130                 mdvi_free(filename);
131                 filename = NULL;
132         } else if(filename) {
133                 *hdpi = *vdpi = type.dpi;
134         }
135         return filename;        
136 }
137
138 static inline int pk_get_nyb(FILE *p, pkread *pk)
139 {
140         unsigned t;
141         int     nb;
142         char    c;
143         
144         t = c = pk->currbyte;
145         nb = pk->nybpos;
146         
147         switch(nb) {
148         case 0:
149                 c = pk->currbyte = fuget1(p);
150                 t = (c >> 4);
151                 break;
152         case 1:
153                 t = c;
154                 break;
155         }
156         pk->nybpos = !nb;
157         return (t & 0xf);
158 }
159
160 /* 
161  * this is a bit cumbersome because we have to pass around
162  * the `pkread' data...
163  */
164 static int pk_packed_num(FILE *p, pkread *pkr, int *repeat)
165 {
166         int     i, j;
167         int     dyn_f = pkr->dyn_f;
168         
169         i = pk_get_nyb(p, pkr);
170         if(i == 0) {
171                 do {
172                         j = pk_get_nyb(p, pkr);
173                         i++;
174                 } while(j == 0);
175                 while(i-- > 0)
176                         j = (j << 4) + pk_get_nyb(p, pkr);
177                 return (j - 15 + ((13 - dyn_f) << 4) + 
178                         dyn_f);
179         } else if(i <= dyn_f)
180                 return i;
181         else if(i < 14)
182                 return ((i - dyn_f - 1) << 4) + 
183                         pk_get_nyb(p, pkr) + dyn_f + 1;
184         else {
185                 *repeat = 1;
186                 if(i == 14)
187                         *repeat = pk_packed_num(p, pkr, repeat);
188                 return pk_packed_num(p, pkr, repeat);
189         }
190 }
191
192 #define ROUND(x,y)      (((x) + (y) - 1) / (y))
193
194 static BITMAP *get_bitmap(FILE *p, int w, int h, int flags)
195 {
196         int     i, j;
197         BmUnit  *ptr;
198         BITMAP  *bm;
199         int     bitpos;
200         int     currch;
201                 
202         flags = 0; /* shut up that compiler */
203         bitpos = -1;
204         if((bm = bitmap_alloc(w, h)) == NULL)
205                 return NULL;
206         DEBUG((DBG_BITMAPS, "get_bitmap(%d,%d,%d): reading raw bitmap\n",
207                 w, h, flags));
208         ptr = bm->data;
209         currch = 0;
210         for(i = 0; i < h; i++) {
211                 BmUnit  mask;
212                 
213                 mask = FIRSTMASK;
214                 for(j = 0; j < w; j++) {
215                         if(bitpos < 0) {
216                                 currch = fuget1(p);
217                                 bitpos = 7;
218                         }
219                         if(currch & (1 << bitpos))
220                                 *ptr |= mask;
221                         bitpos--;
222                         if(mask == LASTMASK) {
223                                 ptr++;
224                                 mask = FIRSTMASK;
225                         } else
226                                 NEXTMASK(mask);
227                 }
228                 ptr = bm_offset(ptr, bm->stride);
229         }
230         return bm;
231 }
232
233 static BITMAP *get_packed(FILE *p, int w, int h, int flags)
234 {
235         int     inrow, count;
236         int     row;
237         BITMAP  *bm;
238         int     repeat_count;
239         int     paint;
240         pkread  pkr;
241                 
242         pkr.nybpos = 0;
243         pkr.currbyte = 0;
244         pkr.dyn_f = PK_DYN_F(flags);
245         paint = !!(flags & 0x8);
246
247         repeat_count = 0;
248         row = 0;
249         inrow = w;
250         if((bm = bitmap_alloc(w, h)) == NULL)
251                 return NULL;
252         DEBUG((DBG_BITMAPS, "get_packed(%d,%d,%d): reading packed glyph\n",
253                 w, h, flags));
254         while(row < h) {
255                 int     i = 0;
256                 
257                 count = pk_packed_num(p, &pkr, &i);
258                 if(i > 0) {
259                         if(repeat_count)
260                                 fprintf(stderr, "second repeat count for this row (had %d and got %d)\n",
261                                         repeat_count, i);
262                         repeat_count = i;
263                 }
264                 
265                 if(count >= inrow) {
266                         Uchar   *r, *t;
267                         BmUnit  *a, mask;
268                         
269                         /* first finish current row */
270                         if(paint)
271                                 bitmap_set_row(bm, row, w - inrow, inrow, paint);
272                         /* now copy it as many times as required */
273                         r = (Uchar *)bm->data + row * bm->stride;
274                         while(repeat_count-- > 0) {
275                                 t = r + bm->stride;
276                                 /* copy entire lines */
277                                 memcpy(t, r, bm->stride);
278                                 r = t;
279                                 row++;
280                         }
281                         repeat_count = 0;
282                         /* count first row we drew */
283                         row++;
284                         /* update run count */
285                         count -= inrow;
286                         /* now r points to the beginning of the last row we finished */
287                         if(paint)
288                                 mask = ~((BmUnit)0);
289                         else
290                                 mask = 0;
291                         /* goto next row */
292                         a = (BmUnit *)(r + bm->stride);
293                         /* deal with entirely with/black rows */
294                         while(count >= w) {
295                                 /* count number of atoms in a row */
296                                 i = ROUND(w, BITMAP_BITS);
297                                 while(i-- > 0)
298                                         *a++ = mask;
299                                 count -= w;
300                                 row++;
301                         }
302                         inrow = w;
303                 }
304                 if(count > 0)
305                         bitmap_set_row(bm, row, w - inrow, count, paint);
306                 inrow -= count;
307                 paint = !paint;
308         }
309         if(row != h || inrow != w) {
310                 mdvi_error(_("Bad PK file: More bits than required\n"));
311                 bitmap_destroy(bm);
312                 return NULL;
313         }
314         return bm;
315 }
316
317 static BITMAP *get_char(FILE *p, int w, int h, int flags)
318 {
319         /* check if dyn_f == 14 */
320         if(((flags >> 4) & 0xf) == 14)
321                 return get_bitmap(p, w, h, flags);
322         else
323                 return get_packed(p, w, h, flags);
324 }
325
326 /* supports any number of characters in a font */
327 static int pk_load_font(DviParams *unused, DviFont *font)
328 {
329         int     i;
330         int     flag_byte;
331         int     loc, hic, maxch;
332         Int32   checksum;
333         FILE    *p;
334 #ifndef NODEBUG
335         char    s[256];
336 #endif
337         long    alpha, beta, z;
338
339         font->chars = xnalloc(DviFontChar, 256);
340         p = font->in;
341         memzero(font->chars, 256 * sizeof(DviFontChar));
342         for(i = 0; i < 256; i++)
343                 font->chars[i].offset = 0;
344
345         /* check the preamble */
346         loc = fuget1(p); hic = fuget1(p);
347         if(loc != PK_PRE || hic != PK_ID)
348                 goto badpk;
349         i = fuget1(p);
350 #ifndef NODEBUG
351         for(loc = 0; loc < i; loc++)
352                 s[loc] = fuget1(p);
353         s[loc] = 0;
354         DEBUG((DBG_FONTS, "(pk) %s: %s\n", font->fontname, s));
355 #else
356         fseek(in, (long)i, SEEK_CUR);
357 #endif
358         /* get the design size */
359         font->design = fuget4(p);
360         /* get the checksum */
361         checksum = fuget4(p);
362         if(checksum && font->checksum && font->checksum != checksum) {
363                 mdvi_warning(_("%s: checksum mismatch (expected %u, got %u)\n"),
364                              font->fontname, font->checksum, checksum);
365         } else if(!font->checksum)
366                 font->checksum = checksum;
367         /* skip pixel per point ratios */
368         fuget4(p);
369         fuget4(p);
370         if(feof(p))
371                 goto badpk;     
372
373         /* now start reading the font */
374         loc = 256; hic = -1; maxch = 256;
375         
376         /* initialize alpha and beta for TFM width computation */
377         TFMPREPARE(font->scale, z, alpha, beta);
378
379         while((flag_byte = fuget1(p)) != PK_POST) {
380                 if(feof(p))
381                         break;
382                 if(flag_byte >= PK_CMD_START) {
383                         switch(flag_byte) {
384                         case PK_X1:
385                         case PK_X2:
386                         case PK_X3:
387                         case PK_X4: {
388 #ifndef NODEBUG
389                                 char    *t;
390                                 int     n;
391                                 
392                                 i = fugetn(p, flag_byte - PK_X1 + 1);
393                                 if(i < 256)
394                                         t = &s[0];
395                                 else
396                                         t = mdvi_malloc(i + 1);
397                                 for(n = 0; n < i; n++)
398                                         t[n] = fuget1(p);
399                                 t[n] = 0;
400                                 DEBUG((DBG_SPECIAL, "(pk) %s: Special \"%s\"\n",
401                                         font->fontname, t));
402                                 if(t != &s[0])
403                                         mdvi_free(t);
404 #else
405                                 i = fugetn(p, flag_byte - PK_X1 + 1);
406                                 while(i-- > 0)
407                                         fuget1(p);
408 #endif
409                                 break;
410                         }
411                         case PK_Y:
412                                 i = fuget4(p);
413                                 DEBUG((DBG_SPECIAL, "(pk) %s: MF special %u\n",
414                                         font->fontname, (unsigned)i));
415                                 break;
416                         case PK_POST:
417                         case PK_NOOP:
418                                 break;
419                         case PK_PRE:
420                                 mdvi_error(_("%s: unexpected preamble\n"), font->fontname);
421                                 goto error;
422                         }
423                 } else {
424                         int     pl;
425                         int     cc;
426                         int     w, h;
427                         int     x, y;
428                         int     offset;
429                         long    tfm;
430                         
431                         switch(flag_byte & 0x7) {
432                         case 7:
433                                 pl = fuget4(p);
434                                 cc = fuget4(p);
435                                 offset = ftell(p) + pl;
436                                 tfm = fuget4(p);
437                                 fsget4(p); /* skip dx */
438                                 fsget4(p); /* skip dy */
439                                 w  = fuget4(p);
440                                 h  = fuget4(p); 
441                                 x  = fsget4(p);
442                                 y  = fsget4(p);
443                                 break;
444                         case 4:
445                         case 5:
446                         case 6:                         
447                                 pl = (flag_byte % 4) * 65536 + fuget2(p);
448                                 cc = fuget1(p);
449                                 offset = ftell(p) + pl;
450                                 tfm = fuget3(p);
451                                 fsget2(p); /* skip dx */
452                                            /* dy assumed 0 */
453                                 w = fuget2(p);
454                                 h = fuget2(p);
455                                 x = fsget2(p);
456                                 y = fsget2(p);
457                                 break;
458                         default:
459                                 pl = (flag_byte % 4) * 256 + fuget1(p);
460                                 cc = fuget1(p);
461                                 offset = ftell(p) + pl;
462                                 tfm = fuget3(p);
463                                 fsget1(p); /* skip dx */
464                                            /* dy assumed 0 */
465                                 w = fuget1(p);
466                                 h = fuget1(p);
467                                 x = fsget1(p);
468                                 y = fsget1(p);
469                         }
470                         if(feof(p))
471                                 break;
472
473                         /* Although the PK format support bigger char codes,
474                          * XeTeX and other extended TeX engines support charcodes up to
475                          * 65536, while normal TeX engine supports only charcode up to 255.*/
476                         if (cc < 0 || cc > 65536) {
477                                 mdvi_error (_("%s: unexpected charcode (%d)\n"),
478                                             font->fontname,cc);
479                                 goto error;
480                         } 
481                         if(cc < loc)
482                                 loc = cc;
483                         if(cc > hic)
484                                 hic = cc;
485                         if(cc > maxch) {
486                                 font->chars = xresize(font->chars, 
487                                         DviFontChar, cc + 16);
488                                 for(i = maxch; i < cc + 16; i++)
489                                         font->chars[i].offset = 0;
490                                 maxch = cc + 16;
491                         }
492                         font->chars[cc].code = cc;
493                         font->chars[cc].flags = flag_byte;
494                         font->chars[cc].offset = ftell(p);
495                         font->chars[cc].width = w;
496                         font->chars[cc].height = h;
497                         font->chars[cc].glyph.data = NULL;
498                         font->chars[cc].x = x;
499                         font->chars[cc].y = y;
500                         font->chars[cc].glyph.x = x;
501                         font->chars[cc].glyph.y = y;
502                         font->chars[cc].glyph.w = w;
503                         font->chars[cc].glyph.h = h;
504                         font->chars[cc].grey.data = NULL;
505                         font->chars[cc].shrunk.data = NULL;
506                         font->chars[cc].tfmwidth = TFMSCALE(z, tfm, alpha, beta);
507                         font->chars[cc].loaded = 0;
508                         fseek(p, (long)offset, SEEK_SET);
509                 }
510         }
511         if(flag_byte != PK_POST) {
512                 mdvi_error(_("%s: unexpected end of file (no postamble)\n"),
513                            font->fontname);
514                 goto error;
515         }
516         while((flag_byte = fuget1(p)) != EOF) {
517                 if(flag_byte != PK_NOOP) {
518                         mdvi_error(_("invalid PK file! (junk in postamble)\n"));
519                         goto error;
520                 }
521         }
522
523         /* resize font char data */
524         if(loc > 0 && hic < maxch-1) {
525                 memmove(font->chars, font->chars + loc, 
526                         (hic - loc + 1) * sizeof(DviFontChar));
527                 font->chars = xresize(font->chars,
528                         DviFontChar, hic - loc + 1);
529         }
530         font->loc = loc;
531         font->hic = hic;                
532         return 0;
533
534 badpk:
535         mdvi_error(_("%s: File corrupted, or not a PK file\n"), font->fontname);
536 error:
537         mdvi_free(font->chars);
538         font->chars = NULL;
539         font->loc = font->hic = 0;
540         return -1;
541 }
542
543 static int pk_font_get_glyph(DviParams *params, DviFont *font, int code)
544 {
545         DviFontChar     *ch;
546
547         if((ch = FONTCHAR(font, code)) == NULL)
548                 return -1;
549         
550         if(ch->offset == 0)
551                 return -1;
552         DEBUG((DBG_GLYPHS, "(pk) loading glyph for character %d (%dx%d) in font `%s'\n",
553                 code, ch->width, ch->height, font->fontname));
554         if(font->in == NULL && font_reopen(font) < 0)
555                 return -1;
556         if(!ch->width || !ch->height) {
557                 /* this happens for ` ' (ASCII 32) in some fonts */
558                 ch->glyph.x = ch->x;
559                 ch->glyph.y = ch->y;
560                 ch->glyph.w = ch->width;
561                 ch->glyph.h = ch->height;
562                 ch->glyph.data = NULL;
563                 return 0; 
564         }
565         if(fseek(font->in, ch->offset, SEEK_SET) == -1)
566                 return -1;
567         ch->glyph.data = get_char(font->in, 
568                 ch->width, ch->height, ch->flags);
569         if(ch->glyph.data) {
570                 /* restore original settings */
571                 ch->glyph.x = ch->x;
572                 ch->glyph.y = ch->y;
573                 ch->glyph.w = ch->width;
574                 ch->glyph.h = ch->height;
575         } else
576                 return -1;
577         ch->loaded = 1;
578         return 0;
579 }