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