/* Copyright (c) 2002-2003 by Juliusz Chroboczek Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* $XFree86: xc/programs/fonttosfnt/write.c,v 1.4tsi Exp $ */ #if defined(linux) && !defined(_GNU_SOURCE) /* for fwrite_unlocked and fread_unlocked */ #define _GNU_SOURCE 1 #endif #include #include #include #include #include "X11/Xos.h" #include "fonttosfnt.h" #if !defined(I_LOVE_POSIX) && \ defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 1 #define DO_FWRITE fwrite_unlocked #define DO_FREAD fread_unlocked #else #define DO_FWRITE fwrite #define DO_FREAD fread #endif static int writeDir(FILE*, FontPtr, int, unsigned*); static int fixupDir(FILE*, FontPtr, int, int*, int*); static int fixupChecksum(FILE*, int, int); static int writeEBDT(FILE*, FontPtr); static int writeEBLC(FILE*, FontPtr); static int writeOS2(FILE*, FontPtr); static int writePCLT(FILE*, FontPtr); static int writecmap(FILE*, FontPtr); static int writeglyf(FILE*, FontPtr); static int writehead(FILE*, FontPtr); static int writehhea(FILE*, FontPtr); static int writehmtx(FILE*, FontPtr); static int writeloca(FILE*, FontPtr); static int writemaxp(FILE*, FontPtr); static int writename(FILE*, FontPtr); static int writepost(FILE*, FontPtr); static CmapPtr current_cmap = NULL; static int numglyphs, nummetrics; static int write_error_occurred, read_error_occurred; /* floor(log2(x)) */ static int log2_floor(int x) { int i, j; if(x <= 0) abort(); i = 0; j = 1; while(2 * j < x) { i++; j *= 2; } return i; } /* 2 ** floor(log2(x)) */ static int two_log2_floor(int x) { int j; if(x <= 0) abort(); j = 1; while(2 * j < x) { j *= 2; } return j; } static void write_error(int rc) { /* Real Men program in C and don't use exceptions. */ if(write_error_occurred) return; write_error_occurred = 1; if(rc < 0) perror("Couldn't write"); else fprintf(stderr, "Short write.\n"); } static void read_error(int rc) { if(read_error_occurred) return; read_error_occurred = 1; if(rc < 0) perror("Couldn't read"); else fprintf(stderr, "Short read.\n"); } static void writeBYTE(FILE *out, unsigned char val) { int rc; rc = DO_FWRITE(&val, 1, 1, out); if(rc != 1) write_error(rc); } static void writeBYTEs(FILE *out, unsigned char *chars, int n) { int rc; rc = DO_FWRITE(chars, 1, n, out); if(rc != n) write_error(rc); } static void writeCHAR(FILE *out, char val) { int rc; rc = DO_FWRITE(&val, 1, 1, out); if(rc != 1) write_error(rc); } static void writeCHARs(FILE *out, char *chars, int n) { int rc; rc = DO_FWRITE(chars, 1, n, out); if(rc != n) write_error(rc); } static void writeUSHORT(FILE *out, unsigned short val) { int rc; val = htons(val); rc = DO_FWRITE(&val, 2, 1, out); if(rc != 1) write_error(rc); } static void writeSHORT(FILE *out, short val) { int rc; val = htons(val); rc = DO_FWRITE(&val, 2, 1, out); if(rc != 1) write_error(rc); } static void writeULONG(FILE *out, unsigned int val) { int rc; val = htonl(val); rc = DO_FWRITE(&val, 4, 1, out); if(rc != 1) write_error(rc); } static void writeLONG(FILE *out, int val) { int rc; val = htonl(val); rc = DO_FWRITE(&val, 4, 1, out); if(rc != 1) write_error(rc); } static unsigned readULONG(FILE *out) { int rc; unsigned val; rc = DO_FREAD(&val, 4, 1, out); if(rc != 1) { read_error(rc); return 0xDEADBEEF; } return ntohl(val); } void fontMetrics(FontPtr font) { int i, rc; double sumAwidth = 0; unsigned count = 0; font->metrics.maxAwidth = 0; font->metrics.maxX = -10000 * TWO_SIXTEENTH; font->metrics.maxY = -10000 * TWO_SIXTEENTH; font->metrics.minX = 10000 * TWO_SIXTEENTH; font->metrics.minY = 10000 * TWO_SIXTEENTH; for(i = 0; i < FONT_CODES; i++) { int awidth, x0, y0, x1, y1; rc = glyphMetrics(font, i, &awidth, &x0, &y0, &x1, &y1); if(rc < 0) continue; if(awidth > font->metrics.maxAwidth) font->metrics.maxAwidth = awidth; if(x0 < font->metrics.minX) font->metrics.minX = x0; if(y0 < font->metrics.minY) font->metrics.minY = y0; if(x1 > font->metrics.maxX) font->metrics.maxX = x1; if(y1 > font->metrics.maxY) font->metrics.maxY = y1; if(awidth > 0) { sumAwidth += awidth; count++; } } if (count) font->metrics.awidth = sumAwidth / count; font->metrics.size = TWO_SIXTEENTH; if(font->pxMetrics.size == UNDEF) { font->pxMetrics.size = font->pxMetrics.height; } font->metrics.height = font->pxMetrics.height * font->metrics.size / font->pxMetrics.size; if(font->pxMetrics.ascent == UNDEF) { font->metrics.ascent = font->metrics.maxY; font->pxMetrics.ascent = font->metrics.ascent * font->pxMetrics.size / font->metrics.size; } else font->metrics.ascent = font->pxMetrics.ascent * font->metrics.size / font->pxMetrics.size; if(font->pxMetrics.descent == UNDEF) { font->metrics.descent = - font->metrics.minY; font->pxMetrics.descent = font->metrics.descent * font->pxMetrics.size / font->metrics.size; } else font->metrics.descent = font->pxMetrics.descent * font->metrics.size / font->pxMetrics.size; if(font->pxMetrics.capHeight == UNDEF) { if(glyphMetrics(font, 'X', NULL, NULL, NULL, NULL, &font->metrics.capHeight) != 1) font->metrics.capHeight = font->metrics.ascent; font->pxMetrics.capHeight = font->metrics.capHeight * font->pxMetrics.size / font->metrics.size; } else font->metrics.capHeight = font->pxMetrics.capHeight * font->metrics.size / font->pxMetrics.size; if(font->pxMetrics.xHeight == UNDEF) { if(glyphMetrics(font, 'x', NULL, NULL, NULL, NULL, &font->metrics.xHeight) != 1) font->metrics.xHeight = font->metrics.capHeight * 2 / 3; font->pxMetrics.xHeight = font->metrics.xHeight * font->pxMetrics.size / font->metrics.size; } else font->metrics.xHeight = font->pxMetrics.xHeight * font->metrics.size / font->pxMetrics.size; if(font->pxMetrics.underlinePosition == UNDEF) font->metrics.underlinePosition = - font->metrics.descent * 2; else { font->metrics.underlinePosition = font->pxMetrics.underlinePosition * font->metrics.size / font->pxMetrics.size; } if(font->pxMetrics.underlineThickness == UNDEF) /* make sure thickness is at least one pixel. */ /* TODO: this could be refined according to * X Logical Font Description Conventions (xlfd.txt) * by also considering the font weight. */ font->metrics.underlineThickness = font->metrics.size / (font->pxMetrics.size < 9 ? font->pxMetrics.size : 9); else font->metrics.underlineThickness = font->pxMetrics.underlineThickness * font->metrics.size / font->pxMetrics.size; } int writeFile(char *filename, FontPtr font) { int rc; FILE *out; unsigned tables[15]; int head_position = 0; int full_length; int (*(table_writers[15]))(FILE*, FontPtr); int i, j; int offset[15], length[15]; StrikePtr strike; out = fopen(filename, "wb+"); if(out == NULL) return -1; current_cmap = makeCmap(font); if(current_cmap == NULL) { fprintf(stderr, "Couldn't build cmap.\n"); goto fail; } fontMetrics(font); write_error_occurred = 0; read_error_occurred = 0; if(glyph_flag >= 2) { numglyphs = maxIndex(current_cmap) + 1; if(metrics_flag >= 2) nummetrics = numglyphs - 1; else if(metrics_flag >= 1) nummetrics = 1; else nummetrics = 0; } else if(glyph_flag == 1) { numglyphs = 1; nummetrics = (metrics_flag >= 1) ? 1 : 0; } else { numglyphs = 0; nummetrics = 0; } strike = font->strikes; while(strike) { strike->indexSubTables = makeIndexSubTables(strike, current_cmap); if(!strike->indexSubTables) { fprintf(stderr, "Couldn't build indexSubTable.\n"); goto fail; } strike = strike->next; } /* These must be sorted lexicographically */ i = 0; tables[i] = makeName("EBDT"); table_writers[i] = writeEBDT; i++; tables[i] = makeName("EBLC"); table_writers[i] = writeEBLC; i++; tables[i] = makeName("OS/2"); table_writers[i] = writeOS2; i++; tables[i] = makeName("PCLT"); table_writers[i] = writePCLT; i++; tables[i] = makeName("cmap"); table_writers[i] = writecmap; i++; if(numglyphs >= 1) { tables[i] = makeName("glyf"); table_writers[i] = writeglyf; i++; } tables[i] = makeName("head"); table_writers[i] = writehead; i++; tables[i] = makeName("hhea"); table_writers[i] = writehhea; i++; if(nummetrics >= 1) { tables[i] = makeName("hmtx"); table_writers[i] = writehmtx; i++; } if(numglyphs >= 1) { tables[i] = makeName("loca"); table_writers[i] = writeloca; i++; } tables[i] = makeName("maxp"); table_writers[i] = writemaxp; i++; tables[i] = makeName("name"); table_writers[i] = writename; i++; tables[i] = makeName("post"); table_writers[i] = writepost; i++; rc = writeDir(out, font, i, tables); if(rc < 0) goto fail; for(j = 0; j < i; j++) { offset[j] = ftell(out); if(offset[j] < 0) { perror("Couldn't compute table offset"); goto fail; } if(tables[j] == makeName("head")) head_position = offset[j]; rc = table_writers[j](out, font); if(rc < 0 || write_error_occurred || read_error_occurred) goto fail; length[j] = ftell(out) - offset[j]; if(length[j] < 0) { perror("Couldn't compute table size"); goto fail; } if(length[j] % 4 != 0) { /* Pad -- recommended by the spec, and assumed by computeChecksum. */ int k; for(k = 0; k < (4 - length[j] % 4); k++) { /* This must be 0 -- see computeChecksum. */ writeBYTE(out, 0); } if(write_error_occurred || read_error_occurred) goto fail; } } rc = fixupDir(out, font, i, offset, length); if(rc < 0) goto fail; full_length = ftell(out); if(full_length < 0) { perror("Couldn't compute file size"); goto fail; } while(full_length % 4 != 0) { /* pad for computeChecksum */ writeBYTE(out, 0); full_length++; } if(write_error_occurred || read_error_occurred) goto fail; rc = fixupChecksum(out, full_length, head_position); if(rc < 0) goto fail; fclose(out); return 0; fail: fclose(out); unlink(filename); return -1; } static int writeDir(FILE *out, FontPtr font, int numTables, unsigned *tables) { int i, ti; i = 0; ti = 1; while(2 * ti < numTables) { i++; ti = 2 * ti; } writeULONG(out, 0x10000); /* version */ writeUSHORT(out, numTables); /* numTables */ writeUSHORT(out, 16 * ti); /* searchRange */ writeUSHORT(out, i); /* entrySelector */ writeUSHORT(out, 16 * (numTables - ti)); /* rangeShift */ /* see fixupDir */ for(i = 0; i < numTables; i++) { writeULONG(out, tables[i]); writeULONG(out, 0xDEADFACE); /* checkSum */ writeULONG(out, 0xDEADFACE); /* offset */ writeULONG(out, 0xDEADFACE); /* length */ } return 0; } static unsigned computeChecksum(FILE *out, int offset, int length) { int rc; int i; unsigned sum = 0; if(offset % 4 != 0) { fprintf(stderr, "Offset %d is not a multiple of 4\n", offset); return ~0; } rc = fseek(out, offset, SEEK_SET); if(rc < 0) { perror("Couldn't seek"); return ~0; } /* This relies on the fact that we always pad tables with zeroes. */ for(i = 0; i < length; i += 4) { sum += readULONG(out); } return sum; } static int fixupDir(FILE *out, FontPtr font, int numTables, int *offset, int *length) { int rc, i; unsigned sum; for(i = 0; i < numTables; i++) { sum = computeChecksum(out, offset[i], length[i]); rc = fseek(out, 12 + 16 * i + 4, SEEK_SET); if(rc != 0) { perror("Couldn't seek"); return -1; } writeULONG(out, sum); writeULONG(out, offset[i]); writeULONG(out, length[i]); } return 0; } static int fixupChecksum(FILE *out, int full_length, int head_position) { int rc, checksum; checksum = computeChecksum(out, 0, full_length); rc = fseek(out, head_position + 8, SEEK_SET); if(rc != 0) { perror("Couldn't seek"); return -1; } writeULONG(out, 0xB1B0AFBA - checksum); /* checkSumAdjustment */ return 0; } static int writehead(FILE* out, FontPtr font) { int time_hi = 0; unsigned time_lo = 0; macTime(&time_hi, &time_lo); writeULONG(out, 0x00010000); writeULONG(out, 0x00010000); /* fontRevision */ writeULONG(out, 0); /* checkSumAdjustment -- filled in later */ writeULONG(out,0x5F0F3CF5); /* magicNumber */ writeUSHORT(out, 1); /* flags */ writeUSHORT(out, UNITS_PER_EM); /* unitsPerEm */ writeLONG(out, time_hi); /* created */ writeULONG(out, time_lo); writeLONG(out, time_hi); /* modified */ writeULONG(out, time_lo); /* bounding box for all glyphs */ writeUSHORT(out, FONT_UNITS_FLOOR(font->metrics.minX)); writeUSHORT(out, FONT_UNITS_FLOOR(font->metrics.minY)); writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.maxX)); writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.maxY)); writeUSHORT(out, font->flags); /* macStyle */ writeUSHORT(out, 1); /* lowestRecPPEM */ writeSHORT(out, 0); /* fontDirectionHint */ writeSHORT(out, 0); /* indexToLocFormat */ writeSHORT(out, 0); /* glyphDataFormat */ return 0; } static int outputRaster(FILE *out, char *raster, int width, int height, int stride, int bit_aligned) { int i, j; int len = 0; if(!bit_aligned || width % 8 == 0) { for(i = 0; i < height; i++) { writeCHARs(out, raster + i * stride, (width + 7) / 8); len += (width + 7) / 8; } } else { int bit = 0; unsigned char v = 0; for(i = 0; i < height; i++) { for(j = 0; j < width; j++) { if(BITREF(raster, stride, j, i)) v |= 1 << (7 - bit); bit++; if(bit >= 8) { writeBYTE(out, v); len++; bit = 0; v = 0; } } } if(bit > 0) { writeBYTE(out, v); len++; } } return len; } static int writeEBDT(FILE* out, FontPtr font) { StrikePtr strike; BitmapPtr bitmap; IndexSubTablePtr table; int i; int offset; int ebdt_start; ebdt_start = ftell(out); writeULONG(out, 0x00020000); /* version */ offset = 4; strike = font->strikes; while(strike) { table = strike->indexSubTables; while(table) { for(i = table->firstGlyphIndex; i <= table->lastGlyphIndex; i++) { bitmap = strikeBitmapIndex(strike, current_cmap, i); bitmap->location = offset; if(bit_aligned_flag && table->constantMetrics) { /* image format 5 */ ; } else { /* image format 1 or 2 */ writeBYTE(out, bitmap->height); writeBYTE(out, bitmap->width); writeCHAR(out, bitmap->horiBearingX); writeCHAR(out, bitmap->horiBearingY); writeBYTE(out, bitmap->advanceWidth); offset += 5; } offset += outputRaster(out, bitmap->raster, bitmap->width, bitmap->height, bitmap->stride, bit_aligned_flag); } table->lastLocation = offset; table = table->next; } strike = strike->next; } if(ftell(out) != ebdt_start + offset) abort(); return 0; } static int writeEBLC(FILE* out, FontPtr font) { int i, rc, numstrikes, eblc_start, num, den; StrikePtr strike; IndexSubTablePtr table; degreesToFraction(font->italicAngle, &num, &den); numstrikes = 0; strike = font->strikes; while(strike) { numstrikes++; strike = strike->next; } eblc_start = ftell(out); writeULONG(out, 0x00020000); /* version */ writeULONG(out, numstrikes); /* numSizes */ /* bitmapSizeTable */ strike = font->strikes; while(strike) { strike->bitmapSizeTableLocation = ftell(out); writeULONG(out, 0xDEADFACE); /* indexSubTableArrayOffset */ writeULONG(out, 0xDEADFACE); /* indexTablesSize */ writeULONG(out, 0xDEADFACE); /* numberOfIndexSubTables */ writeULONG(out, 0); /* colorRef */ for (i = 0; i <= 1; i++) { writeCHAR(out, font->pxMetrics.ascent); /* ascender */ writeCHAR(out, -font->pxMetrics.descent); /* descender */ writeBYTE(out, strikeMaxWidth(strike)); /* widthMax */ writeCHAR(out, num); /* caretSlopeNumerator */ writeCHAR(out, den); /* caretSlopeDenominator */ writeCHAR(out, 0); /* caretOffset */ writeCHAR(out, 0); /* minOriginSB */ writeCHAR(out, 0); /* minAdvanceSB */ writeCHAR(out, 0); /* maxBeforeBL */ writeCHAR(out, 0); /* minAfterBL */ writeCHAR(out, 0); /* pad1 */ writeCHAR(out, 0); /* pad2 */ } writeUSHORT(out, 0); /* startGlyphIndex */ writeUSHORT(out, 0xFFFD); /* endGlyphIndex */ writeBYTE(out, strike->sizeX); /* ppemX */ writeBYTE(out, strike->sizeY); /* ppemY */ writeBYTE(out, 1); /* bitDepth */ writeCHAR(out, 1); /* flags */ strike = strike->next; } /* indexSubTableArray, one per strike */ strike = font->strikes; while(strike) { int endoffset; int numtables = 0; strike->indexSubTableLocation = ftell(out); table = strike->indexSubTables; while(table) { table->location = ftell(out); writeUSHORT(out, table->firstGlyphIndex); writeUSHORT(out, table->lastGlyphIndex); writeULONG(out, 0xDEADFACE); /* additionalOffsetToIndexSubtable */ numtables++; table = table->next; } endoffset = ftell(out); rc = fseek(out, strike->bitmapSizeTableLocation, SEEK_SET); if(rc != 0) { perror("Couldn't seek"); return -1; } writeULONG(out, strike->indexSubTableLocation - eblc_start); /* indexSubTableArrayOffset */ writeULONG(out, endoffset - strike->indexSubTableLocation); /* indexTablesSize */ writeULONG(out, numtables); /* numberOfIndexSubTables */ rc = fseek(out, endoffset, SEEK_SET); if(rc != 0) { perror("Couldn't seek"); return -1; } strike = strike->next; } /* actual indexSubTables */ strike = font->strikes; while(strike) { table = strike->indexSubTables; while(table) { int location; int data_location; int short_offsets; int offset; location = ftell(out); if (location == -1) { perror("Couldn't ftell"); return -1; } rc = fseek(out, table->location + 4, SEEK_SET); if(rc != 0) { perror("Couldn't seek"); return -1; } /* additionalOffsetToIndexSubtable */ writeULONG(out, location - strike->indexSubTableLocation); rc = fseek(out, location, SEEK_SET); if(rc != 0) { perror("Couldn't seek"); return -1; } data_location = strikeBitmapIndex(strike, current_cmap, table->firstGlyphIndex)->location; short_offsets = 1; for(i = table->firstGlyphIndex; i <= table->lastGlyphIndex; i++) { if(strikeBitmapIndex(strike, current_cmap, i)->location - data_location > 0xFFFF) { short_offsets = 0; break; } } /* indexFormat */ if(table->constantMetrics) writeUSHORT(out, 2); else if(short_offsets) writeUSHORT(out, 3); else writeUSHORT(out, 1); /* imageFormat */ if(bit_aligned_flag) { if(table->constantMetrics) writeUSHORT(out, 5); else writeUSHORT(out, 2); } else { writeUSHORT(out, 1); } writeULONG(out, data_location); if(table->constantMetrics) { int size; BitmapPtr bitmap = strikeBitmapIndex(strike, current_cmap, table->firstGlyphIndex); size = strikeBitmapIndex(strike, current_cmap, table->firstGlyphIndex + 1)->location - bitmap->location; writeULONG(out, size); /* imageSize */ /* bigMetrics */ writeBYTE(out, bitmap->height); writeBYTE(out, bitmap->width); writeCHAR(out, bitmap->horiBearingX); writeCHAR(out, bitmap->horiBearingY); writeBYTE(out, bitmap->advanceWidth); writeCHAR(out, bitmap->horiBearingX); /* vertBearingX */ writeCHAR(out, bitmap->horiBearingY); /* vertBearingY */ writeBYTE(out, font->metrics.maxAwidth); /* vertAdvance */ } else { for(i = table->firstGlyphIndex; i <= table->lastGlyphIndex; i++) { offset = strikeBitmapIndex(strike, current_cmap, i)->location - data_location; if(short_offsets) writeUSHORT(out, offset); else writeULONG(out, offset); } /* Dummy glyph of size 0 to mark the end of the table */ if(short_offsets) { writeUSHORT(out, table->lastLocation - data_location); writeUSHORT(out, table->lastLocation - data_location); } else { writeULONG(out, table->lastLocation - data_location); writeULONG(out, table->lastLocation - data_location); } } location = ftell(out); while(location % 4 != 0) { writeCHAR(out, 0); location--; } table = table->next; } strike = strike->next; } return 0; } static int writecmap(FILE* out, FontPtr font) { int rc, cmap_start, cmap_end; CmapPtr cmap; int segcount; segcount = 0; cmap = current_cmap; while(cmap) { segcount++; cmap = cmap->next; } segcount++; /* dummy segment to end table */ cmap_start = ftell(out); writeUSHORT(out, 0); /* version */ writeUSHORT(out, 1); /* number of encoding tables */ writeUSHORT(out, 3); /* platform ID */ writeUSHORT(out, (font->flags & FACE_SYMBOL) ? 0 : 1); /* encoding ID */ writeULONG(out, 12); /* offset to beginning of subtable */ /* subtable */ writeUSHORT(out, 4); /* format */ writeUSHORT(out, 0xDEAD); /* length */ writeUSHORT(out, 0); /* language */ /* How baroque can you get? */ writeUSHORT(out, segcount * 2); /* segCountX2 */ writeUSHORT(out, 2 * two_log2_floor(segcount)); /* searchRange */ writeUSHORT(out, 1 + log2_floor(segcount)); /* entrySelector */ writeUSHORT(out, 2 * (segcount - two_log2_floor(segcount))); /* rangeShift */ cmap = current_cmap; while(cmap) { writeUSHORT(out, cmap->endCode); cmap = cmap->next; } writeUSHORT(out, 0xFFFF); writeUSHORT(out, 0); /* reservedPad */ cmap = current_cmap; while(cmap) { writeUSHORT(out, cmap->startCode); cmap = cmap->next; } writeUSHORT(out, 0xFFFF); /* idDelta */ cmap = current_cmap; while(cmap) { writeUSHORT(out, (cmap->index - cmap->startCode) & 0xFFFF); cmap = cmap->next; } writeUSHORT(out, 1); /* idRangeOffset */ cmap = current_cmap; while(cmap) { writeUSHORT(out, 0); cmap = cmap->next; } writeUSHORT(out, 0); /* glyphIDArray is empty */ cmap_end = ftell(out); rc = fseek(out, cmap_start + 12 + 2, SEEK_SET); if(rc != 0) { perror("Couldn't seek"); return -1; } writeUSHORT(out, cmap_end - cmap_start - 12); /* length */ rc = fseek(out, cmap_end, SEEK_SET); if(rc != 0) { perror("Couldn't seek"); return -1; } return 0; } static int writeglyf(FILE* out, FontPtr font) { return 0; } int writehhea(FILE* out, FontPtr font) { int num, den; degreesToFraction(font->italicAngle, &num, &den); writeULONG(out, 0x00010000); /* version */ writeSHORT(out, FONT_UNITS_CEIL(font->metrics.ascent)); /* ascender */ writeSHORT(out, -FONT_UNITS_CEIL(font->metrics.descent)); /* descender */ writeSHORT(out, FONT_UNITS(font->metrics.size - font->metrics.ascent - font->metrics.descent)); /* lineGap */ writeUSHORT(out, FONT_UNITS(font->metrics.maxAwidth)); /* advanceWidthMax */ /* TODO: the next three are not calculated according to spec, are they ? * https://docs.microsoft.com/en-us/typography/opentype/spec/hhea */ writeSHORT(out, FONT_UNITS_FLOOR(font->metrics.minX)); /* minLeftSideBearing */ writeSHORT(out, FONT_UNITS_FLOOR(font->metrics.minX)); /* minRightSideBearing */ writeSHORT(out, FONT_UNITS_CEIL(font->metrics.maxX)); /* xMaxExtent */ writeSHORT(out, den); /* caretSlopeRise */ writeSHORT(out, num); /* caretSlopeRun */ writeSHORT(out, 0); /* reserved */ writeSHORT(out, 0); /* reserved */ writeSHORT(out, 0); /* reserved */ writeSHORT(out, 0); /* reserved */ writeSHORT(out, 0); /* reserved */ writeSHORT(out, 0); /* metricDataFormat */ writeSHORT(out, nummetrics); /* numberOfHMetrics */ return 0; } static int writehmtx(FILE* out, FontPtr font) { int rc, i; for(i = 0; i <= numglyphs; i++) { int code, width, lsb; code = findCode(current_cmap, i); if(code < 0) rc = -1; else rc = glyphMetrics(font, code, &width, &lsb, NULL, NULL, NULL); if(rc < 0) { width = UNITS_PER_EM / 3; lsb = 0; } if(i < nummetrics) { writeSHORT(out, FONT_UNITS(width)); writeSHORT(out, FONT_UNITS(lsb)); } else { writeSHORT(out, FONT_UNITS(lsb)); } } return 0; } static int writeloca(FILE* out, FontPtr font) { int i; /* All glyphs undefined -- loca table is empty, so offset 0 */ for(i = 0; i < numglyphs; i++) { writeSHORT(out, 0); } writeSHORT(out, 0); return 0; } static int writemaxp(FILE* out, FontPtr font) { writeLONG(out, 0x00010000); /* version */ writeUSHORT(out, numglyphs); /* numGlyphs */ writeUSHORT(out, 0); /* maxPoints */ writeUSHORT(out, 0); /* maxContours */ writeUSHORT(out, 0); /* maxCompositePoints */ writeUSHORT(out, 0); /* maxCompositeContours */ writeUSHORT(out, 1); /* maxZones */ writeUSHORT(out, 0); /* maxTwilightPoints */ writeUSHORT(out, 0); /* maxStorage */ writeUSHORT(out, 0); /* maxFunctionDefs */ writeUSHORT(out, 0); /* maxInstructionDefs */ writeUSHORT(out, 0); /* maxStackElements */ writeUSHORT(out, 0); /* maxSizeOfInstructions */ writeUSHORT(out, 0); /* maxComponentElements */ writeUSHORT(out, 0); /* maxComponentDepth */ return 0; } static int writename(FILE* out, FontPtr font) { int i; int offset; writeUSHORT(out, 0); /* format selector */ writeUSHORT(out, font->numNames); writeUSHORT(out, 6 + font->numNames * 12); /* offset to string storage */ offset = 0; for(i = 0; i < font->numNames; i++) { writeUSHORT(out, 3); /* platform id -- Microsoft */ writeUSHORT(out, 1); /* encoding -- Unicode */ writeUSHORT(out, 0x409); /* language id -- American English */ writeUSHORT(out, font->names[i].nid); /* name id */ writeUSHORT(out, font->names[i].size); /* string length */ writeUSHORT(out, offset); /* string offset */ offset += font->names[i].size; } for(i = 0; i < font->numNames; i++) writeCHARs(out, font->names[i].value, font->names[i].size); return 0; } static int writepost(FILE* out, FontPtr font) { int i, rc, previous_width, width, fixed_pitch; fixed_pitch = 1; previous_width = -1; for(i = 0; i < FONT_CODES; i++) { rc = glyphMetrics(font, i, &width, NULL, NULL, NULL, NULL); if(rc < 0) continue; if(previous_width >= 0) { if(width != previous_width) { fixed_pitch = 0; break; } } previous_width = width; } writeULONG(out, 0x00030000); /* FormatType */ writeULONG(out, font->italicAngle); /* italicAngle */ writeSHORT(out, FONT_UNITS(font->metrics.underlinePosition)); writeSHORT(out, FONT_UNITS(font->metrics.underlineThickness)); writeULONG(out, fixed_pitch); /* isFixedPitch */ writeULONG(out, 0); /* minMemType42 */ writeULONG(out, 0); /* maxMemType42 */ writeULONG(out, 0); /* minMemType1 */ writeULONG(out, 0); /* maxMemType1 */ return 0; } static int writeOS2(FILE* out, FontPtr font) { int i; writeUSHORT(out, 5); /* version */ writeSHORT(out, FONT_UNITS(font->metrics.awidth)); /* xAvgCharWidth; */ writeUSHORT(out, font->weight); /* usWeightClass; */ writeUSHORT(out, font->width); /* usWidthClass; */ writeSHORT(out, 0); /* fsType; */ writeSHORT(out, UNITS_PER_EM / 5); /* ySubscriptXSize; */ writeSHORT(out, UNITS_PER_EM / 5); /* ySubscriptYSize; */ writeSHORT(out, 0); /* ySubscriptXOffset; */ writeSHORT(out, UNITS_PER_EM / 5); /* ySubscriptYOffset; */ writeSHORT(out, UNITS_PER_EM / 5); /* ySuperscriptXSize; */ writeSHORT(out, UNITS_PER_EM / 5); /* ySuperscriptYSize; */ writeSHORT(out, 0); /* ySuperscriptXOffset; */ writeSHORT(out, UNITS_PER_EM / 5); /* ySuperscriptYOffset; */ writeSHORT(out, FONT_UNITS(font->metrics.underlineThickness)); /* yStrikeoutSize; */ writeSHORT(out, UNITS_PER_EM / 4); /* yStrikeoutPosition; */ writeSHORT(out, 0); /* sFamilyClass; */ for(i = 0; i < 10; i++) writeBYTE(out, 0); /* panose; */ writeULONG(out, 0xFFFF); /* ulUnicodeRange1; */ writeULONG(out, 0xFFFF); /* ulUnicodeRange2; */ writeULONG(out, 0x03FF); /* ulUnicodeRange3; */ writeULONG(out, 0U); /* ulUnicodeRange4; */ writeULONG(out, font->foundry); /* achVendID[4]; */ i = 0; if (font->flags & FACE_ITALIC) i |= 1 << 0; if (font->flags & FACE_BOLD) i |= 1 << 5; if (!i) i |= 1 << 6; #ifndef NO_TYPO_METRICS i |= 1 << 7; /* USE_TYPO_METRICS instead usWin metrics for line spacing. */ #endif writeUSHORT(out, i); /* fsSelection; */ writeUSHORT(out, 0x20); /* usFirstCharIndex; */ writeUSHORT(out, 0xFFFD); /* usLastCharIndex; */ writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.ascent)); /* sTypoAscender; */ writeSHORT(out, -FONT_UNITS_CEIL(font->metrics.descent)); /* sTypoDescender; */ writeSHORT(out, FONT_UNITS(font->metrics.size - font->metrics.ascent - font->metrics.descent)); /* sTypoLineGap */ #ifdef NO_TYPO_METRICS writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.ascent)); /* usWinAscent; */ writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.descent)); /* usWinDescent; */ #else writeUSHORT(out, FONT_UNITS_CEIL(font->metrics.maxY)); /* usWinAscent; */ writeUSHORT(out, -FONT_UNITS_FLOOR(font->metrics.minY)); /* usWinDescent; */ #endif writeULONG(out, 3); /* ulCodePageRange1; */ writeULONG(out, 0); /* ulCodePageRange2; */ writeSHORT(out, FONT_UNITS_CEIL(font->metrics.xHeight)); /* sxHeight; */ writeSHORT(out, FONT_UNITS_CEIL(font->metrics.capHeight)); /* sCapHeight; */ writeUSHORT(out, 0); /* usDefaultChar; */ writeUSHORT(out, 20); /* usBreakChar; */ writeUSHORT(out, 0); /* usMaxContext; */ writeUSHORT(out, 0); /* usLowerOpticalPointSize; */ writeUSHORT(out, 0xffff); /* usUpperOpticalPointSize; */ return 0; } static int writePCLT(FILE* out, FontPtr font) { char name[16] = "X11 font "; char filename[6] = "X11R00"; unsigned char charComplement[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0x0B, 0xFF, 0xFF, 0xFE}; int style, w, strokeWeight, widthType; style = 0; if(font->flags & FACE_ITALIC) style = 1; w = (font->weight + 50) / 100; if(w < 5) strokeWeight = w - 6; else if(w == 5) strokeWeight = 0; else strokeWeight = w - 4; if(font->width <= 2) widthType = -3; else if(font->width <= 4) widthType = -2; else if(font->width <= 6) widthType = 0; else if(font->width <= 7) widthType = 2; else widthType = 3; writeULONG(out, 0x00010000); /* version */ writeULONG(out, 0); /* FontNumber */ writeUSHORT(out, FONT_UNITS(font->metrics.maxAwidth)); /* pitch */ writeUSHORT(out, FONT_UNITS(font->metrics.xHeight)); /* xHeight */ writeUSHORT(out, style); /* style */ writeUSHORT(out, 6 << 12); /* TypeFamily */ writeUSHORT(out, FONT_UNITS(font->metrics.xHeight)); /* CapHeight */ writeUSHORT(out, 0); /* SymbolSet */ writeCHARs(out, name, 16); /* TypeFace */ writeBYTEs(out, charComplement, 8); /* CharacterComplement */ writeCHARs(out, filename, 6); /* FileName */ writeCHAR(out, strokeWeight); /* StrokeWeight */ writeCHAR(out, widthType); /* WidthType */ writeCHAR(out, 1 << 6); /* SerifStyle */ writeCHAR(out, 0); /* Reserved */ return 0; }