write.c revision c813b494
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	fprintf(stderr, "Setting underlinePosition. pxMetrics.underlinePosition is %d. height is %d\n",
299		font->pxMetrics.underlinePosition, font->pxMetrics.height);
300	font->metrics.underlinePosition =
301	    font->pxMetrics.underlinePosition
302	    * TWO_SIXTEENTH / font->pxMetrics.height;
303    }
304
305    if(font->pxMetrics.underlineThickness == UNDEF)
306	/* make sure thickness is at least one pixel. */
307	/* TODO: this could be refined according to
308	 * X Logical Font Description Conventions (xlfd.txt)
309	 * by also considering the font weight. */
310	font->metrics.underlineThickness =
311	    TWO_SIXTEENTH
312	    / (font->pxMetrics.height < 9 ? font->pxMetrics.height : 9);
313    else
314	font->metrics.underlineThickness =
315	    font->pxMetrics.underlineThickness
316	    * TWO_SIXTEENTH / font->pxMetrics.height;
317}
318
319int
320writeFile(char *filename, FontPtr font)
321{
322    int rc;
323    FILE *out;
324    unsigned tables[15];
325    int head_position = 0;
326    int full_length;
327    int (*(table_writers[15]))(FILE*, FontPtr);
328    int i, j;
329    int offset[15], length[15];
330    StrikePtr strike;
331
332    out = fopen(filename, "wb+");
333    if(out == NULL)
334        return -1;
335
336    current_cmap = makeCmap(font);
337    if(current_cmap == NULL) {
338        fprintf(stderr, "Couldn't build cmap.\n");
339        return -1;
340    }
341
342    fontMetrics(font);
343
344    write_error_occurred = 0;
345    read_error_occurred = 0;
346
347    if(glyph_flag >= 2) {
348        numglyphs = maxIndex(current_cmap) + 1;
349        if(metrics_flag >= 2)
350            nummetrics = numglyphs - 1;
351        else if(metrics_flag >= 1)
352            nummetrics = 1;
353        else
354            nummetrics = 0;
355    } else if(glyph_flag == 1) {
356        numglyphs = 1;
357        nummetrics = (metrics_flag >= 1) ? 1 : 0;
358    } else {
359        numglyphs = 0;
360        nummetrics = 0;
361    }
362
363    strike = font->strikes;
364    while(strike) {
365        strike->indexSubTables = makeIndexSubTables(strike, current_cmap);
366        if(!strike->indexSubTables) {
367            fprintf(stderr, "Couldn't build indexSubTable.\n");
368            return -1;
369        }
370        strike = strike->next;
371    }
372
373    /* These must be sorted lexicographically */
374    i = 0;
375    tables[i] = makeName("EBDT"); table_writers[i] = writeEBDT; i++;
376    tables[i] = makeName("EBLC"); table_writers[i] = writeEBLC; i++;
377    tables[i] = makeName("OS/2"); table_writers[i] = writeOS2; i++;
378    tables[i] = makeName("PCLT"); table_writers[i] = writePCLT; i++;
379    tables[i] = makeName("cmap"); table_writers[i] = writecmap; i++;
380    if(numglyphs >= 1) {
381        tables[i] = makeName("glyf");
382        table_writers[i] = writeglyf; i++;
383    }
384    tables[i] = makeName("head"); table_writers[i] = writehead; i++;
385    tables[i] = makeName("hhea"); table_writers[i] = writehhea; i++;
386    if(nummetrics >= 1) {
387        tables[i] = makeName("hmtx");
388        table_writers[i] = writehmtx; i++;
389    }
390    if(numglyphs >= 1) {
391        tables[i] = makeName("loca");
392        table_writers[i] = writeloca; i++;
393    }
394    tables[i] = makeName("maxp"); table_writers[i] = writemaxp; i++;
395    tables[i] = makeName("name"); table_writers[i] = writename; i++;
396    tables[i] = makeName("post"); table_writers[i] = writepost; i++;
397
398    rc = writeDir(out, font, i, tables);
399    if(rc < 0)
400        goto fail;
401
402    for(j = 0; j < i; j++) {
403        offset[j] = ftell(out);
404        if(offset[j] < 0) {
405            perror("Couldn't compute table offset");
406            goto fail;
407        }
408        if(tables[j] == makeName("head"))
409            head_position = offset[j];
410        rc = table_writers[j](out, font);
411        if(rc < 0 || write_error_occurred || read_error_occurred)
412            goto fail;
413        length[j] = ftell(out) - offset[j];
414        if(length[j] < 0) {
415            perror("Couldn't compute table size");
416            goto fail;
417        }
418        if(length[j] % 4 != 0) {
419            /* Pad -- recommended by the spec, and assumed by
420               computeChecksum. */
421            int k;
422            for(k = 0; k < (4 - length[j] % 4); k++) {
423                /* This must be 0 -- see computeChecksum. */
424                writeBYTE(out, 0);
425            }
426            if(write_error_occurred || read_error_occurred)
427                goto fail;
428        }
429    }
430
431    rc = fixupDir(out, font, i, offset, length);
432    if(rc < 0)
433        goto fail;
434
435    full_length = ftell(out);
436    if(full_length < 0) {
437        perror("Couldn't compute file size");
438        goto fail;
439    }
440    while(full_length % 4 != 0) {
441        /* pad for computeChecksum */
442        writeBYTE(out, 0);
443        full_length++;
444    }
445    if(write_error_occurred || read_error_occurred)
446        goto fail;
447    rc = fixupChecksum(out, full_length, head_position);
448    if(rc < 0)
449        goto fail;
450    fclose(out);
451    return 0;
452
453 fail:
454    unlink(filename);
455    return -1;
456}
457
458static int
459writeDir(FILE *out, FontPtr font, int numTables, unsigned *tables)
460{
461    int i, ti;
462    i = 0; ti = 1;
463    while(2 * ti < numTables) {
464        i++;
465        ti = 2 * ti;
466    }
467
468    writeULONG(out, 0x10000);   /* version */
469    writeUSHORT(out, numTables); /* numTables */
470    writeUSHORT(out, 16 * ti);  /* searchRange */
471    writeUSHORT(out, i);        /* entrySelector */
472    writeUSHORT(out, 16 * (numTables - ti)); /* rangeShift */
473
474    /* see fixupDir */
475    for(i = 0; i < numTables; i++) {
476        writeULONG(out, tables[i]);
477        writeULONG(out, 0xDEADFACE); /* checkSum */
478        writeULONG(out, 0xDEADFACE); /* offset */
479        writeULONG(out, 0xDEADFACE); /* length */
480    }
481    return 0;
482}
483
484static unsigned
485computeChecksum(FILE *out, int offset, int length)
486{
487    int rc;
488    int i;
489    unsigned sum = 0;
490
491    if(offset % 4 != 0) {
492        fprintf(stderr, "Offset %d is not a multiple of 4\n", offset);
493        return ~0;
494    }
495
496    rc = fseek(out, offset, SEEK_SET);
497    if(rc < 0) {
498        perror("Couldn't seek");
499        return ~0;
500    }
501
502    /* This relies on the fact that we always pad tables with zeroes. */
503    for(i = 0; i < length; i += 4) {
504        sum += readULONG(out);
505    }
506    return sum;
507}
508
509static int
510fixupDir(FILE *out, FontPtr font, int numTables, int *offset, int *length)
511{
512    int rc, i;
513    unsigned sum;
514
515    for(i = 0; i < numTables; i++) {
516        sum = computeChecksum(out, offset[i], length[i]);
517        rc = fseek(out, 12 + 16 * i + 4, SEEK_SET);
518        if(rc != 0) {
519            perror("Couldn't seek");
520            return -1;
521        }
522        writeULONG(out, sum);
523        writeULONG(out, offset[i]);
524        writeULONG(out, length[i]);
525    }
526    return 0;
527}
528
529static int
530fixupChecksum(FILE *out, int full_length, int head_position)
531{
532    int rc, checksum;
533    checksum = computeChecksum(out, 0, full_length);
534    rc = fseek(out, head_position + 8, SEEK_SET);
535    if(rc != 0) {
536        perror("Couldn't seek");
537        return -1;
538    }
539    writeULONG(out, 0xB1B0AFBA - checksum); /* checkSumAdjustment */
540    return 0;
541}
542
543
544static int
545writehead(FILE* out, FontPtr font)
546{
547    int time_hi = 0;
548    unsigned time_lo = 0;
549
550    macTime(&time_hi, &time_lo);
551
552    writeULONG(out, 0x00010000);
553    writeULONG(out, 0x00010000); /* fontRevision */
554    writeULONG(out, 0);         /* checkSumAdjustment -- filled in later */
555    writeULONG(out,0x5F0F3CF5); /* magicNumber */
556    writeUSHORT(out, 1);        /* flags */
557    writeUSHORT(out, UNITS_PER_EM); /* unitsPerEm */
558
559    writeLONG(out, time_hi);    /* created */
560    writeULONG(out, time_lo);
561    writeLONG(out, time_hi);    /* modified */
562    writeULONG(out, time_lo);
563
564    /* bounding box for all glyphs */
565    writeUSHORT(out, FONT_UNITS_FLOOR(font->metrics.minX));
566    writeUSHORT(out, FONT_UNITS_FLOOR(font->metrics.minY));
567    writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.maxX));
568    writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.maxY));
569
570    writeUSHORT(out, font->flags); /* macStyle */
571    writeUSHORT(out, 1);        /* lowestRecPPEM */
572    writeSHORT(out, 0);         /* fontDirectionHint */
573    writeSHORT(out, 0);         /* indexToLocFormat */
574    writeSHORT(out, 0);         /* glyphDataFormat */
575    return 0;
576}
577
578static int
579outputRaster(FILE *out, char *raster, int width, int height, int stride,
580             int bit_aligned)
581{
582    int i, j;
583    int len = 0;
584
585    if(!bit_aligned || width % 8 == 0) {
586        for(i = 0; i < height; i++) {
587            writeCHARs(out, raster + i * stride, (width + 7) / 8);
588            len += (width + 7) / 8;
589        }
590    } else {
591        int bit = 0;
592        unsigned char v = 0;
593        for(i = 0; i < height; i++) {
594            for(j = 0; j < width; j++) {
595                if(BITREF(raster, stride, j, i))
596                    v |= 1 << (7 - bit);
597                bit++;
598                if(bit >= 8) {
599                    writeBYTE(out, v);
600                    len++;
601                    bit = 0;
602                    v = 0;
603                }
604            }
605        }
606        if(bit > 0) {
607            writeBYTE(out, v);
608            len++;
609        }
610    }
611    return len;
612}
613
614static int
615writeEBDT(FILE* out, FontPtr font)
616{
617    StrikePtr strike;
618    BitmapPtr bitmap;
619    IndexSubTablePtr table;
620    int i;
621    int offset;
622    int ebdt_start;
623
624    ebdt_start = ftell(out);
625
626    writeULONG(out, 0x00020000); /* version */
627    offset = 4;
628
629    strike = font->strikes;
630    while(strike) {
631        table = strike->indexSubTables;
632        while(table) {
633            for(i = table->firstGlyphIndex; i <= table->lastGlyphIndex; i++) {
634                bitmap = strikeBitmapIndex(strike, current_cmap, i);
635                bitmap->location = offset;
636                if(bit_aligned_flag && table->constantMetrics) {
637                    /* image format 5 */
638                    ;
639                } else {
640                    /* image format 1 or 2 */
641                    writeBYTE(out, bitmap->height);
642                    writeBYTE(out, bitmap->width);
643                    writeCHAR(out, bitmap->horiBearingX);
644                    writeCHAR(out, bitmap->horiBearingY);
645                    writeBYTE(out, bitmap->advanceWidth);
646                    offset += 5;
647                }
648                offset += outputRaster(out,
649                                       bitmap->raster,
650                                       bitmap->width, bitmap->height,
651                                       bitmap->stride,
652                                       bit_aligned_flag);
653            }
654            table->lastLocation = offset;
655            table = table->next;
656        }
657        strike = strike->next;
658    }
659    if(ftell(out) != ebdt_start + offset)
660        abort();
661    return 0;
662}
663
664static int
665writeEBLC(FILE* out, FontPtr font)
666{
667    int i, rc, numstrikes, eblc_start, num, den;
668    StrikePtr strike;
669    IndexSubTablePtr table;
670
671    degreesToFraction(font->italicAngle, &num, &den);
672
673    numstrikes = 0;
674    strike = font->strikes;
675    while(strike) {
676        numstrikes++;
677        strike = strike->next;
678    }
679
680    eblc_start = ftell(out);
681
682    writeULONG(out, 0x00020000); /* version */
683    writeULONG(out, numstrikes); /* numSizes */
684
685    /* bitmapSizeTable */
686    strike = font->strikes;
687    while(strike) {
688        strike->bitmapSizeTableLocation = ftell(out);
689        writeULONG(out, 0xDEADFACE); /* indexSubTableArrayOffset */
690        writeULONG(out, 0xDEADFACE); /* indexTablesSize */
691        writeULONG(out, 0xDEADFACE); /* numberOfIndexSubTables */
692        writeULONG(out, 0);     /* colorRef */
693	for (i = 0; i <= 1; i++) {
694	  writeCHAR(out, font->pxMetrics.ascent);	/* ascender */
695	  writeCHAR(out, -font->pxMetrics.descent);	/* descender */
696	  writeBYTE(out, strikeMaxWidth(strike));	/* widthMax */
697	  writeCHAR(out, num);				/* caretSlopeNumerator */
698	  writeCHAR(out, den);				/* caretSlopeDenominator */
699	  writeCHAR(out, 0);				/* caretOffset */
700	  writeCHAR(out, 0);				/* minOriginSB */
701	  writeCHAR(out, 0);				/* minAdvanceSB */
702	  writeCHAR(out, 0);				/* maxBeforeBL */
703	  writeCHAR(out, 0);				/* minAfterBL */
704	  writeCHAR(out, 0);				/* pad1 */
705	  writeCHAR(out, 0);				/* pad2 */
706	}
707        writeUSHORT(out, 0);    /* startGlyphIndex */
708        writeUSHORT(out, 0xFFFD); /* endGlyphIndex */
709        writeBYTE(out, strike->sizeX); /* ppemX */
710        writeBYTE(out, strike->sizeY); /* ppemY */
711        writeBYTE(out, 1);      /* bitDepth */
712        writeCHAR(out, 1);      /* flags */
713        strike = strike->next;
714    }
715
716    /* indexSubTableArray, one per strike */
717    strike = font->strikes;
718    while(strike) {
719        int endoffset;
720        int numtables = 0;
721
722        strike->indexSubTableLocation = ftell(out);
723        table = strike->indexSubTables;
724        while(table) {
725            table->location = ftell(out);
726            writeUSHORT(out, table->firstGlyphIndex);
727            writeUSHORT(out, table->lastGlyphIndex);
728            writeULONG(out, 0xDEADFACE); /* additionalOffsetToIndexSubtable */
729            numtables++;
730            table = table->next;
731        }
732        endoffset = ftell(out);
733        rc = fseek(out, strike->bitmapSizeTableLocation, SEEK_SET);
734        if(rc != 0) {
735            perror("Couldn't seek");
736            return -1;
737        }
738        writeULONG(out, strike->indexSubTableLocation - eblc_start);
739                                              /* indexSubTableArrayOffset */
740        writeULONG(out, endoffset - strike->indexSubTableLocation);
741                                              /* indexTablesSize */
742        writeULONG(out, numtables);           /* numberOfIndexSubTables */
743        rc = fseek(out, endoffset, SEEK_SET);
744        if(rc != 0) {
745            perror("Couldn't seek");
746            return -1;
747        }
748        strike = strike->next;
749    }
750
751    /* actual indexSubTables */
752    strike = font->strikes;
753    while(strike) {
754        table = strike->indexSubTables;
755        while(table) {
756            int location;
757            int data_location;
758            int short_offsets;
759            int offset;
760
761            location = ftell(out);
762            rc = fseek(out, table->location + 4, SEEK_SET);
763            if(rc != 0) {
764                perror("Couldn't seek");
765                return -1;
766            }
767            /* additionalOffsetToIndexSubtable */
768            writeULONG(out, location - strike->indexSubTableLocation);
769            rc = fseek(out, location, SEEK_SET);
770            if(rc != 0) {
771                perror("Couldn't seek");
772                return -1;
773            }
774            data_location =
775                strikeBitmapIndex(strike, current_cmap,
776                                  table->firstGlyphIndex)->location;
777            short_offsets = 1;
778            for(i = table->firstGlyphIndex; i <= table->lastGlyphIndex; i++) {
779                if(strikeBitmapIndex(strike, current_cmap, i)->location -
780                   data_location > 0xFFFF) {
781                    short_offsets = 0;
782                    break;
783                }
784            }
785            /* indexFormat */
786            if(table->constantMetrics)
787                writeUSHORT(out, 2);
788            else if(short_offsets)
789                writeUSHORT(out, 3);
790            else
791                writeUSHORT(out, 1);
792            /* imageFormat */
793            if(bit_aligned_flag) {
794                if(table->constantMetrics)
795                    writeUSHORT(out, 5);
796                else
797                    writeUSHORT(out, 2);
798            } else {
799                writeUSHORT(out, 1);
800            }
801            writeULONG(out, data_location);
802            if(table->constantMetrics) {
803                int size;
804                BitmapPtr bitmap =
805                    strikeBitmapIndex(strike, current_cmap,
806                                      table->firstGlyphIndex);
807
808                size =
809                    strikeBitmapIndex(strike, current_cmap,
810                                      table->firstGlyphIndex + 1)->location -
811                    bitmap->location;
812                writeULONG(out, size); /* imageSize */
813                /* bigMetrics */
814                writeBYTE(out, bitmap->height);
815                writeBYTE(out, bitmap->width);
816                writeCHAR(out, bitmap->horiBearingX);
817                writeCHAR(out, bitmap->horiBearingY);
818                writeBYTE(out, bitmap->advanceWidth);
819                writeCHAR(out, bitmap->horiBearingX); /* vertBearingX */
820                writeCHAR(out, bitmap->horiBearingY); /* vertBearingY */
821                writeBYTE(out, font->metrics.maxAwidth); /* vertAdvance */
822            } else {
823                for(i = table->firstGlyphIndex;
824                    i <= table->lastGlyphIndex; i++) {
825                    offset =
826                        strikeBitmapIndex(strike, current_cmap, i)->location -
827                        data_location;
828                    if(short_offsets)
829                        writeUSHORT(out, offset);
830                    else
831                        writeULONG(out, offset);
832                }
833                /* Dummy glyph of size 0 to mark the end of the table */
834                if(short_offsets) {
835                    writeUSHORT(out, table->lastLocation - data_location);
836                    writeUSHORT(out, table->lastLocation - data_location);
837                } else {
838                    writeULONG(out, table->lastLocation - data_location);
839                    writeULONG(out, table->lastLocation - data_location);
840                }
841            }
842            location = ftell(out);
843            while(location % 4 != 0) {
844                writeCHAR(out, 0);
845                location--;
846            }
847            table = table->next;
848        }
849        strike = strike->next;
850    }
851    return 0;
852}
853
854static int
855writecmap(FILE* out, FontPtr font)
856{
857    int rc, cmap_start, cmap_end;
858    CmapPtr cmap;
859    int segcount;
860
861    segcount = 0;
862    cmap = current_cmap;
863    while(cmap) {
864        segcount++;
865        cmap = cmap->next;
866    }
867
868    segcount++;                 /* dummy segment to end table */
869
870    cmap_start = ftell(out);
871
872    writeUSHORT(out, 0);        /* version */
873    writeUSHORT(out, 1);        /* number of encoding tables */
874    writeUSHORT(out, 3);        /* platform ID */
875    writeUSHORT(out, (font->flags & FACE_SYMBOL) ? 0 : 1);
876                                /* encoding ID */
877    writeULONG(out, 12);        /* offset to beginning of subtable */
878
879    /* subtable */
880    writeUSHORT(out, 4);        /* format */
881    writeUSHORT(out, 0xDEAD);   /* length */
882    writeUSHORT(out, 0);        /* language */
883    /* How baroque can you get? */
884    writeUSHORT(out, segcount * 2); /* segCountX2 */
885    writeUSHORT(out, 2 * two_log2_floor(segcount)); /* searchRange */
886    writeUSHORT(out, 1 + log2_floor(segcount));   /* entrySelector */
887    writeUSHORT(out, 2 * (segcount - two_log2_floor(segcount)));
888                                /* rangeShift */
889
890    cmap = current_cmap;
891    while(cmap) {
892        writeUSHORT(out, cmap->endCode);
893        cmap = cmap->next;
894    }
895    writeUSHORT(out, 0xFFFF);
896
897    writeUSHORT(out, 0);        /* reservedPad */
898
899    cmap = current_cmap;
900    while(cmap) {
901        writeUSHORT(out, cmap->startCode);
902        cmap = cmap->next;
903    }
904    writeUSHORT(out, 0xFFFF);
905
906    /* idDelta */
907    cmap = current_cmap;
908    while(cmap) {
909        writeUSHORT(out, (cmap->index - cmap->startCode) & 0xFFFF);
910        cmap = cmap->next;
911    }
912    writeUSHORT(out, 1);
913
914    /* idRangeOffset */
915    cmap = current_cmap;
916    while(cmap) {
917        writeUSHORT(out, 0);
918        cmap = cmap->next;
919    }
920    writeUSHORT(out, 0);
921
922    /* glyphIDArray is empty */
923
924    cmap_end = ftell(out);
925    rc = fseek(out, cmap_start + 12 + 2, SEEK_SET);
926    if(rc != 0) {
927        perror("Couldn't seek");
928        return -1;
929    }
930    writeUSHORT(out, cmap_end - cmap_start - 12); /* length */
931    rc = fseek(out, cmap_end, SEEK_SET);
932    if(rc != 0) {
933        perror("Couldn't seek");
934        return -1;
935    }
936    return 0;
937}
938
939static int
940writeglyf(FILE* out, FontPtr font)
941{
942    return 0;
943}
944
945int
946writehhea(FILE* out, FontPtr font)
947{
948    int num, den;
949    degreesToFraction(font->italicAngle, &num, &den);
950
951    writeULONG(out, 0x00010000); /* version */
952    writeSHORT(out, FONT_UNITS_CEIL(font->metrics.ascent)); /* ascender */
953    writeSHORT(out, -FONT_UNITS_CEIL(font->metrics.descent)); /* descender */
954    writeSHORT(out, FONT_UNITS(font->metrics.size - font->metrics.ascent - font->metrics.descent));	/* lineGap */
955    writeUSHORT(out, FONT_UNITS(font->metrics.maxAwidth)); /* advanceWidthMax */
956    /* TODO: the next three are not calculated according to spec, are they ?
957     * https://docs.microsoft.com/en-us/typography/opentype/spec/hhea */
958    writeSHORT(out, FONT_UNITS_FLOOR(font->metrics.minX)); /* minLeftSideBearing */
959    writeSHORT(out, FONT_UNITS_FLOOR(font->metrics.minX)); /* minRightSideBearing */
960    writeSHORT(out, FONT_UNITS_CEIL(font->metrics.maxX)); /* xMaxExtent */
961    writeSHORT(out, den);       /* caretSlopeRise */
962    writeSHORT(out, num);       /* caretSlopeRun */
963    writeSHORT(out, 0);         /* reserved */
964    writeSHORT(out, 0);         /* reserved */
965    writeSHORT(out, 0);         /* reserved */
966    writeSHORT(out, 0);         /* reserved */
967    writeSHORT(out, 0);         /* reserved */
968    writeSHORT(out, 0);         /* metricDataFormat */
969    writeSHORT(out, nummetrics); /* numberOfHMetrics */
970    return 0;
971}
972
973static int
974writehmtx(FILE* out, FontPtr font)
975{
976    int rc, i;
977
978    for(i = 0; i <= numglyphs; i++) {
979        int code, width, lsb;
980        code = findCode(current_cmap, i);
981        if(code < 0)
982            rc = -1;
983        else
984            rc = glyphMetrics(font, code, &width, &lsb, NULL, NULL, NULL);
985        if(rc < 0) {
986            width = UNITS_PER_EM / 3;
987            lsb = 0;
988        }
989        if(i < nummetrics) {
990            writeSHORT(out, FONT_UNITS(width));
991            writeSHORT(out, FONT_UNITS(lsb));
992        } else {
993            writeSHORT(out, FONT_UNITS(lsb));
994        }
995    }
996    return 0;
997}
998
999static int
1000writeloca(FILE* out, FontPtr font)
1001{
1002    int i;
1003
1004    /* All glyphs undefined -- loca table is empty, so offset 0 */
1005    for(i = 0; i < numglyphs; i++) {
1006        writeSHORT(out, 0);
1007    }
1008    writeSHORT(out, 0);
1009    return 0;
1010}
1011
1012static int
1013writemaxp(FILE* out, FontPtr font)
1014{
1015    writeLONG(out, 0x00010000); /* version */
1016    writeUSHORT(out, numglyphs); /* numGlyphs */
1017    writeUSHORT(out, 0);        /* maxPoints */
1018    writeUSHORT(out, 0);        /* maxContours */
1019    writeUSHORT(out, 0);        /* maxCompositePoints */
1020    writeUSHORT(out, 0);        /* maxCompositeContours */
1021    writeUSHORT(out, 1);        /* maxZones */
1022    writeUSHORT(out, 0);        /* maxTwilightPoints */
1023    writeUSHORT(out, 0);        /* maxStorage */
1024    writeUSHORT(out, 0);        /* maxFunctionDefs */
1025    writeUSHORT(out, 0);        /* maxInstructionDefs */
1026    writeUSHORT(out, 0);        /* maxStackElements */
1027    writeUSHORT(out, 0);        /* maxSizeOfInstructions */
1028    writeUSHORT(out, 0);        /* maxComponentElements */
1029    writeUSHORT(out, 0);        /* maxComponentDepth */
1030    return 0;
1031}
1032
1033static int
1034writename(FILE* out, FontPtr font)
1035{
1036    int i;
1037    int offset;
1038
1039    writeUSHORT(out, 0);        /* format selector */
1040    writeUSHORT(out, font->numNames);
1041    writeUSHORT(out, 6 + font->numNames * 12); /* offset to string storage */
1042    offset = 0;
1043    for(i = 0; i < font->numNames; i++) {
1044        writeUSHORT(out, 3);    /* platform id -- Microsoft */
1045        writeUSHORT(out, 1);    /* encoding -- Unicode */
1046        writeUSHORT(out, 0x409); /* language id -- American English */
1047        writeUSHORT(out, font->names[i].nid); /* name id */
1048        writeUSHORT(out, font->names[i].size); /* string length */
1049        writeUSHORT(out, offset); /* string offset */
1050        offset += font->names[i].size;
1051    }
1052    for(i = 0; i < font->numNames; i++)
1053        writeCHARs(out, font->names[i].value, font->names[i].size);
1054    return 0;
1055}
1056
1057static int
1058writepost(FILE* out, FontPtr font)
1059{
1060    int i, rc, previous_width, width, fixed_pitch;
1061
1062    fixed_pitch = 1;
1063    previous_width = -1;
1064    for(i = 0; i < FONT_CODES; i++) {
1065        rc = glyphMetrics(font, i, &width, NULL, NULL, NULL, NULL);
1066        if(rc < 0)
1067            continue;
1068        if(previous_width >= 0) {
1069            if(width != previous_width) {
1070                fixed_pitch = 0;
1071                break;
1072            }
1073        }
1074        previous_width = width;
1075    }
1076
1077    writeULONG(out, 0x00030000); /* FormatType */
1078    writeULONG(out, font->italicAngle); /* italicAngle */
1079    writeSHORT(out, FONT_UNITS(font->metrics.underlinePosition));
1080    writeSHORT(out, FONT_UNITS(font->metrics.underlineThickness));
1081    writeULONG(out, fixed_pitch); /* isFixedPitch */
1082    writeULONG(out, 0);         /* minMemType42 */
1083    writeULONG(out, 0);         /* maxMemType42 */
1084    writeULONG(out, 0);         /* minMemType1 */
1085    writeULONG(out, 0);         /* maxMemType1 */
1086    return 0;
1087}
1088
1089static int
1090writeOS2(FILE* out, FontPtr font)
1091{
1092    int i;
1093
1094    writeUSHORT(out, 5); /* version */
1095    writeSHORT(out, FONT_UNITS(font->metrics.awidth)); /* xAvgCharWidth; */
1096    writeUSHORT(out, font->weight);  /* usWeightClass; */
1097    writeUSHORT(out, font->width); /* usWidthClass; */
1098    writeSHORT(out, 0);         /* fsType; */
1099    writeSHORT(out, UNITS_PER_EM / 5); /* ySubscriptXSize; */
1100    writeSHORT(out, UNITS_PER_EM / 5); /* ySubscriptYSize; */
1101    writeSHORT(out, 0);         /* ySubscriptXOffset; */
1102    writeSHORT(out, UNITS_PER_EM / 5); /* ySubscriptYOffset; */
1103    writeSHORT(out, UNITS_PER_EM / 5); /* ySuperscriptXSize; */
1104    writeSHORT(out, UNITS_PER_EM / 5); /* ySuperscriptYSize; */
1105    writeSHORT(out, 0);         /* ySuperscriptXOffset; */
1106    writeSHORT(out, UNITS_PER_EM / 5); /* ySuperscriptYOffset; */
1107    writeSHORT(out, FONT_UNITS(font->metrics.underlineThickness));
1108    /* yStrikeoutSize; */
1109    writeSHORT(out, UNITS_PER_EM / 4); /* yStrikeoutPosition; */
1110    writeSHORT(out, 0);         /* sFamilyClass; */
1111    for(i = 0; i < 10; i++)
1112        writeBYTE(out, 0);      /* panose; */
1113    writeULONG(out, 0xFFFF);    /* ulUnicodeRange1; */
1114    writeULONG(out, 0xFFFF);    /* ulUnicodeRange2; */
1115    writeULONG(out, 0x03FF);    /* ulUnicodeRange3; */
1116    writeULONG(out, 0U);        /* ulUnicodeRange4; */
1117    writeULONG(out, font->foundry); /* achVendID[4]; */
1118    i = 0;
1119    if (font->flags & FACE_ITALIC)
1120	i |= 1 << 0;
1121    if (font->flags & FACE_BOLD)
1122	i |= 1 << 5;
1123    if (!i)
1124	i |= 1 << 6;
1125#ifndef NO_TYPO_METRICS
1126    i |= 1 << 7; /* USE_TYPO_METRICS instead usWin metrics for line spacing. */
1127#endif
1128    writeUSHORT(out, i);	/* fsSelection; */
1129    writeUSHORT(out, 0x20);     /* usFirstCharIndex; */
1130    writeUSHORT(out, 0xFFFD);   /* usLastCharIndex; */
1131    writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.ascent)); /* sTypoAscender; */
1132    writeSHORT(out, -FONT_UNITS_CEIL(font->metrics.descent)); /* sTypoDescender; */
1133    writeSHORT(out, FONT_UNITS(font->metrics.size - font->metrics.ascent - font->metrics.descent));	/* sTypoLineGap */
1134#ifdef NO_TYPO_METRICS
1135    writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.ascent)); /* usWinAscent; */
1136    writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.descent)); /* usWinDescent; */
1137#else
1138    writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.maxY)); /* usWinAscent; */
1139    writeUSHORT(out, -FONT_UNITS_FLOOR(font->metrics.minY)); /* usWinDescent; */
1140#endif
1141    writeULONG(out, 3);						/* ulCodePageRange1; */
1142    writeULONG(out, 0);         				/* ulCodePageRange2; */
1143    writeSHORT(out, FONT_UNITS_CEIL(font->metrics.xHeight));	/* sxHeight; */
1144    writeSHORT(out, FONT_UNITS_CEIL(font->metrics.capHeight));	/* sCapHeight; */
1145    writeUSHORT(out, 0); 					/* usDefaultChar; */
1146    writeUSHORT(out, 20); 					/* usBreakChar; */
1147    writeUSHORT(out, 0); 					/* usMaxContext; */
1148    writeUSHORT(out, 0); 					/* usLowerOpticalPointSize; */
1149    writeUSHORT(out, 0xffff); 					/* usUpperOpticalPointSize; */
1150    return 0;
1151}
1152
1153static int
1154writePCLT(FILE* out, FontPtr font)
1155{
1156    char name[16] = "X11 font        ";
1157    char filename[6] = "X11R00";
1158    unsigned char charComplement[8] =
1159        {0xFF, 0xFF, 0xFF, 0xFF, 0x0B, 0xFF, 0xFF, 0xFE};
1160    int style, w, strokeWeight, widthType;
1161
1162    style = 0;
1163    if(font->flags & FACE_ITALIC)
1164        style = 1;
1165
1166    w = (font->weight + 50) / 100;
1167    if(w < 5)
1168        strokeWeight = w - 6;
1169    else if(w == 5)
1170        strokeWeight = 0;
1171    else
1172        strokeWeight = w - 4;
1173
1174    if(font->width <= 2)
1175        widthType = -3;
1176    else if(font->width <= 4)
1177        widthType = -2;
1178    else if(font->width <= 6)
1179        widthType = 0;
1180    else if(font->width <= 7)
1181        widthType = 2;
1182    else
1183        widthType = 3;
1184
1185    writeULONG(out, 0x00010000); /* version */
1186    writeULONG(out, 0);         /* FontNumber */
1187    writeUSHORT(out, FONT_UNITS(font->metrics.maxAwidth)); /* pitch */
1188    writeUSHORT(out, FONT_UNITS(font->metrics.xHeight));    /* xHeight */
1189    writeUSHORT(out, style);    /* style */
1190    writeUSHORT(out, 6 << 12);  /* TypeFamily */
1191    writeUSHORT(out, FONT_UNITS(font->metrics.xHeight)); /* CapHeight */
1192    writeUSHORT(out, 0);        /* SymbolSet */
1193    writeCHARs(out, name, 16);  /* TypeFace */
1194    writeBYTEs(out, charComplement, 8); /* CharacterComplement */
1195    writeCHARs(out, filename, 6); /* FileName */
1196    writeCHAR(out, strokeWeight); /* StrokeWeight */
1197    writeCHAR(out, widthType);  /* WidthType */
1198    writeCHAR(out, 1 << 6);     /* SerifStyle */
1199    writeCHAR(out, 0);          /* Reserved */
1200    return 0;
1201}
1202