/* Copyright (C) 1999, 2000 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gdevpsft.c,v 1.8.2.1.2.1 2003/01/17 00:49:01 giles Exp $ */ /* Write an embedded TrueType font */ #include "memory_.h" #include /* for qsort */ #include "gx.h" #include "gserrors.h" #include "gsmatrix.h" #include "gsutil.h" #include "gxfcid.h" #include "gxfont.h" #include "gxfont42.h" #include "gxttf.h" #include "stream.h" #include "spprint.h" #include "gdevpsf.h" #define MAX_COMPOSITE_PIECES 3 /* adhoc */ /* ---------------- Utilities ---------------- */ #define ACCESS(base, length, vptr)\ BEGIN\ code = string_proc(pfont, (ulong)(base), length, &vptr);\ if (code < 0) return code;\ END /* Pad to a multiple of 4 bytes. */ private void put_pad(stream *s, uint length) { static const byte pad_to_4[3] = {0, 0, 0}; stream_write(s, pad_to_4, (uint)(-length & 3)); } /* Put short and long values on a stream. */ private void put_ushort(stream *s, uint v) { stream_putc(s, (byte)(v >> 8)); stream_putc(s, (byte)v); } private void put_ulong(stream *s, ulong v) { put_ushort(s, (uint)(v >> 16)); put_ushort(s, (uint)v); } private void put_loca(stream *s, ulong offset, int indexToLocFormat) { if (indexToLocFormat) put_ulong(s, offset); else put_ushort(s, (uint)(offset >> 1)); } /* Get or put 2- or 4-byte quantities from/into a table. */ #define U8(p) ((uint)((p)[0])) #define S8(p) (int)((U8(p) ^ 0x80) - 0x80) #define U16(p) (((uint)((p)[0]) << 8) + (p)[1]) #define S16(p) (int)((U16(p) ^ 0x8000) - 0x8000) #define u32(p) get_u32_msb(p) private void put_u16(byte *p, uint v) { p[0] = (byte)(v >> 8); p[1] = (byte)v; } private void put_u32(byte *p, ulong v) { p[0] = (byte)(v >> 24); p[1] = (byte)(v >> 16); p[2] = (byte)(v >> 8); p[3] = (byte)v; } private ulong put_table(byte tab[16], const char *tname, ulong checksum, ulong offset, uint length) { memcpy(tab, (const byte *)tname, 4); put_u32(tab + 4, checksum); put_u32(tab + 8, offset + 0x40000000); put_u32(tab + 12, (ulong)length); return offset + round_up(length, 4); } /* Write one range of a TrueType font. */ private int write_range(stream *s, gs_font_type42 *pfont, ulong start, uint length) { ulong base = start; ulong limit = base + length; int (*string_proc)(P4(gs_font_type42 *, ulong, uint, const byte **)) = pfont->data.string_proc; if_debug3('l', "[l]write_range pos = %ld, start = %lu, length = %u\n", stell(s), start, length); while (base < limit) { uint size = limit - base; const byte *ptr; int code; /* Write the largest block we can access consecutively. */ while ((code = string_proc(pfont, base, size, &ptr)) < 0) { if (size <= 1) return code; size >>= 1; } stream_write(s, ptr, size); base += size; } return 0; } /* * Determine the Macintosh glyph number for a given character, if any. * If no glyph can be found, return -1 and store the name in *pstr. */ private int mac_glyph_index(gs_font *font, int ch, gs_const_string *pstr) { gs_glyph glyph = font->procs.encode_char(font, (gs_char)ch, GLYPH_SPACE_NAME); if (glyph == gs_no_glyph) return 0; /* .notdef */ pstr->data = (const byte *) font->procs.callbacks.glyph_name(glyph, &pstr->size); if (glyph < gs_min_cid_glyph) { gs_char mac_char; gs_glyph mac_glyph; gs_const_string mstr; /* Look (not very hard) for a match in the Mac glyph space. */ if (ch >= 32 && ch <= 126) mac_char = ch - 29; else if (ch >= 128 && ch <= 255) mac_char = ch - 30; else return -1; mac_glyph = font->procs.callbacks.known_encode(mac_char, ENCODING_INDEX_MACGLYPH); if (mac_glyph == gs_no_glyph) return -1; mstr.data =(const byte *) font->procs.callbacks.glyph_name(mac_glyph, &mstr.size); if (!bytes_compare(pstr->data, pstr->size, mstr.data, mstr.size)) return (int)mac_char; } return -1; } /* ---------------- Individual tables ---------------- */ /* ------ cmap ------ */ /* Write a generated cmap table. */ static const byte cmap_initial_0[] = { 0, 0, /* table version # = 0 */ 0, 2, /* # of encoding tables = 2 */ /* First table, Macintosh */ 0, 1, /* platform ID = Macintosh */ 0, 0, /* platform encoding ID = ??? */ 0, 0, 0, 4+8+8, /* offset to table start */ /* Second table, Windows */ 0, 3, /* platform ID = Microsoft */ 0, 0, /* platform encoding ID = unknown */ 0, 0, 1, 4+8+8+6, /* offset to table start */ /* Start of Macintosh format 0 table */ 0, 0, /* format = 0, byte encoding table */ 1, 6, /* length */ 0, 0 /* version number */ }; static const byte cmap_initial_6[] = { 0, 0, /* table version # = 0 */ 0, 2, /* # of encoding tables = 2 */ /* First table, Macintosh */ 0, 1, /* platform ID = Macintosh */ 0, 0, /* platform encoding ID = ??? */ 0, 0, 0, 4+8+8, /* offset to table start */ /* Second table, Windows */ 0, 3, /* platform ID = Microsoft */ 0, 0, /* platform encoding ID = unknown */ 0, 0, 0, 4+8+8+10, /* offset to table start */ /* *VARIABLE*, add 2 x # of entries */ /* Start of Macintosh format 6 table */ 0, 6, /* format = 6, trimmed table mapping */ 0, 10, /* length *VARIABLE*, add 2 x # of entries */ 0, 0, /* version number */ 0, 0, /* first character code */ 0, 0 /* # of entries *VARIABLE* */ }; static const byte cmap_initial_4[] = { 0, 0, /* table version # = 0 */ 0, 1, /* # of encoding tables = 2 */ /* Single table, Windows */ 0, 3, /* platform ID = Microsoft */ 0, 0, /* platform encoding ID = unknown */ 0, 0, 0, 4+8 /* offset to table start */ }; static const byte cmap_sub_initial[] = { 0, 4, /* format = 4, segment mapping */ 0, 32, /* length ** VARIABLE, add 2 x # of glyphs ** */ 0, 0, /* version # */ 0, 4, /* 2 x segCount */ 0, 4, /* searchRange = 2 x 2 ^ floor(log2(segCount)) */ 0, 1, /* floor(log2(segCount)) */ 0, 0, /* 2 x segCount - searchRange */ 0, 0, /* endCount[0] **VARIABLE** */ 255, 255, /* endCount[1] */ 0, 0, /* reservedPad */ 0, 0, /* startCount[0] **VARIABLE** */ 255, 255, /* startCount[1] */ 0, 0, /* idDelta[0] */ 0, 1, /* idDelta[1] */ 0, 4, /* idRangeOffset[0] */ 0, 0 /* idRangeOffset[1] */ }; private void write_cmap(stream *s, gs_font *font, uint first_code, int num_glyphs, gs_glyph max_glyph, int options, uint cmap_length) { byte cmap_sub[sizeof(cmap_sub_initial)]; byte entries[256 * 2]; int first_entry = 0, end_entry = num_glyphs; bool can_use_trimmed = !(options & WRITE_TRUETYPE_NO_TRIMMED_TABLE); uint merge = 0; uint num_entries; int i; /* Collect the table entries. */ for (i = 0; i < num_glyphs; ++i) { gs_glyph glyph = font->procs.encode_char(font, (gs_char)i, GLYPH_SPACE_INDEX); uint glyph_index; if (glyph == gs_no_glyph || glyph < gs_min_cid_glyph || glyph > max_glyph ) glyph = gs_min_cid_glyph; glyph_index = (uint)(glyph - gs_min_cid_glyph); merge |= glyph_index; put_u16(entries + 2 * i, glyph_index); } while (end_entry > first_entry && !U16(entries + 2 * end_entry - 2)) --end_entry; while (first_entry < end_entry && !U16(entries + 2 * first_entry)) ++first_entry; num_entries = end_entry - first_entry; /* Write the table header and Macintosh sub-table (if any). */ if (merge == (byte)merge && (num_entries <= 127 || !can_use_trimmed)) { /* Use byte encoding format. */ memset(entries + 2 * num_glyphs, 0, sizeof(entries) - 2 * num_glyphs); stream_write(s, cmap_initial_0, sizeof(cmap_initial_0)); for (i = 0; i <= 0xff; ++i) sputc(s, (byte)entries[2 * i + 1]); } else if (can_use_trimmed) { /* Use trimmed table format. */ byte cmap_data[sizeof(cmap_initial_6)]; memcpy(cmap_data, cmap_initial_6, sizeof(cmap_initial_6)); put_u16(cmap_data + 18, U16(cmap_data + 18) + num_entries * 2); /* offset */ put_u16(cmap_data + 22, U16(cmap_data + 22) + num_entries * 2); /* length */ put_u16(cmap_data + 26, first_code + first_entry); put_u16(cmap_data + 28, num_entries); stream_write(s, cmap_data, sizeof(cmap_data)); stream_write(s, entries + first_entry * 2, num_entries * 2); } else { /* * Punt. Acrobat Reader 3 can't handle any other Mac table format. * (AR3 for Linux doesn't seem to be able to handle Windows format, * either, but maybe AR3 for Windows can.) */ stream_write(s, cmap_initial_4, sizeof(cmap_initial_4)); } /* Write the Windows sub-table. */ memcpy(cmap_sub, cmap_sub_initial, sizeof(cmap_sub_initial)); put_u16(cmap_sub + 2, U16(cmap_sub + 2) + num_entries * 2); /* length */ put_u16(cmap_sub + 14, first_code + end_entry - 1); /* endCount[0] */ put_u16(cmap_sub + 20, first_code + first_entry); /* startCount[0] */ stream_write(s, cmap_sub, sizeof(cmap_sub)); stream_write(s, entries + first_entry * 2, num_entries * 2); put_pad(s, cmap_length); } private uint size_cmap(gs_font *font, uint first_code, int num_glyphs, gs_glyph max_glyph, int options) { stream poss; swrite_position_only(&poss); write_cmap(&poss, font, first_code, num_glyphs, max_glyph, options, 0); return stell(&poss); } /* ------ name ------ */ /* Write a generated name table. */ static const byte name_initial[] = { 0, 0, /* format */ 0, 1, /* # of records = 1 */ 0, 18, /* start of string storage */ 0, 2, /* platform ID = ISO */ 0, 2, /* encoding ID = ISO 8859-1 */ 0, 0, /* language ID (none) */ 0, 6, /* name ID = PostScript name */ 0, 0, /* length *VARIABLE* */ 0, 0 /* start of string within string storage */ }; private uint size_name(const gs_const_string *font_name) { return sizeof(name_initial) + font_name->size; } private void write_name(stream *s, const gs_const_string *font_name) { byte name_bytes[sizeof(name_initial)]; memcpy(name_bytes, name_initial, sizeof(name_initial)); put_u16(name_bytes + 14, font_name->size); stream_write(s, name_bytes, sizeof(name_bytes)); stream_write(s, font_name->data, font_name->size); put_pad(s, size_name(font_name)); } /* ------ OS/2 ------ */ /* Write a generated OS/2 table. */ #define OS_2_LENGTH sizeof(ttf_OS_2_t) private void update_OS_2(ttf_OS_2_t *pos2, uint first_glyph, int num_glyphs) { put_u16(pos2->usFirstCharIndex, first_glyph); put_u16(pos2->usLastCharIndex, first_glyph + num_glyphs - 1); } private void write_OS_2(stream *s, gs_font *font, uint first_glyph, int num_glyphs) { ttf_OS_2_t os2; /* * We don't bother to set most of the fields. The really important * ones, which affect character mapping, are usFirst/LastCharIndex. * We also need to set usWeightClass and usWidthClass to avoid * crashing ttfdump. */ memset(&os2, 0, sizeof(os2)); put_u16(os2.version, 1); put_u16(os2.usWeightClass, 400); /* Normal */ put_u16(os2.usWidthClass, 5); /* Normal */ update_OS_2(&os2, first_glyph, num_glyphs); if (first_glyph >= 0xf000) os2.ulCodePageRanges[3] = 1; /* bit 31, symbolic */ stream_write(s, &os2, sizeof(os2)); put_pad(s, sizeof(os2)); } /* ------ post ------ */ /* Construct and then write the post table. */ typedef struct post_glyph_s { byte char_index; byte size; ushort glyph_index; } post_glyph_t; private int compare_post_glyphs(const void *pg1, const void *pg2) { gs_glyph g1 = ((const post_glyph_t *)pg1)->glyph_index, g2 = ((const post_glyph_t *)pg2)->glyph_index; return (g1 < g2 ? -1 : g1 > g2 ? 1 : 0); } typedef struct post_s { post_glyph_t glyphs[256 + 1]; int count, glyph_count; uint length; } post_t; /* * If necessary, compute the length of the post table. Note that we * only generate post entries for characters in the Encoding. */ private void compute_post(gs_font *font, post_t *post) { int i; for (i = 0, post->length = 32 + 2; i <= 255; ++i) { gs_const_string str; gs_glyph glyph = font->procs.encode_char(font, (gs_char)i, GLYPH_SPACE_INDEX); int mac_index = mac_glyph_index(font, i, &str); if (mac_index != 0) { post->glyphs[post->count].char_index = i; post->glyphs[post->count].size = (mac_index < 0 ? str.size + 1 : 0); post->glyphs[post->count].glyph_index = glyph - gs_min_cid_glyph; post->count++; } } if (post->count) { int j; qsort(post->glyphs, post->count, sizeof(post->glyphs[0]), compare_post_glyphs); /* Eliminate duplicate references to the same glyph. */ for (i = j = 0; i < post->count; ++i) { if (i == 0 || post->glyphs[i].glyph_index != post->glyphs[i - 1].glyph_index ) { post->length += post->glyphs[i].size; post->glyphs[j++] = post->glyphs[i]; } } post->count = j; post->glyph_count = post->glyphs[post->count - 1].glyph_index + 1; } post->length += post->glyph_count * 2; } /* Write the post table */ private void write_post(stream *s, gs_font *font, post_t *post) { byte post_initial[32 + 2]; uint name_index; uint glyph_index; int i; memset(post_initial, 0, 32); put_u32(post_initial, 0x00020000); put_u16(post_initial + 32, post->glyph_count); stream_write(s, post_initial, sizeof(post_initial)); /* Write the name index table. */ for (i = 0, name_index = 258, glyph_index = 0; i < post->count; ++i) { gs_const_string str; int ch = post->glyphs[i].char_index; int mac_index = mac_glyph_index(font, ch, &str); for (; glyph_index < post->glyphs[i].glyph_index; ++glyph_index) put_ushort(s, 0); glyph_index++; if (mac_index >= 0) put_ushort(s, mac_index); else { put_ushort(s, name_index); name_index++; } } /* Write the string names of the glyphs. */ for (i = 0; i < post->count; ++i) { gs_const_string str; int ch = post->glyphs[i].char_index; int mac_index = mac_glyph_index(font, ch, &str); if (mac_index < 0) { spputc(s, str.size); stream_write(s, str.data, str.size); } } put_pad(s, post->length); } /* ---------------- Main program ---------------- */ /* Write the definition of a TrueType font. */ private int compare_table_tags(const void *pt1, const void *pt2) { ulong t1 = u32(pt1), t2 = u32(pt2); return (t1 < t2 ? -1 : t1 > t2 ? 1 : 0); } private int psf_write_truetype_data(stream *s, gs_font_type42 *pfont, int options, psf_glyph_enum_t *penum, bool is_subset, const gs_const_string *alt_font_name) { gs_font *const font = (gs_font *)pfont; gs_const_string font_name; int (*string_proc)(P4(gs_font_type42 *, ulong, uint, const byte **)) = pfont->data.string_proc; const byte *OffsetTable; uint numTables_stored, numTables, numTables_out; #define MAX_NUM_TABLES 40 byte tables[MAX_NUM_TABLES * 16]; uint i; ulong offset; gs_glyph glyph, glyph_prev; ulong max_glyph; uint glyf_length, glyf_checksum = 0 /****** NO CHECKSUM ******/; uint loca_length, loca_checksum[2]; uint numGlyphs; /* original value from maxp */ byte head[56]; /* 0 mod 4 */ post_t post; ulong head_checksum, file_checksum = 0; int indexToLocFormat; bool writing_cid = (options & WRITE_TRUETYPE_CID) != 0, have_cmap = writing_cid, have_name = !(options & WRITE_TRUETYPE_NAME), have_OS_2 = writing_cid, have_post = writing_cid; uint cmap_length; ulong OS_2_start; uint OS_2_length = OS_2_LENGTH; int code; if (alt_font_name) font_name = *alt_font_name; else font_name.data = font->font_name.chars, font_name.size = font->font_name.size; /* * Count the number of tables, including the eventual glyf and loca * (which may not actually be present in the font), and copy the * table directory. */ ACCESS(0, 12, OffsetTable); numTables_stored = U16(OffsetTable + 4); for (i = numTables = 0; i < numTables_stored; ++i) { const byte *tab; const byte *data; ulong start; uint length; if (numTables == MAX_NUM_TABLES) return_error(gs_error_limitcheck); ACCESS(12 + i * 16, 16, tab); start = u32(tab + 8); length = u32(tab + 12); /* Copy the table data now, since another ACCESS may invalidate it. */ memcpy(&tables[numTables * 16], tab, 16); #define W(a,b,c,d)\ ( ((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) switch (W(tab[0], tab[1], tab[2], tab[3])) { case W('h','e','a','d'): if (length != 54) return_error(gs_error_invalidfont); ACCESS(start, length, data); memcpy(head, data, length); continue; case W('g','l','y','f'): /* synthesized */ case W('g','l','y','x'): /* Adobe bogus */ case W('l','o','c','a'): /* synthesized */ case W('l','o','c','x'): /* Adobe bogus */ case W('g','d','i','r'): /* Adobe marker */ continue; case W('c','m','a','p'): if (options & (WRITE_TRUETYPE_CMAP | WRITE_TRUETYPE_CID)) continue; have_cmap = true; break; case W('m','a','x','p'): ACCESS(start, length, data); numGlyphs = U16(data + 4); break; case W('n','a','m','e'): if (writing_cid) continue; have_name = true; break; case W('O','S','/','2'): if (writing_cid) continue; have_OS_2 = true; if (length > OS_2_LENGTH) return_error(gs_error_invalidfont); OS_2_start = start; OS_2_length = length; continue; case W('p','o','s','t'): have_post = true; break; case W('h','h','e','a'): case W('c','v','t',' '): case W('p','r','e','p'): case W('h','m','t','x'): case W('f','p','g','m'): case W('g','a','s','p'): case W('k','e','r','n'): case W('v','h','e','a'): case W('v','m','t','x'): break; /* always copy these if present */ default: if (writing_cid) continue; break; } #undef W numTables++; } /* * Enumerate the glyphs to get the size of glyf and loca, * and to compute the checksums for these tables. */ /****** NO CHECKSUMS YET ******/ for (max_glyph = 0, glyf_length = 0; (code = psf_enumerate_glyphs_next(penum, &glyph)) != 1; ) { uint glyph_index; gs_const_string glyph_string; if (glyph < gs_min_cid_glyph) return_error(gs_error_invalidfont); glyph_index = glyph - gs_min_cid_glyph; if_debug1('L', "[L]glyph_index %u\n", glyph_index); if ((code = pfont->data.get_outline(pfont, glyph_index, &glyph_string)) >= 0) { max_glyph = max(max_glyph, glyph_index); glyf_length += glyph_string.size; if_debug1('L', "[L] size %u\n", glyph_string.size); if (code > 0) gs_free_const_string(pfont->memory, glyph_string.data, glyph_string.size, "psf_write_truetype_data"); } } /* Acrobat Reader won't accept fonts with empty glyfs. */ if (glyf_length == 0) glyf_length = 1; if_debug2('l', "[l]max_glyph = %lu, glyf_length = %lu\n", (ulong)max_glyph, (ulong)glyf_length); /* * For subset fonts, we should trim the loca table so that it only * contains entries through max_glyph. Unfortunately, this would * require changing numGlyphs in maxp, which in turn would affect hdmx, * hhea, hmtx, vdmx, vhea, vmtx, and possibly other tables. This is way * more work than we want to do right now. */ /*loca_length = (max_glyph + 2) << 2;*/ loca_length = (numGlyphs + 1) << 2; indexToLocFormat = (glyf_length > 0x1fffc); if (!indexToLocFormat) loca_length >>= 1; /* * If necessary, compute the length of the post table. Note that we * only generate post entries for characters in the Encoding. */ if (!have_post) { memset(&post, 0, sizeof(post)); if (options & WRITE_TRUETYPE_POST) compute_post(font, &post); else post.length = 32; /* dummy table */ } /* Fix up the head table. */ memset(head + 8, 0, 4); head[51] = (byte)indexToLocFormat; memset(head + 54, 0, 2); for (head_checksum = 0, i = 0; i < 56; i += 4) head_checksum += u32(&head[i]); /* * Construct the table directory, except for glyf, loca, head, OS/2, * and, if necessary, generated cmap, name, and post tables. * Note that the existing directory is already sorted by tag. */ numTables_out = numTables + 3 /* head, glyf, loca */ + !writing_cid /* OS/2 */ + !have_cmap + !have_name + !have_post; if (numTables_out >= MAX_NUM_TABLES) return_error(gs_error_limitcheck); offset = 12 + numTables_out * 16; for (i = 0; i < numTables; ++i) { byte *tab = &tables[i * 16]; ulong length = u32(tab + 12); offset += round_up(length, 4); } /* Make the table directory entries for generated tables. */ { byte *tab = &tables[numTables * 16]; offset = put_table(tab, "glyf", glyf_checksum, offset, glyf_length); tab += 16; offset = put_table(tab, "loca", loca_checksum[indexToLocFormat], offset, loca_length); tab += 16; if (!have_cmap) { cmap_length = size_cmap(font, 0xf000, 256, gs_min_cid_glyph + max_glyph, options); offset = put_table(tab, "cmap", 0L /****** NO CHECKSUM ******/, offset, cmap_length); tab += 16; } if (!have_name) { offset = put_table(tab, "name", 0L /****** NO CHECKSUM ******/, offset, size_name(&font_name)); tab += 16; } if (!writing_cid) { offset = put_table(tab, "OS/2", 0L /****** NO CHECKSUM ******/, offset, OS_2_length); tab += 16; } if (!have_post) { offset = put_table(tab, "post", 0L /****** NO CHECKSUM ******/, offset, post.length); tab += 16; } /* * Note that the 'head' table must have length 54, even though * it occupies 56 bytes on the file. */ offset = put_table(tab, "head", head_checksum, offset, 54); tab += 16; } numTables = numTables_out; /* Write the font header. */ { static const byte version[4] = {0, 1, 0, 0}; stream_write(s, version, 4); } put_ushort(s, numTables); for (i = 0; 1 << i <= numTables; ++i) DO_NOTHING; --i; put_ushort(s, 16 << i); /* searchRange */ put_ushort(s, i); /* entrySelectors */ put_ushort(s, numTables * 16 - (16 << i)); /* rangeShift */ /* Write the table directory. */ qsort(tables, numTables, 16, compare_table_tags); offset = 12 + numTables * 16; for (i = 0; i < numTables; ++i) { const byte *tab = &tables[i * 16]; byte entry[16]; memcpy(entry, tab, 16); if (entry[8] < 0x40) { /* Not a generated table. */ uint length = u32(tab + 12); put_u32(entry + 8, offset); offset += round_up(length, 4); } else { entry[8] -= 0x40; } stream_write(s, entry, 16); } /* Write tables other than the ones we generate here. */ for (i = 0; i < numTables; ++i) { const byte *tab = &tables[i * 16]; if (tab[8] < 0x40) { ulong start = u32(tab + 8); uint length = u32(tab + 12); write_range(s, pfont, start, length); put_pad(s, length); } } /* Write glyf. */ if (is_subset) psf_enumerate_glyphs_reset(penum); else psf_enumerate_glyphs_begin(penum, font, NULL, max_glyph + 1, GLYPH_SPACE_INDEX); for (offset = 0; psf_enumerate_glyphs_next(penum, &glyph) != 1; ) { gs_const_string glyph_string; if ((code = pfont->data.get_outline(pfont, glyph - gs_min_cid_glyph, &glyph_string)) >= 0 ) { stream_write(s, glyph_string.data, glyph_string.size); offset += glyph_string.size; if_debug2('L', "[L]glyf index = %u, size = %u\n", i, glyph_string.size); if (code > 0) gs_free_const_string(pfont->memory, glyph_string.data, glyph_string.size, "psf_write_truetype_data"); } } if_debug1('l', "[l]glyf final offset = %lu\n", offset); /* Add a dummy byte if necessary to make glyf non-empty. */ while (offset < glyf_length) stream_putc(s, 0), ++offset; put_pad(s, (uint)offset); /* Write loca. */ psf_enumerate_glyphs_reset(penum); glyph_prev = gs_min_cid_glyph; for (offset = 0; psf_enumerate_glyphs_next(penum, &glyph) != 1; ) { gs_const_string glyph_string; for (; glyph_prev <= glyph; ++glyph_prev) put_loca(s, offset, indexToLocFormat); if ((code = pfont->data.get_outline(pfont, glyph - gs_min_cid_glyph, &glyph_string)) >= 0 ) { offset += glyph_string.size; if (code > 0) gs_free_const_string(pfont->memory, glyph_string.data, glyph_string.size, "psf_write_truetype_data"); } } /* Pad to numGlyphs + 1 entries (including the trailing entry). */ for (; glyph_prev <= gs_min_cid_glyph + numGlyphs; ++glyph_prev) put_loca(s, offset, indexToLocFormat); put_pad(s, loca_length); /* If necessary, write cmap, name, and OS/2. */ if (!have_cmap) write_cmap(s, font, 0xf000, 256, gs_min_cid_glyph + max_glyph, options, cmap_length); if (!have_name) write_name(s, &font_name); if (!have_OS_2) write_OS_2(s, font, 0xf000, 256); else if (!have_cmap) { /* * Adjust the first and last character indices in the OS/2 table * to reflect the values in the generated cmap. */ const byte *pos2; ttf_OS_2_t os2; ACCESS(OS_2_start, OS_2_length, pos2); memcpy(&os2, pos2, min(OS_2_length, sizeof(os2))); update_OS_2(&os2, 0xf000, 256); stream_write(s, &os2, OS_2_length); put_pad(s, OS_2_length); } else if (!writing_cid) { /* Just copy the existing OS/2 table. */ write_range(s, pfont, OS_2_start, OS_2_length); put_pad(s, OS_2_length); } /* If necessary, write post. */ if (!have_post) { if (options & WRITE_TRUETYPE_POST) write_post(s, font, &post); else { byte post_initial[32 + 2]; memset(post_initial, 0, 32); put_u32(post_initial, 0x00030000); stream_write(s, post_initial, 32); } } /* Write head. */ /****** CHECKSUM WAS NEVER COMPUTED ******/ /* * The following nonsense is to avoid warnings about the constant * 0xb1b0afbaL being "unsigned in ANSI C, signed with -traditional". */ #if ARCH_SIZEOF_LONG > ARCH_SIZEOF_INT # define HEAD_MAGIC 0xb1b0afbaL #else # define HEAD_MAGIC ((ulong)~0x4e4f5045) #endif put_u32(head + 8, HEAD_MAGIC - file_checksum); /* per spec */ #undef HEAD_MAGIC stream_write(s, head, 56); return 0; } /* Write a TrueType font. */ int psf_write_truetype_font(stream *s, gs_font_type42 *pfont, int options, gs_glyph *orig_subset_glyphs, uint orig_subset_size, const gs_const_string *alt_font_name) { gs_font *const font = (gs_font *)pfont; psf_glyph_enum_t genum; gs_glyph subset_data[256 * MAX_COMPOSITE_PIECES]; gs_glyph *subset_glyphs = orig_subset_glyphs; uint subset_size = orig_subset_size; /* Sort the subset glyphs, if any. */ if (subset_glyphs) { /* Add the component glyphs for composites. */ int code; memcpy(subset_data, orig_subset_glyphs, sizeof(gs_glyph) * subset_size); subset_glyphs = subset_data; code = psf_add_subset_pieces(subset_glyphs, &subset_size, countof(subset_data), countof(subset_data), font); if (code < 0) return code; subset_size = psf_sort_glyphs(subset_glyphs, subset_size); } psf_enumerate_glyphs_begin(&genum, font, subset_glyphs, (subset_glyphs ? subset_size : 0), GLYPH_SPACE_INDEX); return psf_write_truetype_data(s, pfont, options & ~WRITE_TRUETYPE_CID, &genum, subset_glyphs != 0, alt_font_name); } /* Write a CIDFontType 2 font. */ int psf_write_cid2_font(stream *s, gs_font_cid2 *pfont, int options, const byte *subset_bits, uint subset_size, const gs_const_string *alt_font_name) { gs_font *const font = (gs_font *)pfont; psf_glyph_enum_t genum; psf_enumerate_bits_begin(&genum, font, subset_bits, (subset_bits ? subset_size : 0), GLYPH_SPACE_INDEX); return psf_write_truetype_data(s, (gs_font_type42 *)font, WRITE_TRUETYPE_CID, &genum, subset_bits != 0, alt_font_name); }