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