pcfwrite.c revision fa2b3b63
1/*
2
3Copyright 1990, 1994, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included
12in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall
23not be used in advertising or otherwise to promote the sale, use or
24other dealings in this Software without prior written authorization
25from The Open Group.
26
27*/
28
29/*
30 * Author:  Keith Packard, MIT X Consortium
31 */
32
33#ifdef HAVE_CONFIG_H
34#include <config.h>
35#endif
36
37#include "fntfilst.h"
38#include "bitmap.h"
39#include "pcf.h"
40
41#include <stdarg.h>
42#include <stdio.h>
43
44/* Write PCF font files */
45
46static CARD32  current_position;
47
48void
49pcfError(const char* message, ...)
50{
51    va_list args;
52
53    va_start(args, message);
54
55    fprintf(stderr, "PCF Error: ");
56    vfprintf(stderr, message, args);
57    va_end(args);
58}
59static int
60pcfWrite(FontFilePtr file, char *b, int c)
61{
62    current_position += c;
63    return FontFileWrite(file, b, c);
64}
65
66static int
67pcfPutLSB32(FontFilePtr file, int c)
68{
69    current_position += 4;
70    (void) FontFilePutc(c, file);
71    (void) FontFilePutc(c >> 8, file);
72    (void) FontFilePutc(c >> 16, file);
73    return FontFilePutc(c >> 24, file);
74}
75
76static int
77pcfPutINT32(FontFilePtr file, CARD32 format, int c)
78{
79    current_position += 4;
80    if (PCF_BYTE_ORDER(format) == MSBFirst) {
81	(void) FontFilePutc(c >> 24, file);
82	(void) FontFilePutc(c >> 16, file);
83	(void) FontFilePutc(c >> 8, file);
84	return FontFilePutc(c, file);
85    } else {
86	(void) FontFilePutc(c, file);
87	(void) FontFilePutc(c >> 8, file);
88	(void) FontFilePutc(c >> 16, file);
89	return FontFilePutc(c >> 24, file);
90    }
91}
92
93static int
94pcfPutINT16(FontFilePtr file, CARD32 format, int c)
95{
96    current_position += 2;
97    if (PCF_BYTE_ORDER(format) == MSBFirst) {
98	(void) FontFilePutc(c >> 8, file);
99	return FontFilePutc(c, file);
100    } else {
101	(void) FontFilePutc(c, file);
102	return FontFilePutc(c >> 8, file);
103    }
104}
105
106/*ARGSUSED*/
107static int
108pcfPutINT8(FontFilePtr file, CARD32 format, int c)
109{
110    current_position += 1;
111    return FontFilePutc(c, file);
112}
113
114static void
115pcfWriteTOC(FontFilePtr file, PCFTablePtr table, int count)
116{
117    CARD32      version;
118    int         i;
119
120    version = PCF_FILE_VERSION;
121    pcfPutLSB32(file, version);
122    pcfPutLSB32(file, count);
123    for (i = 0; i < count; i++) {
124	pcfPutLSB32(file, table->type);
125	pcfPutLSB32(file, table->format);
126	pcfPutLSB32(file, table->size);
127	pcfPutLSB32(file, table->offset);
128	table++;
129    }
130}
131
132static void
133pcfPutCompressedMetric(FontFilePtr file, CARD32 format, xCharInfo *metric)
134{
135    pcfPutINT8(file, format, metric->leftSideBearing + 0x80);
136    pcfPutINT8(file, format, metric->rightSideBearing + 0x80);
137    pcfPutINT8(file, format, metric->characterWidth + 0x80);
138    pcfPutINT8(file, format, metric->ascent + 0x80);
139    pcfPutINT8(file, format, metric->descent + 0x80);
140}
141
142static void
143pcfPutMetric(FontFilePtr file, CARD32 format, xCharInfo *metric)
144{
145    pcfPutINT16(file, format, metric->leftSideBearing);
146    pcfPutINT16(file, format, metric->rightSideBearing);
147    pcfPutINT16(file, format, metric->characterWidth);
148    pcfPutINT16(file, format, metric->ascent);
149    pcfPutINT16(file, format, metric->descent);
150    pcfPutINT16(file, format, metric->attributes);
151}
152
153static void
154pcfPutBitmap(FontFilePtr file, CARD32 format, CharInfoPtr pCI)
155{
156    int         count;
157    unsigned char *bits;
158
159    count = BYTES_FOR_GLYPH(pCI, PCF_GLYPH_PAD(format));
160    bits = (unsigned char *) pCI->bits;
161    current_position += count;
162    while (count--)
163	FontFilePutc(*bits++, file);
164}
165
166static void
167pcfPutAccel(FontFilePtr file, CARD32 format, FontInfoPtr pFontInfo)
168{
169    pcfPutINT8(file, format, pFontInfo->noOverlap);
170    pcfPutINT8(file, format, pFontInfo->constantMetrics);
171    pcfPutINT8(file, format, pFontInfo->terminalFont);
172    pcfPutINT8(file, format, pFontInfo->constantWidth);
173    pcfPutINT8(file, format, pFontInfo->inkInside);
174    pcfPutINT8(file, format, pFontInfo->inkMetrics);
175    pcfPutINT8(file, format, pFontInfo->drawDirection);
176    pcfPutINT8(file, format, 0);
177    pcfPutINT32(file, format, pFontInfo->fontAscent);
178    pcfPutINT32(file, format, pFontInfo->fontDescent);
179    pcfPutINT32(file, format, pFontInfo->maxOverlap);
180    pcfPutMetric(file, format, &pFontInfo->minbounds);
181    pcfPutMetric(file, format, &pFontInfo->maxbounds);
182    if (PCF_FORMAT_MATCH(format, PCF_ACCEL_W_INKBOUNDS)) {
183	pcfPutMetric(file, format, &pFontInfo->ink_minbounds);
184	pcfPutMetric(file, format, &pFontInfo->ink_maxbounds);
185    }
186}
187
188#define S32 4
189#define S16 2
190#define S8 1
191
192#define Pad(s)	    (RoundUp(s) - (s))
193#define RoundUp(s)  (((s) + 3) & ~3)
194
195#define Compressable(i)	(-128 <= (i) && (i) <= 127)
196
197#define CanCompressMetric(m)	(Compressable((m)->leftSideBearing) && \
198				 Compressable((m)->rightSideBearing) && \
199				 Compressable((m)->characterWidth) && \
200				 Compressable((m)->ascent) && \
201				 Compressable((m)->descent) && \
202				 (m)->attributes == 0)
203
204#define CanCompressMetrics(min,max) (CanCompressMetric(min) && CanCompressMetric(max))
205
206static char *
207pcfNameForAtom(Atom a)
208{
209    return NameForAtom(a);
210}
211
212int
213pcfWriteFont(FontPtr pFont, FontFilePtr file)
214{
215    PCFTableRec tables[32],
216               *table;
217    CARD32      mask,
218                bit;
219    int         ntables;
220    int         size;
221    CARD32      format;
222    int         i;
223    int         cur_table;
224    int         prop_string_size;
225    int         glyph_string_size;
226    xCharInfo  *minbounds,
227               *maxbounds;
228    xCharInfo  *ink_minbounds,
229               *ink_maxbounds;
230    BitmapFontPtr  bitmapFont;
231    int         nencodings = 0;
232    int         header_size;
233    FontPropPtr offsetProps;
234    int         prop_pad = 0;
235    char       *atom_name;
236    int         glyph;
237    CARD32      offset;
238
239    bitmapFont = (BitmapFontPtr) pFont->fontPrivate;
240    if (bitmapFont->bitmapExtra) {
241	minbounds = &bitmapFont->bitmapExtra->info.minbounds;
242	maxbounds = &bitmapFont->bitmapExtra->info.maxbounds;
243	ink_minbounds = &bitmapFont->bitmapExtra->info.ink_minbounds;
244	ink_maxbounds = &bitmapFont->bitmapExtra->info.ink_maxbounds;
245    } else {
246	minbounds = &pFont->info.minbounds;
247	maxbounds = &pFont->info.maxbounds;
248	ink_minbounds = &pFont->info.ink_minbounds;
249	ink_maxbounds = &pFont->info.ink_maxbounds;
250    }
251    offsetProps = malloc(pFont->info.nprops * sizeof(FontPropRec));
252    if (!offsetProps) {
253	pcfError("pcfWriteFont(): Couldn't allocate offsetProps (%d*%d)",
254		 pFont->info.nprops, (int) sizeof(FontPropRec));
255	return AllocError;
256    }
257    prop_string_size = 0;
258    for (i = 0; i < pFont->info.nprops; i++) {
259	offsetProps[i].name = prop_string_size;
260	prop_string_size += strlen(pcfNameForAtom(pFont->info.props[i].name)) + 1;
261	if (pFont->info.isStringProp[i]) {
262	    offsetProps[i].value = prop_string_size;
263	    prop_string_size += strlen(pcfNameForAtom(pFont->info.props[i].value)) + 1;
264	} else
265	    offsetProps[i].value = pFont->info.props[i].value;
266    }
267    format = PCF_FORMAT(pFont->bit, pFont->byte, pFont->glyph, pFont->scan);
268    mask = 0xFFFFFFF;
269    ntables = 0;
270    table = tables;
271    while (mask) {
272	bit = lowbit(mask);
273	mask &= ~bit;
274	table->type = bit;
275	switch (bit) {
276	case PCF_PROPERTIES:
277	    table->format = PCF_DEFAULT_FORMAT | format;
278	    size = S32 + S32 + (S32 + S8 + S32) * pFont->info.nprops;
279	    prop_pad = Pad(size);
280	    table->size = RoundUp(size) + S32 +
281		RoundUp(prop_string_size);
282	    table++;
283	    break;
284	case PCF_ACCELERATORS:
285	    if (bitmapFont->bitmapExtra->info.inkMetrics)
286		table->format = PCF_ACCEL_W_INKBOUNDS | format;
287	    else
288		table->format = PCF_DEFAULT_FORMAT | format;
289	    table->size = 100;
290	    table++;
291	    break;
292	case PCF_METRICS:
293	    if (CanCompressMetrics(minbounds, maxbounds)) {
294		table->format = PCF_COMPRESSED_METRICS | format;
295		size = S32 + S16 + bitmapFont->num_chars * (5 * S8);
296		table->size = RoundUp(size);
297	    } else {
298		table->format = PCF_DEFAULT_FORMAT | format;
299		table->size = S32 + S32 + bitmapFont->num_chars * (6 * S16);
300	    }
301	    table++;
302	    break;
303	case PCF_BITMAPS:
304	    table->format = PCF_DEFAULT_FORMAT | format;
305	    size = S32 + S32 + bitmapFont->num_chars * S32 +
306		GLYPHPADOPTIONS * S32 +
307		bitmapFont->bitmapExtra->bitmapsSizes[PCF_GLYPH_PAD_INDEX(format)];
308	    table->size = RoundUp(size);
309	    table++;
310	    break;
311	case PCF_INK_METRICS:
312	    if (bitmapFont->ink_metrics) {
313		if (CanCompressMetrics(ink_minbounds, ink_maxbounds)) {
314		    table->format = PCF_COMPRESSED_METRICS | format;
315		    size = S32 + S16 + bitmapFont->num_chars * (5 * S8);
316		    table->size = RoundUp(size);
317		} else {
318		    table->format = PCF_DEFAULT_FORMAT | format;
319		    table->size = S32 + S32 + bitmapFont->num_chars * (6 * S16);
320		}
321		table++;
322	    }
323	    break;
324	case PCF_BDF_ENCODINGS:
325	    table->format = PCF_DEFAULT_FORMAT | format;
326	    nencodings = (pFont->info.lastRow - pFont->info.firstRow + 1) *
327		(pFont->info.lastCol - pFont->info.firstCol + 1);
328	    size = S32 + 5 * S16 + nencodings * S16;
329	    table->size = RoundUp(size);
330	    table++;
331	    break;
332	case PCF_SWIDTHS:
333	    table->format = PCF_DEFAULT_FORMAT | format;
334	    table->size = S32 + S32 + bitmapFont->num_chars * S32;
335	    table++;
336	    break;
337	case PCF_GLYPH_NAMES:
338	    table->format = PCF_DEFAULT_FORMAT | format;
339	    glyph_string_size = 0;
340	    for (i = 0; i < bitmapFont->num_chars; i++)
341		glyph_string_size += strlen(pcfNameForAtom(bitmapFont->bitmapExtra->glyphNames[i])) + 1;
342	    table->size = S32 + S32 + bitmapFont->num_chars * S32 +
343		S32 + RoundUp(glyph_string_size);
344	    table++;
345	    break;
346	case PCF_BDF_ACCELERATORS:
347	    if (pFont->info.inkMetrics)
348		table->format = PCF_ACCEL_W_INKBOUNDS | format;
349	    else
350		table->format = PCF_DEFAULT_FORMAT | format;
351	    table->size = 100;
352	    table++;
353	    break;
354	}
355    }
356    ntables = table - tables;
357    offset = 0;
358    header_size = S32 + S32 + ntables * (4 * S32);
359    offset = header_size;
360    for (cur_table = 0, table = tables;
361	    cur_table < ntables;
362	    cur_table++, table++) {
363	table->offset = offset;
364	offset += table->size;
365    }
366    current_position = 0;
367    pcfWriteTOC(file, tables, ntables);
368    for (cur_table = 0, table = tables;
369	    cur_table < ntables;
370	    cur_table++, table++) {
371	if (current_position > table->offset) {
372	    printf("can't go backwards... %d > %d\n",
373		   (int)current_position, (int)table->offset);
374	    free(offsetProps);
375	    return BadFontName;
376	}
377	while (current_position < table->offset)
378	    pcfPutINT8(file, format, '\0');
379	pcfPutLSB32(file, table->format);
380	switch (table->type) {
381	case PCF_PROPERTIES:
382	    pcfPutINT32(file, format, pFont->info.nprops);
383	    for (i = 0; i < pFont->info.nprops; i++) {
384		pcfPutINT32(file, format, offsetProps[i].name);
385		pcfPutINT8(file, format, pFont->info.isStringProp[i]);
386		pcfPutINT32(file, format, offsetProps[i].value);
387	    }
388	    for (i = 0; i < prop_pad; i++)
389		pcfPutINT8(file, format, 0);
390	    pcfPutINT32(file, format, prop_string_size);
391	    for (i = 0; i < pFont->info.nprops; i++) {
392		atom_name = pcfNameForAtom(pFont->info.props[i].name);
393		pcfWrite(file, atom_name, strlen(atom_name) + 1);
394		if (pFont->info.isStringProp[i]) {
395		    atom_name = pcfNameForAtom(pFont->info.props[i].value);
396		    pcfWrite(file, atom_name, strlen(atom_name) + 1);
397		}
398	    }
399	    break;
400	case PCF_ACCELERATORS:
401	    pcfPutAccel(file, table->format, &bitmapFont->bitmapExtra->info);
402	    break;
403	case PCF_METRICS:
404	    if (PCF_FORMAT_MATCH(table->format, PCF_COMPRESSED_METRICS)) {
405		pcfPutINT16(file, format, bitmapFont->num_chars);
406		for (i = 0; i < bitmapFont->num_chars; i++)
407		    pcfPutCompressedMetric(file, format, &bitmapFont->metrics[i].metrics);
408	    } else {
409		pcfPutINT32(file, format, bitmapFont->num_chars);
410		for (i = 0; i < bitmapFont->num_chars; i++)
411		    pcfPutMetric(file, format, &bitmapFont->metrics[i].metrics);
412	    }
413	    break;
414	case PCF_BITMAPS:
415	    pcfPutINT32(file, format, bitmapFont->num_chars);
416	    glyph = PCF_GLYPH_PAD(format);
417	    offset = 0;
418	    for (i = 0; i < bitmapFont->num_chars; i++) {
419		pcfPutINT32(file, format, offset);
420		offset += BYTES_FOR_GLYPH(&bitmapFont->metrics[i], glyph);
421	    }
422	    for (i = 0; i < GLYPHPADOPTIONS; i++) {
423		pcfPutINT32(file, format,
424			    bitmapFont->bitmapExtra->bitmapsSizes[i]);
425	    }
426	    for (i = 0; i < bitmapFont->num_chars; i++)
427		pcfPutBitmap(file, format, &bitmapFont->metrics[i]);
428	    break;
429	case PCF_INK_METRICS:
430	    if (PCF_FORMAT_MATCH(table->format, PCF_COMPRESSED_METRICS)) {
431		pcfPutINT16(file, format, bitmapFont->num_chars);
432		for (i = 0; i < bitmapFont->num_chars; i++)
433		    pcfPutCompressedMetric(file, format, &bitmapFont->ink_metrics[i]);
434	    } else {
435		pcfPutINT32(file, format, bitmapFont->num_chars);
436		for (i = 0; i < bitmapFont->num_chars; i++)
437		    pcfPutMetric(file, format, &bitmapFont->ink_metrics[i]);
438	    }
439	    break;
440	case PCF_BDF_ENCODINGS:
441	    pcfPutINT16(file, format, pFont->info.firstCol);
442	    pcfPutINT16(file, format, pFont->info.lastCol);
443	    pcfPutINT16(file, format, pFont->info.firstRow);
444	    pcfPutINT16(file, format, pFont->info.lastRow);
445	    pcfPutINT16(file, format, pFont->info.defaultCh);
446	    for (i = 0; i < nencodings; i++) {
447		if (ACCESSENCODING(bitmapFont->encoding,i))
448		    pcfPutINT16(file, format,
449                                ACCESSENCODING(bitmapFont->encoding, i) -
450                                  bitmapFont->metrics);
451		else
452		    pcfPutINT16(file, format, 0xFFFF);
453	    }
454	    break;
455	case PCF_SWIDTHS:
456	    pcfPutINT32(file, format, bitmapFont->num_chars);
457	    for (i = 0; i < bitmapFont->num_chars; i++)
458		pcfPutINT32(file, format, bitmapFont->bitmapExtra->sWidths[i]);
459	    break;
460	case PCF_GLYPH_NAMES:
461	    pcfPutINT32(file, format, bitmapFont->num_chars);
462	    offset = 0;
463	    for (i = 0; i < bitmapFont->num_chars; i++) {
464		pcfPutINT32(file, format, offset);
465		offset += strlen(pcfNameForAtom(bitmapFont->bitmapExtra->glyphNames[i])) + 1;
466	    }
467	    pcfPutINT32(file, format, offset);
468	    for (i = 0; i < bitmapFont->num_chars; i++) {
469		atom_name = pcfNameForAtom(bitmapFont->bitmapExtra->glyphNames[i]);
470		pcfWrite(file, atom_name, strlen(atom_name) + 1);
471	    }
472	    break;
473	case PCF_BDF_ACCELERATORS:
474	    pcfPutAccel(file, table->format, &pFont->info);
475	    break;
476	}
477    }
478
479    free(offsetProps);
480    return Successful;
481}
482