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
pk_lookup(const char * name,Ushort * hdpi,Ushort * vdpi)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
pk_lookupn(const char * name,Ushort * hdpi,Ushort * vdpi)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
pk_get_nyb(FILE * p,pkread * pk)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 */
pk_packed_num(FILE * p,pkread * pkr,int * repeat)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
get_bitmap(FILE * p,int w,int h,int flags)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
get_packed(FILE * p,int w,int h,int flags)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
get_char(FILE * p,int w,int h,int flags)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 */
pk_load_font(DviParams * unused,DviFont * font)327 static int pk_load_font(DviParams *unused, DviFont *font)
328 {
329 int i;
330 int flag_byte;
331 int hic, maxch;
332 Int32 checksum;
333 FILE *p;
334 #ifndef NODEBUG
335 char s[256];
336 #endif
337 long alpha, beta, z;
338 unsigned int loc;
339
340 font->chars = xnalloc(DviFontChar, 256);
341 p = font->in;
342 memzero(font->chars, 256 * sizeof(DviFontChar));
343 for(i = 0; i < 256; i++)
344 font->chars[i].offset = 0;
345
346 /* check the preamble */
347 loc = fuget1(p); hic = fuget1(p);
348 if(loc != PK_PRE || hic != PK_ID)
349 goto badpk;
350 i = fuget1(p);
351 #ifndef NODEBUG
352 for(loc = 0; loc < i; loc++)
353 s[loc] = fuget1(p);
354 s[loc] = 0;
355 DEBUG((DBG_FONTS, "(pk) %s: %s\n", font->fontname, s));
356 #else
357 fseek(in, (long)i, SEEK_CUR);
358 #endif
359 /* get the design size */
360 font->design = fuget4(p);
361 /* get the checksum */
362 checksum = fuget4(p);
363 if(checksum && font->checksum && font->checksum != checksum) {
364 mdvi_warning(_("%s: checksum mismatch (expected %u, got %u)\n"),
365 font->fontname, font->checksum, checksum);
366 } else if(!font->checksum)
367 font->checksum = checksum;
368 /* skip pixel per point ratios */
369 fuget4(p);
370 fuget4(p);
371 if(feof(p))
372 goto badpk;
373
374 /* now start reading the font */
375 loc = 256; hic = -1; maxch = 256;
376
377 /* initialize alpha and beta for TFM width computation */
378 TFMPREPARE(font->scale, z, alpha, beta);
379
380 while((flag_byte = fuget1(p)) != PK_POST) {
381 if(feof(p))
382 break;
383 if(flag_byte >= PK_CMD_START) {
384 switch(flag_byte) {
385 case PK_X1:
386 case PK_X2:
387 case PK_X3:
388 case PK_X4: {
389 #ifndef NODEBUG
390 char *t;
391 int n;
392
393 i = fugetn(p, flag_byte - PK_X1 + 1);
394 if(i < 256)
395 t = &s[0];
396 else
397 t = mdvi_malloc(i + 1);
398 for(n = 0; n < i; n++)
399 t[n] = fuget1(p);
400 t[n] = 0;
401 DEBUG((DBG_SPECIAL, "(pk) %s: Special \"%s\"\n",
402 font->fontname, t));
403 if(t != &s[0])
404 mdvi_free(t);
405 #else
406 i = fugetn(p, flag_byte - PK_X1 + 1);
407 while(i-- > 0)
408 fuget1(p);
409 #endif
410 break;
411 }
412 case PK_Y:
413 i = fuget4(p);
414 DEBUG((DBG_SPECIAL, "(pk) %s: MF special %u\n",
415 font->fontname, (unsigned)i));
416 break;
417 case PK_POST:
418 case PK_NOOP:
419 break;
420 case PK_PRE:
421 mdvi_error(_("%s: unexpected preamble\n"), font->fontname);
422 goto error;
423 }
424 } else {
425 int pl;
426 int cc;
427 int w, h;
428 int x, y;
429 int offset;
430 long tfm;
431
432 switch(flag_byte & 0x7) {
433 case 7:
434 pl = fuget4(p);
435 cc = fuget4(p);
436 offset = ftell(p) + pl;
437 tfm = fuget4(p);
438 fsget4(p); /* skip dx */
439 fsget4(p); /* skip dy */
440 w = fuget4(p);
441 h = fuget4(p);
442 x = fsget4(p);
443 y = fsget4(p);
444 break;
445 case 4:
446 case 5:
447 case 6:
448 pl = (flag_byte % 4) * 65536 + fuget2(p);
449 cc = fuget1(p);
450 offset = ftell(p) + pl;
451 tfm = fuget3(p);
452 fsget2(p); /* skip dx */
453 /* dy assumed 0 */
454 w = fuget2(p);
455 h = fuget2(p);
456 x = fsget2(p);
457 y = fsget2(p);
458 break;
459 default:
460 pl = (flag_byte % 4) * 256 + fuget1(p);
461 cc = fuget1(p);
462 offset = ftell(p) + pl;
463 tfm = fuget3(p);
464 fsget1(p); /* skip dx */
465 /* dy assumed 0 */
466 w = fuget1(p);
467 h = fuget1(p);
468 x = fsget1(p);
469 y = fsget1(p);
470 }
471 if(feof(p))
472 break;
473
474 /* Although the PK format support bigger char codes,
475 * XeTeX and other extended TeX engines support charcodes up to
476 * 65536, while normal TeX engine supports only charcode up to 255.*/
477 if (cc < 0 || cc > 65536) {
478 mdvi_error (_("%s: unexpected charcode (%d)\n"),
479 font->fontname,cc);
480 goto error;
481 }
482 if(cc < loc)
483 loc = cc;
484 if(cc > hic)
485 hic = cc;
486 if(cc > maxch) {
487 font->chars = xresize(font->chars,
488 DviFontChar, cc + 16);
489 for(i = maxch; i < cc + 16; i++)
490 font->chars[i].offset = 0;
491 maxch = cc + 16;
492 }
493 font->chars[cc].code = cc;
494 font->chars[cc].flags = flag_byte;
495 font->chars[cc].offset = ftell(p);
496 font->chars[cc].width = w;
497 font->chars[cc].height = h;
498 font->chars[cc].glyph.data = NULL;
499 font->chars[cc].x = x;
500 font->chars[cc].y = y;
501 font->chars[cc].glyph.x = x;
502 font->chars[cc].glyph.y = y;
503 font->chars[cc].glyph.w = w;
504 font->chars[cc].glyph.h = h;
505 font->chars[cc].grey.data = NULL;
506 font->chars[cc].shrunk.data = NULL;
507 font->chars[cc].tfmwidth = TFMSCALE(z, tfm, alpha, beta);
508 font->chars[cc].loaded = 0;
509 fseek(p, (long)offset, SEEK_SET);
510 }
511 }
512 if(flag_byte != PK_POST) {
513 mdvi_error(_("%s: unexpected end of file (no postamble)\n"),
514 font->fontname);
515 goto error;
516 }
517 while((flag_byte = fuget1(p)) != EOF) {
518 if(flag_byte != PK_NOOP) {
519 mdvi_error(_("invalid PK file! (junk in postamble)\n"));
520 goto error;
521 }
522 }
523
524 /* resize font char data */
525 if(loc > 0 || hic < maxch-1) {
526 memmove(font->chars, font->chars + loc,
527 (hic - loc + 1) * sizeof(DviFontChar));
528 font->chars = xresize(font->chars,
529 DviFontChar, hic - loc + 1);
530 }
531 font->loc = loc;
532 font->hic = hic;
533 return 0;
534
535 badpk:
536 mdvi_error(_("%s: File corrupted, or not a PK file\n"), font->fontname);
537 error:
538 mdvi_free(font->chars);
539 font->chars = NULL;
540 font->loc = font->hic = 0;
541 return -1;
542 }
543
pk_font_get_glyph(DviParams * params,DviFont * font,int code)544 static int pk_font_get_glyph(DviParams *params, DviFont *font, int code)
545 {
546 DviFontChar *ch;
547
548 if((ch = FONTCHAR(font, code)) == NULL)
549 return -1;
550
551 if(ch->offset == 0)
552 return -1;
553 DEBUG((DBG_GLYPHS, "(pk) loading glyph for character %d (%dx%d) in font `%s'\n",
554 code, ch->width, ch->height, font->fontname));
555 if(font->in == NULL && font_reopen(font) < 0)
556 return -1;
557 if(!ch->width || !ch->height) {
558 /* this happens for ` ' (ASCII 32) in some fonts */
559 ch->glyph.x = ch->x;
560 ch->glyph.y = ch->y;
561 ch->glyph.w = ch->width;
562 ch->glyph.h = ch->height;
563 ch->glyph.data = NULL;
564 return 0;
565 }
566 if(fseek(font->in, ch->offset, SEEK_SET) == -1)
567 return -1;
568 ch->glyph.data = get_char(font->in,
569 ch->width, ch->height, ch->flags);
570 if(ch->glyph.data) {
571 /* restore original settings */
572 ch->glyph.x = ch->x;
573 ch->glyph.y = ch->y;
574 ch->glyph.w = ch->width;
575 ch->glyph.h = ch->height;
576 } else
577 return -1;
578 ch->loaded = 1;
579 return 0;
580 }
581