static u8 read_8(struct font_buffer *buf) { u8 result = buf->data[buf->offset]; buf->offset += 1; return(result); } static s8 read_8s(struct font_buffer *buf) { s8 result = buf->data[buf->offset]; buf->offset += 1; return(result); } static u16 be16(u8 *buf) { u16 result = buf[0] << 8 | buf[1]; return(result); } static s16 be16s(u8 *buf) { s16 result = buf[0] << 8 | buf[1]; return(result); } static u16 read_be16(struct font_buffer *buf) { u16 result = be16(buf->data + buf->offset); buf->offset += 2; return(result); } static s16 read_be16s(struct font_buffer *buf) { s16 result = be16s(buf->data + buf->offset); buf->offset += 2; return(result); } static u32 be32(u8 *buf) { u32 result = ((u32) buf[0] << 24) | ((u32) buf[1] << 16) | ((u32) buf[2] << 8) | ((u32) buf[3]); return(result); } static u32 read_be32(struct font_buffer *buf) { u32 result = be32(buf->data + buf->offset); buf->offset += 4; return(result); } static f32 read_be214(struct font_buffer *buf) { f32 result = read_be16s(buf) / 16384.0f; return(result); } static struct font_buffer read_file(char *filename) { FILE *file = fopen(filename, "rb"); struct font_buffer result = { 0 }; ASSERT(file); if (file) { fseek(file, 0, SEEK_END); result.size = ftell(file); result.data = malloc(result.size); fseek(file, 0, SEEK_SET); fread(result.data, result.size, 1, file); fclose(file); } return(result); } static u32 get_glyph_index_format12(struct ttf_font *font, u16 codepoint) { u32 ngroups = be32(font->file.data + font->dir.cmap_offset + 12); font->file.offset = font->dir.cmap_offset + 16; // TODO: binsearch for (u32 g = 0; g < ngroups; ++g) { u32 start_charcode = read_be32(&font->file); u32 end_charcode = read_be32(&font->file); u32 start_glyphcode = read_be32(&font->file); if (start_charcode <= codepoint && codepoint <= end_charcode) { u32 glyph_index = start_glyphcode + (codepoint - start_charcode); return(glyph_index); } if (start_charcode > codepoint) { break; } } return(0); } static s16 get_glyph_index_format4(struct ttf_font *font, u16 codepoint) { int segcount_x2 = be16(font->file.data + font->dir.cmap_offset + 6); u16 *end_codes = (u16 *) (font->file.data + font->dir.cmap_offset + 16); u16 *start_codes = (u16 *) (font->file.data + font->dir.cmap_offset + 16 + segcount_x2 + 2); u16 *id_deltas = (u16 *) (font->file.data + font->dir.cmap_offset + 16 + segcount_x2 * 2 + 2); u16 *id_range_offset = (u16 *) (font->file.data + font->dir.cmap_offset + 16 + segcount_x2 * 3 + 2); int index = -1; for (int i = 0; i < segcount_x2 / 2; ++i) { u16 end_code = be16((u8 *) (end_codes + i)); if (end_code >= codepoint) { index = i; break; } } if (index == -1) { return(0); } u16 start_code = be16((u8 *) (start_codes + index)); if (start_code <= codepoint) { u16 ir_offset = be16((u8 *) (id_range_offset + index)); if (ir_offset != 0) { u16 glyph_index = be16((u8 *) (id_range_offset + index + ir_offset / 2 + (codepoint - start_code))); if (!glyph_index) { return(0); } u16 id_delta = be16((u8 *) (id_deltas + index)); u16 result = (u16) (glyph_index + id_delta); return(result); } u16 id_delta = be16((u8 *) (id_deltas + index)); u16 result = (u16) (codepoint + id_delta); return(result); } return(0); } static u16 get_glyph_index_format6(struct ttf_font *font, u16 codepoint) { u16 first_code = be16(font->file.data + font->dir.cmap_offset + 6); u16 entry_count = be16(font->file.data + font->dir.cmap_offset + 8); u16 *glyph_index_array = (u16 *) (font->file.data + font->dir.cmap_offset + 10); if (first_code <= codepoint && codepoint < first_code + entry_count) { u16 glyph_index = be16((u8 *) (glyph_index_array + (codepoint - first_code))); return(glyph_index); } return(0); } static u16 get_glyph_index_format0(struct ttf_font *font, u16 codepoint) { u8 *glyph_index_array = font->file.data + font->dir.cmap_offset + 6; if (codepoint <= 0xFF) { u8 glyph_index = glyph_index_array[codepoint]; return(glyph_index); } return(0); } static u32 get_glyph_index(struct ttf_font *font, u16 codepoint) { u32 result = 0; switch (font->cmap_format) { case 0: { result = get_glyph_index_format0(font, codepoint); break; } case 12: { result = get_glyph_index_format12(font, codepoint); break; } case 6: { result = get_glyph_index_format6(font, codepoint); break; } case 4: { result = get_glyph_index_format4(font, codepoint); break; } default: { ASSERT(false); } } return(result); } static u32 get_glyph_offset(struct ttf_font *font, u32 glyph_index) { u32 offset = 0; u32 offset_next; if (font->head.itl_format == 1) { font->file.offset = font->dir.loca_offset + glyph_index * 4; offset = read_be32(&font->file); offset_next = read_be32(&font->file); } else { font->file.offset = font->dir.loca_offset + glyph_index * 2; offset = read_be16(&font->file); offset *= 2; offset_next = read_be16(&font->file); offset_next *= 2; } if (offset == offset_next) { return(0); } return(offset); } static s16 get_current_coordinate(struct font_buffer *font_file, int flag_combined) { s16 current_coordinate = 0; switch (flag_combined) { case 0: { current_coordinate = read_be16(font_file); break; } case 1: { current_coordinate = 0; break; } case 2: { current_coordinate = read_8(font_file); current_coordinate *= -1; break; } case 3: { current_coordinate = read_8(font_file); break; } } return(current_coordinate); } static inline u32 pop(struct ttf_graphics_state *state) { u32 result = state->stack[state->stack_head - 1]; --state->stack_head; return(result); } static inline f32 popf(struct ttf_graphics_state *state) { u32 integer = state->stack[state->stack_head - 1]; --state->stack_head; f32 result; memcpy(&result, &integer, sizeof(f32)); return(result); } static inline void push(struct ttf_graphics_state *state, u32 value) { state->stack[state->stack_head] = value; ++state->stack_head; } static u32 skip_until(struct font_buffer *font_file, u32 start, u32 size, enum ttf_instruction until) { u32 result = -1; // -1 means not found u32 base = start; u32 offset = start; while (offset - base < size) { enum ttf_instruction inst = font_file->data[offset]; if (inst == until) { result = offset; break; } ++offset; // thankfully, ONLY push instructions take data from the instructin stream if (inst == NPUSHB) { u8 n = font_file->data[offset++]; offset += n; } else if (inst == NPUSHW) { u8 n = font_file->data[offset++]; offset += n * 2; } else if (PUSHB <= inst && inst <= PUSHB_TOP) { u8 n = inst - PUSHB + 1; offset += n; } else if (PUSHW <= inst && inst <= PUSHW_TOP) { u8 n = inst - PUSHW + 1; offset += n * 2; } } return(result); } static struct v2u32 find_if_pair(struct font_buffer *font_file, u32 start, u32 size) { // Find matching ELSE and EIF for an IF, -1 means not found struct v2u32 result = { -1, -1 }; u32 base = start; u32 offset = start; u32 depth = 1; while (offset - base < size) { enum ttf_instruction inst = font_file->data[offset++]; if (inst == NPUSHB) { u8 n = font_file->data[offset++]; offset += n; } else if (inst == NPUSHW) { u8 n = font_file->data[offset++]; offset += n * 2; } else if (PUSHB <= inst && inst <= PUSHB_TOP) { u8 n = inst - PUSHB + 1; offset += n; } else if (PUSHW <= inst && inst <= PUSHW_TOP) { u8 n = inst - PUSHW + 1; offset += n * 2; } else if (inst == IF) { ++depth; } else if (inst == ELSE) { if (depth == 1) { result.x = offset; } } else if (inst == EIF) { if (depth == 1) { result.y = offset; break; } else { --depth; } } } return(result); } static s16 funits_to_pixels(u32 funits) { // TODO return(funits); } static void iterate_instructions(struct ttf_font *font, struct font_buffer *font_file, u32 offset, u32 size, struct ttf_graphics_state *state) { const struct v2f X_AXIS = { 1.0f, 0.0f }; const struct v2f Y_AXIS = { 0.0f, 1.0f }; u32 base_offset = offset; u32 n_fdefs = 0; int function_define = -1; int if_true = 0; while (offset - base_offset < size) { enum ttf_instruction inst = font_file->data[offset++]; if (function_define >= 0 && inst != ENDF) { continue; } if (offset == 13622) { int __sdfsdf = 0; } #if 0 printf("%6d: ", offset); printf("%s\n", ttfe_instruction_name(inst)); #endif switch (inst) { case NPUSHB: { u8 n = font_file->data[offset++]; for (int b = 0; b < n; ++b) { u8 b = font_file->data[offset++]; push(state, b); } break; } case NPUSHW: { u8 n = font_file->data[offset++]; for (int b = 0; b < n; ++b) { u16 word = be16(font_file->data + offset); offset += 2; push(state, word); } break; } case PUSHB ... PUSHB_TOP: { u8 overflow = inst - PUSHB; int abc = overflow + 1; for (int i = 0; i < abc; ++i) { u8 b = font_file->data[offset++]; push(state, b); } break; } case PUSHW ... PUSHW_TOP: { u8 overflow = inst - PUSHW; int abc = overflow + 1; for (int i = 0; i < abc; ++i) { u16 word = be16(font_file->data + offset); offset += 2; push(state, word); } break; } case RS: { u32 location = pop(state); u32 value = font->storage[location]; push(state, value); break; } case WS: { u32 value = pop(state); u32 location = pop(state); font->storage[location] = value; break; } case WCVTP: { u32 value = pop(state); u32 location = pop(state); s16 cvt_value = value; ASSERT(location <= font->dir.cvt_size / sizeof(s16)); font->cvt[location] = cvt_value; break; } case WCVTF: { u32 value = pop(state); u32 location = pop(state); s16 cvt_value = funits_to_pixels(value); ASSERT(location <= font->dir.cvt_size / sizeof(s16)); font->cvt[location] = cvt_value; break; } case RCVT: { u32 location = pop(state); ASSERT(location <= font->dir.cvt_size / sizeof(s16)); s16 integer = font->cvt[location]; push(state, integer); break; } case SVTCA ... SVTCA_TOP: { u8 overflow = inst - SVTCA; int a = overflow; if (a == 0) { state->projection_vector = Y_AXIS; state->freedom_vector = Y_AXIS; } else if (a == 1) { state->projection_vector = X_AXIS; state->freedom_vector = X_AXIS; } break; } case SPVTCA ... SPVTCA_TOP: { u8 overflow = inst - SPVTCA; int a = overflow; if (a == 0) { state->projection_vector = Y_AXIS; } else if (a == 1) { state->projection_vector = X_AXIS; } break; } case SFVTCA ... SFVTCA_TOP: { u8 overflow = inst - SFVTCA; int a = overflow; if (a == 0) { state->freedom_vector = Y_AXIS; } else if (a == 1) { state->freedom_vector = X_AXIS; } break; } case SPVTL ... SPVTL_TOP: { u8 overflow = inst - SFVTCA; int a = overflow; u32 p1 = pop(state); u32 p2 = pop(state); break; } case SFVTL ... SFVTL_TOP: { break; } case SFVTPV: { break; } case SDPVTL ... SDPVTL_TOP: { break; } case SPVFS: { u32 y = pop(state); u32 x = pop(state); // TODO: convert to 2.14 fixed point break; } case SFVFS: { break; } case GPV: { break; } case GFV: { break; } case SRP0: { break; } case SRP1: { break; } case SRP2: { break; } case SZP0: { break; } case SZP1: { break; } case SZP2: { break; } case SZPS: { break; } case RTHG: { break; } case RTG: { state->round_state = 1; break; } case RTDG: { break; } case RDTG: { break; } case RUTG: { break; } case ROFF: { break; } case SROUND: { break; } case S45ROUND: { break; } case SLOOP: { break; } case SMD: { break; } case INSTCTRL: { break; } case SCANCTRL: { // TODO: turn dropout control (state->scan_control) on or off u32 n = pop(state); break; } case SCANTYPE: { // TODO: dropout control mode u32 val = pop(state); u16 n = (u16) val; break; } case SCVTCI: { u32 cut_in_64th = pop(state); f32 cut_in = cut_in_64th / 64.0f; state->control_value_cut_in = cut_in; break; } case SSWCI: { break; } case SSW: { break; } case FLIPON: { break; } case FLIPOFF: { break; } case SANGW: { break; } case SDB: { break; } case SDS: { break; } case GC_ ... GC_TOP: { break; } case SCFS: { break; } case MD ... MD_TOP: { break; } case MPPEM: { /* (funit -> px) scale = pointSize * resolution / ( 72 points per inch * units_per_em ) */ /* 1em = units_per_em funits */ /* 1em = scale * units_per_em pixels */ u32 dpi = 96; // TODO: query system u32 point_size = 12; // TODO: pass as parameter u16 ppem = point_size * dpi / 72; push(state, ppem); break; } case MPS: { break; } case FLIPPT: { break; } case FLIPRGON: { break; } case FLIPRGOFF: { break; } case SHP ... SHP_TOP: { break; } case SHC ... SHC_TOP: { u8 overflow = inst - SHC; u32 c = pop(state); // TODO break; } case SHZ ... SHZ_TOP: { u8 overflow = inst - SHZ; u32 e = pop(state); // TODO break; } case SHPIX: { break; } case MSIRP ... MSIRP_TOP: { u8 overflow = inst - MSIRP; f32 d = popf(state); u32 p = pop(state); // TODO break; } case MDAP ... MDAP_TOP: { break; } case MIAP ... MIAP_TOP: { // 0: don’t round the distance and don’t look at the control_value_cut_in // 1: round the distance and look at the control_value_cut_in int a = inst - MIAP; ASSERT(state->stack_head >= 2); u32 n = pop(state); // CVT entry number u32 p = pop(state); // point number // TODO break; } case MDRP ... MDRP_TOP: { break; } case MIRP ... MIRP_TOP: { break; } case ALIGNRP: { break; } case ISECT: { break; } case ALIGNPTS: { break; } case IP: { u32 p = pop(state); // TODO break; } case UTP: { u32 p = pop(state); // TODO break; } case IUP ... IUP_TOP: { u8 overflow = inst - IUP; break; } case DELTAP1: { u32 n = pop(state); for (u32 i = 0; i < n; ++i) { u32 pi = pop(state); u32 argi = pop(state); // TODO } break; } case DELTAP2: { u32 n = pop(state); for (u32 i = 0; i < n; ++i) { u32 pi = pop(state); u32 argi = pop(state); // TODO } break; } case DELTAP3: { u32 n = pop(state); for (u32 i = 0; i < n; ++i) { u32 pi = pop(state); u32 argi = pop(state); // TODO } break; } case DELTAC1: { u32 n = pop(state); for (u32 i = 0; i < n; ++i) { u32 ci = pop(state); u32 argi = pop(state); // TODO } break; } case DELTAC2: { u32 n = pop(state); for (u32 i = 0; i < n; ++i) { u32 ci = pop(state); u32 argi = pop(state); // TODO } break; } case DELTAC3: { u32 n = pop(state); for (u32 i = 0; i < n; ++i) { u32 ci = pop(state); u32 argi = pop(state); // TODO } break; } case DUP: { u32 e = pop(state); push(state, e); push(state, e); break; } case POP: { --state->stack_head; break; } case CLEAR: { state->stack_head = 0; break; } case SWAP: { ASSERT(state->stack_head >= 2); u32 last = state->stack[state->stack_head - 1]; u32 prelast = state->stack[state->stack_head - 2]; state->stack[state->stack_head - 2] = last; state->stack[state->stack_head - 1] = prelast; break; } case DEPTH: { break; } case CINDEX: { break; } case MINDEX: { break; } case ROLL: { break; } case IF: { struct v2u32 pair = find_if_pair(font_file, offset, size - (offset - base_offset)); if_true = pop(state); if (!if_true) { ASSERT(pair.x != (u32) -1 || pair.y != (u32) -1); offset = MIN(pair.x, pair.y); } break; } case ELSE: { if (if_true) { offset = skip_until(font_file, offset, size - (offset - base_offset), EIF); ASSERT(offset != (u32) -1); } break; } case EIF: { break; } case JROT: { break; } case JMPR: { break; } case JROF: { break; } case LT: { u32 e2 = pop(state); u32 e1 = pop(state); u32 less = (e1 < e2 ? 1 : 0); push(state, less); break; } case LTEQ: { u32 e2 = pop(state); u32 e1 = pop(state); u32 leq = (e1 <= e2 ? 1 : 0); push(state, leq); break; } case GT: { u32 e2 = pop(state); u32 e1 = pop(state); u32 gt = (e1 > e2 ? 1 : 0); push(state, gt); break; } case GTEQ: { u32 e2 = pop(state); u32 e1 = pop(state); u32 geq = (e1 >= e2 ? 1 : 0); push(state, geq); break; } case EQ: { u32 e2 = pop(state); u32 e1 = pop(state); u32 equal = (e1 == e2 ? 1 : 0); push(state, equal); break; } case NEQ: { u32 e2 = pop(state); u32 e1 = pop(state); u32 neq = (e1 != e2 ? 1 : 0); push(state, neq); break; } case ODD: { u32 e1 = pop(state); u32 odd = e1 & 1; push(state, odd); break; } case EVEN: { u32 e1 = pop(state); u32 even = 1 - (e1 & 1); push(state, even); break; } case AND: { u32 e1 = pop(state); u32 e2 = pop(state); u32 and = (e1 & e2 ? 1 : 0); push(state, and); break; } case OR: { u32 e1 = pop(state); u32 e2 = pop(state); u32 or = (e1 | e2 ? 1 : 0); push(state, or); break; } case NOT: { u32 e1 = pop(state); u32 not = (e1 ? 0 : 1); push(state, not); break; } case ADD: { f32 n1 = popf(state); f32 n2 = popf(state); f32 add = n2 + n1; push(state, add); break; } case SUB: { f32 n1 = popf(state); f32 n2 = popf(state); f32 sub = n2 - n1; push(state, sub); break; } case DIV: { f32 n1 = popf(state); f32 n2 = popf(state); f32 div = n2 / n1; push(state, div); break; } case MUL: { f32 n1 = popf(state); f32 n2 = popf(state); f32 mul = n2 * n1; push(state, mul); break; } case ABS: { f32 n1 = popf(state); f32 abs = fabsf(n1); push(state, abs); break; } case NEG: { f32 n1 = popf(state); f32 neg = -n1; push(state, neg); break; } case FLOOR: { f32 n1 = popf(state); f32 floor = floorf(n1); push(state, floor); break; } case CEILING: { f32 n1 = popf(state); f32 ceil = ceilf(n1); push(state, ceil); break; } case MAX: { u32 e1 = pop(state); u32 e2 = pop(state); u32 max = (e1 > e2 ? e1 : e2); push(state, max); break; } case MIN: { u32 e1 = pop(state); u32 e2 = pop(state); u32 min = (e1 < e2 ? e1 : e2); push(state, min); break; } case ROUND ... ROUND_TOP: { u8 overflow = inst - ROUND; u32 n1 = pop(state); // TODO: do rounding push(state, n1); break; } case NROUND ... NROUND_TOP: { break; } case FDEF: { int f = state->stack[--state->stack_head]; font->functions[f].from = offset; function_define = f; break; } case ENDF: { font->functions[function_define].to = offset - 1; // NOTE: do not include ENDF in subproc function_define = -1; break; } case CALL: { int f = state->stack[--state->stack_head]; ASSERT(f <= font->maxp.max_fdefs); struct ttf_function proc = font->functions[f]; iterate_instructions(font, font_file, proc.from, proc.to - proc.from, state); break; } case LOOPCALL: { break; } case IDEF: { break; } case DEBUG: { break; } case GETINFO: { // TODO? int selector = pop(state); push(state, 0); break; } case GETVARIATION: { break; } default: { printf("Unknown ttf instruction %x\n", inst); __builtin_trap(); } } } } static void get_simple_glyph_points(struct font_buffer *font_file, u16 number_of_countours, struct glyph *result) { u16 *end_pts_of_contours = calloc(1, number_of_countours * sizeof(u16)); for (int c = 0; c < number_of_countours; ++c) { end_pts_of_contours[c] = read_be16(font_file); } // NOTE: skip instructions u16 instruction_length = read_be16(font_file); struct ttf_graphics_state glyph_state = default_graphics_state(); //iterate_instructions(font, font_file, font_file->offset, instruction_length, &glyph_state); font_file->offset += instruction_length; int last_index = end_pts_of_contours[number_of_countours - 1]; union glyph_flag *flags = calloc(1, last_index + 1); struct glyph_point *points = malloc((last_index + 2) * 2 * sizeof(struct v2)); // NOTE: so that we can insert one point at the start if needed // points = points + 1; for (int i = 0; i < last_index + 1; ++i) { flags[i].flag = read_8(font_file); if (flags[i].repeat) { u8 repeat_count = read_8(font_file); while (repeat_count-- > 0) { i++; flags[i] = flags[i - 1]; } } } s16 prev_coordinate = 0; int start = 0; int head = 0; for (int c = 0; c < number_of_countours; ++c) { int end = end_pts_of_contours[c]; struct glyph_point first_point = { 0 }; for (int i = start; i < end + 1; ++i) { // NOTE: read x coordinates int flag_combined = (flags[i].x_short << 1) | flags[i].x_short_pos; s16 current_coordinate = get_current_coordinate(font_file, flag_combined); s16 read_x = current_coordinate + prev_coordinate; int prev = i - 1; if (prev >= start) { //prev = end; if (!flags[i].on_curve && !flags[prev].on_curve) { // NOTE: implicit on-curve point points[head].on_curve = true; points[head].x = (read_x + points[head - 1].x) / 2; ++head; } } points[head].on_curve = flags[i].on_curve; points[head].x = read_x; ++head; if (i == start) { // NOTE: first iteration could not have inserted an implicit point first_point = points[head - 1]; } prev_coordinate = read_x; } if (!flags[start].on_curve && !flags[end].on_curve) { points[head].on_curve = true; points[head].x = (first_point.x + points[head - 1].x) / 2; ++head; } start = end + 1; } prev_coordinate = 0; start = 0; head = 0; int added_points = 0; for (int c = 0; c < number_of_countours; ++c) { int end = end_pts_of_contours[c]; struct glyph_point first_point = { 0 }; for (int i = start; i < end + 1; ++i) { // NOTE: read y coordinates int flag_combined = (flags[i].y_short << 1) | flags[i].y_short_pos; s16 current_coordinate = get_current_coordinate(font_file, flag_combined); s16 read_y = current_coordinate + prev_coordinate; int prev = i - 1; if (prev >= start) { //prev = end; if (!flags[i].on_curve && !flags[prev].on_curve) { // NOTE: implicit on-curve point points[head].on_curve = true; points[head].y = (read_y + points[head - 1].y) / 2; ++head; ++added_points; } } points[head].on_curve = flags[i].on_curve; points[head].y = read_y; ++head; prev_coordinate = read_y; if (i == start) { // NOTE: first iteration could not have inserted an implicit point first_point = points[head - 1]; } } if (!flags[start].on_curve && !flags[end].on_curve) { points[head].on_curve = true; points[head].y = (first_point.y + points[head - 1].y) / 2; ++head; ++added_points; } start = end + 1; end_pts_of_contours[c] += added_points; } result->points = points; result->ncontours = number_of_countours; result->end_pts_of_contours = end_pts_of_contours; } static struct glyph get_glyph_outline(struct ttf_font *font, u32 glyph_index) { u32 glyph_offset = get_glyph_offset(font, glyph_index); struct font_buffer *font_file = &font->file; font_file->offset = font->dir.glyf_offset + glyph_offset; struct glyph result = { 0 }; s16 number_of_countours = read_be16(font_file); result.xmin = read_be16(font_file); result.ymin = read_be16(font_file); result.xmax = read_be16(font_file); result.ymax = read_be16(font_file); if (number_of_countours > 0) { // NOTE: simple glyph get_simple_glyph_points(font_file, number_of_countours, &result); } else if (number_of_countours < 0) { // NOTE: compund glyph int head = 0; result.end_pts_of_contours = malloc(font->maxp.max_component_contours * sizeof(u16)); result.points = malloc(font->maxp.max_component_points * 2 * sizeof(struct glyph_point)); for (;;) { u16 flags = read_be16(font_file); u16 component_index = read_be16(font_file); f32 matrix[6] = {1, 0, 0, 1, 0, 0}; /* NOTE(aolo2): matrix stuff is highjacked from stb */ if (flags & ARG_1_AND_2_ARE_WORDS) { matrix[4] = read_be16(font_file); matrix[5] = read_be16(font_file); } else { matrix[4] = read_8s(font_file); matrix[5] = read_8s(font_file); } if (flags & WE_HAVE_A_SCALE) { f32 scale = read_be214(font_file); matrix[0] = scale; matrix[1] = 0; matrix[2] = 0; matrix[3] = scale; } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE ) { f32 xscale = read_be214(font_file); f32 yscale = read_be214(font_file); matrix[0] = xscale; matrix[1] = 0; matrix[2] = 0; matrix[3] = yscale; } else if (flags & WE_HAVE_A_TWO_BY_TWO) { f32 xscale = read_be214(font_file); f32 scale01 = read_be214(font_file); f32 scale10 = read_be214(font_file); f32 yscale = read_be214(font_file); matrix[0] = xscale; matrix[1] = scale01; matrix[2] = scale10; matrix[3] = yscale; } f32 m = sqrtf(matrix[0] * matrix[0] + matrix[1] * matrix[1]); f32 n = sqrtf(matrix[2] * matrix[2] + matrix[3] * matrix[3]); u32 saved_offset = font_file->offset; struct glyph component = get_glyph_outline(font, component_index); font_file->offset = saved_offset; int startv = 0; for (int c = 0; c < component.ncontours; ++c) { int endv = component.end_pts_of_contours[c] + 1; for (int v = startv; v < endv; ++v) { s16 x = component.points[v].x; s16 y = component.points[v].y; result.points[head].x = m * (matrix[0] * x + matrix[2] * y + matrix[4]); result.points[head].y = n * (matrix[1] * x + matrix[3] * y + matrix[5]); result.points[head].on_curve = component.points[v].on_curve; ++head; } startv = endv; result.end_pts_of_contours[result.ncontours] = head - 1; ++result.ncontours; } if (!(flags & MORE_COMPONENTS)) { break; } } } else { // NOTE: space //advance = 0; // TODO } return(result); } static void get_hmtx(struct ttf_font *font, u32 glyph_index, struct glyph *dest) { struct font_buffer font_file = font->file; if (font->is_monospace) { font_file.offset = font->dir.hmtx_offset; } else { font_file.offset = font->dir.hmtx_offset + glyph_index * 4; } dest->advance = read_be16(&font_file); dest->lsb = read_be16s(&font_file); } static int get_codepoint_width(struct ttf_font *font, f32 scale, u16 codepoint) { struct glyph g = { 0 }; u32 glyph_index = get_glyph_index(font, codepoint); get_hmtx(font, glyph_index, &g); int result = ceil_f32(scale * g.advance); return(result); } static int get_string_width(struct ttf_font *font, int px_size, wchar_t *string, int length) { int result = 0; f32 scale = (f32) px_size / ((f32) (font->hhea.ascent - font->hhea.descent)); for (int i = 0; i < length; ++i) { u16 codepoint = string[i]; // NOTE(aolo2): advance is rounded up for each glyph (and not once in the end), because that's what the rasterizer does result += get_codepoint_width(font, scale, codepoint); } return(result); } static struct glyph get_outline(struct ttf_font *font, u16 codepoint) { u32 glyph_index = get_glyph_index(font, codepoint); struct glyph result = get_glyph_outline(font, glyph_index); get_hmtx(font, glyph_index, &result); int xmin = result.xmin; int ymin = result.ymin; //int ymin = result.ymin; for (int p = 0; p < result.end_pts_of_contours[result.ncontours - 1] + 1; ++p) { struct glyph_point *gp = result.points + p; gp->x -= xmin; gp->y -= ymin; } result.baseline = - (int) result.ymin; result.lsb += xmin; result.xmin = 0; result.xmax -= xmin; result.ymin = 0; result.ymax -= result.ymin; return(result); } static u16 read_offset_table(struct font_buffer *file) { u16 num_tables = be16(file->data + 4); return(num_tables); } static struct font_directory read_font_directory(struct font_buffer *file) { struct font_directory result = { 0 }; u16 table_count = read_offset_table(file); for (int i = 0; i < table_count; ++i) { u32 tag = be32(file->data + 12 + 16 * i + 0); u32 offset = be32(file->data + 12 + 16 * i + 8); u32 length = be32(file->data + 12 + 16 * i + 12); switch (tag) { case 0x636d6170: { /* cmap */ result.cmap_offset = offset; break; } case 0x63767420: { /* cvt */ result.cvt_offset = offset; result.cvt_size = length; break; } case 0x70726570: { /* prep */ result.prep_offset = offset; result.prep_size = length; break; } case 0x6670676d: { /* fpgm */ result.fpgm_offset = offset; result.fpgm_size = length; break; } case 0x6c6f6361: { /* loca */ result.loca_offset = offset; break; } case 0x68656164: { /* head */ result.head_offset = offset; break; } case 0x676c7966: { /* glyf */ result.glyf_offset = offset; break; } case 0x686d7478: { /* hmtx */ result.hmtx_offset = offset; break; } case 0x6d617870: { /* maxp */ result.maxp_offset = offset; break; } case 0x68686561: { /* hhea */ result.hhea_offset = offset; break; } case 0x706f7374: { /* post */ result.post_offset = offset; } } } return(result); } static void read_head(struct font_directory font_dir, struct ttf_font *font) { font->head.units_per_em = be16(font->file.data + font_dir.head_offset + 18); font->head.itl_format = be16(font->file.data + font_dir.head_offset + 50); } static void read_hhea(struct font_directory font_dir, struct ttf_font *font) { font->hhea.ascent = be16s(font->file.data + font_dir.hhea_offset + 4); font->hhea.descent = be16s(font->file.data + font_dir.hhea_offset + 6); font->hhea.line_gap = be16s(font->file.data + font_dir.hhea_offset + 10); font->hhea.max_advance = be16(font->file.data + font_dir.hhea_offset + 14); } static void read_cmap(struct font_directory font_dir, struct ttf_font *font) { font->file.offset = font_dir.cmap_offset + 2; u16 num_tables = read_be16(&font->file); for (int st = 0; st < num_tables; ++st) { u16 platform_id = read_be16(&font->file); u16 platform_specific_id = read_be16(&font->file); u32 subtable_offset = read_be32(&font->file); if ((platform_id == 0 && (platform_specific_id == 3 || platform_specific_id == 4)) || (platform_id == 3 && ((platform_specific_id == 1 || platform_specific_id == 10)))) { font->cmap_format = be16(font->file.data + font_dir.cmap_offset + subtable_offset); font->dir.cmap_offset = font_dir.cmap_offset + subtable_offset; } } } static void read_maxp(struct font_directory font_dir, struct ttf_font *font) { font->maxp.max_component_points = be16(font->file.data + font_dir.maxp_offset + 10); font->maxp.max_component_contours = be16(font->file.data + font_dir.maxp_offset + 12); font->maxp.max_storage = be16(font->file.data + font_dir.maxp_offset + 18); font->maxp.max_fdefs = be16(font->file.data + font_dir.maxp_offset + 20); } static void read_fpgm(struct ttf_font *font, struct font_buffer *font_file, struct font_directory font_dir, struct ttf_graphics_state *state) { iterate_instructions(font, font_file, font_dir.fpgm_offset, font_dir.fpgm_size, state); } static void read_cvt(struct ttf_font *font, struct font_buffer *font_file, struct font_directory font_dir, struct ttf_graphics_state *state) { iterate_instructions(font, font_file, font_dir.prep_offset, font_dir.prep_size, state); } static void read_post(struct font_directory font_dir, struct ttf_font *font) { if (font_dir.post_offset) { font->is_monospace = be32(font->file.data + font_dir.post_offset + 12); } else { printf("Warning: font file doesn't have a 'post' table. Assuming font is proportional\n"); font->is_monospace = 0; } } static struct ttf_font parse_ttf_file(char *filename) { struct ttf_font result = { 0 }; struct font_buffer font_file = read_file(filename); struct font_directory font_dir = read_font_directory(&font_file); result.file = font_file; result.dir = font_dir; result.dir.glyf_offset = font_dir.glyf_offset; result.dir.loca_offset = font_dir.loca_offset; result.dir.hmtx_offset = font_dir.hmtx_offset; result.cvt = (s16 *) (font_file.data + font_dir.cvt_offset); read_head(font_dir, &result); read_post(font_dir, &result); read_maxp(font_dir, &result); read_cmap(font_dir, &result); read_hhea(font_dir, &result); if (result.maxp.max_fdefs) { result.functions = malloc(result.maxp.max_fdefs * sizeof(struct ttf_function)); } if (result.maxp.max_storage) { result.storage = malloc(result.maxp.max_storage * sizeof(struct ttf_function)); } //struct ttf_graphics_state fpgm_state = default_graphics_state(); //struct ttf_graphics_state prep_state = default_graphics_state(); //read_fpgm(&result, &font_file, font_dir, &fpgm_state); // the font program //printf("------------- FPGM END -------------\n"); //read_cvt(&result, &font_file, font_dir, &prep_state); // the cvt program //printf("------------- CVT END -------------\n"); return(result); }