1 /*	$Id$ */
2 /*
3  * Copyright (c) 1994-1996 Sam Leffler
4  * Copyright (c) 1994-1996 Silicon Graphics, Inc.
5  * HylaFAX is a trademark of Silicon Graphics
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and
8  * its documentation for any purpose is hereby granted without fee, provided
9  * that (i) the above copyright notices and this permission notice appear in
10  * all copies of the software and related documentation, and (ii) the names of
11  * Sam Leffler and Silicon Graphics may not be used in any advertising or
12  * publicity relating to the software without the specific, prior written
13  * permission of Sam Leffler and Silicon Graphics.
14  *
15  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
17  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
18  *
19  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
20  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
21  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
22  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
23  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
24  * OF THIS SOFTWARE.
25  */
26 
27 /*
28  * Read and render a PCF font.  This code is VERY distantly
29  * related to the X11R5 code found in the file pcfread.c (the
30  * original copyright is at the bottom of this file).
31  *
32  * This code is specifically written to handle just enough
33  * of the format to image text for outgoing facsimile.
34  */
35 #include <unistd.h>
36 #include "PCFFont.h"
37 #include "tiffio.h"
38 
39 #define LSBFirst		0
40 #define MSBFirst		1
41 
42 struct PCFTableRec {		// on-disk table of contents
43     u_long	type;
44     u_long	format;
45     u_long	size;
46     u_long	offset;
47 };
48 
49 struct charInfo {
50     short	lsb, rsb;	// left+right side bearing from file
51     short	ascent, descent;// ascent+descent from file
52     u_short	cw;		// character width from file
53     u_char*	bits;		// pointer to glyph bitmap
54 };
55 
56 #define PCF_FILE_VERSION	(('p'<<24)|('c'<<16)|('f'<<8)|1)
57 #define	PCF_FORMAT_MASK		0xffffff00
58 
59 #define PCF_DEFAULT_FORMAT	0x00000000
60 #define PCF_COMPRESSED_METRICS	0x00000100
61 #define PCF_ACCEL_W_INKBOUNDS	0x00000100
62 
63 #define PCF_METRICS		(1<<2)	// font metric information
64 #define PCF_BITMAPS		(1<<3)	// glyph bitmaps
65 #define	PCF_BDF_ENCODINGS	(1<<5)	// BDF-based encoding
66 #define PCF_BDF_ACCELERATORS	(1<<8)	// BDF-derived accelerator information
67 
68 #define PCF_GLYPH_PAD_MASK	(3<<0)
69 #define PCF_BYTE_MASK		(1<<2)
70 #define PCF_BIT_MASK		(1<<3)
71 #define PCF_SCAN_UNIT_MASK	(3<<4)
72 
73 #define	CONFIG_BIT	MSBFirst	// required bit order
74 #define	CONFIG_BYTE	MSBFirst	// required byte order
75 #define	CONFIG_GLYPH	2		// glyphs must be word-aligned
76 #define	CONFIG_GLYPH_IX	1		// index for word-aligned glyphs
77 
isFormat(u_long f) const78 inline bool PCFFont::isFormat(u_long f) const
79     { return ((format&PCF_FORMAT_MASK) == f); }
byteOrder() const80 inline u_int PCFFont::byteOrder() const
81     { return ((format&PCF_BYTE_MASK) ? MSBFirst : LSBFirst); }
bitOrder() const82 inline u_int PCFFont::bitOrder() const
83     { return ((format&PCF_BIT_MASK) ? MSBFirst : LSBFirst); }
glyphPadIndex() const84 inline u_int PCFFont::glyphPadIndex() const
85     { return ((format) & PCF_GLYPH_PAD_MASK); }
glyphPad() const86 inline u_int PCFFont::glyphPad() const
87     { return (1<<glyphPadIndex()); }
scanUnit() const88 inline u_int PCFFont::scanUnit() const
89     { return (1<<((format) & PCF_SCAN_UNIT_MASK)); }
90 
PCFFont()91 PCFFont::PCFFont()
92 {
93     file = NULL;
94     filename = NULL;
95     encoding = NULL;
96     bitmaps = NULL;
97     metrics = NULL;
98     toc = NULL;
99     cdef = NULL;
100     { union { int32 i; char c[4]; } u; u.i = 1; isBigEndian = u.c[0] == 0; }
101 }
102 
~PCFFont()103 PCFFont::~PCFFont()
104 {
105     cleanup();
106 }
107 
108 void
cleanup()109 PCFFont::cleanup()
110 {
111     if (file != NULL)
112 	fclose(file), file = NULL;
113     ready = false;
114     delete toc, toc = NULL;
115     delete encoding, encoding = NULL;
116     delete bitmaps, bitmaps = NULL;
117     delete metrics, metrics = NULL;
118     cdef = NULL;
119 }
120 
121 /*
122  * Read only the useful bits from a PCF font file:
123  * font metrics, font bitmaps, font encoding, and
124  * some of the accelerator info.
125  */
126 bool
read(const char * name)127 PCFFont::read(const char* name)
128 {
129     cleanup();
130     filename = name;				// for error diagnostics
131     file = fopen(filename, "r");
132     if (file == NULL) {
133 	error("Can not open file");
134 	return (false);
135     }
136     if (!readTOC())
137 	return (false);
138     if (seekToTable(PCF_METRICS)) {
139 	format = getLSB32();
140 	if (isFormat(PCF_DEFAULT_FORMAT))
141 	    numGlyphs = getINT32();
142 	else if (isFormat(PCF_COMPRESSED_METRICS))
143 	    numGlyphs = getINT16();
144 	else {
145 	    error("Bad font metric format 0x%lx", format);
146 	    return (false);
147 	}
148 	metrics = new charInfo[numGlyphs];
149 	if (!metrics) {
150 	    error("No space for font metric information");
151 	    return (false);
152 	}
153 	for (u_int i = 0; i < numGlyphs; i++) {
154 	    if (isFormat(PCF_DEFAULT_FORMAT))
155 		getMetric(metrics[i]);
156 	    else
157 		getCompressedMetric(metrics[i]);
158 	}
159     } else {
160 	error("Can not seek to font metric information");
161 	return (false);
162     }
163 
164     if (seekToTable(PCF_BITMAPS)) {
165 	format = getLSB32();
166 	if (!isFormat(PCF_DEFAULT_FORMAT)) {
167 	    error("Bad bitmap data format 0x%lx", format);
168 	    return (false);
169 	}
170 	u_long nbitmaps = getINT32();
171 	u_long* offsets = new u_long[nbitmaps];
172 	if (!offsets) {
173 	    error("No space for bitmap offsets array");
174 	    return (false);
175 	}
176 	for (u_int i = 0; i < nbitmaps; i++)
177 	    offsets[i] = getINT32();
178 	u_long bitmapSizes[4];
179 	bitmapSizes[0] = getINT32();
180 	bitmapSizes[1] = getINT32();
181 	bitmapSizes[2] = getINT32();
182 	bitmapSizes[3] = getINT32();
183 	u_long sizebitmaps = bitmapSizes[glyphPadIndex()];
184 	bitmaps = new u_char[sizebitmaps];
185 	if (!bitmaps) {
186 	    error("No space for bitmap data array");
187 	    delete offsets;
188 	    return (false);
189 	}
190 	if (fread(bitmaps, (u_int) sizebitmaps, 1, file) != 1) {
191 	    error("Error reading bitmap data");
192 	    delete offsets;
193 	    return (false);
194 	}
195 	if (bitOrder() != CONFIG_BIT)
196 	    TIFFReverseBits(bitmaps, sizebitmaps);
197 	if (byteOrder() != bitOrder()) {
198 	    switch (scanUnit()) {
199 	    case 2:
200 		TIFFSwabArrayOfShort((uint16*) bitmaps, sizebitmaps/2);
201 		break;
202 	    case 4:
203 		TIFFSwabArrayOfLong((uint32*) bitmaps, sizebitmaps/4);
204 		break;
205 	    default:
206 		error("Unknown scan unit format %d\n", scanUnit());
207 		break;
208 	    }
209 	}
210 	if (!isBigEndian)		// NB: rasterizer requires BE byte order
211 	    TIFFSwabArrayOfShort((u_short*) bitmaps, sizebitmaps/2);
212 	if (glyphPad() != CONFIG_GLYPH) {
213 	    u_long sizepadbitmaps = bitmapSizes[CONFIG_GLYPH_IX];
214 	    u_char* padbitmaps = new u_char[sizepadbitmaps];
215 	    if (!padbitmaps) {
216 		error("No space for padded bitmap data array");
217 		delete offsets;
218 		return (false);
219 	    }
220 	    int newoff = 0;
221 	    for (u_int i = 0; i < nbitmaps; i++) {
222 		off_t old = offsets[i];
223 		offsets[i] = newoff;
224 		const charInfo& metric = metrics[i];
225 		newoff += repadBitmap(bitmaps + old, padbitmaps + newoff,
226 			  glyphPad(), CONFIG_GLYPH,
227 			  metric.rsb - metric.lsb,
228 			  metric.ascent + metric.descent);
229 	    }
230 	    delete bitmaps;
231 	    bitmaps = padbitmaps;
232 	}
233 	for (u_int j = 0; j < nbitmaps; j++) {
234 	    metrics[j].bits = bitmaps + offsets[j];
235 	    if ((unsigned long) metrics[j].bits & 1) {
236 		error("Internal error, bitmap data not word-aligned");
237 		delete offsets;
238 		return (false);
239 	    }
240 	}
241 	delete offsets;
242     } else {
243 	error("Can not seek to bitmap data");
244 	return (false);
245     }
246 
247     if (seekToTable(PCF_BDF_ENCODINGS)) {
248 	format = getLSB32();
249 	if (!isFormat(PCF_DEFAULT_FORMAT)) {
250 	    error("Bad encodings format 0x%lx", format);
251 	    return (false);
252 	}
253 	firstCol = getINT16();
254 	lastCol = getINT16();
255 	u_short firstRow = getINT16();
256 	u_short lastRow = getINT16();
257 	u_short defaultCh = getINT16();
258 
259 	u_int nencoding = (lastCol-firstCol+1) * (lastRow-firstRow+1);
260 	encoding = new charInfo*[nencoding];
261 	if (!encoding) {
262 	    error("No space for character encoding vector");
263 	    return (false);
264 	}
265 	for (u_int i = 0; i < nencoding; i++) {
266 	    int encodingOffset = getINT16();
267 	    encoding[i] = (encodingOffset == 0xffff) ?
268 		0 : metrics + encodingOffset;
269 	}
270 	if (defaultCh != (u_short)-1) {
271 	    int r = defaultCh >> 8;
272 	    int c = defaultCh & 0xff;
273 	    if (firstRow <= r && r <= lastRow && firstCol <= c && c <= lastCol) {
274 		int cols = lastCol - firstCol + 1;
275 		r = r - firstRow;
276 		c = c - firstCol;
277 		cdef = encoding[r * cols + c];
278 	    }
279 	}
280     } else {
281 	error("Can not seek to encoding data");
282 	return (false);
283     }
284 
285     if (seekToTable(PCF_BDF_ACCELERATORS)) {
286 	format = getLSB32();
287 	if (!isFormat(PCF_DEFAULT_FORMAT) &&
288 	    !isFormat(PCF_ACCEL_W_INKBOUNDS)) {
289 	    error("Bad BDF accelerator format 0x%lx", format);
290 	    return (false);
291 	}
292 	fseek(file, 8, SEEK_CUR);	// skip a bunch of junk
293 	fontAscent = (short) getINT32();
294 	fontDescent = (short) getINT32();
295 	// more stuff...
296     } else {
297 	error("Can not seek to BDF accelerator information");
298 	return (false);
299     }
300     fclose(file), file = NULL;
301     filename = NULL;
302     return (ready = true);
303 }
304 
305 u_long
getLSB32()306 PCFFont::getLSB32()
307 {
308     u_long c = getc(file);
309     c |= getc(file) << 8;
310     c |= getc(file) << 16;
311     c |= getc(file) << 24;
312     return (c);
313 }
314 
315 u_long
getINT32()316 PCFFont::getINT32()
317 {
318     u_long c;
319     if (byteOrder() == MSBFirst) {
320 	c = getc(file) << 24;
321 	c |= getc(file) << 16;
322 	c |= getc(file) << 8;
323 	c |= getc(file);
324     } else {
325 	c = getc(file);
326 	c |= getc(file) << 8;
327 	c |= getc(file) << 16;
328 	c |= getc(file) << 24;
329     }
330     return (c);
331 }
332 
333 int
getINT16()334 PCFFont::getINT16()
335 {
336     int c;
337     if (byteOrder() == MSBFirst) {
338 	c = getc(file) << 8;
339 	c |= getc(file);
340     } else {
341 	c = getc(file);
342 	c |= getc(file) << 8;
343     }
344     return (c);
345 }
346 
getINT8()347 int PCFFont::getINT8() { return getc(file); }
348 
349 /*
350  * PCF supports two formats for metrics, both the regular
351  * jumbo size, and 'lite' metrics, which are useful
352  * for most fonts which have even vaguely reasonable
353  * metrics
354  */
355 void
getMetric(charInfo & metric)356 PCFFont::getMetric(charInfo& metric)
357 {
358     metric.lsb = getINT16();
359     metric.rsb = getINT16();
360     metric.cw = getINT16();
361     metric.ascent = getINT16();
362     metric.descent = getINT16();
363     (void) getINT16();			// attributes
364 }
365 
366 void
getCompressedMetric(charInfo & metric)367 PCFFont::getCompressedMetric(charInfo& metric)
368 {
369     metric.lsb = getINT8() - 0x80;
370     metric.rsb = getINT8() - 0x80;
371     metric.cw = getINT8() - 0x80;
372     metric.ascent = getINT8() - 0x80;
373     metric.descent = getINT8() - 0x80;
374 }
375 
376 /*
377  * Position to the begining of the specified table.
378  */
379 bool
seekToTable(u_long type)380 PCFFont::seekToTable(u_long type)
381 {
382     for (u_int i = 0; i < tocSize; i++)
383 	if (toc[i].type == type) {
384 	    if (fseek(file, toc[i].offset, SEEK_SET) == -1) {
385 		error("Can not seek; fseek failed");
386 		return (false);
387 	    }
388 	    format = toc[i].format;
389 	    return (true);
390 	}
391     error("Can not seek; no such entry in the TOC");
392     return (false);
393 }
394 
395 /*
396  * Read the table-of-contents for the font file.
397  */
398 bool
readTOC()399 PCFFont::readTOC()
400 {
401     u_long version = getLSB32();
402     if (version != PCF_FILE_VERSION) {
403 	error("Cannot read TOC; bad version number %lu", version);
404 	return (false);
405     }
406     tocSize = getLSB32();
407     toc = new PCFTableRec[tocSize];
408     if (!toc) {
409 	error("Cannot read TOC; no space for %lu records", tocSize);
410 	return (false);
411     }
412     for (u_int i = 0; i < tocSize; i++) {
413 	toc[i].type = getLSB32();
414 	toc[i].format = getLSB32();
415 	toc[i].size = getLSB32();
416 	toc[i].offset = getLSB32();
417     }
418     return (true);
419 }
420 
421 int
repadBitmap(u_char * src,u_char * dst,u_long spad,u_long dpad,int w,int h)422 PCFFont::repadBitmap(u_char* src, u_char* dst, u_long spad, u_long dpad, int w, int h)
423 {
424     int srcWidthBytes;
425     switch (spad) {
426     case 1:	srcWidthBytes = (w+7)>>3; break;
427     case 2:	srcWidthBytes = ((w+15)>>4)<<1; break;
428     case 4:	srcWidthBytes = ((w+31)>>5)<<2; break;
429     case 8:	srcWidthBytes = ((w+63)>>6)<<3; break;
430     default:	return 0;
431     }
432     int dstWidthBytes;
433     switch (dpad) {
434     case 1:	dstWidthBytes = (w+7)>>3; break;
435     case 2:	dstWidthBytes = ((w+15)>>4)<<1; break;
436     case 4:	dstWidthBytes = ((w+31)>>5)<<2; break;
437     case 8:	dstWidthBytes = ((w+63)>>6)<<3; break;
438     default:	return 0;
439     }
440     w = srcWidthBytes;
441     if (w > dstWidthBytes)
442 	w = dstWidthBytes;
443     for (int row = 0; row < h; row++) {
444 	int col;
445 	for (col = 0; col < w; col++)
446 	    *dst++ = *src++;
447 	while (col < dstWidthBytes) {
448 	    *dst++ = '\0';
449 	    col++;
450 	}
451 	src += srcWidthBytes - w;
452     }
453     return (dstWidthBytes * h);
454 }
455 
456 u_int
charWidth(u_int c) const457 PCFFont::charWidth(u_int c) const
458 {
459     if (ready) {
460 	charInfo* ci = (firstCol <= c && c <= lastCol) ?
461 	    encoding[c - firstCol] : cdef;
462 	return (ci ? ci->cw : 0);
463     } else
464 	return (0);
465 }
466 
467 void
strWidth(const char * text,u_int & sw,u_int & sh) const468 PCFFont::strWidth(const char* text, u_int &sw, u_int& sh) const
469 {
470     sh = fontHeight();
471     sw = 0;
472     if (ready) {
473 	for (const char* cp = text; *cp; cp++) {
474 	    u_int g = *cp;
475 	    charInfo* ci = (firstCol <= g && g <= lastCol) ?
476 		encoding[g - firstCol] : cdef;
477 	    if (ci)
478 		sw += ci->cw;
479 	}
480     }
481 }
482 
483 /* merge Left/Right bits from a word in a glyph bitmap */
484 #define	MERGEL(r,g,dx,dm) \
485     (r) = ((r) &~ (dm)) | (((g)>>dx) & (dm))
486 #define	MERGER(r,g,dx,dm) \
487     (r) = ((r) &~ (dm)) | (((g)<<(dx)) & (dm))
488 /*
489  * Image text into a raster.  The raster origin (0,0) is assumed to
490  * be in the upper left.  Text is imaged from top-to-bottom and
491  * left-to-right.  The height of the rendered text is returned.
492  */
493 u_int
imageText(const char * text,u_short * raster,u_int w,u_int h,u_int lm,u_int rm,u_int tm,u_int bm) const494 PCFFont::imageText(const char* text,
495     u_short* raster, u_int w, u_int h,
496     u_int lm, u_int rm, u_int tm, u_int bm) const
497 {
498     if (!ready)
499 	return (0);
500     u_int rowwords = howmany(w,16);
501     u_int y = tm + fontAscent;
502     u_int x = lm;
503     /*
504      * The rasterize assumes words have a big-endian
505      * byte order.  For now (rather than fix it) we
506      * byte swap the data coming in and going out.
507      */
508     if (!isBigEndian)				// XXX
509 	TIFFSwabArrayOfShort((u_short*) raster, h*rowwords);
510     for (const char* cp = text; *cp; cp++) {
511 	u_int g = (u_char)*cp;
512 	charInfo* ci = (firstCol <= g && g <= lastCol) ?
513 	    encoding[g - firstCol] : cdef;
514 	if (!ci)
515 	    continue;
516 	if (x + ci->cw > w - rm) {		// no space on line, move down
517 	    if (y+fontHeight() >= h-bm)
518 		break;				// raster completely full
519 	    y += fontHeight();
520 	    x = lm;
521 	}
522 	/*
523 	 * Blit glyph bitmap into raster.  The work done here is
524 	 * not designed for speed.  We break the work into two cases;
525 	 * where the destination location in the raster is word-aligned
526 	 * and where it's not word-aligned.  Glyph bitmaps are assumed
527 	 * to be word-padded and to have the bits ``left adjusted''
528 	 * within words.  Note that we handle glyphs that are at
529 	 * most 47 bits wide; this should be sufficient for our needs.
530 	 */
531 	u_short cw = ci->rsb - ci->lsb;		// bitmap glyph width
532 	u_short cwords = cw>>4;			// full words in glyph
533 	if (cwords > 2)				// skip too wide glyph
534 	    continue;
535 	int cx = x + ci->lsb;			// left edge of glyph
536 	int ch = ci->ascent + ci->descent;	// glyph height
537 	u_short* rp = raster + (y-ci->ascent)*rowwords + (cx>>4);
538 	u_short* gp = (u_short*) ci->bits;
539 	u_short dx0 = cx&15;			// raster word offset
540 	u_short rowdelta = rowwords - cwords;	// raster row adjust factor
541 	u_short cbits = cw&15;			// partial glyph word
542 	if (dx0 != 0) {				// hard case, raster unaligned
543 	    u_short dm0 = 0xffff>>dx0;
544 	    u_short dx1 = 16-dx0;
545 	    u_short dm1 = ~dm0;
546 	    u_short dm2, dm3;
547 	    if (cbits > dx1) {			// spills into 2nd word
548 		dm2 = dm0;
549 		dm3 = ~((1<<dx1)-1);
550 	    } else {				// fits entirely into 1st word
551 		dm2 = dm0 &~ ((1<<(dx1-cbits))-1);
552 		dm3 = 0;
553 	    }
554 	    for (short r = 0; r < ch; r++) {
555 		switch (cwords) {		// merge complete glyph words
556 		case 2: MERGEL(rp[0], gp[0], dx0, dm0);
557 			MERGER(rp[1], gp[0], dx1, dm1);
558 			rp++, gp++;
559 		case 1: MERGEL(rp[0], gp[0], dx0, dm0);
560 			MERGER(rp[1], gp[0], dx1, dm1);
561 			rp++, gp++;
562 		}
563 		if (cbits) {
564 		    MERGEL(rp[0], gp[0], dx0, dm2);
565 		    MERGER(rp[1], gp[0], dx1, dm3);
566 		    gp++;
567 		}
568 		rp += rowdelta;
569 	    }
570 	} else {				// raster word-aligned
571 	    u_short dm = 0xffff<<(16-cbits);
572 	    for (short r = 0; r < ch; r++) {
573 		switch (cwords) {
574 		case 2: *rp++ = *gp++;
575 		case 1: *rp++ = *gp++;
576 		}
577 		if (cbits)
578 		    MERGEL(*rp, *gp++, 0, dm);
579 		rp += rowdelta;
580 	    }
581 	}
582 	x += ci->cw;
583     }
584     if (!isBigEndian)				// XXX
585 	TIFFSwabArrayOfShort((u_short*) raster, h*rowwords);
586     return (y+fontDescent+bm);
587 }
588 #undef MERGEL
589 #undef MERGER
590 
591 #include <ctype.h>
592 
593 void
print(FILE * fd) const594 PCFFont::print(FILE* fd) const
595 {
596     if (ready) {
597 	fprintf(fd, "Font Ascent: %d Descent: %d\n", fontAscent, fontDescent);
598 	fprintf(fd, "FirstCol: %u LastCol: %u\n", firstCol, lastCol);
599 	fprintf(fd, "%lu glyphs:\n", numGlyphs);
600 	for (u_int c = firstCol; c <= lastCol; c++) {
601 	    charInfo* ci = encoding[c - firstCol];
602 	    if (!ci)
603 		continue;
604 	    if (isprint(c))
605 		fprintf(fd,
606 		    "'%c': lsb %2d rsb %2d cw %2d ascent %2d descent %d\n",
607 		    c, ci->lsb, ci->rsb, ci->cw, ci->ascent, ci->descent);
608 	    else
609 		fprintf(fd,
610 		    "%3d: lsb %2d rsb %2d cw %2d ascent %2d descent %d\n",
611 		    c, ci->lsb, ci->rsb, ci->cw, ci->ascent, ci->descent);
612 	}
613     }
614 }
615 
616 #include "Str.h"
617 
618 extern void vlogError(const char* fmt, va_list ap);
619 
620 void
error(const char * fmt0...)621 PCFFont::error(const char* fmt0 ...)
622 {
623     va_list ap;
624     va_start(ap, fmt0);
625     fxStr fmt = fxStr::format("PCFFont: %s: %s",
626 	filename ? filename : "<unknown file>", fmt0);
627     vlogError(fmt, ap);
628     va_end(ap);
629 }
630 
631 /*
632  * $XConsortium: pcfread.c,v 1.10 92/05/12 18:07:47 gildea Exp $
633  *
634  * Copyright 1990 Massachusetts Institute of Technology
635  *
636  * Permission to use, copy, modify, distribute, and sell this software and its
637  * documentation for any purpose is hereby granted without fee, provided that
638  * the above copyright notice appear in all copies and that both that
639  * copyright notice and this permission notice appear in supporting
640  * documentation, and that the name of M.I.T. not be used in advertising or
641  * publicity pertaining to distribution of the software without specific,
642  * written prior permission.  M.I.T. makes no representations about the
643  * suitability of this software for any purpose.  It is provided "as is"
644  * without express or implied warranty.
645  *
646  * M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
647  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL M.I.T.
648  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
649  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
650  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
651  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
652  *
653  * Author:  Keith Packard, MIT X Consortium
654  */
655