write.c revision d2f28e1b
1/*
2Copyright (c) 2002-2003 by Juliusz Chroboczek
3
4Permission is hereby granted, free of charge, to any person obtaining a copy
5of this software and associated documentation files (the "Software"), to deal
6in the Software without restriction, including without limitation the rights
7to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8copies of the Software, and to permit persons to whom the Software is
9furnished to do so, subject to the following conditions:
10
11The above copyright notice and this permission notice shall be included in
12all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20THE SOFTWARE.
21*/
22/* $XFree86: xc/programs/fonttosfnt/write.c,v 1.4tsi Exp $ */
23
24#if defined(linux) && !defined(_GNU_SOURCE)
25/* for fwrite_unlocked and fread_unlocked */
26#define _GNU_SOURCE 1
27#endif
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <sys/types.h>
32#include <netinet/in.h>
33#include "X11/Xos.h"
34
35#include "fonttosfnt.h"
36
37#if !defined(I_LOVE_POSIX) && \
38    defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1
39#define DO_FWRITE fwrite_unlocked
40#define DO_FREAD fread_unlocked
41#else
42#define DO_FWRITE fwrite
43#define DO_FREAD fread
44#endif
45
46static int writeDir(FILE*, FontPtr, int, unsigned*);
47static int fixupDir(FILE*, FontPtr, int, int*, int*);
48static int fixupChecksum(FILE*, int, int);
49
50static int writeEBDT(FILE*, FontPtr);
51static int writeEBLC(FILE*, FontPtr);
52static int writeOS2(FILE*, FontPtr);
53static int writePCLT(FILE*, FontPtr);
54static int writecmap(FILE*, FontPtr);
55static int writeglyf(FILE*, FontPtr);
56static int writehead(FILE*, FontPtr);
57static int writehhea(FILE*, FontPtr);
58static int writehmtx(FILE*, FontPtr);
59static int writeloca(FILE*, FontPtr);
60static int writemaxp(FILE*, FontPtr);
61static int writename(FILE*, FontPtr);
62static int writepost(FILE*, FontPtr);
63
64static CmapPtr current_cmap = NULL;
65static int numglyphs, nummetrics;
66static int write_error_occurred, read_error_occurred;
67
68/* floor(log2(x)) */
69static int
70log2_floor(int x)
71{
72    int i, j;
73
74    if(x <= 0)
75        abort();
76
77    i = 0;
78    j = 1;
79    while(2 * j < x) {
80        i++;
81        j *= 2;
82    }
83    return i;
84}
85
86/* 2 ** floor(log2(x)) */
87static int
88two_log2_floor(int x)
89{
90    int j;
91
92    if(x <= 0)
93        abort();
94
95    j = 1;
96    while(2 * j < x) {
97        j *= 2;
98    }
99    return j;
100}
101
102static void
103write_error(int rc)
104{
105    /* Real Men program in C and don't use exceptions. */
106    if(write_error_occurred)
107        return;
108    write_error_occurred = 1;
109    if(rc < 0)
110        perror("Couldn't write");
111    else
112        fprintf(stderr, "Short write.\n");
113}
114
115static void
116read_error(int rc)
117{
118    if(read_error_occurred)
119        return;
120    read_error_occurred = 1;
121    if(rc < 0)
122        perror("Couldn't read");
123    else
124        fprintf(stderr, "Short read.\n");
125}
126
127static void
128writeBYTE(FILE *out, unsigned char val)
129{
130    int rc;
131    rc = DO_FWRITE(&val, 1, 1, out);
132    if(rc != 1) write_error(rc);
133}
134
135static void
136writeBYTEs(FILE *out, unsigned char *chars, int n)
137{
138    int rc;
139    rc = DO_FWRITE(chars, 1, n, out);
140    if(rc != n) write_error(rc);
141}
142
143static void
144writeCHAR(FILE *out, char val)
145{
146    int rc;
147    rc = DO_FWRITE(&val, 1, 1, out);
148    if(rc != 1) write_error(rc);
149}
150
151static void
152writeCHARs(FILE *out, char *chars, int n)
153{
154    int rc;
155    rc = DO_FWRITE(chars, 1, n, out);
156    if(rc != n) write_error(rc);
157}
158
159static void
160writeUSHORT(FILE *out, unsigned short val)
161{
162    int rc;
163    val = htons(val);
164    rc = DO_FWRITE(&val, 2, 1, out);
165    if(rc != 1) write_error(rc);
166}
167
168static void
169writeSHORT(FILE *out, short val)
170{
171    int rc;
172    val = htons(val);
173    rc = DO_FWRITE(&val, 2, 1, out);
174    if(rc != 1) write_error(rc);
175}
176
177static void
178writeULONG(FILE *out, unsigned int val)
179{
180    int rc;
181    val = htonl(val);
182    rc = DO_FWRITE(&val, 4, 1, out);
183    if(rc != 1) write_error(rc);
184}
185
186static void
187writeLONG(FILE *out, int val)
188{
189    int rc;
190    val = htonl(val);
191    rc = DO_FWRITE(&val, 4, 1, out);
192    if(rc != 1) write_error(rc);
193}
194
195static unsigned
196readULONG(FILE *out)
197{
198    int rc;
199    unsigned val;
200    rc = DO_FREAD(&val, 4, 1, out);
201    if(rc != 1) {
202        read_error(rc);
203        return 0xDEADBEEF;
204    }
205    return ntohl(val);
206}
207
208void
209fontMetrics(FontPtr font)
210{
211    int i, rc;
212    double sumAwidth = 0;
213    unsigned count = 0;
214
215    font->metrics.maxAwidth = 0;
216    font->metrics.maxX = -10000 * TWO_SIXTEENTH;
217    font->metrics.maxY = -10000 * TWO_SIXTEENTH;
218    font->metrics.minX = 10000 * TWO_SIXTEENTH;
219    font->metrics.minY = 10000 * TWO_SIXTEENTH;
220
221    for(i = 0; i < FONT_CODES; i++) {
222        int awidth, x0, y0, x1, y1;
223        rc = glyphMetrics(font, i, &awidth, &x0, &y0, &x1, &y1);
224        if(rc < 0)
225            continue;
226
227        if(awidth > font->metrics.maxAwidth) font->metrics.maxAwidth = awidth;
228        if(x0 < font->metrics.minX) font->metrics.minX = x0;
229        if(y0 < font->metrics.minY) font->metrics.minY = y0;
230        if(x1 > font->metrics.maxX) font->metrics.maxX = x1;
231        if(y1 > font->metrics.maxY) font->metrics.maxY = y1;
232
233	if(awidth > 0) {
234	    sumAwidth += awidth;
235	    count++;
236	}
237    }
238
239    if (count) font->metrics.awidth = sumAwidth / count;
240
241    font->metrics.height = TWO_SIXTEENTH;
242
243    if(font->pxMetrics.size == UNDEF) {
244	font->pxMetrics.size = font->pxMetrics.height;
245	font->metrics.size = font->metrics.height;
246    }
247
248    font->metrics.size = font->pxMetrics.size
249	    * TWO_SIXTEENTH / font->pxMetrics.height;
250
251    if(font->pxMetrics.ascent == UNDEF) {
252	font->metrics.ascent = font->metrics.maxY;
253	font->pxMetrics.ascent =
254	    font->metrics.ascent
255	    * font->pxMetrics.height / TWO_SIXTEENTH;
256    }
257    else
258	font->metrics.ascent =
259	    font->pxMetrics.ascent
260	    * TWO_SIXTEENTH / font->pxMetrics.height;
261
262    if(font->pxMetrics.descent == UNDEF) {
263	font->metrics.descent = - font->metrics.minY;
264	font->pxMetrics.descent =
265	    font->metrics.descent
266	    * font->pxMetrics.height / TWO_SIXTEENTH;
267    }
268    else
269	font->metrics.descent =
270	    font->pxMetrics.descent
271	    * TWO_SIXTEENTH / font->pxMetrics.height;
272
273    if(font->pxMetrics.capHeight == UNDEF) {
274	if(glyphMetrics(font, 'X', NULL, NULL, NULL, NULL, &font->metrics.capHeight) != 1)
275	    font->metrics.capHeight = font->metrics.ascent;
276	font->pxMetrics.capHeight =
277	    font->metrics.capHeight * font->pxMetrics.height / TWO_SIXTEENTH;
278    }
279    else
280	font->metrics.capHeight =
281	    font->pxMetrics.capHeight
282	    * TWO_SIXTEENTH / font->pxMetrics.height;
283
284    if(font->pxMetrics.xHeight == UNDEF) {
285	if(glyphMetrics(font, 'x', NULL, NULL, NULL, NULL, &font->metrics.xHeight) != 1)
286	    font->metrics.xHeight = font->metrics.capHeight * 2 / 3;
287	font->pxMetrics.xHeight =
288	    font->metrics.xHeight * font->pxMetrics.height / TWO_SIXTEENTH;
289    }
290    else
291	font->metrics.xHeight =
292	    font->pxMetrics.xHeight
293	    * TWO_SIXTEENTH / font->pxMetrics.height;
294
295    if(font->pxMetrics.underlinePosition == UNDEF)
296	font->metrics.underlinePosition = - font->metrics.descent * 2;
297    else {
298	font->metrics.underlinePosition =
299	    font->pxMetrics.underlinePosition
300	    * TWO_SIXTEENTH / font->pxMetrics.height;
301    }
302
303    if(font->pxMetrics.underlineThickness == UNDEF)
304	/* make sure thickness is at least one pixel. */
305	/* TODO: this could be refined according to
306	 * X Logical Font Description Conventions (xlfd.txt)
307	 * by also considering the font weight. */
308	font->metrics.underlineThickness =
309	    TWO_SIXTEENTH
310	    / (font->pxMetrics.height < 9 ? font->pxMetrics.height : 9);
311    else
312	font->metrics.underlineThickness =
313	    font->pxMetrics.underlineThickness
314	    * TWO_SIXTEENTH / font->pxMetrics.height;
315}
316
317int
318writeFile(char *filename, FontPtr font)
319{
320    int rc;
321    FILE *out;
322    unsigned tables[15];
323    int head_position = 0;
324    int full_length;
325    int (*(table_writers[15]))(FILE*, FontPtr);
326    int i, j;
327    int offset[15], length[15];
328    StrikePtr strike;
329
330    out = fopen(filename, "wb+");
331    if(out == NULL)
332        return -1;
333
334    current_cmap = makeCmap(font);
335    if(current_cmap == NULL) {
336        fprintf(stderr, "Couldn't build cmap.\n");
337        goto fail;
338    }
339
340    fontMetrics(font);
341
342    write_error_occurred = 0;
343    read_error_occurred = 0;
344
345    if(glyph_flag >= 2) {
346        numglyphs = maxIndex(current_cmap) + 1;
347        if(metrics_flag >= 2)
348            nummetrics = numglyphs - 1;
349        else if(metrics_flag >= 1)
350            nummetrics = 1;
351        else
352            nummetrics = 0;
353    } else if(glyph_flag == 1) {
354        numglyphs = 1;
355        nummetrics = (metrics_flag >= 1) ? 1 : 0;
356    } else {
357        numglyphs = 0;
358        nummetrics = 0;
359    }
360
361    strike = font->strikes;
362    while(strike) {
363        strike->indexSubTables = makeIndexSubTables(strike, current_cmap);
364        if(!strike->indexSubTables) {
365            fprintf(stderr, "Couldn't build indexSubTable.\n");
366            goto fail;
367        }
368        strike = strike->next;
369    }
370
371    /* These must be sorted lexicographically */
372    i = 0;
373    tables[i] = makeName("EBDT"); table_writers[i] = writeEBDT; i++;
374    tables[i] = makeName("EBLC"); table_writers[i] = writeEBLC; i++;
375    tables[i] = makeName("OS/2"); table_writers[i] = writeOS2; i++;
376    tables[i] = makeName("PCLT"); table_writers[i] = writePCLT; i++;
377    tables[i] = makeName("cmap"); table_writers[i] = writecmap; i++;
378    if(numglyphs >= 1) {
379        tables[i] = makeName("glyf");
380        table_writers[i] = writeglyf; i++;
381    }
382    tables[i] = makeName("head"); table_writers[i] = writehead; i++;
383    tables[i] = makeName("hhea"); table_writers[i] = writehhea; i++;
384    if(nummetrics >= 1) {
385        tables[i] = makeName("hmtx");
386        table_writers[i] = writehmtx; i++;
387    }
388    if(numglyphs >= 1) {
389        tables[i] = makeName("loca");
390        table_writers[i] = writeloca; i++;
391    }
392    tables[i] = makeName("maxp"); table_writers[i] = writemaxp; i++;
393    tables[i] = makeName("name"); table_writers[i] = writename; i++;
394    tables[i] = makeName("post"); table_writers[i] = writepost; i++;
395
396    rc = writeDir(out, font, i, tables);
397    if(rc < 0)
398        goto fail;
399
400    for(j = 0; j < i; j++) {
401        offset[j] = ftell(out);
402        if(offset[j] < 0) {
403            perror("Couldn't compute table offset");
404            goto fail;
405        }
406        if(tables[j] == makeName("head"))
407            head_position = offset[j];
408        rc = table_writers[j](out, font);
409        if(rc < 0 || write_error_occurred || read_error_occurred)
410            goto fail;
411        length[j] = ftell(out) - offset[j];
412        if(length[j] < 0) {
413            perror("Couldn't compute table size");
414            goto fail;
415        }
416        if(length[j] % 4 != 0) {
417            /* Pad -- recommended by the spec, and assumed by
418               computeChecksum. */
419            int k;
420            for(k = 0; k < (4 - length[j] % 4); k++) {
421                /* This must be 0 -- see computeChecksum. */
422                writeBYTE(out, 0);
423            }
424            if(write_error_occurred || read_error_occurred)
425                goto fail;
426        }
427    }
428
429    rc = fixupDir(out, font, i, offset, length);
430    if(rc < 0)
431        goto fail;
432
433    full_length = ftell(out);
434    if(full_length < 0) {
435        perror("Couldn't compute file size");
436        goto fail;
437    }
438    while(full_length % 4 != 0) {
439        /* pad for computeChecksum */
440        writeBYTE(out, 0);
441        full_length++;
442    }
443    if(write_error_occurred || read_error_occurred)
444        goto fail;
445    rc = fixupChecksum(out, full_length, head_position);
446    if(rc < 0)
447        goto fail;
448    fclose(out);
449    return 0;
450
451 fail:
452    fclose(out);
453    unlink(filename);
454    return -1;
455}
456
457static int
458writeDir(FILE *out, FontPtr font, int numTables, unsigned *tables)
459{
460    int i, ti;
461    i = 0; ti = 1;
462    while(2 * ti < numTables) {
463        i++;
464        ti = 2 * ti;
465    }
466
467    writeULONG(out, 0x10000);   /* version */
468    writeUSHORT(out, numTables); /* numTables */
469    writeUSHORT(out, 16 * ti);  /* searchRange */
470    writeUSHORT(out, i);        /* entrySelector */
471    writeUSHORT(out, 16 * (numTables - ti)); /* rangeShift */
472
473    /* see fixupDir */
474    for(i = 0; i < numTables; i++) {
475        writeULONG(out, tables[i]);
476        writeULONG(out, 0xDEADFACE); /* checkSum */
477        writeULONG(out, 0xDEADFACE); /* offset */
478        writeULONG(out, 0xDEADFACE); /* length */
479    }
480    return 0;
481}
482
483static unsigned
484computeChecksum(FILE *out, int offset, int length)
485{
486    int rc;
487    int i;
488    unsigned sum = 0;
489
490    if(offset % 4 != 0) {
491        fprintf(stderr, "Offset %d is not a multiple of 4\n", offset);
492        return ~0;
493    }
494
495    rc = fseek(out, offset, SEEK_SET);
496    if(rc < 0) {
497        perror("Couldn't seek");
498        return ~0;
499    }
500
501    /* This relies on the fact that we always pad tables with zeroes. */
502    for(i = 0; i < length; i += 4) {
503        sum += readULONG(out);
504    }
505    return sum;
506}
507
508static int
509fixupDir(FILE *out, FontPtr font, int numTables, int *offset, int *length)
510{
511    int rc, i;
512    unsigned sum;
513
514    for(i = 0; i < numTables; i++) {
515        sum = computeChecksum(out, offset[i], length[i]);
516        rc = fseek(out, 12 + 16 * i + 4, SEEK_SET);
517        if(rc != 0) {
518            perror("Couldn't seek");
519            return -1;
520        }
521        writeULONG(out, sum);
522        writeULONG(out, offset[i]);
523        writeULONG(out, length[i]);
524    }
525    return 0;
526}
527
528static int
529fixupChecksum(FILE *out, int full_length, int head_position)
530{
531    int rc, checksum;
532    checksum = computeChecksum(out, 0, full_length);
533    rc = fseek(out, head_position + 8, SEEK_SET);
534    if(rc != 0) {
535        perror("Couldn't seek");
536        return -1;
537    }
538    writeULONG(out, 0xB1B0AFBA - checksum); /* checkSumAdjustment */
539    return 0;
540}
541
542
543static int
544writehead(FILE* out, FontPtr font)
545{
546    int time_hi = 0;
547    unsigned time_lo = 0;
548
549    macTime(&time_hi, &time_lo);
550
551    writeULONG(out, 0x00010000);
552    writeULONG(out, 0x00010000); /* fontRevision */
553    writeULONG(out, 0);         /* checkSumAdjustment -- filled in later */
554    writeULONG(out,0x5F0F3CF5); /* magicNumber */
555    writeUSHORT(out, 1);        /* flags */
556    writeUSHORT(out, UNITS_PER_EM); /* unitsPerEm */
557
558    writeLONG(out, time_hi);    /* created */
559    writeULONG(out, time_lo);
560    writeLONG(out, time_hi);    /* modified */
561    writeULONG(out, time_lo);
562
563    /* bounding box for all glyphs */
564    writeUSHORT(out, FONT_UNITS_FLOOR(font->metrics.minX));
565    writeUSHORT(out, FONT_UNITS_FLOOR(font->metrics.minY));
566    writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.maxX));
567    writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.maxY));
568
569    writeUSHORT(out, font->flags); /* macStyle */
570    writeUSHORT(out, 1);        /* lowestRecPPEM */
571    writeSHORT(out, 0);         /* fontDirectionHint */
572    writeSHORT(out, 0);         /* indexToLocFormat */
573    writeSHORT(out, 0);         /* glyphDataFormat */
574    return 0;
575}
576
577static int
578outputRaster(FILE *out, char *raster, int width, int height, int stride,
579             int bit_aligned)
580{
581    int i, j;
582    int len = 0;
583
584    if(!bit_aligned || width % 8 == 0) {
585        for(i = 0; i < height; i++) {
586            writeCHARs(out, raster + i * stride, (width + 7) / 8);
587            len += (width + 7) / 8;
588        }
589    } else {
590        int bit = 0;
591        unsigned char v = 0;
592        for(i = 0; i < height; i++) {
593            for(j = 0; j < width; j++) {
594                if(BITREF(raster, stride, j, i))
595                    v |= 1 << (7 - bit);
596                bit++;
597                if(bit >= 8) {
598                    writeBYTE(out, v);
599                    len++;
600                    bit = 0;
601                    v = 0;
602                }
603            }
604        }
605        if(bit > 0) {
606            writeBYTE(out, v);
607            len++;
608        }
609    }
610    return len;
611}
612
613static int
614writeEBDT(FILE* out, FontPtr font)
615{
616    StrikePtr strike;
617    BitmapPtr bitmap;
618    IndexSubTablePtr table;
619    int i;
620    int offset;
621    int ebdt_start;
622
623    ebdt_start = ftell(out);
624
625    writeULONG(out, 0x00020000); /* version */
626    offset = 4;
627
628    strike = font->strikes;
629    while(strike) {
630        table = strike->indexSubTables;
631        while(table) {
632            for(i = table->firstGlyphIndex; i <= table->lastGlyphIndex; i++) {
633                bitmap = strikeBitmapIndex(strike, current_cmap, i);
634                bitmap->location = offset;
635                if(bit_aligned_flag && table->constantMetrics) {
636                    /* image format 5 */
637                    ;
638                } else {
639                    /* image format 1 or 2 */
640                    writeBYTE(out, bitmap->height);
641                    writeBYTE(out, bitmap->width);
642                    writeCHAR(out, bitmap->horiBearingX);
643                    writeCHAR(out, bitmap->horiBearingY);
644                    writeBYTE(out, bitmap->advanceWidth);
645                    offset += 5;
646                }
647                offset += outputRaster(out,
648                                       bitmap->raster,
649                                       bitmap->width, bitmap->height,
650                                       bitmap->stride,
651                                       bit_aligned_flag);
652            }
653            table->lastLocation = offset;
654            table = table->next;
655        }
656        strike = strike->next;
657    }
658    if(ftell(out) != ebdt_start + offset)
659        abort();
660    return 0;
661}
662
663static int
664writeEBLC(FILE* out, FontPtr font)
665{
666    int i, rc, numstrikes, eblc_start, num, den;
667    StrikePtr strike;
668    IndexSubTablePtr table;
669
670    degreesToFraction(font->italicAngle, &num, &den);
671
672    numstrikes = 0;
673    strike = font->strikes;
674    while(strike) {
675        numstrikes++;
676        strike = strike->next;
677    }
678
679    eblc_start = ftell(out);
680
681    writeULONG(out, 0x00020000); /* version */
682    writeULONG(out, numstrikes); /* numSizes */
683
684    /* bitmapSizeTable */
685    strike = font->strikes;
686    while(strike) {
687        strike->bitmapSizeTableLocation = ftell(out);
688        writeULONG(out, 0xDEADFACE); /* indexSubTableArrayOffset */
689        writeULONG(out, 0xDEADFACE); /* indexTablesSize */
690        writeULONG(out, 0xDEADFACE); /* numberOfIndexSubTables */
691        writeULONG(out, 0);     /* colorRef */
692	for (i = 0; i <= 1; i++) {
693	  writeCHAR(out, font->pxMetrics.ascent);	/* ascender */
694	  writeCHAR(out, -font->pxMetrics.descent);	/* descender */
695	  writeBYTE(out, strikeMaxWidth(strike));	/* widthMax */
696	  writeCHAR(out, num);				/* caretSlopeNumerator */
697	  writeCHAR(out, den);				/* caretSlopeDenominator */
698	  writeCHAR(out, 0);				/* caretOffset */
699	  writeCHAR(out, 0);				/* minOriginSB */
700	  writeCHAR(out, 0);				/* minAdvanceSB */
701	  writeCHAR(out, 0);				/* maxBeforeBL */
702	  writeCHAR(out, 0);				/* minAfterBL */
703	  writeCHAR(out, 0);				/* pad1 */
704	  writeCHAR(out, 0);				/* pad2 */
705	}
706        writeUSHORT(out, 0);    /* startGlyphIndex */
707        writeUSHORT(out, 0xFFFD); /* endGlyphIndex */
708        writeBYTE(out, strike->sizeX); /* ppemX */
709        writeBYTE(out, strike->sizeY); /* ppemY */
710        writeBYTE(out, 1);      /* bitDepth */
711        writeCHAR(out, 1);      /* flags */
712        strike = strike->next;
713    }
714
715    /* indexSubTableArray, one per strike */
716    strike = font->strikes;
717    while(strike) {
718        int endoffset;
719        int numtables = 0;
720
721        strike->indexSubTableLocation = ftell(out);
722        table = strike->indexSubTables;
723        while(table) {
724            table->location = ftell(out);
725            writeUSHORT(out, table->firstGlyphIndex);
726            writeUSHORT(out, table->lastGlyphIndex);
727            writeULONG(out, 0xDEADFACE); /* additionalOffsetToIndexSubtable */
728            numtables++;
729            table = table->next;
730        }
731        endoffset = ftell(out);
732        rc = fseek(out, strike->bitmapSizeTableLocation, SEEK_SET);
733        if(rc != 0) {
734            perror("Couldn't seek");
735            return -1;
736        }
737        writeULONG(out, strike->indexSubTableLocation - eblc_start);
738                                              /* indexSubTableArrayOffset */
739        writeULONG(out, endoffset - strike->indexSubTableLocation);
740                                              /* indexTablesSize */
741        writeULONG(out, numtables);           /* numberOfIndexSubTables */
742        rc = fseek(out, endoffset, SEEK_SET);
743        if(rc != 0) {
744            perror("Couldn't seek");
745            return -1;
746        }
747        strike = strike->next;
748    }
749
750    /* actual indexSubTables */
751    strike = font->strikes;
752    while(strike) {
753        table = strike->indexSubTables;
754        while(table) {
755            int location;
756            int data_location;
757            int short_offsets;
758            int offset;
759
760            location = ftell(out);
761            rc = fseek(out, table->location + 4, SEEK_SET);
762            if(rc != 0) {
763                perror("Couldn't seek");
764                return -1;
765            }
766            /* additionalOffsetToIndexSubtable */
767            writeULONG(out, location - strike->indexSubTableLocation);
768            rc = fseek(out, location, SEEK_SET);
769            if(rc != 0) {
770                perror("Couldn't seek");
771                return -1;
772            }
773            data_location =
774                strikeBitmapIndex(strike, current_cmap,
775                                  table->firstGlyphIndex)->location;
776            short_offsets = 1;
777            for(i = table->firstGlyphIndex; i <= table->lastGlyphIndex; i++) {
778                if(strikeBitmapIndex(strike, current_cmap, i)->location -
779                   data_location > 0xFFFF) {
780                    short_offsets = 0;
781                    break;
782                }
783            }
784            /* indexFormat */
785            if(table->constantMetrics)
786                writeUSHORT(out, 2);
787            else if(short_offsets)
788                writeUSHORT(out, 3);
789            else
790                writeUSHORT(out, 1);
791            /* imageFormat */
792            if(bit_aligned_flag) {
793                if(table->constantMetrics)
794                    writeUSHORT(out, 5);
795                else
796                    writeUSHORT(out, 2);
797            } else {
798                writeUSHORT(out, 1);
799            }
800            writeULONG(out, data_location);
801            if(table->constantMetrics) {
802                int size;
803                BitmapPtr bitmap =
804                    strikeBitmapIndex(strike, current_cmap,
805                                      table->firstGlyphIndex);
806
807                size =
808                    strikeBitmapIndex(strike, current_cmap,
809                                      table->firstGlyphIndex + 1)->location -
810                    bitmap->location;
811                writeULONG(out, size); /* imageSize */
812                /* bigMetrics */
813                writeBYTE(out, bitmap->height);
814                writeBYTE(out, bitmap->width);
815                writeCHAR(out, bitmap->horiBearingX);
816                writeCHAR(out, bitmap->horiBearingY);
817                writeBYTE(out, bitmap->advanceWidth);
818                writeCHAR(out, bitmap->horiBearingX); /* vertBearingX */
819                writeCHAR(out, bitmap->horiBearingY); /* vertBearingY */
820                writeBYTE(out, font->metrics.maxAwidth); /* vertAdvance */
821            } else {
822                for(i = table->firstGlyphIndex;
823                    i <= table->lastGlyphIndex; i++) {
824                    offset =
825                        strikeBitmapIndex(strike, current_cmap, i)->location -
826                        data_location;
827                    if(short_offsets)
828                        writeUSHORT(out, offset);
829                    else
830                        writeULONG(out, offset);
831                }
832                /* Dummy glyph of size 0 to mark the end of the table */
833                if(short_offsets) {
834                    writeUSHORT(out, table->lastLocation - data_location);
835                    writeUSHORT(out, table->lastLocation - data_location);
836                } else {
837                    writeULONG(out, table->lastLocation - data_location);
838                    writeULONG(out, table->lastLocation - data_location);
839                }
840            }
841            location = ftell(out);
842            while(location % 4 != 0) {
843                writeCHAR(out, 0);
844                location--;
845            }
846            table = table->next;
847        }
848        strike = strike->next;
849    }
850    return 0;
851}
852
853static int
854writecmap(FILE* out, FontPtr font)
855{
856    int rc, cmap_start, cmap_end;
857    CmapPtr cmap;
858    int segcount;
859
860    segcount = 0;
861    cmap = current_cmap;
862    while(cmap) {
863        segcount++;
864        cmap = cmap->next;
865    }
866
867    segcount++;                 /* dummy segment to end table */
868
869    cmap_start = ftell(out);
870
871    writeUSHORT(out, 0);        /* version */
872    writeUSHORT(out, 1);        /* number of encoding tables */
873    writeUSHORT(out, 3);        /* platform ID */
874    writeUSHORT(out, (font->flags & FACE_SYMBOL) ? 0 : 1);
875                                /* encoding ID */
876    writeULONG(out, 12);        /* offset to beginning of subtable */
877
878    /* subtable */
879    writeUSHORT(out, 4);        /* format */
880    writeUSHORT(out, 0xDEAD);   /* length */
881    writeUSHORT(out, 0);        /* language */
882    /* How baroque can you get? */
883    writeUSHORT(out, segcount * 2); /* segCountX2 */
884    writeUSHORT(out, 2 * two_log2_floor(segcount)); /* searchRange */
885    writeUSHORT(out, 1 + log2_floor(segcount));   /* entrySelector */
886    writeUSHORT(out, 2 * (segcount - two_log2_floor(segcount)));
887                                /* rangeShift */
888
889    cmap = current_cmap;
890    while(cmap) {
891        writeUSHORT(out, cmap->endCode);
892        cmap = cmap->next;
893    }
894    writeUSHORT(out, 0xFFFF);
895
896    writeUSHORT(out, 0);        /* reservedPad */
897
898    cmap = current_cmap;
899    while(cmap) {
900        writeUSHORT(out, cmap->startCode);
901        cmap = cmap->next;
902    }
903    writeUSHORT(out, 0xFFFF);
904
905    /* idDelta */
906    cmap = current_cmap;
907    while(cmap) {
908        writeUSHORT(out, (cmap->index - cmap->startCode) & 0xFFFF);
909        cmap = cmap->next;
910    }
911    writeUSHORT(out, 1);
912
913    /* idRangeOffset */
914    cmap = current_cmap;
915    while(cmap) {
916        writeUSHORT(out, 0);
917        cmap = cmap->next;
918    }
919    writeUSHORT(out, 0);
920
921    /* glyphIDArray is empty */
922
923    cmap_end = ftell(out);
924    rc = fseek(out, cmap_start + 12 + 2, SEEK_SET);
925    if(rc != 0) {
926        perror("Couldn't seek");
927        return -1;
928    }
929    writeUSHORT(out, cmap_end - cmap_start - 12); /* length */
930    rc = fseek(out, cmap_end, SEEK_SET);
931    if(rc != 0) {
932        perror("Couldn't seek");
933        return -1;
934    }
935    return 0;
936}
937
938static int
939writeglyf(FILE* out, FontPtr font)
940{
941    return 0;
942}
943
944int
945writehhea(FILE* out, FontPtr font)
946{
947    int num, den;
948    degreesToFraction(font->italicAngle, &num, &den);
949
950    writeULONG(out, 0x00010000); /* version */
951    writeSHORT(out, FONT_UNITS_CEIL(font->metrics.ascent)); /* ascender */
952    writeSHORT(out, -FONT_UNITS_CEIL(font->metrics.descent)); /* descender */
953    writeSHORT(out, FONT_UNITS(font->metrics.size - font->metrics.ascent - font->metrics.descent));	/* lineGap */
954    writeUSHORT(out, FONT_UNITS(font->metrics.maxAwidth)); /* advanceWidthMax */
955    /* TODO: the next three are not calculated according to spec, are they ?
956     * https://docs.microsoft.com/en-us/typography/opentype/spec/hhea */
957    writeSHORT(out, FONT_UNITS_FLOOR(font->metrics.minX)); /* minLeftSideBearing */
958    writeSHORT(out, FONT_UNITS_FLOOR(font->metrics.minX)); /* minRightSideBearing */
959    writeSHORT(out, FONT_UNITS_CEIL(font->metrics.maxX)); /* xMaxExtent */
960    writeSHORT(out, den);       /* caretSlopeRise */
961    writeSHORT(out, num);       /* caretSlopeRun */
962    writeSHORT(out, 0);         /* reserved */
963    writeSHORT(out, 0);         /* reserved */
964    writeSHORT(out, 0);         /* reserved */
965    writeSHORT(out, 0);         /* reserved */
966    writeSHORT(out, 0);         /* reserved */
967    writeSHORT(out, 0);         /* metricDataFormat */
968    writeSHORT(out, nummetrics); /* numberOfHMetrics */
969    return 0;
970}
971
972static int
973writehmtx(FILE* out, FontPtr font)
974{
975    int rc, i;
976
977    for(i = 0; i <= numglyphs; i++) {
978        int code, width, lsb;
979        code = findCode(current_cmap, i);
980        if(code < 0)
981            rc = -1;
982        else
983            rc = glyphMetrics(font, code, &width, &lsb, NULL, NULL, NULL);
984        if(rc < 0) {
985            width = UNITS_PER_EM / 3;
986            lsb = 0;
987        }
988        if(i < nummetrics) {
989            writeSHORT(out, FONT_UNITS(width));
990            writeSHORT(out, FONT_UNITS(lsb));
991        } else {
992            writeSHORT(out, FONT_UNITS(lsb));
993        }
994    }
995    return 0;
996}
997
998static int
999writeloca(FILE* out, FontPtr font)
1000{
1001    int i;
1002
1003    /* All glyphs undefined -- loca table is empty, so offset 0 */
1004    for(i = 0; i < numglyphs; i++) {
1005        writeSHORT(out, 0);
1006    }
1007    writeSHORT(out, 0);
1008    return 0;
1009}
1010
1011static int
1012writemaxp(FILE* out, FontPtr font)
1013{
1014    writeLONG(out, 0x00010000); /* version */
1015    writeUSHORT(out, numglyphs); /* numGlyphs */
1016    writeUSHORT(out, 0);        /* maxPoints */
1017    writeUSHORT(out, 0);        /* maxContours */
1018    writeUSHORT(out, 0);        /* maxCompositePoints */
1019    writeUSHORT(out, 0);        /* maxCompositeContours */
1020    writeUSHORT(out, 1);        /* maxZones */
1021    writeUSHORT(out, 0);        /* maxTwilightPoints */
1022    writeUSHORT(out, 0);        /* maxStorage */
1023    writeUSHORT(out, 0);        /* maxFunctionDefs */
1024    writeUSHORT(out, 0);        /* maxInstructionDefs */
1025    writeUSHORT(out, 0);        /* maxStackElements */
1026    writeUSHORT(out, 0);        /* maxSizeOfInstructions */
1027    writeUSHORT(out, 0);        /* maxComponentElements */
1028    writeUSHORT(out, 0);        /* maxComponentDepth */
1029    return 0;
1030}
1031
1032static int
1033writename(FILE* out, FontPtr font)
1034{
1035    int i;
1036    int offset;
1037
1038    writeUSHORT(out, 0);        /* format selector */
1039    writeUSHORT(out, font->numNames);
1040    writeUSHORT(out, 6 + font->numNames * 12); /* offset to string storage */
1041    offset = 0;
1042    for(i = 0; i < font->numNames; i++) {
1043        writeUSHORT(out, 3);    /* platform id -- Microsoft */
1044        writeUSHORT(out, 1);    /* encoding -- Unicode */
1045        writeUSHORT(out, 0x409); /* language id -- American English */
1046        writeUSHORT(out, font->names[i].nid); /* name id */
1047        writeUSHORT(out, font->names[i].size); /* string length */
1048        writeUSHORT(out, offset); /* string offset */
1049        offset += font->names[i].size;
1050    }
1051    for(i = 0; i < font->numNames; i++)
1052        writeCHARs(out, font->names[i].value, font->names[i].size);
1053    return 0;
1054}
1055
1056static int
1057writepost(FILE* out, FontPtr font)
1058{
1059    int i, rc, previous_width, width, fixed_pitch;
1060
1061    fixed_pitch = 1;
1062    previous_width = -1;
1063    for(i = 0; i < FONT_CODES; i++) {
1064        rc = glyphMetrics(font, i, &width, NULL, NULL, NULL, NULL);
1065        if(rc < 0)
1066            continue;
1067        if(previous_width >= 0) {
1068            if(width != previous_width) {
1069                fixed_pitch = 0;
1070                break;
1071            }
1072        }
1073        previous_width = width;
1074    }
1075
1076    writeULONG(out, 0x00030000); /* FormatType */
1077    writeULONG(out, font->italicAngle); /* italicAngle */
1078    writeSHORT(out, FONT_UNITS(font->metrics.underlinePosition));
1079    writeSHORT(out, FONT_UNITS(font->metrics.underlineThickness));
1080    writeULONG(out, fixed_pitch); /* isFixedPitch */
1081    writeULONG(out, 0);         /* minMemType42 */
1082    writeULONG(out, 0);         /* maxMemType42 */
1083    writeULONG(out, 0);         /* minMemType1 */
1084    writeULONG(out, 0);         /* maxMemType1 */
1085    return 0;
1086}
1087
1088static int
1089writeOS2(FILE* out, FontPtr font)
1090{
1091    int i;
1092
1093    writeUSHORT(out, 5); /* version */
1094    writeSHORT(out, FONT_UNITS(font->metrics.awidth)); /* xAvgCharWidth; */
1095    writeUSHORT(out, font->weight);  /* usWeightClass; */
1096    writeUSHORT(out, font->width); /* usWidthClass; */
1097    writeSHORT(out, 0);         /* fsType; */
1098    writeSHORT(out, UNITS_PER_EM / 5); /* ySubscriptXSize; */
1099    writeSHORT(out, UNITS_PER_EM / 5); /* ySubscriptYSize; */
1100    writeSHORT(out, 0);         /* ySubscriptXOffset; */
1101    writeSHORT(out, UNITS_PER_EM / 5); /* ySubscriptYOffset; */
1102    writeSHORT(out, UNITS_PER_EM / 5); /* ySuperscriptXSize; */
1103    writeSHORT(out, UNITS_PER_EM / 5); /* ySuperscriptYSize; */
1104    writeSHORT(out, 0);         /* ySuperscriptXOffset; */
1105    writeSHORT(out, UNITS_PER_EM / 5); /* ySuperscriptYOffset; */
1106    writeSHORT(out, FONT_UNITS(font->metrics.underlineThickness));
1107    /* yStrikeoutSize; */
1108    writeSHORT(out, UNITS_PER_EM / 4); /* yStrikeoutPosition; */
1109    writeSHORT(out, 0);         /* sFamilyClass; */
1110    for(i = 0; i < 10; i++)
1111        writeBYTE(out, 0);      /* panose; */
1112    writeULONG(out, 0xFFFF);    /* ulUnicodeRange1; */
1113    writeULONG(out, 0xFFFF);    /* ulUnicodeRange2; */
1114    writeULONG(out, 0x03FF);    /* ulUnicodeRange3; */
1115    writeULONG(out, 0U);        /* ulUnicodeRange4; */
1116    writeULONG(out, font->foundry); /* achVendID[4]; */
1117    i = 0;
1118    if (font->flags & FACE_ITALIC)
1119	i |= 1 << 0;
1120    if (font->flags & FACE_BOLD)
1121	i |= 1 << 5;
1122    if (!i)
1123	i |= 1 << 6;
1124#ifndef NO_TYPO_METRICS
1125    i |= 1 << 7; /* USE_TYPO_METRICS instead usWin metrics for line spacing. */
1126#endif
1127    writeUSHORT(out, i);	/* fsSelection; */
1128    writeUSHORT(out, 0x20);     /* usFirstCharIndex; */
1129    writeUSHORT(out, 0xFFFD);   /* usLastCharIndex; */
1130    writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.ascent)); /* sTypoAscender; */
1131    writeSHORT(out, -FONT_UNITS_CEIL(font->metrics.descent)); /* sTypoDescender; */
1132    writeSHORT(out, FONT_UNITS(font->metrics.size - font->metrics.ascent - font->metrics.descent));	/* sTypoLineGap */
1133#ifdef NO_TYPO_METRICS
1134    writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.ascent)); /* usWinAscent; */
1135    writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.descent)); /* usWinDescent; */
1136#else
1137    writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.maxY)); /* usWinAscent; */
1138    writeUSHORT(out, -FONT_UNITS_FLOOR(font->metrics.minY)); /* usWinDescent; */
1139#endif
1140    writeULONG(out, 3);						/* ulCodePageRange1; */
1141    writeULONG(out, 0);         				/* ulCodePageRange2; */
1142    writeSHORT(out, FONT_UNITS_CEIL(font->metrics.xHeight));	/* sxHeight; */
1143    writeSHORT(out, FONT_UNITS_CEIL(font->metrics.capHeight));	/* sCapHeight; */
1144    writeUSHORT(out, 0); 					/* usDefaultChar; */
1145    writeUSHORT(out, 20); 					/* usBreakChar; */
1146    writeUSHORT(out, 0); 					/* usMaxContext; */
1147    writeUSHORT(out, 0); 					/* usLowerOpticalPointSize; */
1148    writeUSHORT(out, 0xffff); 					/* usUpperOpticalPointSize; */
1149    return 0;
1150}
1151
1152static int
1153writePCLT(FILE* out, FontPtr font)
1154{
1155    char name[16] = "X11 font        ";
1156    char filename[6] = "X11R00";
1157    unsigned char charComplement[8] =
1158        {0xFF, 0xFF, 0xFF, 0xFF, 0x0B, 0xFF, 0xFF, 0xFE};
1159    int style, w, strokeWeight, widthType;
1160
1161    style = 0;
1162    if(font->flags & FACE_ITALIC)
1163        style = 1;
1164
1165    w = (font->weight + 50) / 100;
1166    if(w < 5)
1167        strokeWeight = w - 6;
1168    else if(w == 5)
1169        strokeWeight = 0;
1170    else
1171        strokeWeight = w - 4;
1172
1173    if(font->width <= 2)
1174        widthType = -3;
1175    else if(font->width <= 4)
1176        widthType = -2;
1177    else if(font->width <= 6)
1178        widthType = 0;
1179    else if(font->width <= 7)
1180        widthType = 2;
1181    else
1182        widthType = 3;
1183
1184    writeULONG(out, 0x00010000); /* version */
1185    writeULONG(out, 0);         /* FontNumber */
1186    writeUSHORT(out, FONT_UNITS(font->metrics.maxAwidth)); /* pitch */
1187    writeUSHORT(out, FONT_UNITS(font->metrics.xHeight));    /* xHeight */
1188    writeUSHORT(out, style);    /* style */
1189    writeUSHORT(out, 6 << 12);  /* TypeFamily */
1190    writeUSHORT(out, FONT_UNITS(font->metrics.xHeight)); /* CapHeight */
1191    writeUSHORT(out, 0);        /* SymbolSet */
1192    writeCHARs(out, name, 16);  /* TypeFace */
1193    writeBYTEs(out, charComplement, 8); /* CharacterComplement */
1194    writeCHARs(out, filename, 6); /* FileName */
1195    writeCHAR(out, strokeWeight); /* StrokeWeight */
1196    writeCHAR(out, widthType);  /* WidthType */
1197    writeCHAR(out, 1 << 6);     /* SerifStyle */
1198    writeCHAR(out, 0);          /* Reserved */
1199    return 0;
1200}
1201