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 <X11/fonts/fntfilst.h>
38#include <X11/fonts/bitmap.h>
39#include <X11/fonts/pcf.h>
40
41/* Write PCF font files */
42
43static CARD32  current_position;
44
45static int
46pcfWrite(FontFilePtr file, char *b, int c)
47{
48    current_position += c;
49    return FontFileWrite(file, b, c);
50}
51
52static int
53pcfPutLSB32(FontFilePtr file, int c)
54{
55    current_position += 4;
56    (void) FontFilePutc(c, file);
57    (void) FontFilePutc(c >> 8, file);
58    (void) FontFilePutc(c >> 16, file);
59    return FontFilePutc(c >> 24, file);
60}
61
62static int
63pcfPutINT32(FontFilePtr file, CARD32 format, int c)
64{
65    current_position += 4;
66    if (PCF_BYTE_ORDER(format) == MSBFirst) {
67	(void) FontFilePutc(c >> 24, file);
68	(void) FontFilePutc(c >> 16, file);
69	(void) FontFilePutc(c >> 8, file);
70	return FontFilePutc(c, file);
71    } else {
72	(void) FontFilePutc(c, file);
73	(void) FontFilePutc(c >> 8, file);
74	(void) FontFilePutc(c >> 16, file);
75	return FontFilePutc(c >> 24, file);
76    }
77}
78
79static int
80pcfPutINT16(FontFilePtr file, CARD32 format, int c)
81{
82    current_position += 2;
83    if (PCF_BYTE_ORDER(format) == MSBFirst) {
84	(void) FontFilePutc(c >> 8, file);
85	return FontFilePutc(c, file);
86    } else {
87	(void) FontFilePutc(c, file);
88	return FontFilePutc(c >> 8, file);
89    }
90}
91
92/*ARGSUSED*/
93static int
94pcfPutINT8(FontFilePtr file, CARD32 format, int c)
95{
96    current_position += 1;
97    return FontFilePutc(c, file);
98}
99
100static void
101pcfWriteTOC(FontFilePtr file, PCFTablePtr table, int count)
102{
103    CARD32      version;
104    int         i;
105
106    version = PCF_FILE_VERSION;
107    pcfPutLSB32(file, version);
108    pcfPutLSB32(file, count);
109    for (i = 0; i < count; i++) {
110	pcfPutLSB32(file, table->type);
111	pcfPutLSB32(file, table->format);
112	pcfPutLSB32(file, table->size);
113	pcfPutLSB32(file, table->offset);
114	table++;
115    }
116}
117
118static void
119pcfPutCompressedMetric(FontFilePtr file, CARD32 format, xCharInfo *metric)
120{
121    pcfPutINT8(file, format, metric->leftSideBearing + 0x80);
122    pcfPutINT8(file, format, metric->rightSideBearing + 0x80);
123    pcfPutINT8(file, format, metric->characterWidth + 0x80);
124    pcfPutINT8(file, format, metric->ascent + 0x80);
125    pcfPutINT8(file, format, metric->descent + 0x80);
126}
127
128static void
129pcfPutMetric(FontFilePtr file, CARD32 format, xCharInfo *metric)
130{
131    pcfPutINT16(file, format, metric->leftSideBearing);
132    pcfPutINT16(file, format, metric->rightSideBearing);
133    pcfPutINT16(file, format, metric->characterWidth);
134    pcfPutINT16(file, format, metric->ascent);
135    pcfPutINT16(file, format, metric->descent);
136    pcfPutINT16(file, format, metric->attributes);
137}
138
139static void
140pcfPutBitmap(FontFilePtr file, CARD32 format, CharInfoPtr pCI)
141{
142    int         count;
143    unsigned char *bits;
144
145    count = BYTES_FOR_GLYPH(pCI, PCF_GLYPH_PAD(format));
146    bits = (unsigned char *) pCI->bits;
147    current_position += count;
148    while (count--)
149	FontFilePutc(*bits++, file);
150}
151
152static void
153pcfPutAccel(FontFilePtr file, CARD32 format, FontInfoPtr pFontInfo)
154{
155    pcfPutINT8(file, format, pFontInfo->noOverlap);
156    pcfPutINT8(file, format, pFontInfo->constantMetrics);
157    pcfPutINT8(file, format, pFontInfo->terminalFont);
158    pcfPutINT8(file, format, pFontInfo->constantWidth);
159    pcfPutINT8(file, format, pFontInfo->inkInside);
160    pcfPutINT8(file, format, pFontInfo->inkMetrics);
161    pcfPutINT8(file, format, pFontInfo->drawDirection);
162    pcfPutINT8(file, format, 0);
163    pcfPutINT32(file, format, pFontInfo->fontAscent);
164    pcfPutINT32(file, format, pFontInfo->fontDescent);
165    pcfPutINT32(file, format, pFontInfo->maxOverlap);
166    pcfPutMetric(file, format, &pFontInfo->minbounds);
167    pcfPutMetric(file, format, &pFontInfo->maxbounds);
168    if (PCF_FORMAT_MATCH(format, PCF_ACCEL_W_INKBOUNDS)) {
169	pcfPutMetric(file, format, &pFontInfo->ink_minbounds);
170	pcfPutMetric(file, format, &pFontInfo->ink_maxbounds);
171    }
172}
173
174#define S32 4
175#define S16 2
176#define S8 1
177
178#define Pad(s)	    (RoundUp(s) - (s))
179#define RoundUp(s)  (((s) + 3) & ~3)
180
181#define Compressable(i)	(-128 <= (i) && (i) <= 127)
182
183#define CanCompressMetric(m)	(Compressable((m)->leftSideBearing) && \
184				 Compressable((m)->rightSideBearing) && \
185				 Compressable((m)->characterWidth) && \
186				 Compressable((m)->ascent) && \
187				 Compressable((m)->descent) && \
188				 (m)->attributes == 0)
189
190#define CanCompressMetrics(min,max) (CanCompressMetric(min) && CanCompressMetric(max))
191
192static char *
193pcfNameForAtom(Atom a)
194{
195    return NameForAtom(a);
196}
197
198int
199pcfWriteFont(FontPtr pFont, FontFilePtr file)
200{
201    PCFTableRec tables[32],
202               *table;
203    CARD32      mask,
204                bit;
205    int         ntables;
206    int         size;
207    CARD32      format;
208    int         i;
209    int         cur_table;
210    int         prop_string_size;
211    int         glyph_string_size;
212    xCharInfo  *minbounds,
213               *maxbounds;
214    xCharInfo  *ink_minbounds,
215               *ink_maxbounds;
216    BitmapFontPtr  bitmapFont;
217    int         nencodings = 0;
218    int         header_size;
219    FontPropPtr offsetProps;
220    int         prop_pad = 0;
221    char       *atom_name;
222    int         glyph;
223    CARD32      offset;
224
225    bitmapFont = (BitmapFontPtr) pFont->fontPrivate;
226    if (bitmapFont->bitmapExtra) {
227	minbounds = &bitmapFont->bitmapExtra->info.minbounds;
228	maxbounds = &bitmapFont->bitmapExtra->info.maxbounds;
229	ink_minbounds = &bitmapFont->bitmapExtra->info.ink_minbounds;
230	ink_maxbounds = &bitmapFont->bitmapExtra->info.ink_maxbounds;
231    } else {
232	minbounds = &pFont->info.minbounds;
233	maxbounds = &pFont->info.maxbounds;
234	ink_minbounds = &pFont->info.ink_minbounds;
235	ink_maxbounds = &pFont->info.ink_maxbounds;
236    }
237    offsetProps = malloc(pFont->info.nprops * sizeof(FontPropRec));
238    if (!offsetProps) {
239	pcfError("pcfWriteFont(): Couldn't allocate offsetProps (%d*%d)",
240		 pFont->info.nprops, (int) sizeof(FontPropRec));
241	return AllocError;
242    }
243    prop_string_size = 0;
244    for (i = 0; i < pFont->info.nprops; i++) {
245	offsetProps[i].name = prop_string_size;
246	prop_string_size += strlen(pcfNameForAtom(pFont->info.props[i].name)) + 1;
247	if (pFont->info.isStringProp[i]) {
248	    offsetProps[i].value = prop_string_size;
249	    prop_string_size += strlen(pcfNameForAtom(pFont->info.props[i].value)) + 1;
250	} else
251	    offsetProps[i].value = pFont->info.props[i].value;
252    }
253    format = PCF_FORMAT(pFont->bit, pFont->byte, pFont->glyph, pFont->scan);
254    mask = 0xFFFFFFF;
255    ntables = 0;
256    table = tables;
257    while (mask) {
258	bit = lowbit(mask);
259	mask &= ~bit;
260	table->type = bit;
261	switch (bit) {
262	case PCF_PROPERTIES:
263	    table->format = PCF_DEFAULT_FORMAT | format;
264	    size = S32 + S32 + (S32 + S8 + S32) * pFont->info.nprops;
265	    prop_pad = Pad(size);
266	    table->size = RoundUp(size) + S32 +
267		RoundUp(prop_string_size);
268	    table++;
269	    break;
270	case PCF_ACCELERATORS:
271	    if (bitmapFont->bitmapExtra->info.inkMetrics)
272		table->format = PCF_ACCEL_W_INKBOUNDS | format;
273	    else
274		table->format = PCF_DEFAULT_FORMAT | format;
275	    table->size = 100;
276	    table++;
277	    break;
278	case PCF_METRICS:
279	    if (CanCompressMetrics(minbounds, maxbounds)) {
280		table->format = PCF_COMPRESSED_METRICS | format;
281		size = S32 + S16 + bitmapFont->num_chars * (5 * S8);
282		table->size = RoundUp(size);
283	    } else {
284		table->format = PCF_DEFAULT_FORMAT | format;
285		table->size = S32 + S32 + bitmapFont->num_chars * (6 * S16);
286	    }
287	    table++;
288	    break;
289	case PCF_BITMAPS:
290	    table->format = PCF_DEFAULT_FORMAT | format;
291	    size = S32 + S32 + bitmapFont->num_chars * S32 +
292		GLYPHPADOPTIONS * S32 +
293		bitmapFont->bitmapExtra->bitmapsSizes[PCF_GLYPH_PAD_INDEX(format)];
294	    table->size = RoundUp(size);
295	    table++;
296	    break;
297	case PCF_INK_METRICS:
298	    if (bitmapFont->ink_metrics) {
299		if (CanCompressMetrics(ink_minbounds, ink_maxbounds)) {
300		    table->format = PCF_COMPRESSED_METRICS | format;
301		    size = S32 + S16 + bitmapFont->num_chars * (5 * S8);
302		    table->size = RoundUp(size);
303		} else {
304		    table->format = PCF_DEFAULT_FORMAT | format;
305		    table->size = S32 + S32 + bitmapFont->num_chars * (6 * S16);
306		}
307		table++;
308	    }
309	    break;
310	case PCF_BDF_ENCODINGS:
311	    table->format = PCF_DEFAULT_FORMAT | format;
312	    nencodings = (pFont->info.lastRow - pFont->info.firstRow + 1) *
313		(pFont->info.lastCol - pFont->info.firstCol + 1);
314	    size = S32 + 5 * S16 + nencodings * S16;
315	    table->size = RoundUp(size);
316	    table++;
317	    break;
318	case PCF_SWIDTHS:
319	    table->format = PCF_DEFAULT_FORMAT | format;
320	    table->size = S32 + S32 + bitmapFont->num_chars * S32;
321	    table++;
322	    break;
323	case PCF_GLYPH_NAMES:
324	    table->format = PCF_DEFAULT_FORMAT | format;
325	    glyph_string_size = 0;
326	    for (i = 0; i < bitmapFont->num_chars; i++)
327		glyph_string_size += strlen(pcfNameForAtom(bitmapFont->bitmapExtra->glyphNames[i])) + 1;
328	    table->size = S32 + S32 + bitmapFont->num_chars * S32 +
329		S32 + RoundUp(glyph_string_size);
330	    table++;
331	    break;
332	case PCF_BDF_ACCELERATORS:
333	    if (pFont->info.inkMetrics)
334		table->format = PCF_ACCEL_W_INKBOUNDS | format;
335	    else
336		table->format = PCF_DEFAULT_FORMAT | format;
337	    table->size = 100;
338	    table++;
339	    break;
340	}
341    }
342    ntables = table - tables;
343    offset = 0;
344    header_size = S32 + S32 + ntables * (4 * S32);
345    offset = header_size;
346    for (cur_table = 0, table = tables;
347	    cur_table < ntables;
348	    cur_table++, table++) {
349	table->offset = offset;
350	offset += table->size;
351    }
352    current_position = 0;
353    pcfWriteTOC(file, tables, ntables);
354    for (cur_table = 0, table = tables;
355	    cur_table < ntables;
356	    cur_table++, table++) {
357	if (current_position > table->offset) {
358	    printf("can't go backwards... %d > %d\n",
359		   (int)current_position, (int)table->offset);
360	    free(offsetProps);
361	    return BadFontName;
362	}
363	while (current_position < table->offset)
364	    pcfPutINT8(file, format, '\0');
365	pcfPutLSB32(file, table->format);
366	switch (table->type) {
367	case PCF_PROPERTIES:
368	    pcfPutINT32(file, format, pFont->info.nprops);
369	    for (i = 0; i < pFont->info.nprops; i++) {
370		pcfPutINT32(file, format, offsetProps[i].name);
371		pcfPutINT8(file, format, pFont->info.isStringProp[i]);
372		pcfPutINT32(file, format, offsetProps[i].value);
373	    }
374	    for (i = 0; i < prop_pad; i++)
375		pcfPutINT8(file, format, 0);
376	    pcfPutINT32(file, format, prop_string_size);
377	    for (i = 0; i < pFont->info.nprops; i++) {
378		atom_name = pcfNameForAtom(pFont->info.props[i].name);
379		pcfWrite(file, atom_name, strlen(atom_name) + 1);
380		if (pFont->info.isStringProp[i]) {
381		    atom_name = pcfNameForAtom(pFont->info.props[i].value);
382		    pcfWrite(file, atom_name, strlen(atom_name) + 1);
383		}
384	    }
385	    break;
386	case PCF_ACCELERATORS:
387	    pcfPutAccel(file, table->format, &bitmapFont->bitmapExtra->info);
388	    break;
389	case PCF_METRICS:
390	    if (PCF_FORMAT_MATCH(table->format, PCF_COMPRESSED_METRICS)) {
391		pcfPutINT16(file, format, bitmapFont->num_chars);
392		for (i = 0; i < bitmapFont->num_chars; i++)
393		    pcfPutCompressedMetric(file, format, &bitmapFont->metrics[i].metrics);
394	    } else {
395		pcfPutINT32(file, format, bitmapFont->num_chars);
396		for (i = 0; i < bitmapFont->num_chars; i++)
397		    pcfPutMetric(file, format, &bitmapFont->metrics[i].metrics);
398	    }
399	    break;
400	case PCF_BITMAPS:
401	    pcfPutINT32(file, format, bitmapFont->num_chars);
402	    glyph = PCF_GLYPH_PAD(format);
403	    offset = 0;
404	    for (i = 0; i < bitmapFont->num_chars; i++) {
405		pcfPutINT32(file, format, offset);
406		offset += BYTES_FOR_GLYPH(&bitmapFont->metrics[i], glyph);
407	    }
408	    for (i = 0; i < GLYPHPADOPTIONS; i++) {
409		pcfPutINT32(file, format,
410			    bitmapFont->bitmapExtra->bitmapsSizes[i]);
411	    }
412	    for (i = 0; i < bitmapFont->num_chars; i++)
413		pcfPutBitmap(file, format, &bitmapFont->metrics[i]);
414	    break;
415	case PCF_INK_METRICS:
416	    if (PCF_FORMAT_MATCH(table->format, PCF_COMPRESSED_METRICS)) {
417		pcfPutINT16(file, format, bitmapFont->num_chars);
418		for (i = 0; i < bitmapFont->num_chars; i++)
419		    pcfPutCompressedMetric(file, format, &bitmapFont->ink_metrics[i]);
420	    } else {
421		pcfPutINT32(file, format, bitmapFont->num_chars);
422		for (i = 0; i < bitmapFont->num_chars; i++)
423		    pcfPutMetric(file, format, &bitmapFont->ink_metrics[i]);
424	    }
425	    break;
426	case PCF_BDF_ENCODINGS:
427	    pcfPutINT16(file, format, pFont->info.firstCol);
428	    pcfPutINT16(file, format, pFont->info.lastCol);
429	    pcfPutINT16(file, format, pFont->info.firstRow);
430	    pcfPutINT16(file, format, pFont->info.lastRow);
431	    pcfPutINT16(file, format, pFont->info.defaultCh);
432	    for (i = 0; i < nencodings; i++) {
433		if (ACCESSENCODING(bitmapFont->encoding,i))
434		    pcfPutINT16(file, format,
435                                ACCESSENCODING(bitmapFont->encoding, i) -
436                                  bitmapFont->metrics);
437		else
438		    pcfPutINT16(file, format, 0xFFFF);
439	    }
440	    break;
441	case PCF_SWIDTHS:
442	    pcfPutINT32(file, format, bitmapFont->num_chars);
443	    for (i = 0; i < bitmapFont->num_chars; i++)
444		pcfPutINT32(file, format, bitmapFont->bitmapExtra->sWidths[i]);
445	    break;
446	case PCF_GLYPH_NAMES:
447	    pcfPutINT32(file, format, bitmapFont->num_chars);
448	    offset = 0;
449	    for (i = 0; i < bitmapFont->num_chars; i++) {
450		pcfPutINT32(file, format, offset);
451		offset += strlen(pcfNameForAtom(bitmapFont->bitmapExtra->glyphNames[i])) + 1;
452	    }
453	    pcfPutINT32(file, format, offset);
454	    for (i = 0; i < bitmapFont->num_chars; i++) {
455		atom_name = pcfNameForAtom(bitmapFont->bitmapExtra->glyphNames[i]);
456		pcfWrite(file, atom_name, strlen(atom_name) + 1);
457	    }
458	    break;
459	case PCF_BDF_ACCELERATORS:
460	    pcfPutAccel(file, table->format, &pFont->info);
461	    break;
462	}
463    }
464
465    free(offsetProps);
466    return Successful;
467}
468