]> git.donarmstrong.com Git - lilypond.git/blob - lily/ttf.cc
Completely revised support for conversion of TTFs to T42 fonts.
[lilypond.git] / lily / ttf.cc
1 /*
2   ttf.cc --  implement ttf -> pfa routine.
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2005--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "freetype.hh"
10
11 #include <freetype/tttables.h>
12
13 #include "memory-stream.hh"
14 #include "warn.hh"
15 #include "lily-guile.hh"
16 #include "main.hh"
17 #include "open-type-font.hh"
18
19
20 Index_to_charcode_map
21 make_index_to_charcode_map (FT_Face face)
22 {
23   Index_to_charcode_map m;
24   FT_ULong charcode;
25   FT_UInt gindex;
26
27   FT_CharMap current_cmap = face->charmap;
28   FT_Select_Charmap (face, FT_ENCODING_UNICODE);
29
30   int j = 0;
31   for (charcode = FT_Get_First_Char (face, &gindex); gindex != 0;
32        charcode = FT_Get_Next_Char (face, charcode, &gindex))
33     {
34       m[gindex] = charcode;
35       j ++;
36     }
37   FT_Set_Charmap (face, current_cmap);
38
39   return m;
40 }
41
42 /*
43   Based on ttfps by Juliusz Chroboczek
44 */
45 static void
46 print_header (void *out, FT_Face face)
47 {
48   lily_cookie_fprintf (out, "%%!PS-TrueTypeFont\n");
49
50   TT_Postscript *pt
51     = (TT_Postscript *) FT_Get_Sfnt_Table (face, ft_sfnt_post);
52
53   if (pt->maxMemType42)
54     lily_cookie_fprintf (out, "%%%%VMUsage: %d %d\n", 0, 0);
55
56   lily_cookie_fprintf (out, "%d dict begin\n", 11);
57   lily_cookie_fprintf (out, "/FontName /%s def\n",
58                        FT_Get_Postscript_Name (face));
59
60   lily_cookie_fprintf (out, "/Encoding StandardEncoding def\n");
61   lily_cookie_fprintf (out, "/PaintType 0 def\n");
62   lily_cookie_fprintf (out, "/FontMatrix [1 0 0 1 0 0] def\n");
63
64   TT_Header *ht
65     = (TT_Header *)FT_Get_Sfnt_Table (face, ft_sfnt_head);
66
67   lily_cookie_fprintf (out, "/FontBBox [%lf %lf %lf %lf] def\n",
68                        float (ht->xMin) / ht->Units_Per_EM,
69                        float (ht->yMin) / ht->Units_Per_EM,
70                        float (ht->xMax) / ht->Units_Per_EM,
71                        float (ht->yMax) / ht->Units_Per_EM);
72
73   lily_cookie_fprintf (out, "/FontType 42 def\n");
74   lily_cookie_fprintf (out, "/FontInfo 8 dict dup begin\n");
75   lily_cookie_fprintf (out, "/version (%.3f) def\n",
76                        ht->Font_Revision / 65536.0);
77
78 #if 0
79   if (strings[0])
80     {
81       lily_cookie_fprintf (out, "/Notice (");
82       fputpss (strings[0], out);
83       lily_cookie_fprintf (out, ") def\n");
84     }
85   if (strings[4])
86     {
87       lily_cookie_fprintf (out, "/FullName (");
88       fputpss (strings[4], out);
89       lily_cookie_fprintf (out, ") def\n");
90     }
91   if (strings[1])
92     {
93       lily_cookie_fprintf (out, "/FamilyName (");
94       fputpss (strings[1], out);
95       lily_cookie_fprintf (out, ") def\n");
96     }
97 #endif
98
99   lily_cookie_fprintf (out, "/isFixedPitch %s def\n",
100                        pt->isFixedPitch ? "true" : "false");
101   lily_cookie_fprintf (out, "/UnderlinePosition %lf def\n",
102                        float (pt->underlinePosition) / ht->Units_Per_EM);
103   lily_cookie_fprintf (out, "/UnderlineThickness %lf def\n",
104                        float (pt->underlineThickness) / ht->Units_Per_EM);
105   lily_cookie_fprintf (out, "end readonly def\n");
106 }
107
108 #define CHUNKSIZE 65534
109
110 const FT_ULong FT_ENC_TAG (glyf_tag, 'g', 'l', 'y', 'f');
111 const FT_ULong FT_ENC_TAG (head_tag, 'h', 'e', 'a', 'd');
112 const FT_ULong FT_ENC_TAG (loca_tag, 'l', 'o', 'c', 'a');
113
114 static
115 void t42_write_table (void *out, FT_Face face, unsigned char const *buffer,
116                       size_t s, bool is_glyf,
117                       FT_ULong head_length, FT_ULong loca_length)
118 {
119   vector<FT_UShort> chunks;             /* FIXME: use dynamic array */
120
121   if (is_glyf)
122     {
123       /* compute chunk sizes */
124       unsigned char *head_buf = new unsigned char[head_length];
125       FT_Error error = FT_Load_Sfnt_Table (face, head_tag, 0, head_buf, NULL);
126       if (error)
127         programming_error ("FT_Load_Sfnt_Table (): error.");
128
129       /* we access the lower byte of indexToLocFormat */
130       bool long_offsets = head_buf[4*4 + 2*2 + 2*8 + 4*2 + 3*2 + 1] == 1;
131
132       delete[] head_buf;
133
134       unsigned char *loca_buf = new unsigned char[loca_length];
135       error = FT_Load_Sfnt_Table (face, loca_tag, 0, loca_buf, NULL);
136       if (error)
137         programming_error ("FT_Load_Sfnt_Table (): error.");
138
139       unsigned char *p = loca_buf;
140       unsigned char *endp = loca_buf + loca_length;
141
142       FT_ULong offset = 0, last_offset = 0, last_chunk = 0;
143       while (p < endp)
144       {
145         if (long_offsets)
146           {
147             offset = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
148             p += 4;
149           }
150         else
151           {
152             offset = ((p[0] << 8) | p[1]) << 1;
153             p += 2;
154           }
155         if (offset > last_offset + CHUNKSIZE)
156           {
157             if (last_chunk != last_offset)
158               {
159                 chunks.push_back (last_offset - last_chunk);
160               }
161             /*
162               a single glyph with more than 64k data
163               is a pathological case but...
164              */
165             FT_ULong rest = offset - last_offset;
166             while (rest > CHUNKSIZE)
167               {
168                 chunks.push_back (CHUNKSIZE);
169                 rest -= CHUNKSIZE;
170               }
171             chunks.push_back (rest);
172             last_chunk = offset;
173           }
174         else if (offset > last_chunk + CHUNKSIZE)
175           {
176             chunks.push_back (last_offset - last_chunk);
177             last_chunk = last_offset;
178             assert (cur_chunk_idx < 100);/* FIXME: only for static arrays */
179           }
180
181         last_offset = offset;
182       }
183       chunks.push_back (s - last_chunk)
184
185       delete[] loca_buf;
186     }
187   else if (s > CHUNKSIZE)
188     {
189       FT_ULong rest = s;
190       while (rest > CHUNKSIZE)
191         {
192           chunks.push_back (CHUNKSIZE);
193           rest -= CHUNKSIZE;
194         }
195       chunks.push_back (rest);
196     }
197   else
198     chunks.push_back (CHUNKSIZE);
199
200   lily_cookie_fprintf (out, "\n"
201                             " <");
202
203   int l = 0;
204   static char xdigits[] = "0123456789ABCDEF";
205
206   int cur_chunk_idx = 0;
207   for (size_t j = 0; j < s; j++)
208     {
209       if (l >= chunks[cur_chunk_idx])
210         {
211           lily_cookie_fprintf (out, "\n"
212                                     " 00>\n"
213                                     " <");
214           l = 0;
215           cur_chunk_idx ++;
216         }
217
218       if (l % 31 == 0)
219         lily_cookie_fprintf (out, "\n"
220                                   "  ");
221
222       /* lily_cookie_fprintf (out,"%02X",(int)buffer[j]) is too slow */
223       lily_cookie_putc (xdigits[(buffer[j] & 0xF0) >> 4], out);
224       lily_cookie_putc (xdigits[buffer[j] & 0x0F], out);
225
226       l ++;
227     }
228
229   /* pad to four-byte boundary */
230   while ((s ++) % 4 != 0)
231     lily_cookie_fprintf (out, "00");
232
233   lily_cookie_fprintf (out, "\n"
234                             "  00\n"
235                             " >");
236 }
237
238 static void
239 print_body (void *out, FT_Face face)
240 {
241   FT_UInt idx = 0;
242   FT_ULong head_length = 0, loca_length = 0;
243   FT_ULong tag, length;
244   FT_ULong lengths[100], tags[100];     /* FIXME: use dynamic arrays */
245
246   /*
247     we must build our own TTF header -- the original font
248     might be a TTC where tables are not contiguous, or the font
249     contains tables which aren't indexed at all
250    */
251   while (FT_Sfnt_Table_Info (face, idx, &tag, &length)
252          != FT_Err_Table_Missing)
253   {
254     assert (idx < 100);                 /* FIXME: only for static arrays */
255     lengths[idx] = length;
256     tags[idx ++] = tag;
257     if (tag == head_tag)
258       head_length = length;
259     else if (tag == loca_tag)
260       loca_length = length;
261   }
262
263   FT_ULong hlength = 12 + 16 * idx;
264
265   unsigned char *hbuf = new unsigned char[hlength];
266   unsigned char *p;
267
268   hbuf[0] = 0x00;                       /* version */
269   hbuf[1] = 0x01;
270   hbuf[2] = 0x00;
271   hbuf[3] = 0x00;
272   hbuf[4] = (idx & 0xFF00) >> 8;        /* numTables */
273   hbuf[5] = idx & 0x00FF;
274
275   FT_UInt searchRange, entrySelector, rangeShift;
276   FT_UInt i, j;
277   for (i = 1, j = 2; j <= idx; i++, j <<= 1)
278     ;
279   entrySelector = i - 1;
280   searchRange = 0x10 << entrySelector;
281   rangeShift = (idx << 4) - searchRange;
282
283   hbuf[6] = (searchRange & 0xFF00) >> 8;
284   hbuf[7] = searchRange & 0x00FF;
285   hbuf[8] = (entrySelector & 0xFF00) >> 8;
286   hbuf[9] = entrySelector & 0x00FF;
287   hbuf[10] = (rangeShift & 0xFF00) >> 8;
288   hbuf[11] = rangeShift & 0x00FF;
289
290   p = &hbuf[12];
291
292   FT_ULong checksum, font_checksum = 0;
293
294   FT_ULong offset = hlength;            /* first table offset */
295
296   for (FT_UInt i = 0; i < idx; i++)
297   {
298     /* here, the buffer length must be a multiple of 4 */
299     FT_ULong len = (lengths[i] + 3) & ~3;
300     unsigned char *buf = new unsigned char[len];
301
302     buf[len - 1] = 0x00;                /* assure padding with zeros */
303     buf[len - 2] = 0x00;
304     buf[len - 3] = 0x00;
305
306     FT_Error error = FT_Load_Sfnt_Table (face, tags[i], 0, buf, NULL);
307     if (error)
308       programming_error ("FT_Load_Sfnt_Table (): error.");
309
310     if (tag == head_tag)
311       {
312         /*
313           first pass of computing the font checksum
314           needs checkSumAdjustment = 0
315          */
316         buf[8] = 0x00;
317         buf[9] = 0x00;
318         buf[10] = 0x00;
319         buf[11] = 0x00;
320       }
321
322     checksum = 0;
323     unsigned char *endq = buf + len;
324     for (unsigned char *q = buf; q < endq; q += 4)
325       checksum += (q[0] << 24) | (q[1] << 16) | (q[2] << 8) | q[3];
326     font_checksum += checksum;
327
328     delete[] buf;
329
330     *(p++) = (tags[i] & 0xFF000000UL) >> 24;
331     *(p++) = (tags[i] & 0x00FF0000UL) >> 16;
332     *(p++) = (tags[i] & 0x0000FF00UL) >> 8;
333     *(p++) = tags[i] & 0x000000FFUL;
334
335     *(p++) = (checksum & 0xFF000000UL) >> 24;
336     *(p++) = (checksum & 0x00FF0000UL) >> 16;
337     *(p++) = (checksum & 0x0000FF00UL) >> 8;
338     *(p++) = checksum & 0x000000FFUL;
339
340     *(p++) = (offset & 0xFF000000UL) >> 24;
341     *(p++) = (offset & 0x00FF0000UL) >> 16;
342     *(p++) = (offset & 0x0000FF00UL) >> 8;
343     *(p++) = offset & 0x000000FFUL;
344
345     *(p++) = (lengths[i] & 0xFF000000UL) >> 24;
346     *(p++) = (lengths[i] & 0x00FF0000UL) >> 16;
347     *(p++) = (lengths[i] & 0x0000FF00UL) >> 8;
348     *(p++) = lengths[i] & 0x000000FFUL;
349
350     /* offset must be a multiple of 4 */
351     offset += (lengths[i] + 3) & ~3;
352   }
353
354   /* add checksum of TTF header */
355   checksum = 0;
356   for (unsigned char *q = hbuf; q < p; q += 4)
357     checksum += (q[0] << 24) | (q[1] << 16) | (q[2] << 8) | q[3];
358   font_checksum += checksum;
359   font_checksum = 0xB1B0AFBAUL - font_checksum;
360
361   /*
362     see Adobe technical note 5012.Type42_Spec.pdf for details how
363     the /sfnts array must be constructed
364    */
365   lily_cookie_fprintf (out, "/sfnts [");
366   t42_write_table (out, face, hbuf, hlength, false,
367                    head_length, loca_length);
368   delete[] hbuf;
369
370   idx = 0;
371
372   while (FT_Sfnt_Table_Info (face, idx, &tag, &length)
373          != FT_Err_Table_Missing)
374     {
375       unsigned char *buf = new unsigned char[length];
376       FT_Error error = FT_Load_Sfnt_Table (face, tag, 0, buf, NULL);
377       if (error)
378         programming_error ("FT_Load_Sfnt_Table (): error.");
379
380       if (tag == head_tag)
381         {
382           /* in the second pass simply store the computed font checksum */
383           buf[8] = (font_checksum & 0xFF000000UL) >> 24;
384           buf[9] = (font_checksum & 0x00FF0000UL) >> 16;
385           buf[10] = (font_checksum & 0x0000FF00UL) >> 8;
386           buf[11] = font_checksum & 0x000000FFUL;
387         }
388
389       bool is_glyf_table = tag == glyf_tag && length > CHUNKSIZE;
390       t42_write_table (out, face, buf, length, is_glyf_table,
391                        head_length, loca_length);
392
393       delete[] buf;
394       idx ++;
395     }
396   lily_cookie_fprintf (out, "\n] def\n");
397 }
398
399 static void
400 print_trailer (void *out,
401                FT_Face face)
402 {
403   const int GLYPH_NAME_LEN = 256;
404   char glyph_name[GLYPH_NAME_LEN];
405
406   TT_MaxProfile *mp
407     = (TT_MaxProfile *)FT_Get_Sfnt_Table (face, ft_sfnt_maxp);
408
409   lily_cookie_fprintf (out, "/CharStrings %d dict dup begin\n", mp->numGlyphs);
410
411   Index_to_charcode_map ic_map (make_index_to_charcode_map (face));
412
413   int output_count = 0;
414   for (int i = 0; i < mp->numGlyphs; i++)
415     {
416       glyph_name[0] = 0;
417       if (face->face_flags & FT_FACE_FLAG_GLYPH_NAMES)
418         {
419           FT_Error error = FT_Get_Glyph_Name (face, i, glyph_name,
420                                               GLYPH_NAME_LEN);
421           if (error)
422             {
423               programming_error ("FT_Get_Glyph_Name (): error.");
424               glyph_name[0] = 0;
425             }
426         }
427
428       if (!glyph_name[0] && ic_map.find (i) != ic_map.end ())
429         {
430           FT_ULong ucode = ic_map[i];
431           get_unicode_name (glyph_name, ucode);
432         }
433
434       if (i == 0)
435         sprintf (glyph_name, ".notdef");
436       else if (glyph_name == string (".notdef"))
437         glyph_name[0] = '\0';
438
439       if (!glyph_name[0])
440         get_glyph_index_name (glyph_name, i);
441
442       if (glyph_name[0])
443         {
444           lily_cookie_fprintf (out, "/%s %d def ", glyph_name, i);
445           output_count ++;
446         }
447       else
448         programming_error (to_string ("no name for glyph %d", i));
449                         
450       if (! (output_count % 5))
451         lily_cookie_fprintf (out, "\n");
452     }
453
454   lily_cookie_fprintf (out, "end readonly def\n");
455   lily_cookie_fprintf (out, "FontName currentdict end definefont pop\n");
456 }
457
458 static void
459 create_type42_font (void *out, string name)
460 {
461   FT_Face face = open_ft_face (name);
462
463   print_header (out, face);
464   print_body (out, face);
465   print_trailer (out, face);
466
467   FT_Done_Face (face);
468 }
469
470
471 LY_DEFINE (ly_ttf_ps_name, "ly:ttf-ps-name",
472            1, 0, 0, (SCM ttf_file_name),
473            "Extract the PostScript name from a TrueType font.")
474 {
475   LY_ASSERT_TYPE (scm_is_string, ttf_file_name, 1);
476   string file_name = ly_scm2string (ttf_file_name);
477   if (be_verbose_global)
478     progress_indication ("[" + file_name);
479
480   FT_Face face = open_ft_face (file_name);
481   char const *ps_name_str0 = FT_Get_Postscript_Name (face);
482   SCM ps_name = scm_from_locale_string (ps_name_str0 ? ps_name_str0 : "");
483
484   FT_Done_Face (face);
485
486   if (be_verbose_global)
487     progress_indication ("]");
488
489   return ps_name;
490 }
491
492
493
494 LY_DEFINE (ly_ttf_2_pfa, "ly:ttf->pfa",
495            1, 0, 0, (SCM ttf_file_name),
496            "Convert the contents of a TTF file to Type42 PFA, returning it as"
497            " a string.")
498 {
499   LY_ASSERT_TYPE (scm_is_string, ttf_file_name, 1);
500
501   string file_name = ly_scm2string (ttf_file_name);
502   if (be_verbose_global)
503     progress_indication ("[" + file_name);
504
505   Memory_out_stream stream;
506
507   create_type42_font (&stream, file_name);
508   SCM asscm = scm_from_locale_stringn (stream.get_string (),
509                                        stream.get_length ());
510
511   if (be_verbose_global)
512     progress_indication ("]");
513
514   return asscm;
515 }