diff options
Diffstat (limited to 'tools/tt2pcl.c')
-rw-r--r-- | tools/tt2pcl.c | 629 |
1 files changed, 629 insertions, 0 deletions
diff --git a/tools/tt2pcl.c b/tools/tt2pcl.c new file mode 100644 index 000000000..56583ddfc --- /dev/null +++ b/tools/tt2pcl.c @@ -0,0 +1,629 @@ +/* Convert a TrueType font to a downloadable pcl soft font. */ +#include <stdio.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <stdlib.h> + +unsigned long stdout_offset = 0; + +#define get_uint16(bptr)\ + (((bptr)[0] << 8) | (bptr)[1]) +#define get_int16(bptr)\ + (((int)get_uint16(bptr) ^ 0x8000) - 0x8000) + +int +pl_get_int16(const unsigned char *bptr) +{ return get_int16(bptr); +} + +unsigned int +pl_get_uint16(const unsigned char *bptr) +{ return get_uint16(bptr); +} + +long +pl_get_int32(const unsigned char *bptr) +{ return ((long)get_int16(bptr) << 16) | get_uint16(bptr + 2); +} + +unsigned long +pl_get_uint32(const unsigned char *bptr) +{ return ((unsigned long)get_uint16(bptr) << 16) | get_uint16(bptr + 2); +} + +typedef struct pcl_font_header_s { + unsigned short FontDescriptorSize; + unsigned char HeaderFormat; + unsigned char FontType; + unsigned char StyleMSB; + unsigned char Reserved; /* must be 0 */ + unsigned short BaselinePosition; + unsigned short CellWidth; + unsigned short CellHeight; + unsigned char Orientation; + unsigned char Spacing; + unsigned short SymbolSet; + unsigned short Pitch; + unsigned short Height; + unsigned short xHeight; + unsigned char WidthType; + unsigned char StyleLSB; + unsigned char StrokeWeight; + unsigned char TypefaceLSB; + unsigned char TypefaceMSB; + unsigned char SerifStyle; + unsigned char Quality; + unsigned char Placement; + unsigned char UnderlinePosition; + unsigned char UnderlineThickness; + unsigned short TextHeight; + unsigned short TextWidth; + unsigned short FirstCode; + unsigned short LastCode; + unsigned char PitchExtended; + unsigned char HeightExtended; + unsigned short CapHeight; + unsigned long FontNumber; + char FontName[16]; + unsigned short ScaleFactor; + unsigned short Master_Underline_Position; + unsigned short Master_Underline_Thickness; + unsigned char Font_Scaling_Technology; + unsigned char Variety; +} pcl_font_header_t; + +typedef struct pcl_segment_s { + unsigned short id; + unsigned short size; +} pcl_segment_t; + +#define SYMSET_NUM 1023 +#define SYMSET_LETTER 'Z' + +#define NUM_TABLES 8 +/* alphabetical */ +const char *tables[NUM_TABLES] = { + "cvt ", + "fpgm", + "gdir", + "head", + "hhea", + "hmtx", + "maxp", + "prep" +}; + + +typedef struct table_directory_s { + unsigned long sfnt_version; /* should be fixed 16.16 */ + unsigned short num_tables; + unsigned short searchRange; + unsigned short entrySelector; + unsigned short rangeShift; +} table_directory_t; + +typedef struct table_s { + unsigned long tag; + unsigned long checkSum; + unsigned long offset; + unsigned long length; +} table_t; + +static void +write_pcl_header() +{ + pcl_font_header_t hdr; + /* init header */ + memset(&hdr, 0, sizeof(hdr)); + /* truetype */ + hdr.HeaderFormat = 15; + /* unicode */ + //hdr.FontType = 11; + hdr.FontType = 2; + hdr.Spacing = 1; + hdr.TypefaceLSB = 100; /* don't know */ + // hdr.SymbolSet = htons((SYMSET_NUM * 32) + (SYMSET_LETTER - 64)); + hdr.FontDescriptorSize = htons(sizeof(pcl_font_header_t)); + hdr.Font_Scaling_Technology = 1; + hdr.Pitch = htons(500); /* arbitrary */ + hdr.CellHeight = htons(2048); /* don't know */ + hdr.CellWidth = htons(2048); /* don't know */ + hdr.FirstCode = htons(0); + hdr.LastCode = htons(255); + hdr.FontName[0] = 'f'; + stdout_offset += fwrite(&hdr, 1, sizeof(pcl_font_header_t), stdout); +} + +static int +log2(int n) +{ + int lg = 0; + + while (n != 1) { + n /= 2; + lg += 1; + } + return lg; +} + +static void +write_table_directory() +{ + table_directory_t td; + if ( NUM_TABLES & -NUM_TABLES != NUM_TABLES ) { + fprintf( stderr, "number of tables must be a power of 2"); + exit(EXIT_FAILURE); + } + fprintf(stderr, "Table Directory started at %d\n", stdout_offset); + td.sfnt_version = htonl(0x00010000); + td.num_tables = htons(NUM_TABLES); + td.searchRange = htons(NUM_TABLES * 16); + td.entrySelector = htons(log2(NUM_TABLES)); + td.rangeShift = htons(0); /* since power of 2 */ + + stdout_offset += fwrite(&td, 1, sizeof(table_directory_t), stdout); +} + +static int +get_tt_buffer(char *filename, unsigned char **pptt_font_data, unsigned long *size) +{ + + unsigned long len; + FILE *fp; + + fp = fopen(filename, "r"); + if ( fp == NULL ) { + fprintf(stderr, "file %s not found\n", filename); + return -1; + } + fseek(fp, 0L, SEEK_END); + len = ftell(fp); + rewind(fp); + *size = len; + *pptt_font_data = (unsigned char *)malloc(*size); + if ( *pptt_font_data == 0 ) { + fprintf(stderr, "failed to allocate memory to buffer file %s\n", filename); + fclose(fp); + return -1; + } + fread(*pptt_font_data, 1, len, fp); + return 0; +} + +/* offset of table "table_name" */ +static unsigned long +find_table(const unsigned char *ptt_data, const char *table_name, unsigned long *table_size) +{ + unsigned int num_tables; + const unsigned char *ptt_table_directory; + int i; + num_tables = pl_get_uint16(ptt_data + 4); + ptt_table_directory = ptt_data + 12; + // fprintf(stderr, "number of tables %d ", num_tables); + for ( i = 0; i < num_tables; i++ ) { + const unsigned char *tab = ptt_table_directory + i * 16; + // fprintf(stderr, "found table %c%c%c%c table size=%d\n", tab[0], tab[1], tab[2], tab[3], pl_get_uint32(tab + 12)); + if ( !memcmp(table_name, tab, 4) ) { + *table_size = pl_get_uint32(tab + 12); + return pl_get_uint32(tab + 8); + } + } + *table_size = 0; + return 0; +} + + +static int +write_table_entrys(unsigned char *ptt_data) +{ + unsigned long current_offset = sizeof(table_directory_t) + + sizeof(table_t) * NUM_TABLES; + int i; + for ( i = 0; i < NUM_TABLES; i++ ) { + table_t t; + unsigned long length; + unsigned long offset; + unsigned long *ptable = (unsigned long *)tables[i]; + t.tag = *ptable; + t.offset = htonl(current_offset); + offset = find_table(ptt_data, tables[i], &length); + if (offset < 0) { + fprintf(stderr, "table %s not found\n", tables[i]); + exit(EXIT_FAILURE); + } + t.length = htonl(length); + { + unsigned long sum = 0; + int k; + for ( k = 0; k < length; k++ ) + sum += ptt_data[k + offset]; + t.checkSum = sum; + } + fprintf(stderr, "writing table entry %c%c%c%c at %d\n", + tables[i][0], tables[i][1], tables[i][2], tables[i][3], stdout_offset); + fprintf(stderr, "the table should end up at offset %d with length %d \n", current_offset, length); + current_offset += length; + stdout_offset += fwrite(&t, 1, sizeof(t), stdout); + } +} + +void +write_tables(unsigned char *ptt_data) +{ + int i; + unsigned long offset; + for ( i = 0; i < NUM_TABLES; i++ ) { + unsigned long length; + + offset = find_table(ptt_data, tables[i], &length); + if (offset < 0) { + fprintf(stderr, "table %s not found\n", tables[i]); + exit(EXIT_FAILURE); + } + fprintf(stderr, "writing table %c%c%c%c at %d\n", + tables[i][0], tables[i][1], tables[i][2], tables[i][3], stdout_offset); + stdout_offset += fwrite(ptt_data+offset, 1, length, stdout); + } +} + +static unsigned long +total_segments_size(unsigned char *ptt_data) +{ + int i; + unsigned long offset; + unsigned long total = 0; + for ( i = 0; i < NUM_TABLES; i++ ) { + unsigned long this_table; + offset = find_table(ptt_data, tables[i], &this_table); + if ( offset < 0 ) { + fprintf(stderr, "table %s not found\n", tables[i]); + exit(EXIT_FAILURE); + } + total += this_table; + } + return total; +} + +static void +write_GT_segment(unsigned char *ptt_data) +{ + + pcl_segment_t GT_seg; + GT_seg.id = htons(18260); /* GT */ + GT_seg.size = htons(sizeof(table_directory_t) + + sizeof(table_t) * NUM_TABLES + + total_segments_size(ptt_data)); + fprintf(stderr, "GT seg started at %d size = %d\n", stdout_offset, sizeof(table_directory_t) + + sizeof(table_t) * NUM_TABLES + total_segments_size(ptt_data)); + stdout_offset += fwrite(>_seg, 1, sizeof(pcl_segment_t), stdout); + write_table_directory(); + write_table_entrys(ptt_data); + write_tables(ptt_data); + +} + +static void +write_NULL_segment() +{ + pcl_segment_t NULL_seg; + NULL_seg.id = htons(0xffff); + NULL_seg.size = htons(0); + fprintf( stderr, "NULL segment started at %d\n", stdout_offset); + stdout_offset += fwrite(&NULL_seg, 1, sizeof(pcl_segment_t), stdout); +} + + +static void +set_id(char *fontname) +{ + stdout_offset += fprintf(stdout, "\033*c1D"); + /* nb alphanumeric is broken in pcl */ + // fprintf(stdout, "\033&n%dW%c%s", strlen(fontname) + 1, 0 /* operation */, fontname); +} + +typedef struct pcl_symbol_set_s { + unsigned short HeaderSize; + unsigned short EncodedSymbolSetDesignator; + unsigned char Format; + unsigned char SymbolSetType; + unsigned short FirstCode; + unsigned short LastCode; + unsigned char CharacterRequirements[8]; +} pcl_symbol_set_t; + +static void +write_mac_glyph_pcl_symbol_set(unsigned char *glyph_table) +{ + int i; + pcl_symbol_set_t sym_hdr; + short symbol_set = (SYMSET_NUM * 32) + (SYMSET_LETTER - 64); + sym_hdr.HeaderSize = htons(18); + sym_hdr.EncodedSymbolSetDesignator = htons(symbol_set); + sym_hdr.Format = 3; /* unicode */ + sym_hdr.SymbolSetType = 2; /* 0 - 255 are printable */ + sym_hdr.FirstCode = htons(0); + sym_hdr.LastCode = htons(255); + memset(sym_hdr.CharacterRequirements, 0, sizeof(sym_hdr.CharacterRequirements)); + sym_hdr.CharacterRequirements[7] = 1; + stdout_offset += fprintf(stdout, "\033*c%dR", symbol_set); + stdout_offset += fprintf(stdout, "\033(f%dW", sizeof(sym_hdr) + 256 * 2); + stdout_offset += fwrite(&sym_hdr, 1, sizeof(sym_hdr), stdout); + for (i = 0; i < 256; i++ ) { + unsigned short glyph = glyph_table[i]; + + // NB identity. + unsigned short pcl_glyph = glyph == 0 ? htons(0xffff) : htons(i); + // unsigned short pcl_glyph = glyph == 0 ? htons(0xffff) : htons(glyph); + + stdout_offset += fwrite(&pcl_glyph, 1, sizeof(pcl_glyph), stdout); + } +} + +static unsigned char * +get_mac_glyph_index(unsigned char *ptt_data) +{ + unsigned long length, cmap_offset; + unsigned short table_version; + unsigned short n, i; + cmap_offset = find_table(ptt_data, "cmap", &length); + if ( cmap_offset <= 0 ) { + fprintf(stderr, "could not find cmap\n"); + exit(EXIT_FAILURE); + } + + table_version = pl_get_uint16(ptt_data + cmap_offset); + n = pl_get_uint16(ptt_data + cmap_offset + 2); + for ( i = 0; i < n; i++ ) { + unsigned char *enc_tab = + ptt_data + cmap_offset + 4 + i * 8; + unsigned short platform_id, platform_enconding; + unsigned long offset_subtable; + platform_id = pl_get_uint16(enc_tab); + platform_enconding = pl_get_uint16(enc_tab + 2); + offset_subtable = pl_get_uint32(enc_tab + 4); + fprintf(stderr, "found cmap platform id=%d, platform encoding=%d\n", + platform_id, platform_enconding); + { + unsigned char *subtable = ptt_data + cmap_offset + offset_subtable; + unsigned short format, length, version; + format = pl_get_uint16(subtable); + length = pl_get_uint16(subtable + 2); + version = pl_get_uint16(subtable + 4); + if ( (platform_id == 3) && (platform_enconding == 1) && (format == 4) ) + return subtable; + } + } + return 0; +} + +void +write_symbol_set(unsigned char *ptt_data) +{ + // unsigned char *table; + // if ( (table = get_mac_glyph_index(ptt_data)) == 0 ) { + // fprintf(stderr, "mac table not found\n"); + // exit(EXIT_FAILURE); + // } + // write_mac_glyph_pcl_symbol_set(table); +} + +typedef struct character_descriptor_s { + unsigned char format; + unsigned char continuation; + unsigned char descriptor_size; + unsigned char class; + unsigned char addititonal_data[2]; + unsigned short character_data_size; + unsigned short glyph_id; +} character_descriptor_t; + +static unsigned char * +find_glyph_data(const glyph_index, const unsigned char *ptt_data, unsigned long *glyph_length) +{ + unsigned long length; + unsigned long offset; + unsigned char indexToLocFormat; + const unsigned char *loca_table, *glyf_table; + offset = find_table(ptt_data, "head", &length); + if ( offset <= 0 ) { + fprintf(stderr, "could not find head table\n"); + exit(EXIT_FAILURE); + } + indexToLocFormat = pl_get_uint16(ptt_data + offset + 50); + fprintf(stderr, "glyph index %d uses %s\n", glyph_index, indexToLocFormat == 0 ? "short format" : "long format"); + offset = find_table(ptt_data, "loca", &length); + if ( offset <= 0 ) { + fprintf(stderr, "could not find loca table\n"); + exit(EXIT_FAILURE); + } + loca_table = ptt_data + offset; + offset = find_table(ptt_data, "glyf", &length); + if ( offset <= 0 ) { + fprintf(stderr, "could not find glyf table\n"); + exit(EXIT_FAILURE); + } + glyf_table = ptt_data + offset; + if ( indexToLocFormat == 0 ) { + unsigned char *pthis_glyph = loca_table + glyph_index * 2; + unsigned char *pnext_glyph = pthis_glyph + 2; + *glyph_length = pl_get_uint16(pnext_glyph) * 2 - pl_get_uint16(pthis_glyph) * 2; + return glyf_table + pl_get_uint16(pthis_glyph) * 2; + } else { + unsigned char *pthis_glyph = loca_table + glyph_index * 4; + unsigned char *pnext_glyph = pthis_glyph + 4; + *glyph_length = pl_get_uint32(pnext_glyph) - pl_get_uint32(pthis_glyph); + return glyf_table + pl_get_uint32(pthis_glyph); + } +} + +void +write_CC_segment() +{ + pcl_segment_t CC_seg; + unsigned char CharacterComplement[8]; + memset(CharacterComplement, 0, 8); + CharacterComplement[7] = 0xfe; + CC_seg.id = htons(17219); + CC_seg.size = htons(8); + stdout_offset += fwrite(&CC_seg, 1, sizeof(pcl_segment_t), stdout); + stdout_offset += fwrite(&CharacterComplement, 1, 8, stdout); +} + + +static void +write_character_descriptor(unsigned char *ptt_data) +{ + unsigned char *table; + character_descriptor_t tt_char_des; + unsigned short segment_count, seg; + unsigned char *start_charp; + unsigned char *end_charp; + unsigned char *id_deltap; + unsigned char *id_rangeoffsetp; + unsigned char *glyph_id_arrayp; + if ( (table = get_mac_glyph_index(ptt_data)) == 0 ) { + fprintf(stderr, "mac table not found\n"); + exit(EXIT_FAILURE); + } + + segment_count = pl_get_uint16(table + 6) / 2; + fprintf(stderr, "number of segments %d\n", segment_count); + end_charp = table + 14; + start_charp = end_charp + (segment_count * 2) + 2 /* reservedpad */; + id_deltap = start_charp + (segment_count * 2); + id_rangeoffsetp = id_deltap + (segment_count * 2); + glyph_id_arrayp = id_rangeoffsetp + (segment_count * 2); + for (seg = 0; seg < segment_count; seg++) { + unsigned short first_char = pl_get_uint16(start_charp + 2 * seg); + unsigned short last_char = pl_get_uint16(end_charp + 2 * seg); + unsigned short this_char; + int i; + + // NB doesn't handle 0xffff terminating a segment with valid + // characters in it - don't think this happens in practice. + if ( last_char == 0xffff ) + break; + fprintf(stderr, "segment=%d, first char=%d, last char=%d\n", seg, first_char, last_char); + for (i = 0, this_char = first_char; this_char <= last_char; i++, this_char++) { + unsigned long glyph_length; + unsigned char *glyph_data; + unsigned short deltad_char = this_char + pl_get_uint16(id_deltap + seg * 2); + unsigned short range_offset = pl_get_uint16(id_rangeoffsetp + seg * 2); + unsigned short glyph; + if ( range_offset == 0 ) + glyph = deltad_char; + else + glyph = pl_get_uint16(id_rangeoffsetp + seg * 2 + range_offset + ((this_char - first_char) * 2)); + fprintf(stderr, "this char=%d, char with delta=%d, range offset=%d glyph index %d\n", this_char, deltad_char, range_offset, glyph); + glyph_data = find_glyph_data(glyph, ptt_data, &glyph_length); + if (glyph_length == 0) + continue; + fprintf(stderr, "contours=%d, minx=%d, miny=%d, maxx=%d, maxy=%d\n", + pl_get_uint16(glyph_data), (short)pl_get_uint16(glyph_data+2), + (short)pl_get_uint16(glyph_data+4), (short)pl_get_uint16(glyph_data+6), + (short)pl_get_uint16(glyph_data+8)); + stdout_offset += fprintf(stdout, "\033*c%dE", this_char); + stdout_offset += fprintf(stdout, "\033(s%dW", sizeof(tt_char_des) + glyph_length); + tt_char_des.format = 15; + tt_char_des.continuation = 0; + tt_char_des.descriptor_size = 4; + tt_char_des.class = 15; + tt_char_des.addititonal_data[0] = 0; tt_char_des.addititonal_data[1] = 0; + tt_char_des.character_data_size = tt_char_des.descriptor_size + glyph_length; + tt_char_des.glyph_id = htons(glyph); + { + unsigned long sum = 0; + int i; + for ( i = 0; i < glyph_length; i++ ) + sum += glyph_data[i]; + + fprintf(stderr, "checksum=%d\n", sum); + } + stdout_offset += fwrite(&tt_char_des, 1, sizeof(character_descriptor_t), stdout); + stdout_offset += fwrite(glyph_data, 1, glyph_length, stdout); + } + } +} + +void +write_test(unsigned char *ptt_data, char *fontname) +{ + unsigned char *table; + character_descriptor_t tt_char_des; + unsigned short segment_count, seg; + unsigned char *start_charp; + unsigned char *end_charp; + unsigned char *id_deltap; + unsigned char *id_rangeoffsetp; + unsigned char *glyph_id_arrayp; + int i; + + if ( (table = get_mac_glyph_index(ptt_data)) == 0 ) { + fprintf(stderr, "mac table not found\n"); + exit(EXIT_FAILURE); + } + stdout_offset += fprintf(stdout, "\033(s1P\033(s14V\033(1X"); + segment_count = pl_get_uint16(table + 6) / 2; + end_charp = table + 14; + start_charp = end_charp + (segment_count * 2) + 2 /* reservedpad */; + id_deltap = start_charp + (segment_count * 2); + id_rangeoffsetp = id_deltap + (segment_count * 2); + glyph_id_arrayp = id_rangeoffsetp + (segment_count * 2); + i = 0; + for (seg = 0; seg < segment_count; seg++) { + unsigned short first_char = pl_get_uint16(start_charp + 2 * seg); + unsigned short last_char = pl_get_uint16(end_charp + 2 * seg); + unsigned short this_char; + + + // NB doesn't handle 0xffff terminating a segment with valid + // characters in it - don't think this happens in practice. + if ( last_char == 0xffff ) + break; + for (this_char = first_char; this_char <= last_char; this_char++) { + if (this_char & 0xff00) continue; + if ( (i % 39 == 0) && (i != 0) ) + stdout_offset += fprintf(stdout, "\r\n"); + i++; + stdout_offset += fprintf(stdout, "\033&p1X%c", this_char & 0xff); + } + } +} + +int +main(int argc, char **argv) +{ + unsigned char *ptt_data; + unsigned long tt_data_size; + + /* NB - no parameter checking */ + set_id(argv[1]); + if ( get_tt_buffer(argv[1], &ptt_data, &tt_data_size) < 0 ) + return -1; + { + unsigned long total_bytes = + sizeof(pcl_font_header_t) + + sizeof(table_directory_t) + + sizeof(table_t) * NUM_TABLES + + total_segments_size(ptt_data) + + 3 * sizeof(pcl_segment_t) + /* GT segment + NULL segment + CC segiment */ + 8 + /* character complement */ + 2 /* reserved + checksum */; + stdout_offset += fprintf(stdout, "\033)s%dW", total_bytes); + } + write_pcl_header(); + write_CC_segment(); + write_GT_segment(ptt_data); + write_NULL_segment(); + { + unsigned char reserved_and_checksum[2]; + reserved_and_checksum[0] = 0; + reserved_and_checksum[1] = 0; + stdout_offset += fwrite(&reserved_and_checksum, 1, 2, stdout); + } + + write_symbol_set(ptt_data); + write_character_descriptor(ptt_data); + write_test(ptt_data, argv[1]); + return 0; +} |