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