2 Parser and writer for truetype font files.
4 Part of the swftools package.
6 Copyright (c) 2010 Matthias Kramm <kramm@quiss.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
31 #define TTCFTAG 0x74746366
33 #define OPENTYPE 0x4f54544f
34 #define TRUETYPE_MACOS 0x74727565
35 #define VERSION_1_0 0x00010000
37 #define TAG_OS2 0x4f532f32
38 #define TAG_CMAP 0x636d6170
39 #define TAG_GLYF 0x676c7966 //required for non opentype
40 #define TAG_HEAD 0x68656164 //required
41 #define TAG_HHEA 0x68686561 //required
42 #define TAG_HMTX 0x686d7478 //required
43 #define TAG_VHEA 0x86686561
44 #define TAG_VMTX 0x866d7478
45 #define TAG_KERN 0x6b65726e
46 #define TAG_LOCA 0x6c6f6361 //required for non opentype
47 #define TAG_MAXP 0x6d617870 //required
48 #define TAG_NAME 0x6e616d65
49 #define TAG_POST 0x706f7374
50 #define TAG_CFF 0x43464620 //required for opentype
53 fpgm - assembly instructions
54 prep - assembly instructions
55 cvt - constant value table
56 gasp - gridfitting procedure
59 static U32 checksum_block(U8*_data, int len)
65 int len_minus_4 = len-4;
66 for(pos=0;pos<=len_minus_4;pos+=4) {
67 sum += data[pos]<<24|data[pos+1]<<16|data[pos+2]<<8|data[pos+3];
70 if(left == 1) sum+= data[pos+0]<<24;
71 if(left == 2) sum+= data[pos+0]<<24|data[pos+1]<<16;
72 if(left == 3) sum+= data[pos+0]<<24|data[pos+1]<<16|data[pos+2]<<8;
76 typedef struct _memreader {
82 static U8 readU8(memreader_t*r)
84 return r->mem[r->pos++];
86 static inline U16 readU16(memreader_t*r)
88 if(r->pos+2>r->size) return 0;
89 U16 val = r->mem[r->pos]<<8|
94 static S16 readS16(memreader_t*r)
96 return (S16)readU16(r);
98 static U32 readU32(memreader_t*r)
100 if(r->pos+4>r->size) return 0;
101 U32 val = r->mem[r->pos]<<24|
102 r->mem[r->pos+1]<<16|
108 static void readBlock(memreader_t*r, void*dest, int len)
110 int remaining = r->size-r->pos;
111 if(len > remaining) {
112 memcpy(dest, r->mem+r->pos, remaining);
113 memset(dest+remaining, 0, len - remaining);
116 memcpy(dest, r->mem+r->pos, len);
120 #define INIT_READ(r,data,length,pos) memreader_t r = {(data),(pos),(length)};
122 static void expand(ttf_table_t*w, int newsize)
124 int v1 = (newsize+63)&~63;
125 int v2 = w->len + w->len / 2;
126 w->memsize = v1>v2?v1:v2;
127 w->data = rfx_realloc(w->data, w->memsize);
129 static void writeU8(ttf_table_t*w, unsigned char b)
131 if(w->memsize<w->len+1)
133 w->data[w->len++] = b;
135 static void writeU16(ttf_table_t*w, unsigned short v)
137 if(w->memsize<w->len+2)
139 w->data[w->len++] = v>>8;
140 w->data[w->len++] = v;
142 #define writeS16 writeU16
143 static void writeU32(ttf_table_t*w, unsigned long v)
145 if(w->memsize<w->len+4)
147 w->data[w->len++] = v>>24;
148 w->data[w->len++] = v>>16;
149 w->data[w->len++] = v>>8;
150 w->data[w->len++] = v;
152 static void writeBlock(ttf_table_t*w, void*data, int len)
154 if(w->memsize<w->len+len)
155 expand(w, w->len+len);
156 memcpy(w->data+w->len, data, len);
160 ttf_table_t*ttf_table_new(U32 id)
162 ttf_table_t*t = rfx_calloc(sizeof(ttf_table_t));
167 ttf_table_t*ttf_addtable(ttf_t*ttf, U32 id)
169 ttf_table_t*t = ttf_table_new(id);
171 ttf_table_t*before,*after=0;
172 for(before=ttf->tables; before&&before->id<id; before=before->next) {
175 if(before && before->id == id) {
176 msg("<error> Error: duplicate table %08x", id);
184 t->next = ttf->tables;
188 t->next = after->next;
195 ttf_table_t*ttf_find_table(ttf_t*ttf, U32 id)
197 ttf_table_t*table = ttf->tables;
205 void ttf_table_delete(ttf_t*ttf, ttf_table_t*table)
207 if(ttf && ttf->tables == table) {
208 ttf->tables = table->next;
211 table->prev->next = table->next;
213 table->next->prev = table->prev;
217 U32 ttf_table_checksum(ttf_table_t*t)
219 U32 checksum = checksum_block(t->data, t->len);
220 if(t->id==TAG_HEAD && t->len>=12) {
221 /* the checksum for the HEAD table is calculated by masking out
222 the checksumadjust field */
223 U32 adjust = t->data[8]<<24|t->data[9]<<16|t->data[10]<<8|t->data[11];
228 static U8 printable(U8 a)
230 if(a<32 || a==127) return '.';
233 static void hexdump(U8*data, int len, const char*prefix)
237 printf("%s -=> ",prefix);
239 printf("%02x ", data[t]);
240 ascii[t&15] = printable(data[t]);
241 if((t && ((t&15)==15)) || (t==len-1))
245 for(s=p-1;s<16;s++) {
249 printf(" %s\n", ascii);
251 printf(" %s\n%s -=> ",ascii,prefix);
255 static void ttf_table_dump(ttf_table_t*t, const char*prefix)
258 hexdump(t->data, t->len, prefix);
261 static table_os2_t*os2_parse(memreader_t*r)
263 table_os2_t*os2 = rfx_calloc(sizeof(table_os2_t));
264 U16 version = readU16(r);
265 if(version!=0 && version!=1 && version!=2)
266 msg("<warning> Unknown OS2 version: %04x", version);
267 os2->xAvgCharWidth = readS16(r);
268 os2->usWeightClass = readU16(r);
269 os2->usWidthClass = readU16(r);
270 os2->fsType = readU16(r);
271 os2->ySubscriptXSize = readU16(r);
272 os2->ySubscriptYSize = readU16(r);
273 os2->ySubscriptXOffset = readU16(r);
274 os2->ySubscriptYOffset = readU16(r);
275 os2->ySuperscriptXSize = readU16(r);
276 os2->ySuperscriptYSize = readU16(r);
277 os2->ySuperscriptXOffset = readU16(r);
278 os2->ySuperscriptYOffset = readU16(r);
279 os2->yStrikeoutSize = readU16(r);
280 os2->yStrikeoutPosition = readU16(r);
281 os2->sFamilyClass = readU16(r);
282 os2->panose_FamilyType = readU8(r);
283 os2->panose_SerifStyle = readU8(r);
284 os2->panose_Weight = readU8(r);
285 os2->panose_Proportion = readU8(r);
286 os2->panose_Contrast = readU8(r);
287 os2->panose_StrokeVariation = readU8(r);
288 os2->panose_ArmStyle = readU8(r);
289 os2->panose_Letterform = readU8(r);
290 os2->panose_Midline = readU8(r);
291 os2->panose_XHeight = readU8(r);
292 os2->ulCharRange[0] = readU32(r);
293 os2->ulCharRange[1] = readU32(r);
294 os2->ulCharRange[2] = readU32(r);
295 os2->ulCharRange[3] = readU32(r);
296 os2->achVendID[0] = readU8(r);
297 os2->achVendID[1] = readU8(r);
298 os2->achVendID[2] = readU8(r);
299 os2->achVendID[3] = readU8(r);
300 os2->fsSelection = readU16(r);
301 os2->fsFirstCharIndex = readU16(r);
302 os2->fsLastCharIndex = readU16(r);
303 os2->sTypoAscender = readS16(r);
304 os2->sTypoDescender = readS16(r);
305 os2->sTypoLineGap = readS16(r);
306 os2->usWinAscent = readU16(r);
307 os2->usWinDescent = readU16(r);
308 if(version<1) return os2;
309 os2->ulCodePageRange1 = readU32(r);
310 os2->ulCodePageRange2 = readU32(r);
311 if(version<2) return os2;
312 os2->sxHeight = readS16(r);
313 os2->sCapHeight = readS16(r);
314 os2->usDefaultChar = readU16(r);
315 os2->usBreakChar = readU16(r);
316 os2->usMaxContext = readU16(r);
320 static os2_write(ttf_t*ttf, ttf_table_t*w)
322 table_os2_t*os2 = ttf->os2;
324 if(os2->sxHeight|os2->sCapHeight|os2->usDefaultChar|os2->usBreakChar|os2->usMaxContext) {
327 writeU16(w, version);
328 writeS16(w, os2->xAvgCharWidth);
329 writeU16(w, os2->usWeightClass);
330 writeU16(w, os2->usWidthClass);
331 writeU16(w, os2->fsType);
332 writeU16(w, os2->ySubscriptXSize);
333 writeU16(w, os2->ySubscriptYSize);
334 writeU16(w, os2->ySubscriptXOffset);
335 writeU16(w, os2->ySubscriptYOffset);
336 writeU16(w, os2->ySuperscriptXSize);
337 writeU16(w, os2->ySuperscriptYSize);
338 writeU16(w, os2->ySuperscriptXOffset);
339 writeU16(w, os2->ySuperscriptYOffset);
340 writeU16(w, os2->yStrikeoutSize);
341 writeU16(w, os2->yStrikeoutPosition);
342 writeU16(w, os2->sFamilyClass);
343 writeU8(w, os2->panose_FamilyType);
344 writeU8(w, os2->panose_SerifStyle);
345 writeU8(w, os2->panose_Weight);
346 writeU8(w, os2->panose_Proportion);
347 writeU8(w, os2->panose_Contrast);
348 writeU8(w, os2->panose_StrokeVariation);
349 writeU8(w, os2->panose_ArmStyle);
350 writeU8(w, os2->panose_Letterform);
351 writeU8(w, os2->panose_Midline);
352 writeU8(w, os2->panose_XHeight);
353 writeU32(w, os2->ulCharRange[0]);
354 writeU32(w, os2->ulCharRange[1]);
355 writeU32(w, os2->ulCharRange[2]);
356 writeU32(w, os2->ulCharRange[3]);
357 writeU8(w, os2->achVendID[0]);
358 writeU8(w, os2->achVendID[1]);
359 writeU8(w, os2->achVendID[2]);
360 writeU8(w, os2->achVendID[3]);
361 writeU16(w, os2->fsSelection);
362 writeU16(w, os2->fsFirstCharIndex);
363 writeU16(w, os2->fsLastCharIndex);
364 writeS16(w, os2->sTypoAscender);
365 writeS16(w, os2->sTypoDescender);
366 writeS16(w, os2->sTypoLineGap);
367 writeU16(w, os2->usWinAscent);
368 writeU16(w, os2->usWinDescent);
369 if(version<1) return;
370 writeU32(w, os2->ulCodePageRange1);
371 writeU32(w, os2->ulCodePageRange2);
372 if(version<2) return;
373 writeS16(w, os2->sxHeight);
374 writeS16(w, os2->sCapHeight);
375 writeU16(w, os2->usDefaultChar);
376 writeU16(w, os2->usBreakChar);
377 writeU16(w, os2->usMaxContext);
379 static void os2_delete(ttf_t*ttf)
386 static os2_dump(ttf_t*ttf)
388 table_os2_t*os2 = ttf->os2;
390 printf("os2->xAvgCharWidth: %d\n", os2->xAvgCharWidth);
391 printf("os2->usWeightClass: %d\n", os2->usWeightClass);
392 printf("os2->usWidthClass: %d\n", os2->usWidthClass);
393 printf("os2->fsType: %d\n", os2->fsType);
394 printf("os2->ySubscriptXSize: %d\n", os2->ySubscriptXSize);
395 printf("os2->ySubscriptYSize: %d\n", os2->ySubscriptYSize);
396 printf("os2->ySubscriptXOffset: %d\n", os2->ySubscriptXOffset);
397 printf("os2->ySubscriptYOffset: %d\n", os2->ySubscriptYOffset);
398 printf("os2->ySuperscriptXSize: %d\n", os2->ySuperscriptXSize);
399 printf("os2->ySuperscriptYSize: %d\n", os2->ySuperscriptYSize);
400 printf("os2->ySuperscriptXOffset: %d\n", os2->ySuperscriptXOffset);
401 printf("os2->ySuperscriptYOffset: %d\n", os2->ySuperscriptYOffset);
402 printf("os2->yStrikeoutSize: %d\n", os2->yStrikeoutSize);
403 printf("os2->yStrikeoutPosition: %d\n", os2->yStrikeoutPosition);
404 printf("os2->sFamilyClass: %d\n", os2->sFamilyClass);
405 printf("os2->panose_FamilyType: %d\n", os2->panose_FamilyType);
406 printf("os2->panose_SerifStyle: %d\n", os2->panose_SerifStyle);
407 printf("os2->panose_Weight: %d\n", os2->panose_Weight);
408 printf("os2->panose_Proportion: %d\n", os2->panose_Proportion);
409 printf("os2->panose_Contrast: %d\n", os2->panose_Contrast);
410 printf("os2->panose_StrokeVariation: %d\n", os2->panose_StrokeVariation);
411 printf("os2->panose_ArmStyle: %d\n", os2->panose_ArmStyle);
412 printf("os2->panose_Letterform: %d\n", os2->panose_Letterform);
413 printf("os2->panose_Midline: %d\n", os2->panose_Midline);
414 printf("os2->panose_XHeight: %d\n", os2->panose_XHeight);
415 printf("os2->ulCharRange[0]: %d\n", os2->ulCharRange[0]);
416 printf("os2->ulCharRange[1]: %d\n", os2->ulCharRange[1]);
417 printf("os2->ulCharRange[2]: %d\n", os2->ulCharRange[2]);
418 printf("os2->ulCharRange[3]: %d\n", os2->ulCharRange[3]);
419 printf("os2->achVendID[0]: %d\n", os2->achVendID[0]);
420 printf("os2->achVendID[1]: %d\n", os2->achVendID[1]);
421 printf("os2->achVendID[2]: %d\n", os2->achVendID[2]);
422 printf("os2->achVendID[3]: %d\n", os2->achVendID[3]);
423 printf("os2->fsSelection: %d\n", os2->fsSelection);
424 printf("os2->fsFirstCharIndex: %d\n", os2->fsFirstCharIndex);
425 printf("os2->fsLastCharIndex: %d\n", os2->fsLastCharIndex);
426 printf("os2->sTypoAscender: %d\n", os2->sTypoAscender);
427 printf("os2->sTypoDescender: %d\n", os2->sTypoDescender);
428 printf("os2->sTypoLineGap: %d\n", os2->sTypoLineGap);
429 printf("os2->usWinAscent: %d\n", os2->usWinAscent);
430 printf("os2->usWinDescent: %d\n", os2->usWinDescent);
431 printf("os2->ulCodePageRange1: %d\n", os2->ulCodePageRange1);
432 printf("os2->ulCodePageRange2: %d\n", os2->ulCodePageRange2);
433 printf("os2->sxHeight: %d\n", os2->sxHeight);
434 printf("os2->sCapHeight: %d\n", os2->sCapHeight);
435 printf("os2->usDefaultChar: %d\n", os2->usDefaultChar);
436 printf("os2->usBreakChar: %d\n", os2->usBreakChar);
437 printf("os2->usMaxContext: %d\n", os2->usMaxContext);
440 static int head_parse(ttf_t*ttf, memreader_t*r)
442 ttf->head = rfx_calloc(sizeof(table_head_t));
443 U32 version = readU32(r);
444 if(version!=VERSION_1_0)
445 msg("<warning> Font HEAD has unknown version %08x", version);
446 U32 revision = readU32(r);
447 if(revision!=VERSION_1_0)
448 msg("<warning> Font HEAD has unknown revision %08x", revision);
449 U32 checksum2 = readU32(r);
450 U32 magic = readU32(r);
451 if(magic!=0x5f0f3cf5)
452 msg("<warning> Font HEAD has unknown magic number %08x", magic);
453 ttf->head->flags = readU16(r);
454 ttf->head->units_per_em = readU16(r);
455 readU32(r);readU32(r); //created
456 readU32(r);readU32(r); //modified
457 ttf->head->xmin = readU16(r);
458 ttf->head->ymin = readU16(r);
459 ttf->head->xmax = readU16(r);
460 ttf->head->ymax = readU16(r);
461 ttf->head->macStyle = readU16(r);
462 ttf->head->lowest_readable_size = readU16(r); //in pixels
463 ttf->head->dir_hint = readS16(r);
464 int loc_index = readS16(r); //used in 'loca' table
466 msg("<warning> loca index format %d unknown", loc_index);
467 U16 glyph_data_format = readS16(r);
468 if(glyph_data_format!=0)
469 msg("<warning> Font glyph data format unknown: %04x", glyph_data_format);
473 static void head_write(ttf_t*ttf, ttf_table_t*w, int loca_size)
475 writeU32(w, 0x10000);
476 writeU32(w, 0x10000);
477 writeU32(w, 0); //checksum
478 writeU32(w, 0x5f0f3cf5); //magic
479 writeU16(w, ttf->head->flags);
480 writeU16(w, ttf->head->units_per_em);
481 writeU32(w, 0);writeU32(w, 0); //created
482 writeU32(w, 0);writeU32(w, 0); //modified
483 writeU16(w, ttf->head->xmin);
484 writeU16(w, ttf->head->ymin);
485 writeU16(w, ttf->head->xmax);
486 writeU16(w, ttf->head->ymax);
487 writeU16(w, ttf->head->macStyle);
488 writeU16(w, ttf->head->lowest_readable_size);
489 writeS16(w, ttf->head->dir_hint);
490 writeS16(w, loca_size); //loca index size (32 bit)
491 writeS16(w, 0); //glyph data format
493 static void head_dump(ttf_t*ttf)
495 printf("head->flags: %d\n", ttf->head->flags);
496 printf("head->units_per_em: %d\n", ttf->head->units_per_em);
497 printf("head->xmin: %d\n", ttf->head->xmin);
498 printf("head->ymin: %d\n", ttf->head->ymin);
499 printf("head->xmax: %d\n", ttf->head->xmax);
500 printf("head->ymax: %d\n", ttf->head->ymax);
501 printf("head->macStyle: %d\n", ttf->head->macStyle);
502 printf("head->lowest_readable_size: %d\n", ttf->head->lowest_readable_size);
503 printf("head->dir_hint: %d\n", ttf->head->dir_hint);
505 static void head_delete(ttf_t*ttf)
513 static table_maxp_t* maxp_parse(ttf_t*ttf, memreader_t*r)
515 U32 version = readU32(r);
516 ttf->num_glyphs = readU16(r);
517 /* according to freetype, older fonts (version<0x10000)
518 apparently only contain the number of glyphs. this is
519 rather rare, though. */
520 if(version<0x10000) return 0;
522 table_maxp_t*maxp = rfx_calloc(sizeof(table_maxp_t));
523 maxp->maxPoints = readU16(r);
524 maxp->maxContours = readU16(r);
525 maxp->maxComponentPoints = readU16(r);
526 maxp->maxComponentContours = readU16(r);
527 maxp->maxZones = readU16(r);
528 maxp->maxTwilightPoints = readU16(r);
529 maxp->maxStorage = readU16(r);
530 maxp->maxFunctionDefs = readU16(r);
531 maxp->maxInstructionDefs = readU16(r);
532 maxp->maxStackElements = readU16(r);
533 maxp->maxSizeOfInstructions = readU16(r);
534 maxp->maxComponentElements = readU16(r);
535 maxp->maxComponentDepth = readU16(r);
539 static void maxp_write(ttf_t*ttf, ttf_table_t*w)
541 table_maxp_t*maxp = ttf->maxp;
542 writeU32(w, 0x10000); //version
543 writeU16(w, ttf->num_glyphs);
544 writeU16(w, maxp->maxPoints);
545 writeU16(w, maxp->maxContours);
546 writeU16(w, maxp->maxComponentPoints);
547 writeU16(w, maxp->maxComponentContours);
548 writeU16(w, maxp->maxZones);
549 writeU16(w, maxp->maxTwilightPoints);
550 writeU16(w, maxp->maxStorage);
551 writeU16(w, maxp->maxFunctionDefs);
552 writeU16(w, maxp->maxInstructionDefs);
553 writeU16(w, maxp->maxStackElements);
554 writeU16(w, maxp->maxSizeOfInstructions);
555 writeU16(w, maxp->maxComponentElements);
556 writeU16(w, maxp->maxComponentDepth);
559 static void maxp_dump(ttf_t*ttf)
561 table_maxp_t*maxp = ttf->maxp;
563 printf("maxp->maxPoints: %d\n", maxp->maxPoints);
564 printf("maxp->maxContours: %d\n", maxp->maxContours);
565 printf("maxp->maxComponentPoints: %d\n", maxp->maxComponentPoints);
566 printf("maxp->maxComponentContours: %d\n", maxp->maxComponentContours);
567 printf("maxp->maxZones: %d\n", maxp->maxZones);
568 printf("maxp->maxTwilightPoints: %d\n", maxp->maxTwilightPoints);
569 printf("maxp->maxStorage: %d\n", maxp->maxStorage);
570 printf("maxp->maxFunctionDefs: %d\n", maxp->maxFunctionDefs);
571 printf("maxp->maxInstructionDefs: %d\n", maxp->maxInstructionDefs);
572 printf("maxp->maxStackElements: %d\n", maxp->maxStackElements);
573 printf("maxp->maxSizeOfInstructions: %d\n", maxp->maxSizeOfInstructions);
574 printf("maxp->maxComponentElements: %d\n", maxp->maxComponentElements);
575 printf("maxp->maxComponentDepth: %d\n", maxp->maxComponentDepth);
578 static void maxp_delete(ttf_t*ttf)
586 static int hea_parse(memreader_t*r, ttf_t*ttf)
588 table_hea_t*hea = ttf->hea = rfx_calloc(sizeof(table_hea_t));
589 U32 version = readU32(r);
590 hea->ascent = readS16(r);
591 hea->descent = readS16(r);
592 hea->lineGap = readS16(r);
593 hea->advanceWidthMax = readU16(r);
594 hea->minLeftSideBearing = readS16(r);
595 hea->minRightSideBearing = readS16(r);
596 hea->xMaxExtent = readS16(r);
597 hea->caretSlopeRise = readS16(r);
598 hea->caretSlopeRun = readS16(r);
599 hea->caretOffset = readS16(r);
600 readS16(r); //reserved[0]
601 readS16(r); //reserved[1]
602 readS16(r); //reserved[2]
603 readS16(r); //reserved[3]
604 S16 metricDataFormat = readS16(r); //should be 0
605 if(metricDataFormat!=0) {
606 msg("<warning> Unknown metric format %d", metricDataFormat);
608 int num_advances = readU16(r);
609 if(num_advances > ttf->num_glyphs) {
610 msg("<warning> bad number of horizontal metrics: %d", num_advances);
611 num_advances = ttf->num_glyphs;
616 static table_hea_t*hea_write(ttf_t*ttf, ttf_table_t*w, int num_advances)
618 table_hea_t*hea = ttf->hea;
619 writeU32(w, 0x00010000);
620 writeS16(w, hea->ascent);
621 writeS16(w, hea->descent);
622 writeS16(w, hea->lineGap);
623 writeU16(w, hea->advanceWidthMax);
624 writeS16(w, hea->minLeftSideBearing);
625 writeS16(w, hea->minRightSideBearing);
626 writeS16(w, hea->xMaxExtent);
627 writeS16(w, hea->caretSlopeRise);
628 writeS16(w, hea->caretSlopeRun);
629 writeS16(w, hea->caretOffset);
630 writeS16(w, 0); //reserved
631 writeS16(w, 0); //reserved
632 writeS16(w, 0); //reserved
633 writeS16(w, 0); //reserved
634 writeS16(w, 0); //metricDataFormat
635 writeU16(w, num_advances);
638 static void hea_dump(ttf_t*ttf)
640 table_hea_t*hea = ttf->hea;
641 const char*dir = ttf->is_vertical?"v":"h";
642 printf("%shea->ascent: %d\n", dir, hea->ascent);
643 printf("%shea->descent: %d\n", dir, hea->descent);
644 printf("%shea->lineGap: %d\n", dir, hea->lineGap);
645 printf("%shea->advanceWidthMax: %d\n", dir, hea->advanceWidthMax);
646 printf("%shea->minLeftSideBearing: %d\n", dir, hea->minLeftSideBearing);
647 printf("%shea->minRightSideBearing: %d\n", dir, hea->minRightSideBearing);
648 printf("%shea->xMaxExtent: %d\n", dir, hea->xMaxExtent);
649 printf("%shea->caretSlopeRise: %d\n", dir, hea->caretSlopeRise);
650 printf("%shea->caretSlopeRun: %d\n", dir, hea->caretSlopeRun);
651 printf("%shea->caretOffset: %d\n", dir, hea->caretOffset);
653 static void hea_delete(ttf_t*ttf)
661 static void hmtx_parse(memreader_t*r, ttf_t*ttf, int num_advances)
665 if(num_advances > r->size/4)
666 num_advances = r->size/4;
667 for(t=0;t<num_advances;t++) {
668 old_advance = ttf->glyphs[t].advance = readU16(r);
669 ttf->glyphs[t].bearing = readS16(r);
671 int rest = (r->size - num_advances*4)/2;
672 if(ttf->num_glyphs < num_advances+rest) {
673 rest = ttf->num_glyphs-num_advances;
675 for(t=0;t<rest;t++) {
676 ttf->glyphs[t].advance = old_advance;
677 ttf->glyphs[t].bearing = readS16(r);
680 static int mtx_write(ttf_t*ttf, ttf_table_t*w)
682 int num_advances = ttf->num_glyphs;
683 if(ttf->num_glyphs>=2) {
685 for(t=ttf->num_glyphs-1;t>0;t--) {
686 if(ttf->glyphs[t-1].advance !=
687 ttf->glyphs[t].advance) break;
689 /* we need to store all individual advances as well
690 as one entry for the constant */
695 for(t=0;t<num_advances;t++) {
696 writeU16(w, ttf->glyphs[t].advance);
697 writeU16(w, ttf->glyphs[t].bearing);
699 for(;t<ttf->num_glyphs;t++) {
700 writeU16(w, ttf->glyphs[t].bearing);
704 static U32*loca_parse(memreader_t*r, ttf_t*ttf, int size)
707 int num = ttf->num_glyphs+1;
708 U32*locations = rfx_calloc(num*sizeof(U32));
710 if(num*4 > r->size) {
711 msg("<warning> Short 'loca' table (32 bit)");
714 if(num*4 < r->size) {
715 msg("<warning> Extraneous data (%d bytes) in 'loca' table (32 bit)", r->size-num*4);
718 locations[t] = readU32(r);
721 if(num*2 > r->size) {
722 msg("<warning> Short 'loca' table (16 bit)");
725 if(num*2 < r->size) {
726 msg("<warning> Extraneous data (%d bytes) in 'loca' table (16 bit)", r->size-num*2);
729 locations[t] = readU16(r)*2;
734 static int loca_write(ttf_t*ttf, ttf_table_t*w, U32*locations)
738 for(t=0;t<=ttf->num_glyphs;t++) {
739 if(locations[t]>=0x20000 || (locations[t]&1)) {
746 for(t=0;t<=ttf->num_glyphs;t++) {
747 writeU32(w, locations[t]);
751 for(t=0;t<=ttf->num_glyphs;t++) {
752 writeU16(w, locations[t]/2);
757 static int parse_simple_glyph(ttf_t*ttf, memreader_t*r, int num_contours, int glyphnr)
759 ttfglyph_t*glyph = &ttf->glyphs[glyphnr];
763 endpoints = malloc(sizeof(U16)*num_contours);
766 for(s=0;s<num_contours;s++) {
767 int pos = endpoints[s] = readU16(r);
769 msg("<warning> Unsorted endpoints array (len:%d) last=%d now=%d", s, pos, lastpos);
774 U16 code_len = readU16(r);
776 glyph->code = malloc(sizeof(U16)*code_len);
777 readBlock(r, glyph->code, code_len);
778 glyph->code_size = code_len;
784 /*msg("<notice> TTF Glyph %d) code_size=%d num_contours=%d glyph->num_points=%d %d/%d/%d/%d",
785 glyphnr, code_len, num_contours, glyph->num_points,
786 xmin, ymin, xmax, ymax);*/
787 INIT_READ(fx, r->mem, r->size, r->pos);
788 INIT_READ(fy, r->mem, r->size, r->pos);
790 glyph->num_points = endpoints[num_contours-1] + 1;
791 glyph->points = rfx_calloc(sizeof(ttfpoint_t)*glyph->num_points);
793 /* parse flag array (1st pass- to determine start of coordinates) */
795 while(num<glyph->num_points) {
798 msg("<error> Bad flags in glyph outline: %02x (at pos %d)", flag, num);
801 glyph->num_points = 0;
807 if(count+num>glyph->num_points) {
808 msg("<warning> Bad count (%d) in glyph (%d) (at pos %d)", count, glyphnr, num);
809 count = glyph->num_points-num;
814 /* parse flag array (2nd pass) and x coordinates */
819 int bytepos = r->pos;
820 while(num<glyph->num_points) {
821 U8 flag = readU8(&fx);
822 int count = flag&8?readU8(&fx)+1:1;
823 count=count>glyph->num_points-num?glyph->num_points-num:(count?count:1);
826 if(contour_pos<num_contours && num==endpoints[contour_pos]) {
831 if((flag&0x12) == 0x12) x += readU8(r);
832 else if((flag&0x12) == 0x02) x -= readU8(r);
833 else if((flag&0x12) == 0x00) x += readS16(r);
835 glyph->points[num].x = x;
836 U8 f = flag&GLYPH_ON_CURVE;
837 if(is_start) f|=GLYPH_CONTOUR_START;
838 if(is_end) f|=GLYPH_CONTOUR_END;
839 glyph->points[num].flags = f;
845 /* parse flag array (3rd pass) and y coordinates */
848 while(num<glyph->num_points) {
849 U8 flag = readU8(&fy);
850 int count = flag&8?readU8(&fy)+1:1;
851 count=count>glyph->num_points-num?glyph->num_points-num:(count?count:1);
853 if((flag&0x24) == 0x24) y += readU8(r);
854 else if((flag&0x24) == 0x04) y -= readU8(r);
855 else if((flag&0x24) == 0x00) y += readS16(r);
856 glyph->points[num].y = y;
863 static void glyf_parse(memreader_t*rr, ttf_t*ttf, U32*loca)
866 char warn_about_compound_glyphs=1;
867 for(t=0;t<ttf->num_glyphs;t++) {
868 INIT_READ(r, rr->mem, rr->size, loca[t]);
869 if(r.pos+10>r.size) {
870 msg("<warning> Unexpected end of glyph array (or bad loca entry %d/%d)", loca[t], r.size);
873 S16 num_contours = readS16(&r);
874 ttf->glyphs[t].xmin = readS16(&r);
875 ttf->glyphs[t].ymin = readS16(&r);
876 ttf->glyphs[t].xmax = readS16(&r);
877 ttf->glyphs[t].ymax = readS16(&r);
880 if(warn_about_compound_glyphs)
881 msg("<error> Compound glyphs not supported yet");
882 warn_about_compound_glyphs=0;
884 if(!parse_simple_glyph(ttf, &r, num_contours, t))
891 void write_simple_glyph(ttf_table_t*w, ttfglyph_t*g)
893 /* endpoints array */
895 for(s=0;s<g->num_points;s++) {
896 if(g->points[s].flags&GLYPH_CONTOUR_END)
901 writeU16(w, g->code_size);
903 writeBlock(w, g->code, g->code_size);
910 for(s=0;s<g->num_points;s++) {
911 ttfpoint_t*p = &g->points[s];
912 int dx = p->x - lastx;
913 int dy = p->y - lasty;
914 U8 flags = p->flags&GLYPH_ON_CURVE;
917 } else if(dx<0 && dx>=-255) {
919 } else if(dx>0 && dx<=255) {
924 } else if(dy<0 && dy>=-255) {
926 } else if(dy>0 && dy<=255) {
929 if(flags == lastflag && flagcount<255) {
934 writeU8(w, lastflag|8);
935 writeU8(w, flagcount);
937 writeU8(w, lastflag);
948 writeU8(w, lastflag|8);
949 writeU8(w, flagcount);
951 writeU8(w, lastflag);
956 int bytepos = w->len;
957 for(s=0;s<g->num_points;s++) {
958 ttfpoint_t*p = &g->points[s];
959 int dx = p->x - lastx;
960 if(dx>32767 || dx<-32768) {
961 msg("<error> Coordinate overflow in glyph");
964 if(dx>0 && dx<=255) writeU8(w, dx);
965 else if(dx<0 && dx>=-255) writeU8(w, -dx);
966 else if(dx) writeS16(w, dx);
970 for(s=0;s<g->num_points;s++) {
971 ttfpoint_t*p = &g->points[s];
972 int dy = p->y - lasty;
973 if(dy>32767 || dy<-32768) {
974 msg("<error> Coordinate overflow in glyph");
977 if(dy>0 && dy<=255) writeU8(w, dy);
978 else if(dy<0 && dy>=-255) writeU8(w, -dy);
979 else if(dy) writeS16(w, dy);
983 U32* glyf_write(ttf_t* ttf, ttf_table_t*w)
985 U32*locations = malloc(sizeof(U32)*(ttf->num_glyphs+1));
987 for(t=0;t<ttf->num_glyphs;t++) {
988 locations[t] = w->len;
989 ttfglyph_t*g = &ttf->glyphs[t];
991 int num_contours = 0;
992 for(s=0;s<g->num_points;s++) {
993 if(g->points[s].flags&GLYPH_CONTOUR_END)
996 writeS16(w, num_contours?num_contours:1);
997 writeS16(w, g->xmin);
998 writeS16(w, g->ymin);
999 writeS16(w, g->xmax);
1000 writeS16(w, g->ymax);
1003 /* some ttf parsers can't deal with zero contours, so in the case
1004 of an empty glyph, write a single point (0,0) */
1005 writeU16(w, 0); //endpoint of 1st contour
1006 writeU16(w, g->code_size);
1008 writeBlock(w, g->code, g->code_size);
1009 writeU8(w, 0x31); //flag (xy=(0,0),on curve)
1011 write_simple_glyph(w, g);
1014 locations[t] = w->len;
1017 void glyf_dump(ttf_t* ttf)
1020 for(t=0;t<ttf->num_glyphs;t++) {
1021 ttfglyph_t*g = &ttf->glyphs[t];
1022 printf("glyph %d)\n", t);
1023 printf(" advance=%d\n", g->advance);
1024 printf(" bearing=%d\n", g->bearing);
1025 printf(" bbox=(%d/%d/%d/%d)\n", g->xmin, g->ymin, g->xmax, g->ymax);
1026 printf(" points=(");
1028 for(s=0;s<g->num_points;s++) {
1030 printf("%d/%d/0x%02x", g->points[s].x, g->points[s].y, g->points[s].flags);
1034 hexdump(g->code, g->code_size, " ");
1038 void glyf_delete(ttf_t* ttf)
1043 for(t=0;t<ttf->num_glyphs;t++) {
1044 if(ttf->glyphs[t].code) {
1045 free(ttf->glyphs[t].code);
1046 ttf->glyphs[t].code = 0;
1048 if(ttf->glyphs[t].points) {
1049 free(ttf->glyphs[t].points);
1050 ttf->glyphs[t].points = 0;
1053 free(ttf->glyphs);ttf->glyphs=0;
1056 static int ttf_parse_tables(ttf_t*ttf)
1060 table = ttf_find_table(ttf, TAG_HEAD);
1062 msg("<error> Font has no head table");
1065 INIT_READ(m, table->data, table->len, 0);
1066 int loc_index = head_parse(ttf, &m);
1067 ttf_table_delete(ttf, table);
1069 table = ttf_find_table(ttf, TAG_MAXP);
1071 msg("<error> Font has no maxp table");
1074 INIT_READ(m2, table->data, table->len, 0);
1075 ttf->maxp = maxp_parse(ttf, &m2);
1076 ttf_table_delete(ttf, table);
1078 if(!ttf->num_glyphs) {
1079 msg("<error> Invalid number of characters");
1082 ttf->glyphs = rfx_calloc(sizeof(ttfglyph_t)*ttf->num_glyphs);
1084 table = ttf_find_table(ttf, TAG_OS2);
1086 INIT_READ(m, table->data, table->len, 0);
1087 ttf->os2 = os2_parse(&m);
1088 ttf_table_delete(ttf, table);
1091 table = ttf_find_table(ttf, TAG_HHEA);
1093 INIT_READ(m, table->data, table->len, 0);
1094 int num_advances = hea_parse(&m, ttf);
1095 ttf_table_delete(ttf, table);
1097 table = ttf_find_table(ttf, TAG_HMTX);
1099 INIT_READ(m, table->data, table->len, 0);
1100 hmtx_parse(&m, ttf, num_advances);
1101 ttf_table_delete(ttf, table);
1104 table = ttf_find_table(ttf, TAG_VHEA);
1107 INIT_READ(m, table->data, table->len, 0);
1108 int num_advances = hea_parse(&m, ttf);
1109 ttf_table_delete(ttf, table);
1111 table = ttf_find_table(ttf, TAG_VMTX);
1113 INIT_READ(m, table->data, table->len, 0);
1114 hmtx_parse(&m, ttf, num_advances);
1115 ttf_table_delete(ttf, table);
1118 msg("<error> Font contains neither HHEA nor VHEA");
1121 table = ttf_find_table(ttf, TAG_LOCA);
1123 INIT_READ(m, table->data, table->len, 0);
1124 U32*loca = loca_parse(&m, ttf, loc_index);
1125 ttf_table_delete(ttf, table);
1126 table = ttf_find_table(ttf, TAG_GLYF);
1128 INIT_READ(m, table->data, table->len, 0);
1129 glyf_parse(&m, ttf, loca);
1130 ttf_table_delete(ttf, table);
1136 static void ttf_collapse_tables(ttf_t*ttf)
1140 table = ttf_addtable(ttf, TAG_MAXP);
1141 maxp_write(ttf, table);
1144 table = ttf_addtable(ttf, TAG_OS2);
1145 os2_write(ttf, table);
1148 if(!ttf->is_vertical) {
1149 table = ttf_addtable(ttf, TAG_HMTX);
1150 int num_advances = mtx_write(ttf, table);
1151 table = ttf_addtable(ttf, TAG_HHEA);
1152 hea_write(ttf, table, num_advances);
1154 table = ttf_addtable(ttf, TAG_VMTX);
1155 int num_advances = mtx_write(ttf, table);
1156 table = ttf_addtable(ttf, TAG_VHEA);
1157 hea_write(ttf, table, num_advances);
1161 if(ttf->num_glyphs) {
1162 table = ttf_addtable(ttf, TAG_GLYF);
1163 U32*locations = glyf_write(ttf, table);
1166 table = ttf_addtable(ttf, TAG_LOCA);
1167 loca_size = loca_write(ttf, table, locations);
1171 table = ttf_addtable(ttf, TAG_HEAD);
1172 head_write(ttf, table, loca_size);
1177 ttf_t* load_ttf(void*data, int length)
1179 INIT_READ(r,data,length, 0);
1182 msg("<error> Truncated Truetype file (%d bytes)", length);
1186 ttf_t*ttf = rfx_calloc(sizeof(ttf_t));
1187 ttf->version = readU32(&r);
1188 if(ttf->version == TTCFTAG) {
1189 /* a ttc collection is a number of truetype fonts
1190 packaged together */
1192 msg("<error> Truncated TTC file (%d bytes)", length);
1195 U32 ttcf_version = readU32(&r); // 0x00000100: v1.0, 0x00000200: v2.0, includes DSIG table
1196 U32 num_fonts = readU32(&r); // number of fonts
1197 U32 font1_position = readU32(&r);
1198 if(font1_position+12 > length) {\
1199 msg("<error> Truncated TTC file (%d bytes, first font at %d)", length, font1_position);
1202 r.pos = font1_position;
1203 ttf->version = readU32(&r);
1206 int num_tables = readU16(&r);
1208 readU16(&r); //search range
1209 readU16(&r); //entry selector
1210 readU16(&r); //range shift
1212 if(num_tables*16 > length) {
1213 msg("<error> Truncated TTC file (table entries: %d)", num_tables);
1214 if(ttf->version != OPENTYPE &&
1215 ttf->version != TRUETYPE_MACOS &&
1216 ttf->version != VERSION_1_0) {
1217 // bad table length, weird version. This is probably not a ttf file.
1222 U32*table_data = malloc(16*num_tables);
1224 for(t=0;t<num_tables*4;t++) {
1225 table_data[t] = readU32(&r);
1227 for(t=0;t<num_tables;t++) {
1228 U32 tag = table_data[t*4];
1229 U32 checksum = table_data[t*4+1];
1230 U32 pos = table_data[t*4+2];
1231 U32 len = table_data[t*4+3];
1233 if(pos+len > length) {
1234 msg("<error> TTF Table %02x%02x%02x%02x outside of stream (pos %d)", (tag>>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, (tag)&0xff, pos);
1236 U8*mem = malloc(len);
1238 readBlock(&r, mem, len);
1240 ttf_table_t*table = ttf_addtable(ttf, tag);
1242 table->len = table->memsize = len;
1244 U32 checksum2 = ttf_table_checksum(table);
1245 if(checksum2!=checksum) {
1246 msg("<warning> Checksum mismatch in tag %02x%02x%02x%02x %c%c%c%c (%d bytes) %08x!=%08x",
1247 (tag>>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, (tag)&0xff,
1248 (tag>>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, (tag)&0xff,
1249 len, checksum2, checksum);
1256 if(!ttf_parse_tables(ttf))
1262 ttf_table_t* ttf_write(ttf_t*ttf)
1264 ttf_collapse_tables(ttf);
1266 ttf_table_t*file = ttf_table_new(0);
1267 writeU32(file, VERSION_1_0);
1269 /* write number of tables */
1271 ttf_table_t*t = ttf->tables;
1276 writeU16(file, num_tables);
1278 /* write search range */
1279 int tmp = num_tables;
1280 int search_range = 0;
1287 writeU16(file, search_range);
1289 /* write entry selector */
1290 int entry_selector = 0;
1295 writeU16(file, entry_selector);
1297 /* write range shift */
1298 int range_shift = num_tables*16 - search_range;
1299 writeU16(file, range_shift);
1301 /* write table dictionary */
1302 int table_dictionary_pos = file->len;
1303 int data_pos = file->len + num_tables*16;
1304 for(t=ttf->tables;t;t=t->next) {
1305 writeU32(file, t->id);
1306 writeU32(file, ttf_table_checksum(t));
1307 writeU32(file, data_pos);
1308 writeU32(file, t->len);
1310 data_pos += (-t->len)&3; //pad
1315 U8 zero[4]={0,0,0,0};
1316 for(t=ttf->tables;t;t=t->next) {
1317 if(t->id == TAG_HEAD)
1318 head_pos = file->len;
1319 writeBlock(file, t->data, t->len);
1320 writeBlock(file, zero, (-t->len)&3); //pad
1322 U32 checksum = 0xb1b0afba - ttf_table_checksum(file);
1323 U8*checksum2 = file->data + head_pos + 8;
1324 checksum2[0] = checksum>>24;
1325 checksum2[1] = checksum>>16;
1326 checksum2[2] = checksum>>8;
1327 checksum2[3] = checksum>>0;
1331 void ttf_save(ttf_t*ttf, const char*filename)
1333 ttf_table_t* t = ttf_write(ttf);
1334 FILE*fi = fopen(filename, "wb");
1339 fwrite(t->data, t->len, 1, fi);
1341 ttf_table_delete(0, t);
1344 void ttf_dump(ttf_t*ttf)
1346 msg("<notice> Truetype file version %08x%s", ttf->version, ttf->version == OPENTYPE?" (opentype)":"");
1347 ttf_table_t*table = ttf->tables;
1349 U32 tag = table->id;
1350 msg("<notice> Tag %02x%02x%02x%02x [%c%c%c%c] (length: %d)",
1351 (tag>>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, (tag)&0xff,
1352 (tag>>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, (tag)&0xff, table->len);
1353 table = table->next;
1355 //ttf_table_dump(ttf_find_table(ttf, TAG_MAXP));
1363 void ttf_destroy(ttf_t*ttf)
1365 ttf_table_t*table = ttf->tables;
1367 ttf_table_t*next = table->next;
1381 int main(int argn, const char*argv[])
1383 setConsoleLogging(7);
1384 const char*filename = "comic.ttf";
1387 //msg("<notice> Loading %s", filename);
1388 memfile_t*m = memfile_open(filename);
1389 ttf_t*ttf = load_ttf(m->data, m->len);
1393 //printf("os2 version: %04x (%d), maxp size: %d\n",
1394 // ttf->os2->version, ttf->os2->size, ttf->maxp->size);
1395 ttf_save(ttf, "comic2.ttf");