xref: /plan9/sys/src/cmd/gs/src/gdevpsft.c (revision 409b3aff3015608d158d294a437acca386aba474)
1593dc095SDavid du Colombier /* Copyright (C) 1999, 2000, 2002 Aladdin Enterprises.  All rights reserved.
23ff48bf5SDavid du Colombier 
3593dc095SDavid du Colombier   This software is provided AS-IS with no warranty, either express or
4593dc095SDavid du Colombier   implied.
53ff48bf5SDavid du Colombier 
6593dc095SDavid du Colombier   This software is distributed under license and may not be copied,
7593dc095SDavid du Colombier   modified or distributed except as expressly authorized under the terms
8593dc095SDavid du Colombier   of the license contained in the file LICENSE in this distribution.
93ff48bf5SDavid du Colombier 
10593dc095SDavid du Colombier   For more information about licensing, please refer to
11593dc095SDavid du Colombier   http://www.ghostscript.com/licensing/. For information on
12593dc095SDavid du Colombier   commercial licensing, go to http://www.artifex.com/licensing/ or
13593dc095SDavid du Colombier   contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14593dc095SDavid du Colombier   San Rafael, CA  94903, U.S.A., +1(415)492-9861.
153ff48bf5SDavid du Colombier */
163ff48bf5SDavid du Colombier 
17593dc095SDavid du Colombier /* $Id: gdevpsft.c,v 1.35 2004/12/08 21:35:13 stefan Exp $ */
183ff48bf5SDavid du Colombier /* Write an embedded TrueType font */
193ff48bf5SDavid du Colombier #include "memory_.h"
20593dc095SDavid du Colombier #include <assert.h>
213ff48bf5SDavid du Colombier #include <stdlib.h>		/* for qsort */
223ff48bf5SDavid du Colombier #include "gx.h"
23593dc095SDavid du Colombier #include "gscencs.h"
243ff48bf5SDavid du Colombier #include "gserrors.h"
253ff48bf5SDavid du Colombier #include "gsmatrix.h"
263ff48bf5SDavid du Colombier #include "gsutil.h"
273ff48bf5SDavid du Colombier #include "gxfcid.h"
283ff48bf5SDavid du Colombier #include "gxfont.h"
293ff48bf5SDavid du Colombier #include "gxfont42.h"
303ff48bf5SDavid du Colombier #include "gxttf.h"
313ff48bf5SDavid du Colombier #include "stream.h"
323ff48bf5SDavid du Colombier #include "spprint.h"
333ff48bf5SDavid du Colombier #include "gdevpsf.h"
343ff48bf5SDavid du Colombier 
35593dc095SDavid du Colombier /* Internally used options */
36593dc095SDavid du Colombier #define WRITE_TRUETYPE_STRIPPED 0x1000	/* internal */
37593dc095SDavid du Colombier #define WRITE_TRUETYPE_CID 0x2000 /* internal */
38593dc095SDavid du Colombier 
393ff48bf5SDavid du Colombier #define MAX_COMPOSITE_PIECES 3	/* adhoc */
403ff48bf5SDavid du Colombier 
41593dc095SDavid du Colombier /*
42593dc095SDavid du Colombier  * The following are only for debugging.  They force various format choices
43593dc095SDavid du Colombier  * in the output.  The normal (non-debugging) values for all of these are
44593dc095SDavid du Colombier  * as indicated in the comments.
45593dc095SDavid du Colombier  *
46593dc095SDavid du Colombier  * Note that these options interact.  Here is the complete list of settings
47593dc095SDavid du Colombier  * that make sense.
48593dc095SDavid du Colombier 	0	-1,0,1	N/A	0,1	0,1
49593dc095SDavid du Colombier 	0xf000	-1	N/A	1	0,1
50593dc095SDavid du Colombier 	0xf000	0,1	0,1	1	0,1
51593dc095SDavid du Colombier  */
52593dc095SDavid du Colombier /* Define whether to use the 0xf000 character bias for generated tables. */
53593dc095SDavid du Colombier #define TT_BIAS 0xf000		/* 0xf000 */
54593dc095SDavid du Colombier /* Define whether to use cmap format 6 never(-1), sometimes(0), always(1). */
55593dc095SDavid du Colombier #define TT_FORCE_CMAP_6 0	/* 0 */
56593dc095SDavid du Colombier /* Define whether to use the bias for the cmap format 6 "first code". */
57593dc095SDavid du Colombier #define TT_BIAS_CMAP_6 0	/* 0 */
58593dc095SDavid du Colombier /* Define whether to generate an OS/2 table if none is supplied. */
59593dc095SDavid du Colombier #define TT_GENERATE_OS_2 1	/* 1 */
60593dc095SDavid du Colombier /* Define whether to adjust the OS/2 range bits. */
61593dc095SDavid du Colombier #define TT_ADJUST_OS_2 1	/* 1 */
62593dc095SDavid du Colombier /*
63593dc095SDavid du Colombier  * End of options.
64593dc095SDavid du Colombier  */
65593dc095SDavid du Colombier 
663ff48bf5SDavid du Colombier /* ---------------- Utilities ---------------- */
673ff48bf5SDavid du Colombier 
683ff48bf5SDavid du Colombier #define ACCESS(base, length, vptr)\
693ff48bf5SDavid du Colombier   BEGIN\
703ff48bf5SDavid du Colombier     code = string_proc(pfont, (ulong)(base), length, &vptr);\
713ff48bf5SDavid du Colombier     if (code < 0) return code;\
723ff48bf5SDavid du Colombier   END
733ff48bf5SDavid du Colombier 
743ff48bf5SDavid du Colombier /* Pad to a multiple of 4 bytes. */
753ff48bf5SDavid du Colombier private void
put_pad(stream * s,uint length)763ff48bf5SDavid du Colombier put_pad(stream *s, uint length)
773ff48bf5SDavid du Colombier {
783ff48bf5SDavid du Colombier     static const byte pad_to_4[3] = {0, 0, 0};
793ff48bf5SDavid du Colombier 
80593dc095SDavid du Colombier     stream_write(s, pad_to_4, (uint)(-(int)length & 3));
813ff48bf5SDavid du Colombier }
823ff48bf5SDavid du Colombier 
833ff48bf5SDavid du Colombier /* Put short and long values on a stream. */
843ff48bf5SDavid du Colombier private void
put_ushort(stream * s,uint v)853ff48bf5SDavid du Colombier put_ushort(stream *s, uint v)
863ff48bf5SDavid du Colombier {
873ff48bf5SDavid du Colombier     stream_putc(s, (byte)(v >> 8));
883ff48bf5SDavid du Colombier     stream_putc(s, (byte)v);
893ff48bf5SDavid du Colombier }
903ff48bf5SDavid du Colombier private void
put_ulong(stream * s,ulong v)913ff48bf5SDavid du Colombier put_ulong(stream *s, ulong v)
923ff48bf5SDavid du Colombier {
933ff48bf5SDavid du Colombier     put_ushort(s, (uint)(v >> 16));
943ff48bf5SDavid du Colombier     put_ushort(s, (uint)v);
953ff48bf5SDavid du Colombier }
963ff48bf5SDavid du Colombier private void
put_loca(stream * s,ulong offset,int indexToLocFormat)973ff48bf5SDavid du Colombier put_loca(stream *s, ulong offset, int indexToLocFormat)
983ff48bf5SDavid du Colombier {
993ff48bf5SDavid du Colombier     if (indexToLocFormat)
1003ff48bf5SDavid du Colombier 	put_ulong(s, offset);
1013ff48bf5SDavid du Colombier     else
1023ff48bf5SDavid du Colombier 	put_ushort(s, (uint)(offset >> 1));
1033ff48bf5SDavid du Colombier }
1043ff48bf5SDavid du Colombier 
1053ff48bf5SDavid du Colombier /* Get or put 2- or 4-byte quantities from/into a table. */
1063ff48bf5SDavid du Colombier #define U8(p) ((uint)((p)[0]))
1073ff48bf5SDavid du Colombier #define S8(p) (int)((U8(p) ^ 0x80) - 0x80)
1083ff48bf5SDavid du Colombier #define U16(p) (((uint)((p)[0]) << 8) + (p)[1])
1093ff48bf5SDavid du Colombier #define S16(p) (int)((U16(p) ^ 0x8000) - 0x8000)
1103ff48bf5SDavid du Colombier #define u32(p) get_u32_msb(p)
1113ff48bf5SDavid du Colombier private void
put_u16(byte * p,uint v)1123ff48bf5SDavid du Colombier put_u16(byte *p, uint v)
1133ff48bf5SDavid du Colombier {
1143ff48bf5SDavid du Colombier     p[0] = (byte)(v >> 8);
1153ff48bf5SDavid du Colombier     p[1] = (byte)v;
1163ff48bf5SDavid du Colombier }
1173ff48bf5SDavid du Colombier private void
put_u32(byte * p,ulong v)1183ff48bf5SDavid du Colombier put_u32(byte *p, ulong v)
1193ff48bf5SDavid du Colombier {
120593dc095SDavid du Colombier     put_u16(p, (ushort)(v >> 16));
121593dc095SDavid du Colombier     put_u16(p + 2, (ushort)v);
1223ff48bf5SDavid du Colombier }
1233ff48bf5SDavid du Colombier private ulong
put_table(byte tab[16],const char * tname,ulong checksum,ulong offset,uint length)1243ff48bf5SDavid du Colombier put_table(byte tab[16], const char *tname, ulong checksum, ulong offset,
1253ff48bf5SDavid du Colombier 	  uint length)
1263ff48bf5SDavid du Colombier {
1273ff48bf5SDavid du Colombier     memcpy(tab, (const byte *)tname, 4);
1283ff48bf5SDavid du Colombier     put_u32(tab + 4, checksum);
1293ff48bf5SDavid du Colombier     put_u32(tab + 8, offset + 0x40000000);
1303ff48bf5SDavid du Colombier     put_u32(tab + 12, (ulong)length);
1313ff48bf5SDavid du Colombier     return offset + round_up(length, 4);
1323ff48bf5SDavid du Colombier }
1333ff48bf5SDavid du Colombier 
1343ff48bf5SDavid du Colombier /* Write one range of a TrueType font. */
1353ff48bf5SDavid du Colombier private int
write_range(stream * s,gs_font_type42 * pfont,ulong start,uint length)1363ff48bf5SDavid du Colombier write_range(stream *s, gs_font_type42 *pfont, ulong start, uint length)
1373ff48bf5SDavid du Colombier {
1383ff48bf5SDavid du Colombier     ulong base = start;
1393ff48bf5SDavid du Colombier     ulong limit = base + length;
1403ff48bf5SDavid du Colombier 
1413ff48bf5SDavid du Colombier     if_debug3('l', "[l]write_range pos = %ld, start = %lu, length = %u\n",
1423ff48bf5SDavid du Colombier 	      stell(s), start, length);
1433ff48bf5SDavid du Colombier     while (base < limit) {
1443ff48bf5SDavid du Colombier 	uint size = limit - base;
1453ff48bf5SDavid du Colombier 	const byte *ptr;
1463ff48bf5SDavid du Colombier 	int code;
1473ff48bf5SDavid du Colombier 
1483ff48bf5SDavid du Colombier 	/* Write the largest block we can access consecutively. */
149593dc095SDavid du Colombier 	while ((code = pfont->data.string_proc(pfont, base, size, &ptr)) < 0) {
1503ff48bf5SDavid du Colombier 	    if (size <= 1)
1513ff48bf5SDavid du Colombier 		return code;
1523ff48bf5SDavid du Colombier 	    size >>= 1;
1533ff48bf5SDavid du Colombier 	}
154593dc095SDavid du Colombier 	if (code > 0 && size > code)
155593dc095SDavid du Colombier 	    size = code; /* Segmented data - see z42_string_proc. */
1563ff48bf5SDavid du Colombier 	stream_write(s, ptr, size);
1573ff48bf5SDavid du Colombier 	base += size;
1583ff48bf5SDavid du Colombier     }
1593ff48bf5SDavid du Colombier     return 0;
1603ff48bf5SDavid du Colombier }
1613ff48bf5SDavid du Colombier 
1623ff48bf5SDavid du Colombier /*
1633ff48bf5SDavid du Colombier  * Determine the Macintosh glyph number for a given character, if any.
1643ff48bf5SDavid du Colombier  * If no glyph can be found, return -1 and store the name in *pstr.
1653ff48bf5SDavid du Colombier  */
1663ff48bf5SDavid du Colombier private int
mac_glyph_index(gs_font * font,int ch,gs_const_string * pstr)1673ff48bf5SDavid du Colombier mac_glyph_index(gs_font *font, int ch, gs_const_string *pstr)
1683ff48bf5SDavid du Colombier {
1693ff48bf5SDavid du Colombier     gs_glyph glyph = font->procs.encode_char(font, (gs_char)ch,
1703ff48bf5SDavid du Colombier 					     GLYPH_SPACE_NAME);
171593dc095SDavid du Colombier     int code;
1723ff48bf5SDavid du Colombier 
1733ff48bf5SDavid du Colombier     if (glyph == gs_no_glyph)
1743ff48bf5SDavid du Colombier 	return 0;		/* .notdef */
175593dc095SDavid du Colombier     code = font->procs.glyph_name(font, glyph, pstr);
176593dc095SDavid du Colombier     assert(code >= 0);
1773ff48bf5SDavid du Colombier     if (glyph < gs_min_cid_glyph) {
1783ff48bf5SDavid du Colombier 	gs_char mac_char;
1793ff48bf5SDavid du Colombier 	gs_glyph mac_glyph;
1803ff48bf5SDavid du Colombier 	gs_const_string mstr;
1813ff48bf5SDavid du Colombier 
1823ff48bf5SDavid du Colombier 	/* Look (not very hard) for a match in the Mac glyph space. */
1833ff48bf5SDavid du Colombier 	if (ch >= 32 && ch <= 126)
1843ff48bf5SDavid du Colombier 	    mac_char = ch - 29;
1853ff48bf5SDavid du Colombier 	else if (ch >= 128 && ch <= 255)
1863ff48bf5SDavid du Colombier 	    mac_char = ch - 30;
1873ff48bf5SDavid du Colombier 	else
1883ff48bf5SDavid du Colombier 	    return -1;
189593dc095SDavid du Colombier 	mac_glyph = gs_c_known_encode(mac_char, ENCODING_INDEX_MACGLYPH);
1903ff48bf5SDavid du Colombier 	if (mac_glyph == gs_no_glyph)
1913ff48bf5SDavid du Colombier 	    return -1;
192593dc095SDavid du Colombier 	code = gs_c_glyph_name(mac_glyph, &mstr);
193593dc095SDavid du Colombier 	assert(code >= 0);
1943ff48bf5SDavid du Colombier 	if (!bytes_compare(pstr->data, pstr->size, mstr.data, mstr.size))
1953ff48bf5SDavid du Colombier 	    return (int)mac_char;
1963ff48bf5SDavid du Colombier     }
1973ff48bf5SDavid du Colombier     return -1;
1983ff48bf5SDavid du Colombier }
1993ff48bf5SDavid du Colombier 
2003ff48bf5SDavid du Colombier /* ---------------- Individual tables ---------------- */
2013ff48bf5SDavid du Colombier 
2023ff48bf5SDavid du Colombier /* ------ cmap ------ */
2033ff48bf5SDavid du Colombier 
2043ff48bf5SDavid du Colombier /* Write a generated cmap table. */
2053ff48bf5SDavid du Colombier static const byte cmap_initial_0[] = {
2063ff48bf5SDavid du Colombier     0, 0,		/* table version # = 0 */
2073ff48bf5SDavid du Colombier     0, 2,		/* # of encoding tables = 2 */
2083ff48bf5SDavid du Colombier 
2093ff48bf5SDavid du Colombier 	/* First table, Macintosh */
2103ff48bf5SDavid du Colombier     0, 1,		/* platform ID = Macintosh */
2113ff48bf5SDavid du Colombier     0, 0,		/* platform encoding ID = ??? */
2123ff48bf5SDavid du Colombier     0, 0, 0, 4+8+8,	/* offset to table start */
2133ff48bf5SDavid du Colombier 	/* Second table, Windows */
2143ff48bf5SDavid du Colombier     0, 3,		/* platform ID = Microsoft */
2153ff48bf5SDavid du Colombier     0, 0,		/* platform encoding ID = unknown */
2163ff48bf5SDavid du Colombier     0, 0, 1, 4+8+8+6,	/* offset to table start */
2173ff48bf5SDavid du Colombier 
2183ff48bf5SDavid du Colombier 	/* Start of Macintosh format 0 table */
2193ff48bf5SDavid du Colombier     0, 0,		/* format = 0, byte encoding table */
2203ff48bf5SDavid du Colombier     1, 6,		/* length */
2213ff48bf5SDavid du Colombier     0, 0		/* version number */
2223ff48bf5SDavid du Colombier };
2233ff48bf5SDavid du Colombier static const byte cmap_initial_6[] = {
2243ff48bf5SDavid du Colombier     0, 0,		/* table version # = 0 */
2253ff48bf5SDavid du Colombier     0, 2,		/* # of encoding tables = 2 */
2263ff48bf5SDavid du Colombier 
2273ff48bf5SDavid du Colombier 	/* First table, Macintosh */
2283ff48bf5SDavid du Colombier     0, 1,		/* platform ID = Macintosh */
2293ff48bf5SDavid du Colombier     0, 0,		/* platform encoding ID = ??? */
2303ff48bf5SDavid du Colombier     0, 0, 0, 4+8+8,	/* offset to table start */
2313ff48bf5SDavid du Colombier 	/* Second table, Windows */
2323ff48bf5SDavid du Colombier     0, 3,		/* platform ID = Microsoft */
2333ff48bf5SDavid du Colombier     0, 0,		/* platform encoding ID = unknown */
2343ff48bf5SDavid du Colombier     0, 0, 0, 4+8+8+10,	/* offset to table start */
2353ff48bf5SDavid du Colombier 			/* *VARIABLE*, add 2 x # of entries */
2363ff48bf5SDavid du Colombier 
2373ff48bf5SDavid du Colombier 	/* Start of Macintosh format 6 table */
2383ff48bf5SDavid du Colombier     0, 6,		/* format = 6, trimmed table mapping */
2393ff48bf5SDavid du Colombier     0, 10,		/* length *VARIABLE*, add 2 x # of entries */
2403ff48bf5SDavid du Colombier     0, 0,		/* version number */
2413ff48bf5SDavid du Colombier     0, 0,		/* first character code */
2423ff48bf5SDavid du Colombier     0, 0		/* # of entries *VARIABLE* */
2433ff48bf5SDavid du Colombier };
2443ff48bf5SDavid du Colombier static const byte cmap_initial_4[] = {
2453ff48bf5SDavid du Colombier     0, 0,		/* table version # = 0 */
2463ff48bf5SDavid du Colombier     0, 1,		/* # of encoding tables = 2 */
2473ff48bf5SDavid du Colombier 
2483ff48bf5SDavid du Colombier 	/* Single table, Windows */
2493ff48bf5SDavid du Colombier     0, 3,		/* platform ID = Microsoft */
2503ff48bf5SDavid du Colombier     0, 0,		/* platform encoding ID = unknown */
2513ff48bf5SDavid du Colombier     0, 0, 0, 4+8	/* offset to table start */
2523ff48bf5SDavid du Colombier };
2533ff48bf5SDavid du Colombier static const byte cmap_sub_initial[] = {
2543ff48bf5SDavid du Colombier     0, 4,		/* format = 4, segment mapping */
2553ff48bf5SDavid du Colombier     0, 32,		/* length ** VARIABLE, add 2 x # of glyphs ** */
2563ff48bf5SDavid du Colombier     0, 0,		/* version # */
2573ff48bf5SDavid du Colombier     0, 4,		/* 2 x segCount */
2583ff48bf5SDavid du Colombier     0, 4,		/* searchRange = 2 x 2 ^ floor(log2(segCount)) */
2593ff48bf5SDavid du Colombier     0, 1,		/* floor(log2(segCount)) */
2603ff48bf5SDavid du Colombier     0, 0,		/* 2 x segCount - searchRange */
2613ff48bf5SDavid du Colombier 
2623ff48bf5SDavid du Colombier     0, 0,		/* endCount[0] **VARIABLE** */
2633ff48bf5SDavid du Colombier     255, 255,		/* endCount[1] */
2643ff48bf5SDavid du Colombier     0, 0,		/* reservedPad */
2653ff48bf5SDavid du Colombier     0, 0,		/* startCount[0] **VARIABLE** */
2663ff48bf5SDavid du Colombier     255, 255,		/* startCount[1] */
2673ff48bf5SDavid du Colombier     0, 0,		/* idDelta[0] */
2683ff48bf5SDavid du Colombier     0, 1,		/* idDelta[1] */
2693ff48bf5SDavid du Colombier     0, 4,		/* idRangeOffset[0] */
2703ff48bf5SDavid du Colombier     0, 0		/* idRangeOffset[1] */
2713ff48bf5SDavid du Colombier };
272593dc095SDavid du Colombier /*
273593dc095SDavid du Colombier  * The following nonsense is required because C defines sizeof()
274593dc095SDavid du Colombier  * inconsistently.
275593dc095SDavid du Colombier  */
276593dc095SDavid du Colombier #define CMAP_ENTRIES_SIZE (256 * 2)
277593dc095SDavid du Colombier private void
write_cmap_0(stream * s,byte * entries,uint num_glyphs)278593dc095SDavid du Colombier write_cmap_0(stream *s, byte* entries /*[CMAP_ENTRIES_SIZE]*/, uint num_glyphs)
279593dc095SDavid du Colombier {
280593dc095SDavid du Colombier     int i;
281593dc095SDavid du Colombier 
282593dc095SDavid du Colombier     memset(entries + 2 * num_glyphs, 0, CMAP_ENTRIES_SIZE - 2 * num_glyphs);
283593dc095SDavid du Colombier     stream_write(s, cmap_initial_0, sizeof(cmap_initial_0));
284593dc095SDavid du Colombier     for (i = 0; i <= 0xff; ++i)
285593dc095SDavid du Colombier 	sputc(s, (byte)entries[2 * i + 1]);
286593dc095SDavid du Colombier }
287593dc095SDavid du Colombier private void
write_cmap_6(stream * s,byte * entries,uint first_code,uint first_entry,uint num_entries)288593dc095SDavid du Colombier write_cmap_6(stream *s, byte *entries /*[CMAP_ENTRIES_SIZE]*/, uint first_code,
289593dc095SDavid du Colombier 	     uint first_entry, uint num_entries)
290593dc095SDavid du Colombier {
291593dc095SDavid du Colombier     byte cmap_data[sizeof(cmap_initial_6)];
292593dc095SDavid du Colombier 
293593dc095SDavid du Colombier     memcpy(cmap_data, cmap_initial_6, sizeof(cmap_initial_6));
294593dc095SDavid du Colombier     put_u16(cmap_data + 18,
295593dc095SDavid du Colombier 	    U16(cmap_data + 18) + num_entries * 2);  /* offset */
296593dc095SDavid du Colombier     put_u16(cmap_data + 22,
297593dc095SDavid du Colombier 	    U16(cmap_data + 22) + num_entries * 2);  /* length */
298593dc095SDavid du Colombier     put_u16(cmap_data + 26,
299593dc095SDavid du Colombier #if TT_BIAS_CMAP_6
300593dc095SDavid du Colombier 	    first_code +
301593dc095SDavid du Colombier #endif
302593dc095SDavid du Colombier 	    first_entry);
303593dc095SDavid du Colombier     put_u16(cmap_data + 28, num_entries);
304593dc095SDavid du Colombier     stream_write(s, cmap_data, sizeof(cmap_data));
305593dc095SDavid du Colombier     stream_write(s, entries + first_entry * 2, num_entries * 2);
306593dc095SDavid du Colombier }
3073ff48bf5SDavid du Colombier private void
write_cmap(stream * s,gs_font * font,uint first_code,int num_glyphs,gs_glyph max_glyph,int options,uint cmap_length)3083ff48bf5SDavid du Colombier write_cmap(stream *s, gs_font *font, uint first_code, int num_glyphs,
3093ff48bf5SDavid du Colombier 	   gs_glyph max_glyph, int options, uint cmap_length)
3103ff48bf5SDavid du Colombier {
3113ff48bf5SDavid du Colombier     byte cmap_sub[sizeof(cmap_sub_initial)];
312593dc095SDavid du Colombier     byte entries[CMAP_ENTRIES_SIZE];
3133ff48bf5SDavid du Colombier     int first_entry = 0, end_entry = num_glyphs;
3143ff48bf5SDavid du Colombier     bool can_use_trimmed = !(options & WRITE_TRUETYPE_NO_TRIMMED_TABLE);
3153ff48bf5SDavid du Colombier     uint merge = 0;
3163ff48bf5SDavid du Colombier     uint num_entries;
3173ff48bf5SDavid du Colombier     int i;
3183ff48bf5SDavid du Colombier 
3193ff48bf5SDavid du Colombier     /* Collect the table entries. */
3203ff48bf5SDavid du Colombier 
3213ff48bf5SDavid du Colombier     for (i = 0; i < num_glyphs; ++i) {
3223ff48bf5SDavid du Colombier 	gs_glyph glyph =
3233ff48bf5SDavid du Colombier 	    font->procs.encode_char(font, (gs_char)i, GLYPH_SPACE_INDEX);
3243ff48bf5SDavid du Colombier 	uint glyph_index;
3253ff48bf5SDavid du Colombier 
326593dc095SDavid du Colombier 	if (glyph == gs_no_glyph || glyph < GS_MIN_GLYPH_INDEX ||
3273ff48bf5SDavid du Colombier 	    glyph > max_glyph
3283ff48bf5SDavid du Colombier 	    )
329593dc095SDavid du Colombier 	    glyph = GS_MIN_GLYPH_INDEX;
330593dc095SDavid du Colombier 	glyph_index = (uint)(glyph - GS_MIN_GLYPH_INDEX);
3313ff48bf5SDavid du Colombier 	merge |= glyph_index;
3323ff48bf5SDavid du Colombier 	put_u16(entries + 2 * i, glyph_index);
3333ff48bf5SDavid du Colombier     }
3343ff48bf5SDavid du Colombier     while (end_entry > first_entry && !U16(entries + 2 * end_entry - 2))
3353ff48bf5SDavid du Colombier 	--end_entry;
3363ff48bf5SDavid du Colombier     while (first_entry < end_entry && !U16(entries + 2 * first_entry))
3373ff48bf5SDavid du Colombier 	++first_entry;
3383ff48bf5SDavid du Colombier     num_entries = end_entry - first_entry;
3393ff48bf5SDavid du Colombier 
3403ff48bf5SDavid du Colombier     /* Write the table header and Macintosh sub-table (if any). */
3413ff48bf5SDavid du Colombier 
342593dc095SDavid du Colombier #if TT_FORCE_CMAP_6 > 0
343593dc095SDavid du Colombier     /* Always use format 6. */
344593dc095SDavid du Colombier     write_cmap_6(s, entries, first_code, first_entry, num_entries);
345593dc095SDavid du Colombier #else
346593dc095SDavid du Colombier # if TT_FORCE_CMAP_6 < 0
347593dc095SDavid du Colombier     /* Never use format 6.  Use format 0 if possible. */
348593dc095SDavid du Colombier     if (merge == (byte)merge)
349593dc095SDavid du Colombier 	write_cmap_0(s, entries, num_glyphs);
350593dc095SDavid du Colombier     else
351593dc095SDavid du Colombier # else /* TT_FORCE_CMAP == 0 */
352593dc095SDavid du Colombier     /*
353593dc095SDavid du Colombier      * Use format 0 if possible and (economical or format 6 disallowed),
354593dc095SDavid du Colombier      * otherwise format 6 if allowed.
355593dc095SDavid du Colombier      */
356593dc095SDavid du Colombier     if (merge == (byte)merge && (num_entries <= 127 || !can_use_trimmed))
357593dc095SDavid du Colombier 	write_cmap_0(s, entries, num_glyphs);
358593dc095SDavid du Colombier     else if (can_use_trimmed)
359593dc095SDavid du Colombier 	write_cmap_6(s, entries, first_code, first_entry, num_entries);
360593dc095SDavid du Colombier     else
361593dc095SDavid du Colombier # endif
362593dc095SDavid du Colombier     {
3633ff48bf5SDavid du Colombier 	/*
3643ff48bf5SDavid du Colombier 	 * Punt.  Acrobat Reader 3 can't handle any other Mac table format.
3653ff48bf5SDavid du Colombier 	 * (AR3 for Linux doesn't seem to be able to handle Windows format,
3663ff48bf5SDavid du Colombier 	 * either, but maybe AR3 for Windows can.)
3673ff48bf5SDavid du Colombier 	 */
3683ff48bf5SDavid du Colombier 	stream_write(s, cmap_initial_4, sizeof(cmap_initial_4));
3693ff48bf5SDavid du Colombier     }
370593dc095SDavid du Colombier #endif
3713ff48bf5SDavid du Colombier 
3723ff48bf5SDavid du Colombier     /* Write the Windows sub-table. */
3733ff48bf5SDavid du Colombier 
3743ff48bf5SDavid du Colombier     memcpy(cmap_sub, cmap_sub_initial, sizeof(cmap_sub_initial));
3753ff48bf5SDavid du Colombier     put_u16(cmap_sub + 2, U16(cmap_sub + 2) + num_entries * 2); /* length */
3763ff48bf5SDavid du Colombier     put_u16(cmap_sub + 14, first_code + end_entry - 1); /* endCount[0] */
3773ff48bf5SDavid du Colombier     put_u16(cmap_sub + 20, first_code + first_entry); /* startCount[0] */
3783ff48bf5SDavid du Colombier     stream_write(s, cmap_sub, sizeof(cmap_sub));
3793ff48bf5SDavid du Colombier     stream_write(s, entries + first_entry * 2, num_entries * 2);
3803ff48bf5SDavid du Colombier     put_pad(s, cmap_length);
3813ff48bf5SDavid du Colombier }
3823ff48bf5SDavid du Colombier private uint
size_cmap(gs_font * font,uint first_code,int num_glyphs,gs_glyph max_glyph,int options)3833ff48bf5SDavid du Colombier size_cmap(gs_font *font, uint first_code, int num_glyphs, gs_glyph max_glyph,
3843ff48bf5SDavid du Colombier 	  int options)
3853ff48bf5SDavid du Colombier {
3863ff48bf5SDavid du Colombier     stream poss;
3873ff48bf5SDavid du Colombier 
388593dc095SDavid du Colombier     s_init(&poss, NULL);
3893ff48bf5SDavid du Colombier     swrite_position_only(&poss);
3903ff48bf5SDavid du Colombier     write_cmap(&poss, font, first_code, num_glyphs, max_glyph, options, 0);
3913ff48bf5SDavid du Colombier     return stell(&poss);
3923ff48bf5SDavid du Colombier }
3933ff48bf5SDavid du Colombier 
394593dc095SDavid du Colombier /* ------ hmtx/vmtx ------ */
395593dc095SDavid du Colombier 
396*409b3affSDavid du Colombier /*
397*409b3affSDavid du Colombier  * avoid fp exceptions storing large doubles into integers by limiting
398*409b3affSDavid du Colombier  * the range of the results.  these are band-aids that don't fix the
399*409b3affSDavid du Colombier  * root cause of the out-of-range results, but they keep gs on the rails.
400*409b3affSDavid du Colombier  */
401*409b3affSDavid du Colombier 
402*409b3affSDavid du Colombier ushort
limdbl2ushort(double d)403*409b3affSDavid du Colombier limdbl2ushort(double d)
404*409b3affSDavid du Colombier {
405*409b3affSDavid du Colombier 	if (d < 0)
406*409b3affSDavid du Colombier 		return 0;
407*409b3affSDavid du Colombier 	else if (d > 64000)
408*409b3affSDavid du Colombier 		return 64000;
409*409b3affSDavid du Colombier 	else
410*409b3affSDavid du Colombier 		return d;
411*409b3affSDavid du Colombier }
412*409b3affSDavid du Colombier 
413*409b3affSDavid du Colombier long
limdbl2long(double d)414*409b3affSDavid du Colombier limdbl2long(double d)
415*409b3affSDavid du Colombier {
416*409b3affSDavid du Colombier 	if (d > 2e9)
417*409b3affSDavid du Colombier 		return 2e9;
418*409b3affSDavid du Colombier 	else if (d < -2e9)
419*409b3affSDavid du Colombier 		return -2e9;
420*409b3affSDavid du Colombier 	else
421*409b3affSDavid du Colombier 		return d;
422*409b3affSDavid du Colombier }
423*409b3affSDavid du Colombier 
424593dc095SDavid du Colombier private void
write_mtx(stream * s,gs_font_type42 * pfont,const gs_type42_mtx_t * pmtx,int wmode)425593dc095SDavid du Colombier write_mtx(stream *s, gs_font_type42 *pfont, const gs_type42_mtx_t *pmtx,
426593dc095SDavid du Colombier 	  int wmode)
427593dc095SDavid du Colombier {
428593dc095SDavid du Colombier     uint num_metrics = pmtx->numMetrics;
429593dc095SDavid du Colombier     uint len = num_metrics * 4;
430593dc095SDavid du Colombier     double factor = pfont->data.unitsPerEm * (wmode ? -1 : 1);
431593dc095SDavid du Colombier     float sbw[4];
432593dc095SDavid du Colombier     uint i;
433593dc095SDavid du Colombier 
434593dc095SDavid du Colombier     sbw[0] = sbw[1] = sbw[2] = sbw[3] = 0; /* in case of failures */
435593dc095SDavid du Colombier     for (i = 0; i < pmtx->numMetrics; ++i) {
436593dc095SDavid du Colombier 	DISCARD(pfont->data.get_metrics(pfont, i, wmode, sbw));
437*409b3affSDavid du Colombier 	put_ushort(s, limdbl2ushort(sbw[wmode + 2] * factor)); /* width */
438*409b3affSDavid du Colombier 	put_ushort(s, limdbl2ushort(sbw[wmode] * factor)); /* lsb, may be <0 */
439593dc095SDavid du Colombier     }
440593dc095SDavid du Colombier     for (; len < pmtx->length; ++i, len += 2) {
441593dc095SDavid du Colombier 	DISCARD(pfont->data.get_metrics(pfont, i, wmode, sbw));
442*409b3affSDavid du Colombier 	put_ushort(s, limdbl2ushort(sbw[wmode] * factor)); /* lsb, may be <0 */
443593dc095SDavid du Colombier     }
444593dc095SDavid du Colombier }
445593dc095SDavid du Colombier 
446593dc095SDavid du Colombier /* Compute the metrics from the glyph_info. */
447593dc095SDavid du Colombier private uint
size_mtx(gs_font_type42 * pfont,gs_type42_mtx_t * pmtx,uint max_glyph,int wmode)448593dc095SDavid du Colombier size_mtx(gs_font_type42 *pfont, gs_type42_mtx_t *pmtx, uint max_glyph,
449593dc095SDavid du Colombier 	 int wmode)
450593dc095SDavid du Colombier {
451*409b3affSDavid du Colombier     int prev_width = min_int, wmode2;
452593dc095SDavid du Colombier     uint last_width = 0; /* pacify compilers */
453593dc095SDavid du Colombier     double factor = pfont->data.unitsPerEm * (wmode ? -1 : 1);
454*409b3affSDavid du Colombier     uint i, j;
455593dc095SDavid du Colombier 
456593dc095SDavid du Colombier     for (i = 0; i <= max_glyph; ++i) {
457593dc095SDavid du Colombier 	float sbw[4];
458*409b3affSDavid du Colombier 	int code, width;
459593dc095SDavid du Colombier 
460*409b3affSDavid du Colombier 	for (j = 0; j < 4; j++)
461*409b3affSDavid du Colombier 		sbw[j] = 0;
462*409b3affSDavid du Colombier 	code = pfont->data.get_metrics(pfont, i, wmode, sbw);
463593dc095SDavid du Colombier 	if (code < 0)
464593dc095SDavid du Colombier 	    continue;
465*409b3affSDavid du Colombier 	wmode2 = wmode + 2;
466*409b3affSDavid du Colombier 	if (wmode2 < 0 || wmode2 >= 4)
467*409b3affSDavid du Colombier 		abort();	/* "wmode2 out of range" */
468*409b3affSDavid du Colombier 	width = limdbl2long(sbw[wmode2] * factor + 0.5);
469593dc095SDavid du Colombier 	if (width != prev_width)
470593dc095SDavid du Colombier 	    prev_width = width, last_width = i;
471593dc095SDavid du Colombier     }
472593dc095SDavid du Colombier     pmtx->numMetrics = last_width + 1;
473593dc095SDavid du Colombier     pmtx->length = pmtx->numMetrics * 4 + (max_glyph - last_width) * 2;
474593dc095SDavid du Colombier     return pmtx->length;
475593dc095SDavid du Colombier }
476593dc095SDavid du Colombier 
4773ff48bf5SDavid du Colombier /* ------ name ------ */
4783ff48bf5SDavid du Colombier 
4793ff48bf5SDavid du Colombier /* Write a generated name table. */
4803ff48bf5SDavid du Colombier static const byte name_initial[] = {
4813ff48bf5SDavid du Colombier     0, 0,			/* format */
4823ff48bf5SDavid du Colombier     0, 1,			/* # of records = 1 */
4833ff48bf5SDavid du Colombier     0, 18,			/* start of string storage */
4843ff48bf5SDavid du Colombier 
4853ff48bf5SDavid du Colombier     0, 2,			/* platform ID = ISO */
4863ff48bf5SDavid du Colombier     0, 2,			/* encoding ID = ISO 8859-1 */
4873ff48bf5SDavid du Colombier     0, 0,			/* language ID (none) */
4883ff48bf5SDavid du Colombier     0, 6,			/* name ID = PostScript name */
4893ff48bf5SDavid du Colombier     0, 0,			/* length *VARIABLE* */
4903ff48bf5SDavid du Colombier     0, 0			/* start of string within string storage */
4913ff48bf5SDavid du Colombier };
4923ff48bf5SDavid du Colombier private uint
size_name(const gs_const_string * font_name)4933ff48bf5SDavid du Colombier size_name(const gs_const_string *font_name)
4943ff48bf5SDavid du Colombier {
4953ff48bf5SDavid du Colombier     return sizeof(name_initial) + font_name->size;
4963ff48bf5SDavid du Colombier }
4973ff48bf5SDavid du Colombier private void
write_name(stream * s,const gs_const_string * font_name)4983ff48bf5SDavid du Colombier write_name(stream *s, const gs_const_string *font_name)
4993ff48bf5SDavid du Colombier {
5003ff48bf5SDavid du Colombier     byte name_bytes[sizeof(name_initial)];
5013ff48bf5SDavid du Colombier 
5023ff48bf5SDavid du Colombier     memcpy(name_bytes, name_initial, sizeof(name_initial));
5033ff48bf5SDavid du Colombier     put_u16(name_bytes + 14, font_name->size);
5043ff48bf5SDavid du Colombier     stream_write(s, name_bytes, sizeof(name_bytes));
5053ff48bf5SDavid du Colombier     stream_write(s, font_name->data, font_name->size);
5063ff48bf5SDavid du Colombier     put_pad(s, size_name(font_name));
5073ff48bf5SDavid du Colombier }
5083ff48bf5SDavid du Colombier 
5093ff48bf5SDavid du Colombier /* ------ OS/2 ------ */
5103ff48bf5SDavid du Colombier 
5113ff48bf5SDavid du Colombier /* Write a generated OS/2 table. */
5123ff48bf5SDavid du Colombier #define OS_2_LENGTH sizeof(ttf_OS_2_t)
5133ff48bf5SDavid du Colombier private void
update_OS_2(ttf_OS_2_t * pos2,uint first_glyph,int num_glyphs)5143ff48bf5SDavid du Colombier update_OS_2(ttf_OS_2_t *pos2, uint first_glyph, int num_glyphs)
5153ff48bf5SDavid du Colombier {
5163ff48bf5SDavid du Colombier     put_u16(pos2->usFirstCharIndex, first_glyph);
5173ff48bf5SDavid du Colombier     put_u16(pos2->usLastCharIndex, first_glyph + num_glyphs - 1);
518593dc095SDavid du Colombier #if TT_ADJUST_OS_2
519593dc095SDavid du Colombier     if (first_glyph >= 0xf000) {
520593dc095SDavid du Colombier 	/* This font is being treated as a symbolic font. */
521593dc095SDavid du Colombier 	memset(pos2->ulUnicodeRanges, 0, sizeof(pos2->ulUnicodeRanges));
522593dc095SDavid du Colombier 	pos2->ulUnicodeRanges[7] = 8; /* bit 60, private use range */
523593dc095SDavid du Colombier 	memset(pos2->ulCodePageRanges, 0, sizeof(pos2->ulCodePageRanges));
524593dc095SDavid du Colombier 	pos2->ulCodePageRanges[3] = 1; /* bit 31, symbolic */
525593dc095SDavid du Colombier     }
526593dc095SDavid du Colombier #endif
5273ff48bf5SDavid du Colombier }
5283ff48bf5SDavid du Colombier private void
write_OS_2(stream * s,gs_font * font,uint first_glyph,int num_glyphs)5293ff48bf5SDavid du Colombier write_OS_2(stream *s, gs_font *font, uint first_glyph, int num_glyphs)
5303ff48bf5SDavid du Colombier {
5313ff48bf5SDavid du Colombier     ttf_OS_2_t os2;
5323ff48bf5SDavid du Colombier 
5333ff48bf5SDavid du Colombier     /*
5343ff48bf5SDavid du Colombier      * We don't bother to set most of the fields.  The really important
5353ff48bf5SDavid du Colombier      * ones, which affect character mapping, are usFirst/LastCharIndex.
5363ff48bf5SDavid du Colombier      * We also need to set usWeightClass and usWidthClass to avoid
5373ff48bf5SDavid du Colombier      * crashing ttfdump.
5383ff48bf5SDavid du Colombier      */
5393ff48bf5SDavid du Colombier     memset(&os2, 0, sizeof(os2));
5403ff48bf5SDavid du Colombier     put_u16(os2.version, 1);
5413ff48bf5SDavid du Colombier     put_u16(os2.usWeightClass, 400); /* Normal */
5423ff48bf5SDavid du Colombier     put_u16(os2.usWidthClass, 5); /* Normal */
5433ff48bf5SDavid du Colombier     update_OS_2(&os2, first_glyph, num_glyphs);
5443ff48bf5SDavid du Colombier     stream_write(s, &os2, sizeof(os2));
5453ff48bf5SDavid du Colombier     put_pad(s, sizeof(os2));
5463ff48bf5SDavid du Colombier }
5473ff48bf5SDavid du Colombier 
5483ff48bf5SDavid du Colombier /* ------ post ------ */
5493ff48bf5SDavid du Colombier 
5503ff48bf5SDavid du Colombier /* Construct and then write the post table. */
5513ff48bf5SDavid du Colombier typedef struct post_glyph_s {
5523ff48bf5SDavid du Colombier     byte char_index;
5533ff48bf5SDavid du Colombier     byte size;
5543ff48bf5SDavid du Colombier     ushort glyph_index;
5553ff48bf5SDavid du Colombier } post_glyph_t;
5563ff48bf5SDavid du Colombier private int
compare_post_glyphs(const void * pg1,const void * pg2)5573ff48bf5SDavid du Colombier compare_post_glyphs(const void *pg1, const void *pg2)
5583ff48bf5SDavid du Colombier {
5593ff48bf5SDavid du Colombier     gs_glyph g1 = ((const post_glyph_t *)pg1)->glyph_index,
5603ff48bf5SDavid du Colombier 	g2 = ((const post_glyph_t *)pg2)->glyph_index;
5613ff48bf5SDavid du Colombier 
5623ff48bf5SDavid du Colombier     return (g1 < g2 ? -1 : g1 > g2 ? 1 : 0);
5633ff48bf5SDavid du Colombier }
5643ff48bf5SDavid du Colombier typedef struct post_s {
5653ff48bf5SDavid du Colombier     post_glyph_t glyphs[256 + 1];
5663ff48bf5SDavid du Colombier     int count, glyph_count;
5673ff48bf5SDavid du Colombier     uint length;
5683ff48bf5SDavid du Colombier } post_t;
5693ff48bf5SDavid du Colombier 
5703ff48bf5SDavid du Colombier /*
5713ff48bf5SDavid du Colombier  * If necessary, compute the length of the post table.  Note that we
5723ff48bf5SDavid du Colombier  * only generate post entries for characters in the Encoding.
5733ff48bf5SDavid du Colombier  */
5743ff48bf5SDavid du Colombier private void
compute_post(gs_font * font,post_t * post)5753ff48bf5SDavid du Colombier compute_post(gs_font *font, post_t *post)
5763ff48bf5SDavid du Colombier {
5773ff48bf5SDavid du Colombier     int i;
5783ff48bf5SDavid du Colombier 
5793ff48bf5SDavid du Colombier     for (i = 0, post->length = 32 + 2; i <= 255; ++i) {
5803ff48bf5SDavid du Colombier 	gs_const_string str;
5813ff48bf5SDavid du Colombier 	gs_glyph glyph = font->procs.encode_char(font, (gs_char)i,
5823ff48bf5SDavid du Colombier 						 GLYPH_SPACE_INDEX);
5833ff48bf5SDavid du Colombier 	int mac_index = mac_glyph_index(font, i, &str);
5843ff48bf5SDavid du Colombier 
5853ff48bf5SDavid du Colombier 	if (mac_index != 0) {
5863ff48bf5SDavid du Colombier 	    post->glyphs[post->count].char_index = i;
5873ff48bf5SDavid du Colombier 	    post->glyphs[post->count].size =
5883ff48bf5SDavid du Colombier 		(mac_index < 0 ? str.size + 1 : 0);
589593dc095SDavid du Colombier 	    post->glyphs[post->count].glyph_index = glyph - GS_MIN_GLYPH_INDEX;
5903ff48bf5SDavid du Colombier 	    post->count++;
5913ff48bf5SDavid du Colombier 	}
5923ff48bf5SDavid du Colombier     }
5933ff48bf5SDavid du Colombier     if (post->count) {
5943ff48bf5SDavid du Colombier 	int j;
5953ff48bf5SDavid du Colombier 
5963ff48bf5SDavid du Colombier 	qsort(post->glyphs, post->count, sizeof(post->glyphs[0]),
5973ff48bf5SDavid du Colombier 	      compare_post_glyphs);
5983ff48bf5SDavid du Colombier 	/* Eliminate duplicate references to the same glyph. */
5993ff48bf5SDavid du Colombier 	for (i = j = 0; i < post->count; ++i) {
6003ff48bf5SDavid du Colombier 	    if (i == 0 ||
6013ff48bf5SDavid du Colombier 		post->glyphs[i].glyph_index !=
6023ff48bf5SDavid du Colombier 		post->glyphs[i - 1].glyph_index
6033ff48bf5SDavid du Colombier 		) {
6043ff48bf5SDavid du Colombier 		post->length += post->glyphs[i].size;
6053ff48bf5SDavid du Colombier 		post->glyphs[j++] = post->glyphs[i];
6063ff48bf5SDavid du Colombier 	    }
6073ff48bf5SDavid du Colombier 	}
6083ff48bf5SDavid du Colombier 	post->count = j;
6093ff48bf5SDavid du Colombier 	post->glyph_count = post->glyphs[post->count - 1].glyph_index + 1;
6103ff48bf5SDavid du Colombier     }
6113ff48bf5SDavid du Colombier     post->length += post->glyph_count * 2;
6123ff48bf5SDavid du Colombier }
6133ff48bf5SDavid du Colombier 
6143ff48bf5SDavid du Colombier /* Write the post table */
6153ff48bf5SDavid du Colombier private void
write_post(stream * s,gs_font * font,post_t * post)6163ff48bf5SDavid du Colombier write_post(stream *s, gs_font *font, post_t *post)
6173ff48bf5SDavid du Colombier {
6183ff48bf5SDavid du Colombier     byte post_initial[32 + 2];
6193ff48bf5SDavid du Colombier     uint name_index;
6203ff48bf5SDavid du Colombier     uint glyph_index;
6213ff48bf5SDavid du Colombier     int i;
6223ff48bf5SDavid du Colombier 
6233ff48bf5SDavid du Colombier     memset(post_initial, 0, 32);
6243ff48bf5SDavid du Colombier     put_u32(post_initial, 0x00020000);
6253ff48bf5SDavid du Colombier     put_u16(post_initial + 32, post->glyph_count);
6263ff48bf5SDavid du Colombier     stream_write(s, post_initial, sizeof(post_initial));
6273ff48bf5SDavid du Colombier 
6283ff48bf5SDavid du Colombier     /* Write the name index table. */
6293ff48bf5SDavid du Colombier 
6303ff48bf5SDavid du Colombier     for (i = 0, name_index = 258, glyph_index = 0; i < post->count; ++i) {
6313ff48bf5SDavid du Colombier 	gs_const_string str;
6323ff48bf5SDavid du Colombier 	int ch = post->glyphs[i].char_index;
6333ff48bf5SDavid du Colombier 	int mac_index = mac_glyph_index(font, ch, &str);
6343ff48bf5SDavid du Colombier 
6353ff48bf5SDavid du Colombier 	for (; glyph_index < post->glyphs[i].glyph_index; ++glyph_index)
6363ff48bf5SDavid du Colombier 	    put_ushort(s, 0);
6373ff48bf5SDavid du Colombier 	glyph_index++;
6383ff48bf5SDavid du Colombier 	if (mac_index >= 0)
6393ff48bf5SDavid du Colombier 	    put_ushort(s, mac_index);
6403ff48bf5SDavid du Colombier 	else {
6413ff48bf5SDavid du Colombier 	    put_ushort(s, name_index);
6423ff48bf5SDavid du Colombier 	    name_index++;
6433ff48bf5SDavid du Colombier 	}
6443ff48bf5SDavid du Colombier     }
6453ff48bf5SDavid du Colombier 
6463ff48bf5SDavid du Colombier     /* Write the string names of the glyphs. */
6473ff48bf5SDavid du Colombier 
6483ff48bf5SDavid du Colombier     for (i = 0; i < post->count; ++i) {
6493ff48bf5SDavid du Colombier 	gs_const_string str;
6503ff48bf5SDavid du Colombier 	int ch = post->glyphs[i].char_index;
6513ff48bf5SDavid du Colombier 	int mac_index = mac_glyph_index(font, ch, &str);
6523ff48bf5SDavid du Colombier 
6533ff48bf5SDavid du Colombier 	if (mac_index < 0) {
654593dc095SDavid du Colombier 	    spputc(s, (byte)str.size);
6553ff48bf5SDavid du Colombier 	    stream_write(s, str.data, str.size);
6563ff48bf5SDavid du Colombier 	}
6573ff48bf5SDavid du Colombier     }
6583ff48bf5SDavid du Colombier     put_pad(s, post->length);
6593ff48bf5SDavid du Colombier }
6603ff48bf5SDavid du Colombier 
6613ff48bf5SDavid du Colombier /* ---------------- Main program ---------------- */
6623ff48bf5SDavid du Colombier 
6633ff48bf5SDavid du Colombier /* Write the definition of a TrueType font. */
6643ff48bf5SDavid du Colombier private int
compare_table_tags(const void * pt1,const void * pt2)6653ff48bf5SDavid du Colombier compare_table_tags(const void *pt1, const void *pt2)
6663ff48bf5SDavid du Colombier {
6673ff48bf5SDavid du Colombier     ulong t1 = u32(pt1), t2 = u32(pt2);
6683ff48bf5SDavid du Colombier 
6693ff48bf5SDavid du Colombier     return (t1 < t2 ? -1 : t1 > t2 ? 1 : 0);
6703ff48bf5SDavid du Colombier }
6713ff48bf5SDavid du Colombier 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)6723ff48bf5SDavid du Colombier psf_write_truetype_data(stream *s, gs_font_type42 *pfont, int options,
6733ff48bf5SDavid du Colombier 			psf_glyph_enum_t *penum, bool is_subset,
6743ff48bf5SDavid du Colombier 			const gs_const_string *alt_font_name)
6753ff48bf5SDavid du Colombier {
6763ff48bf5SDavid du Colombier     gs_font *const font = (gs_font *)pfont;
6773ff48bf5SDavid du Colombier     gs_const_string font_name;
678593dc095SDavid du Colombier     int (*string_proc)(gs_font_type42 *, ulong, uint, const byte **) =
6793ff48bf5SDavid du Colombier 	pfont->data.string_proc;
6803ff48bf5SDavid du Colombier     const byte *OffsetTable;
6813ff48bf5SDavid du Colombier     uint numTables_stored, numTables, numTables_out;
6823ff48bf5SDavid du Colombier #define MAX_NUM_TABLES 40
6833ff48bf5SDavid du Colombier     byte tables[MAX_NUM_TABLES * 16];
6843ff48bf5SDavid du Colombier     uint i;
6853ff48bf5SDavid du Colombier     ulong offset;
6863ff48bf5SDavid du Colombier     gs_glyph glyph, glyph_prev;
6873ff48bf5SDavid du Colombier     ulong max_glyph;
688593dc095SDavid du Colombier     uint glyf_length, loca_length;
689593dc095SDavid du Colombier     ulong glyf_checksum = 0L; /****** NO CHECKSUM ******/
690593dc095SDavid du Colombier     ulong loca_checksum[2] = {0L,0L};
691593dc095SDavid du Colombier     ulong glyf_alignment = 0;
692593dc095SDavid du Colombier     uint numGlyphs = 0;		/* original value from maxp */
6933ff48bf5SDavid du Colombier     byte head[56];		/* 0 mod 4 */
694593dc095SDavid du Colombier     gs_type42_mtx_t mtx[2];
6953ff48bf5SDavid du Colombier     post_t post;
6963ff48bf5SDavid du Colombier     ulong head_checksum, file_checksum = 0;
697593dc095SDavid du Colombier     int indexToLocFormat = 0;
6983ff48bf5SDavid du Colombier     bool
6993ff48bf5SDavid du Colombier 	writing_cid = (options & WRITE_TRUETYPE_CID) != 0,
700593dc095SDavid du Colombier 	writing_stripped = (options & WRITE_TRUETYPE_STRIPPED) != 0,
701593dc095SDavid du Colombier 	generate_mtx = (options & WRITE_TRUETYPE_HVMTX) != 0,
702593dc095SDavid du Colombier 	no_generate = writing_cid | writing_stripped,
703593dc095SDavid du Colombier 	have_cmap = no_generate,
7043ff48bf5SDavid du Colombier 	have_name = !(options & WRITE_TRUETYPE_NAME),
705593dc095SDavid du Colombier 	have_OS_2 = no_generate,
706593dc095SDavid du Colombier 	have_post = no_generate;
707593dc095SDavid du Colombier     int have_hvhea[2];
708593dc095SDavid du Colombier     uint cmap_length = 0;
709593dc095SDavid du Colombier     ulong OS_2_start = 0;
7103ff48bf5SDavid du Colombier     uint OS_2_length = OS_2_LENGTH;
7113ff48bf5SDavid du Colombier     int code;
7123ff48bf5SDavid du Colombier 
713593dc095SDavid du Colombier     have_hvhea[0] = have_hvhea[1] = 0;
7143ff48bf5SDavid du Colombier     if (alt_font_name)
7153ff48bf5SDavid du Colombier 	font_name = *alt_font_name;
7163ff48bf5SDavid du Colombier     else
7173ff48bf5SDavid du Colombier 	font_name.data = font->font_name.chars,
7183ff48bf5SDavid du Colombier 	    font_name.size = font->font_name.size;
7193ff48bf5SDavid du Colombier 
7203ff48bf5SDavid du Colombier     /*
7213ff48bf5SDavid du Colombier      * Count the number of tables, including the eventual glyf and loca
7223ff48bf5SDavid du Colombier      * (which may not actually be present in the font), and copy the
7233ff48bf5SDavid du Colombier      * table directory.
7243ff48bf5SDavid du Colombier      */
7253ff48bf5SDavid du Colombier 
7263ff48bf5SDavid du Colombier     ACCESS(0, 12, OffsetTable);
7273ff48bf5SDavid du Colombier     numTables_stored = U16(OffsetTable + 4);
7283ff48bf5SDavid du Colombier     for (i = numTables = 0; i < numTables_stored; ++i) {
7293ff48bf5SDavid du Colombier 	const byte *tab;
7303ff48bf5SDavid du Colombier 	const byte *data;
7313ff48bf5SDavid du Colombier 	ulong start;
7323ff48bf5SDavid du Colombier 	uint length;
7333ff48bf5SDavid du Colombier 
7343ff48bf5SDavid du Colombier 	if (numTables == MAX_NUM_TABLES)
7353ff48bf5SDavid du Colombier 	    return_error(gs_error_limitcheck);
7363ff48bf5SDavid du Colombier 	ACCESS(12 + i * 16, 16, tab);
7373ff48bf5SDavid du Colombier 	start = u32(tab + 8);
7383ff48bf5SDavid du Colombier 	length = u32(tab + 12);
7393ff48bf5SDavid du Colombier 	/* Copy the table data now, since another ACCESS may invalidate it. */
7403ff48bf5SDavid du Colombier 	memcpy(&tables[numTables * 16], tab, 16);
7413ff48bf5SDavid du Colombier 
7423ff48bf5SDavid du Colombier #define W(a,b,c,d)\
7433ff48bf5SDavid du Colombier   ( ((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
7443ff48bf5SDavid du Colombier 
745593dc095SDavid du Colombier 	switch (u32(tab)) {
7463ff48bf5SDavid du Colombier 	case W('h','e','a','d'):
7473ff48bf5SDavid du Colombier 	    if (length != 54)
7483ff48bf5SDavid du Colombier 		return_error(gs_error_invalidfont);
7493ff48bf5SDavid du Colombier 	    ACCESS(start, length, data);
7503ff48bf5SDavid du Colombier 	    memcpy(head, data, length);
7513ff48bf5SDavid du Colombier 	    continue;
7523ff48bf5SDavid du Colombier 	case W('g','l','y','f'): /* synthesized */
7533ff48bf5SDavid du Colombier 	case W('g','l','y','x'): /* Adobe bogus */
7543ff48bf5SDavid du Colombier 	case W('l','o','c','a'): /* synthesized */
7553ff48bf5SDavid du Colombier 	case W('l','o','c','x'): /* Adobe bogus */
7563ff48bf5SDavid du Colombier 	case W('g','d','i','r'): /* Adobe marker */
7573ff48bf5SDavid du Colombier 	    continue;
7583ff48bf5SDavid du Colombier 	case W('c','m','a','p'):
7593ff48bf5SDavid du Colombier 	    if (options & (WRITE_TRUETYPE_CMAP | WRITE_TRUETYPE_CID))
7603ff48bf5SDavid du Colombier 		continue;
7613ff48bf5SDavid du Colombier 	    have_cmap = true;
7623ff48bf5SDavid du Colombier 	    break;
7633ff48bf5SDavid du Colombier 	case W('m','a','x','p'):
7643ff48bf5SDavid du Colombier 	    ACCESS(start, length, data);
7653ff48bf5SDavid du Colombier 	    numGlyphs = U16(data + 4);
7663ff48bf5SDavid du Colombier 	    break;
7673ff48bf5SDavid du Colombier 	case W('n','a','m','e'):
7683ff48bf5SDavid du Colombier 	    if (writing_cid)
7693ff48bf5SDavid du Colombier 		continue;
7703ff48bf5SDavid du Colombier 	    have_name = true;
7713ff48bf5SDavid du Colombier 	    break;
7723ff48bf5SDavid du Colombier 	case W('O','S','/','2'):
7733ff48bf5SDavid du Colombier 	    if (writing_cid)
7743ff48bf5SDavid du Colombier 		continue;
7753ff48bf5SDavid du Colombier 	    have_OS_2 = true;
7763ff48bf5SDavid du Colombier 	    if (length > OS_2_LENGTH)
7773ff48bf5SDavid du Colombier 		return_error(gs_error_invalidfont);
7783ff48bf5SDavid du Colombier 	    OS_2_start = start;
7793ff48bf5SDavid du Colombier 	    OS_2_length = length;
7803ff48bf5SDavid du Colombier 	    continue;
7813ff48bf5SDavid du Colombier 	case W('p','o','s','t'):
7823ff48bf5SDavid du Colombier 	    have_post = true;
7833ff48bf5SDavid du Colombier 	    break;
7843ff48bf5SDavid du Colombier 	case W('h','h','e','a'):
785593dc095SDavid du Colombier 	    have_hvhea[0] = 1;
786593dc095SDavid du Colombier 	    break;
787593dc095SDavid du Colombier 	case W('v','h','e','a'):
788593dc095SDavid du Colombier 	    have_hvhea[1] = 1;
789593dc095SDavid du Colombier 	    break;
7903ff48bf5SDavid du Colombier 	case W('h','m','t','x'):
791593dc095SDavid du Colombier 	case W('v','m','t','x'):
792593dc095SDavid du Colombier 	    if (generate_mtx)
793593dc095SDavid du Colombier 		continue;
794593dc095SDavid du Colombier 	    /* falls through */
795593dc095SDavid du Colombier 	case W('c','v','t',' '):
7963ff48bf5SDavid du Colombier 	case W('f','p','g','m'):
7973ff48bf5SDavid du Colombier 	case W('g','a','s','p'):
7983ff48bf5SDavid du Colombier 	case W('k','e','r','n'):
799593dc095SDavid du Colombier 	case W('p','r','e','p'):
8003ff48bf5SDavid du Colombier 	    break;		/* always copy these if present */
8013ff48bf5SDavid du Colombier 	default:
8023ff48bf5SDavid du Colombier 	    if (writing_cid)
8033ff48bf5SDavid du Colombier 		continue;
8043ff48bf5SDavid du Colombier 	    break;
8053ff48bf5SDavid du Colombier 	}
8063ff48bf5SDavid du Colombier 	numTables++;
8073ff48bf5SDavid du Colombier     }
8083ff48bf5SDavid du Colombier 
8093ff48bf5SDavid du Colombier     /*
8103ff48bf5SDavid du Colombier      * Enumerate the glyphs to get the size of glyf and loca,
8113ff48bf5SDavid du Colombier      * and to compute the checksums for these tables.
8123ff48bf5SDavid du Colombier      */
8133ff48bf5SDavid du Colombier 
8143ff48bf5SDavid du Colombier     /****** NO CHECKSUMS YET ******/
8153ff48bf5SDavid du Colombier     for (max_glyph = 0, glyf_length = 0;
8163ff48bf5SDavid du Colombier 	 (code = psf_enumerate_glyphs_next(penum, &glyph)) != 1;
8173ff48bf5SDavid du Colombier 	 ) {
8183ff48bf5SDavid du Colombier 	uint glyph_index;
819593dc095SDavid du Colombier 	gs_glyph_data_t glyph_data;
8203ff48bf5SDavid du Colombier 
8213ff48bf5SDavid du Colombier 	if (glyph < gs_min_cid_glyph)
8223ff48bf5SDavid du Colombier 	    return_error(gs_error_invalidfont);
823593dc095SDavid du Colombier 	glyph_index = glyph  & ~GS_GLYPH_TAG;
8243ff48bf5SDavid du Colombier 	if_debug1('L', "[L]glyph_index %u\n", glyph_index);
825593dc095SDavid du Colombier 	glyph_data.memory = pfont->memory;
826593dc095SDavid du Colombier 	if ((code = pfont->data.get_outline(pfont, glyph_index, &glyph_data)) >= 0) {
827593dc095SDavid du Colombier 	    /* Since indexToLocFormat==0 assumes even glyph lengths,
828593dc095SDavid du Colombier 	       round it up here. If later we choose indexToLocFormat==1,
829593dc095SDavid du Colombier 	       subtract the glyf_alignment to compensate it. */
830593dc095SDavid du Colombier 	    uint l = (glyph_data.bits.size + 1) & ~1;
831593dc095SDavid du Colombier 
8323ff48bf5SDavid du Colombier 	    max_glyph = max(max_glyph, glyph_index);
833593dc095SDavid du Colombier 	    glyf_length += l;
834593dc095SDavid du Colombier 	    if (l != glyph_data.bits.size)
835593dc095SDavid du Colombier 		glyf_alignment++;
836593dc095SDavid du Colombier 	    if_debug1('L', "[L]  size %u\n", glyph_data.bits.size);
837593dc095SDavid du Colombier 	    gs_glyph_data_free(&glyph_data, "psf_write_truetype_data");
8383ff48bf5SDavid du Colombier 	}
8393ff48bf5SDavid du Colombier     }
8403ff48bf5SDavid du Colombier     /*
8413ff48bf5SDavid du Colombier      * For subset fonts, we should trim the loca table so that it only
8423ff48bf5SDavid du Colombier      * contains entries through max_glyph.  Unfortunately, this would
8433ff48bf5SDavid du Colombier      * require changing numGlyphs in maxp, which in turn would affect hdmx,
8443ff48bf5SDavid du Colombier      * hhea, hmtx, vdmx, vhea, vmtx, and possibly other tables.  This is way
8453ff48bf5SDavid du Colombier      * more work than we want to do right now.
8463ff48bf5SDavid du Colombier      */
847593dc095SDavid du Colombier     if (writing_stripped) {
848593dc095SDavid du Colombier 	glyf_length = 0;
849593dc095SDavid du Colombier 	loca_length = 0;
850593dc095SDavid du Colombier     } else {
8513ff48bf5SDavid du Colombier 	/*loca_length = (max_glyph + 2) << 2;*/
8523ff48bf5SDavid du Colombier 	loca_length = (numGlyphs + 1) << 2;
8533ff48bf5SDavid du Colombier 	indexToLocFormat = (glyf_length > 0x1fffc);
8543ff48bf5SDavid du Colombier 	if (!indexToLocFormat)
8553ff48bf5SDavid du Colombier 	    loca_length >>= 1;
856593dc095SDavid du Colombier 	else
857593dc095SDavid du Colombier 	    glyf_length -= glyf_alignment;
858593dc095SDavid du Colombier 	/* Acrobat Reader won't accept fonts with empty glyfs. */
859593dc095SDavid du Colombier 	if (glyf_length == 0)
860593dc095SDavid du Colombier 	    glyf_length = 1;
861593dc095SDavid du Colombier     }
862593dc095SDavid du Colombier     if_debug2('l', "[l]max_glyph = %lu, glyf_length = %lu\n",
863593dc095SDavid du Colombier 	      (ulong)max_glyph, (ulong)glyf_length);
8643ff48bf5SDavid du Colombier 
8653ff48bf5SDavid du Colombier     /*
8663ff48bf5SDavid du Colombier      * If necessary, compute the length of the post table.  Note that we
8673ff48bf5SDavid du Colombier      * only generate post entries for characters in the Encoding.  */
8683ff48bf5SDavid du Colombier 
8693ff48bf5SDavid du Colombier     if (!have_post) {
8703ff48bf5SDavid du Colombier 	memset(&post, 0, sizeof(post));
8713ff48bf5SDavid du Colombier 	if (options & WRITE_TRUETYPE_POST)
8723ff48bf5SDavid du Colombier 	    compute_post(font, &post);
8733ff48bf5SDavid du Colombier 	else
8743ff48bf5SDavid du Colombier 	    post.length = 32;	/* dummy table */
8753ff48bf5SDavid du Colombier     }
8763ff48bf5SDavid du Colombier 
8773ff48bf5SDavid du Colombier     /* Fix up the head table. */
8783ff48bf5SDavid du Colombier 
8793ff48bf5SDavid du Colombier     memset(head + 8, 0, 4);
8803ff48bf5SDavid du Colombier     head[51] = (byte)indexToLocFormat;
8813ff48bf5SDavid du Colombier     memset(head + 54, 0, 2);
8823ff48bf5SDavid du Colombier     for (head_checksum = 0, i = 0; i < 56; i += 4)
8833ff48bf5SDavid du Colombier 	head_checksum += u32(&head[i]);
8843ff48bf5SDavid du Colombier 
8853ff48bf5SDavid du Colombier     /*
8863ff48bf5SDavid du Colombier      * Construct the table directory, except for glyf, loca, head, OS/2,
8873ff48bf5SDavid du Colombier      * and, if necessary, generated cmap, name, and post tables.
8883ff48bf5SDavid du Colombier      * Note that the existing directory is already sorted by tag.
8893ff48bf5SDavid du Colombier      */
8903ff48bf5SDavid du Colombier 
891593dc095SDavid du Colombier     numTables_out = numTables + 1 /* head */
892593dc095SDavid du Colombier 	+ !writing_stripped * 2	/* glyf, loca */
893593dc095SDavid du Colombier 	+ generate_mtx * (have_hvhea[0] + have_hvhea[1]) /* hmtx, vmtx */
894593dc095SDavid du Colombier 	+ !have_OS_2		/* OS/2 */
8953ff48bf5SDavid du Colombier 	+ !have_cmap + !have_name + !have_post;
8963ff48bf5SDavid du Colombier     if (numTables_out >= MAX_NUM_TABLES)
8973ff48bf5SDavid du Colombier 	return_error(gs_error_limitcheck);
8983ff48bf5SDavid du Colombier     offset = 12 + numTables_out * 16;
8993ff48bf5SDavid du Colombier     for (i = 0; i < numTables; ++i) {
9003ff48bf5SDavid du Colombier 	byte *tab = &tables[i * 16];
9013ff48bf5SDavid du Colombier 	ulong length = u32(tab + 12);
9023ff48bf5SDavid du Colombier 
9033ff48bf5SDavid du Colombier 	offset += round_up(length, 4);
9043ff48bf5SDavid du Colombier     }
9053ff48bf5SDavid du Colombier 
9063ff48bf5SDavid du Colombier     /* Make the table directory entries for generated tables. */
9073ff48bf5SDavid du Colombier 
9083ff48bf5SDavid du Colombier     {
9093ff48bf5SDavid du Colombier 	byte *tab = &tables[numTables * 16];
9103ff48bf5SDavid du Colombier 
911593dc095SDavid du Colombier 	if (!writing_stripped) {
912593dc095SDavid du Colombier 	    offset = put_table(tab, "glyf", glyf_checksum,
913593dc095SDavid du Colombier 			       offset, glyf_length);
9143ff48bf5SDavid du Colombier 	    tab += 16;
9153ff48bf5SDavid du Colombier 
9163ff48bf5SDavid du Colombier 	    offset = put_table(tab, "loca", loca_checksum[indexToLocFormat],
9173ff48bf5SDavid du Colombier 			       offset, loca_length);
9183ff48bf5SDavid du Colombier 	    tab += 16;
919593dc095SDavid du Colombier 	}
9203ff48bf5SDavid du Colombier 
9213ff48bf5SDavid du Colombier 	if (!have_cmap) {
922593dc095SDavid du Colombier 	    cmap_length = size_cmap(font, TT_BIAS, 256,
923593dc095SDavid du Colombier 				    GS_MIN_GLYPH_INDEX + max_glyph, options);
9243ff48bf5SDavid du Colombier 	    offset = put_table(tab, "cmap", 0L /****** NO CHECKSUM ******/,
9253ff48bf5SDavid du Colombier 			       offset, cmap_length);
9263ff48bf5SDavid du Colombier 	    tab += 16;
9273ff48bf5SDavid du Colombier 	}
9283ff48bf5SDavid du Colombier 
9293ff48bf5SDavid du Colombier 	if (!have_name) {
9303ff48bf5SDavid du Colombier 	    offset = put_table(tab, "name", 0L /****** NO CHECKSUM ******/,
9313ff48bf5SDavid du Colombier 			       offset, size_name(&font_name));
9323ff48bf5SDavid du Colombier 	    tab += 16;
9333ff48bf5SDavid du Colombier 	}
9343ff48bf5SDavid du Colombier 
935593dc095SDavid du Colombier 	if (!no_generate) {
9363ff48bf5SDavid du Colombier 	    offset = put_table(tab, "OS/2", 0L /****** NO CHECKSUM ******/,
9373ff48bf5SDavid du Colombier 			       offset, OS_2_length);
9383ff48bf5SDavid du Colombier 	    tab += 16;
9393ff48bf5SDavid du Colombier 	}
9403ff48bf5SDavid du Colombier 
941593dc095SDavid du Colombier 	if (generate_mtx)
942593dc095SDavid du Colombier 	    for (i = 0; i < 2; ++i)
943593dc095SDavid du Colombier 		if (have_hvhea[i]) {
944593dc095SDavid du Colombier 		    offset = put_table(tab, (i ? "vmtx" : "hmtx"),
945593dc095SDavid du Colombier 				       0L /****** NO CHECKSUM ******/,
946593dc095SDavid du Colombier 				       offset,
947593dc095SDavid du Colombier 				       size_mtx(pfont, &mtx[i], max_glyph, i));
948593dc095SDavid du Colombier 		    tab += 16;
949593dc095SDavid du Colombier 		}
950593dc095SDavid du Colombier 
9513ff48bf5SDavid du Colombier 	if (!have_post) {
9523ff48bf5SDavid du Colombier 	    offset = put_table(tab, "post", 0L /****** NO CHECKSUM ******/,
9533ff48bf5SDavid du Colombier 			       offset, post.length);
9543ff48bf5SDavid du Colombier 	    tab += 16;
9553ff48bf5SDavid du Colombier 	}
9563ff48bf5SDavid du Colombier 
9573ff48bf5SDavid du Colombier 	/*
9583ff48bf5SDavid du Colombier 	 * Note that the 'head' table must have length 54, even though
9593ff48bf5SDavid du Colombier 	 * it occupies 56 bytes on the file.
9603ff48bf5SDavid du Colombier 	 */
9613ff48bf5SDavid du Colombier 	offset = put_table(tab, "head", head_checksum, offset, 54);
9623ff48bf5SDavid du Colombier 	tab += 16;
9633ff48bf5SDavid du Colombier     }
9643ff48bf5SDavid du Colombier     numTables = numTables_out;
9653ff48bf5SDavid du Colombier 
9663ff48bf5SDavid du Colombier     /* Write the font header. */
9673ff48bf5SDavid du Colombier 
9683ff48bf5SDavid du Colombier     {
9693ff48bf5SDavid du Colombier 	static const byte version[4] = {0, 1, 0, 0};
9703ff48bf5SDavid du Colombier 
9713ff48bf5SDavid du Colombier 	stream_write(s, version, 4);
9723ff48bf5SDavid du Colombier     }
9733ff48bf5SDavid du Colombier     put_ushort(s, numTables);
9743ff48bf5SDavid du Colombier     for (i = 0; 1 << i <= numTables; ++i)
9753ff48bf5SDavid du Colombier 	DO_NOTHING;
9763ff48bf5SDavid du Colombier     --i;
9773ff48bf5SDavid du Colombier     put_ushort(s, 16 << i);	/* searchRange */
9783ff48bf5SDavid du Colombier     put_ushort(s, i);		/* entrySelectors */
9793ff48bf5SDavid du Colombier     put_ushort(s, numTables * 16 - (16 << i)); /* rangeShift */
9803ff48bf5SDavid du Colombier 
9813ff48bf5SDavid du Colombier     /* Write the table directory. */
9823ff48bf5SDavid du Colombier 
9833ff48bf5SDavid du Colombier     qsort(tables, numTables, 16, compare_table_tags);
9843ff48bf5SDavid du Colombier     offset = 12 + numTables * 16;
9853ff48bf5SDavid du Colombier     for (i = 0; i < numTables; ++i) {
9863ff48bf5SDavid du Colombier 	const byte *tab = &tables[i * 16];
9873ff48bf5SDavid du Colombier 	byte entry[16];
9883ff48bf5SDavid du Colombier 
9893ff48bf5SDavid du Colombier 	memcpy(entry, tab, 16);
9903ff48bf5SDavid du Colombier 	if (entry[8] < 0x40) {
9913ff48bf5SDavid du Colombier 	    /* Not a generated table. */
9923ff48bf5SDavid du Colombier 	    uint length = u32(tab + 12);
9933ff48bf5SDavid du Colombier 
9943ff48bf5SDavid du Colombier 	    put_u32(entry + 8, offset);
9953ff48bf5SDavid du Colombier 	    offset += round_up(length, 4);
9963ff48bf5SDavid du Colombier 	} else {
9973ff48bf5SDavid du Colombier 	    entry[8] -= 0x40;
9983ff48bf5SDavid du Colombier 	}
9993ff48bf5SDavid du Colombier 	stream_write(s, entry, 16);
10003ff48bf5SDavid du Colombier     }
10013ff48bf5SDavid du Colombier 
10023ff48bf5SDavid du Colombier     /* Write tables other than the ones we generate here. */
10033ff48bf5SDavid du Colombier 
10043ff48bf5SDavid du Colombier     for (i = 0; i < numTables; ++i) {
10053ff48bf5SDavid du Colombier 	const byte *tab = &tables[i * 16];
10063ff48bf5SDavid du Colombier 
10073ff48bf5SDavid du Colombier 	if (tab[8] < 0x40) {
10083ff48bf5SDavid du Colombier 	    ulong start = u32(tab + 8);
10093ff48bf5SDavid du Colombier 	    uint length = u32(tab + 12);
10103ff48bf5SDavid du Colombier 
1011593dc095SDavid du Colombier 	    switch (u32(tab)) {
1012593dc095SDavid du Colombier 	    case W('O','S','/','2'):
1013593dc095SDavid du Colombier 		if (!have_cmap) {
1014593dc095SDavid du Colombier 		    /*
1015593dc095SDavid du Colombier 		     * Adjust the first and last character indices in the OS/2
1016593dc095SDavid du Colombier 		     * table to reflect the values in the generated cmap.
1017593dc095SDavid du Colombier 		     */
1018593dc095SDavid du Colombier 		    const byte *pos2;
1019593dc095SDavid du Colombier 		    ttf_OS_2_t os2;
1020593dc095SDavid du Colombier 
1021593dc095SDavid du Colombier 		    ACCESS(OS_2_start, OS_2_length, pos2);
1022593dc095SDavid du Colombier 		    memcpy(&os2, pos2, min(OS_2_length, sizeof(os2)));
1023593dc095SDavid du Colombier 		    update_OS_2(&os2, TT_BIAS, 256);
1024593dc095SDavid du Colombier 		    stream_write(s, &os2, OS_2_length);
1025593dc095SDavid du Colombier 		    put_pad(s, OS_2_length);
1026593dc095SDavid du Colombier 		} else {
1027593dc095SDavid du Colombier 		    /* Just copy the existing OS/2 table. */
1028593dc095SDavid du Colombier 		    write_range(s, pfont, OS_2_start, OS_2_length);
1029593dc095SDavid du Colombier 		    put_pad(s, OS_2_length);
1030593dc095SDavid du Colombier 		}
1031593dc095SDavid du Colombier 	    break;
1032593dc095SDavid du Colombier 	    case W('h','h','e','a'):
1033593dc095SDavid du Colombier 	    case W('v','h','e','a'):
1034593dc095SDavid du Colombier 		if (generate_mtx) {
1035593dc095SDavid du Colombier 		    write_range(s, pfont, start, length - 2); /* 34 */
1036593dc095SDavid du Colombier 		    put_ushort(s, mtx[tab[0] == 'v'].numMetrics);
1037593dc095SDavid du Colombier 		    break;
1038593dc095SDavid du Colombier 		}
1039593dc095SDavid du Colombier 		/* falls through */
1040593dc095SDavid du Colombier 	    default:
10413ff48bf5SDavid du Colombier 		write_range(s, pfont, start, length);
1042593dc095SDavid du Colombier 	    }
10433ff48bf5SDavid du Colombier 	    put_pad(s, length);
10443ff48bf5SDavid du Colombier 	}
10453ff48bf5SDavid du Colombier     }
10463ff48bf5SDavid du Colombier 
1047593dc095SDavid du Colombier     if (!writing_stripped) {
1048593dc095SDavid du Colombier 
10493ff48bf5SDavid du Colombier 	/* Write glyf. */
10503ff48bf5SDavid du Colombier 
10513ff48bf5SDavid du Colombier 	psf_enumerate_glyphs_reset(penum);
10523ff48bf5SDavid du Colombier 	for (offset = 0; psf_enumerate_glyphs_next(penum, &glyph) != 1; ) {
1053593dc095SDavid du Colombier 	    gs_glyph_data_t glyph_data;
10543ff48bf5SDavid du Colombier 
1055593dc095SDavid du Colombier 	    glyph_data.memory = pfont->memory;
1056593dc095SDavid du Colombier 	    if ((code = pfont->data.get_outline(pfont,
1057593dc095SDavid du Colombier 						glyph & ~GS_GLYPH_TAG,
1058593dc095SDavid du Colombier 						&glyph_data)) >= 0
10593ff48bf5SDavid du Colombier 		) {
1060593dc095SDavid du Colombier 		uint l = glyph_data.bits.size, zero = 0;
1061593dc095SDavid du Colombier 
1062593dc095SDavid du Colombier 		if (!indexToLocFormat)
1063593dc095SDavid du Colombier 		    l = (l + 1) & ~1;
1064593dc095SDavid du Colombier 		stream_write(s, glyph_data.bits.data, glyph_data.bits.size);
1065593dc095SDavid du Colombier 		if (glyph_data.bits.size < l)
1066593dc095SDavid du Colombier 		    stream_write(s, &zero, 1);
1067593dc095SDavid du Colombier 		offset += l;
10683ff48bf5SDavid du Colombier 		if_debug2('L', "[L]glyf index = %u, size = %u\n",
1069593dc095SDavid du Colombier 			  i, glyph_data.bits.size);
1070593dc095SDavid du Colombier 		gs_glyph_data_free(&glyph_data, "psf_write_truetype_data");
10713ff48bf5SDavid du Colombier 	    }
10723ff48bf5SDavid du Colombier 	}
10733ff48bf5SDavid du Colombier 	if_debug1('l', "[l]glyf final offset = %lu\n", offset);
10743ff48bf5SDavid du Colombier 	/* Add a dummy byte if necessary to make glyf non-empty. */
10753ff48bf5SDavid du Colombier 	while (offset < glyf_length)
10763ff48bf5SDavid du Colombier 	    stream_putc(s, 0), ++offset;
10773ff48bf5SDavid du Colombier 	put_pad(s, (uint)offset);
10783ff48bf5SDavid du Colombier 
10793ff48bf5SDavid du Colombier 	/* Write loca. */
10803ff48bf5SDavid du Colombier 
10813ff48bf5SDavid du Colombier 	psf_enumerate_glyphs_reset(penum);
1082593dc095SDavid du Colombier 	glyph_prev = 0;
10833ff48bf5SDavid du Colombier 	for (offset = 0; psf_enumerate_glyphs_next(penum, &glyph) != 1; ) {
1084593dc095SDavid du Colombier 	    gs_glyph_data_t glyph_data;
1085593dc095SDavid du Colombier 	    uint glyph_index = glyph & ~GS_GLYPH_TAG;
10863ff48bf5SDavid du Colombier 
1087593dc095SDavid du Colombier 	    for (; glyph_prev <= glyph_index; ++glyph_prev)
10883ff48bf5SDavid du Colombier 		put_loca(s, offset, indexToLocFormat);
1089593dc095SDavid du Colombier 	    glyph_data.memory = pfont->memory;
1090593dc095SDavid du Colombier 	    if ((code = pfont->data.get_outline(pfont, glyph_index,
1091593dc095SDavid du Colombier 						&glyph_data)) >= 0
10923ff48bf5SDavid du Colombier 		) {
1093593dc095SDavid du Colombier 		uint l = glyph_data.bits.size;
1094593dc095SDavid du Colombier 
1095593dc095SDavid du Colombier 		if (!indexToLocFormat)
1096593dc095SDavid du Colombier 		    l = (l + 1) & ~1;
1097593dc095SDavid du Colombier 		offset += l;
1098593dc095SDavid du Colombier 		gs_glyph_data_free(&glyph_data, "psf_write_truetype_data");
10993ff48bf5SDavid du Colombier 	    }
11003ff48bf5SDavid du Colombier 
11013ff48bf5SDavid du Colombier 	}
11023ff48bf5SDavid du Colombier 	/* Pad to numGlyphs + 1 entries (including the trailing entry). */
1103593dc095SDavid du Colombier 	for (; glyph_prev <= numGlyphs; ++glyph_prev)
11043ff48bf5SDavid du Colombier 	    put_loca(s, offset, indexToLocFormat);
11053ff48bf5SDavid du Colombier 	put_pad(s, loca_length);
11063ff48bf5SDavid du Colombier 
11073ff48bf5SDavid du Colombier 	/* If necessary, write cmap, name, and OS/2. */
11083ff48bf5SDavid du Colombier 
11093ff48bf5SDavid du Colombier 	if (!have_cmap)
1110593dc095SDavid du Colombier 	    write_cmap(s, font, TT_BIAS, 256, GS_MIN_GLYPH_INDEX + max_glyph,
11113ff48bf5SDavid du Colombier 		       options, cmap_length);
11123ff48bf5SDavid du Colombier 	if (!have_name)
11133ff48bf5SDavid du Colombier 	    write_name(s, &font_name);
11143ff48bf5SDavid du Colombier 	if (!have_OS_2)
1115593dc095SDavid du Colombier 	    write_OS_2(s, font, TT_BIAS, 256);
11163ff48bf5SDavid du Colombier 
1117593dc095SDavid du Colombier 	/* If necessary, write [hv]mtx. */
1118593dc095SDavid du Colombier 
1119593dc095SDavid du Colombier 	if (generate_mtx)
1120593dc095SDavid du Colombier 	    for (i = 0; i < 2; ++i)
1121593dc095SDavid du Colombier 		if (have_hvhea[i]) {
1122593dc095SDavid du Colombier 		    write_mtx(s, pfont, &mtx[i], i);
1123593dc095SDavid du Colombier 		    put_pad(s, mtx[i].length);
11243ff48bf5SDavid du Colombier 		}
11253ff48bf5SDavid du Colombier 
11263ff48bf5SDavid du Colombier 	/* If necessary, write post. */
11273ff48bf5SDavid du Colombier 
11283ff48bf5SDavid du Colombier 	if (!have_post) {
11293ff48bf5SDavid du Colombier 	    if (options & WRITE_TRUETYPE_POST)
11303ff48bf5SDavid du Colombier 		write_post(s, font, &post);
11313ff48bf5SDavid du Colombier 	    else {
11323ff48bf5SDavid du Colombier 		byte post_initial[32 + 2];
11333ff48bf5SDavid du Colombier 
11343ff48bf5SDavid du Colombier 		memset(post_initial, 0, 32);
11353ff48bf5SDavid du Colombier 		put_u32(post_initial, 0x00030000);
11363ff48bf5SDavid du Colombier 		stream_write(s, post_initial, 32);
11373ff48bf5SDavid du Colombier 	    }
11383ff48bf5SDavid du Colombier 	}
1139593dc095SDavid du Colombier     }
11403ff48bf5SDavid du Colombier 
11413ff48bf5SDavid du Colombier     /* Write head. */
11423ff48bf5SDavid du Colombier 
11433ff48bf5SDavid du Colombier     /****** CHECKSUM WAS NEVER COMPUTED ******/
11443ff48bf5SDavid du Colombier     /*
11453ff48bf5SDavid du Colombier      * The following nonsense is to avoid warnings about the constant
11463ff48bf5SDavid du Colombier      * 0xb1b0afbaL being "unsigned in ANSI C, signed with -traditional".
11473ff48bf5SDavid du Colombier      */
11483ff48bf5SDavid du Colombier #if ARCH_SIZEOF_LONG > ARCH_SIZEOF_INT
11493ff48bf5SDavid du Colombier #  define HEAD_MAGIC 0xb1b0afbaL
11503ff48bf5SDavid du Colombier #else
11513ff48bf5SDavid du Colombier #  define HEAD_MAGIC ((ulong)~0x4e4f5045)
11523ff48bf5SDavid du Colombier #endif
11533ff48bf5SDavid du Colombier     put_u32(head + 8, HEAD_MAGIC - file_checksum); /* per spec */
11543ff48bf5SDavid du Colombier #undef HEAD_MAGIC
11553ff48bf5SDavid du Colombier     stream_write(s, head, 56);
11563ff48bf5SDavid du Colombier 
11573ff48bf5SDavid du Colombier     return 0;
11583ff48bf5SDavid du Colombier }
11593ff48bf5SDavid du Colombier 
11603ff48bf5SDavid du Colombier /* Write a TrueType font. */
11613ff48bf5SDavid du Colombier 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)11623ff48bf5SDavid du Colombier psf_write_truetype_font(stream *s, gs_font_type42 *pfont, int options,
11633ff48bf5SDavid du Colombier 			gs_glyph *orig_subset_glyphs, uint orig_subset_size,
11643ff48bf5SDavid du Colombier 			const gs_const_string *alt_font_name)
11653ff48bf5SDavid du Colombier {
11663ff48bf5SDavid du Colombier     gs_font *const font = (gs_font *)pfont;
11673ff48bf5SDavid du Colombier     psf_glyph_enum_t genum;
11683ff48bf5SDavid du Colombier     gs_glyph subset_data[256 * MAX_COMPOSITE_PIECES];
11693ff48bf5SDavid du Colombier     gs_glyph *subset_glyphs = orig_subset_glyphs;
11703ff48bf5SDavid du Colombier     uint subset_size = orig_subset_size;
11713ff48bf5SDavid du Colombier 
11723ff48bf5SDavid du Colombier     /* Sort the subset glyphs, if any. */
11733ff48bf5SDavid du Colombier 
11743ff48bf5SDavid du Colombier     if (subset_glyphs) {
11753ff48bf5SDavid du Colombier 	/* Add the component glyphs for composites. */
11763ff48bf5SDavid du Colombier 	int code;
11773ff48bf5SDavid du Colombier 
11783ff48bf5SDavid du Colombier 	memcpy(subset_data, orig_subset_glyphs,
11793ff48bf5SDavid du Colombier 	       sizeof(gs_glyph) * subset_size);
11803ff48bf5SDavid du Colombier 	subset_glyphs = subset_data;
11813ff48bf5SDavid du Colombier 	code = psf_add_subset_pieces(subset_glyphs, &subset_size,
11823ff48bf5SDavid du Colombier 				     countof(subset_data),
11833ff48bf5SDavid du Colombier 				     countof(subset_data),
11843ff48bf5SDavid du Colombier 				     font);
11853ff48bf5SDavid du Colombier 	if (code < 0)
11863ff48bf5SDavid du Colombier 	    return code;
11873ff48bf5SDavid du Colombier 	subset_size = psf_sort_glyphs(subset_glyphs, subset_size);
11883ff48bf5SDavid du Colombier     }
11893ff48bf5SDavid du Colombier     psf_enumerate_glyphs_begin(&genum, font, subset_glyphs,
11903ff48bf5SDavid du Colombier 			       (subset_glyphs ? subset_size : 0),
11913ff48bf5SDavid du Colombier 			       GLYPH_SPACE_INDEX);
11923ff48bf5SDavid du Colombier     return psf_write_truetype_data(s, pfont, options & ~WRITE_TRUETYPE_CID,
11933ff48bf5SDavid du Colombier 				   &genum, subset_glyphs != 0, alt_font_name);
11943ff48bf5SDavid du Colombier }
1195593dc095SDavid du Colombier /* Write a stripped TrueType font. */
1196593dc095SDavid du Colombier int
psf_write_truetype_stripped(stream * s,gs_font_type42 * pfont)1197593dc095SDavid du Colombier psf_write_truetype_stripped(stream *s, gs_font_type42 *pfont)
1198593dc095SDavid du Colombier {
1199593dc095SDavid du Colombier     psf_glyph_enum_t genum;
1200593dc095SDavid du Colombier     byte no_subset = 0;
1201593dc095SDavid du Colombier 
1202593dc095SDavid du Colombier     psf_enumerate_bits_begin(&genum, (gs_font *)pfont, &no_subset, 0,
1203593dc095SDavid du Colombier 			     GLYPH_SPACE_INDEX);
1204593dc095SDavid du Colombier     return psf_write_truetype_data(s, pfont, WRITE_TRUETYPE_STRIPPED,
1205593dc095SDavid du Colombier 				   &genum, true, NULL);
1206593dc095SDavid du Colombier }
12073ff48bf5SDavid du Colombier 
12083ff48bf5SDavid du Colombier /* Write a CIDFontType 2 font. */
12093ff48bf5SDavid du Colombier 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)12103ff48bf5SDavid du Colombier psf_write_cid2_font(stream *s, gs_font_cid2 *pfont, int options,
12113ff48bf5SDavid du Colombier 		    const byte *subset_bits, uint subset_size,
12123ff48bf5SDavid du Colombier 		    const gs_const_string *alt_font_name)
12133ff48bf5SDavid du Colombier {
12143ff48bf5SDavid du Colombier     gs_font *const font = (gs_font *)pfont;
12153ff48bf5SDavid du Colombier     psf_glyph_enum_t genum;
12163ff48bf5SDavid du Colombier 
12173ff48bf5SDavid du Colombier     psf_enumerate_bits_begin(&genum, font, subset_bits,
12183ff48bf5SDavid du Colombier 			     (subset_bits ? subset_size : 0),
12193ff48bf5SDavid du Colombier 			     GLYPH_SPACE_INDEX);
12203ff48bf5SDavid du Colombier     return psf_write_truetype_data(s, (gs_font_type42 *)font,
1221593dc095SDavid du Colombier 				   options | WRITE_TRUETYPE_CID, &genum,
12223ff48bf5SDavid du Colombier 				   subset_bits != 0, alt_font_name);
12233ff48bf5SDavid du Colombier }
1224593dc095SDavid du Colombier 
1225593dc095SDavid du Colombier /* Write a stripped CIDFontType 2 font. */
1226593dc095SDavid du Colombier int
psf_write_cid2_stripped(stream * s,gs_font_cid2 * pfont)1227593dc095SDavid du Colombier psf_write_cid2_stripped(stream *s, gs_font_cid2 *pfont)
1228593dc095SDavid du Colombier {
1229593dc095SDavid du Colombier     gs_font *const font = (gs_font *)pfont;
1230593dc095SDavid du Colombier     psf_glyph_enum_t genum;
1231593dc095SDavid du Colombier     byte no_subset = 0;
1232593dc095SDavid du Colombier 
1233593dc095SDavid du Colombier     psf_enumerate_bits_begin(&genum, font, &no_subset, 0,
1234593dc095SDavid du Colombier 			     GLYPH_SPACE_INDEX);
1235593dc095SDavid du Colombier     return psf_write_truetype_data(s, (gs_font_type42 *)font,
1236593dc095SDavid du Colombier 				   WRITE_TRUETYPE_STRIPPED |
1237593dc095SDavid du Colombier 				     WRITE_TRUETYPE_CID,
1238593dc095SDavid du Colombier 				   &genum, true, NULL);
1239593dc095SDavid du Colombier }
1240