RIP, Vernon...
[ttfautohint.git] / frontend / info.cpp
blob5fa6fd9c2cae24225fa234d5a1d8f000642b79fd
1 // info.cpp
3 // Copyright (C) 2012-2016 by Werner Lemberg.
4 //
5 // This file is part of the ttfautohint library, and may only be used,
6 // modified, and distributed under the terms given in `COPYING'. By
7 // continuing to use, modify, or distribute this file you indicate that you
8 // have read `COPYING' and understand and accept it fully.
9 //
10 // The file `COPYING' mentioned in the previous paragraph is distributed
11 // with the ttfautohint library.
14 #include <config.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <stdlib.h>
18 // the next header file is from gnulib defining function `base_name',
19 // which is a replacement for `basename' that works on Windows also
20 #include "dirname.h"
22 #include "info.h"
23 #include <sds.h>
24 #include <numberset.h>
27 #define TTFAUTOHINT_STRING "; ttfautohint"
28 #define TTFAUTOHINT_STRING_WIDE "\0;\0 \0t\0t\0f\0a\0u\0t\0o\0h\0i\0n\0t"
31 extern "C" {
33 const char invalid_ps_chars[96] =
35 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, // 0x20 %, (, ), /
36 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, // 0x30 <, >
37 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40
38 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, // 0x50 [, ]
39 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60
40 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, // 0x70 {, }, DEL
44 char*
45 check_family_suffix(const char* s)
47 while (*s)
49 int c = (signed char)*s - 0x20;
51 // valid range is 0x20-0x7E minus characters %()/<>[]{}
52 // (well, the space character 0x20 is not valid within a PS name,
53 // but the validity test gets executed before constructing a PS name,
54 // which has all space characters removed)
55 if (c < 0 || invalid_ps_chars[c])
56 return (char*)s;
58 s++;
61 return NULL;
65 // build string that gets appended to the `Version' field(s)
66 // return value 1 means allocation error, value 2 too long a string
67 int
68 build_version_string(Info_Data* idata)
70 // since we use `goto' we have to initialize variables before the jumps
71 unsigned char* info_string;
72 unsigned char* info_string_wide;
73 unsigned char* dt;
74 unsigned char* dtw;
75 char* s = NULL;
76 char strong[4];
77 int count;
78 int ret = 0;
79 sds d;
81 d = sdsempty();
83 d = sdscatprintf(d, TTFAUTOHINT_STRING " (v%s)", VERSION);
84 if (!idata->detailed_info)
85 goto Skip;
87 if (idata->dehint)
89 d = sdscat(d, " -d");
90 goto Skip;
92 d = sdscatprintf(d, " -l %d", idata->hinting_range_min);
93 d = sdscatprintf(d, " -r %d", idata->hinting_range_max);
94 d = sdscatprintf(d, " -G %d", idata->hinting_limit);
95 d = sdscatprintf(d, " -x %d", idata->increase_x_height);
96 if (idata->fallback_stem_width)
97 d = sdscatprintf(d, " -H %d", idata->fallback_stem_width);
98 d = sdscatprintf(d, " -D %s", idata->default_script);
99 d = sdscatprintf(d, " -f %s", idata->fallback_script);
100 if (idata->control_name)
102 char* bn = base_name(idata->control_name);
103 d = sdscatprintf(d, " -m \"%s\"", bn ? bn : idata->control_name);
104 free(bn);
107 count = 0;
108 strong[0] = '\0';
109 strong[1] = '\0';
110 strong[2] = '\0';
111 strong[3] = '\0';
112 if (idata->gray_strong_stem_width)
113 strong[count++] = 'g';
114 if (idata->gdi_cleartype_strong_stem_width)
115 strong[count++] = 'G';
116 if (idata->dw_cleartype_strong_stem_width)
117 strong[count++] = 'D';
118 if (*strong)
119 d = sdscatprintf(d, " -w %s", strong);
120 else
121 d = sdscat(d, " -w \"\"");
123 if (idata->windows_compatibility)
124 d = sdscat(d, " -W");
125 if (idata->adjust_subglyphs)
126 d = sdscat(d, " -p");
127 if (idata->hint_composites)
128 d = sdscat(d, " -c");
129 if (idata->symbol)
130 d = sdscat(d, " -s");
131 if (idata->fallback_scaling)
132 d = sdscat(d, " -S");
133 if (idata->TTFA_info)
134 d = sdscat(d, " -t");
136 if (idata->x_height_snapping_exceptions_string)
138 // only set specific value of `ret' for an allocation error,
139 // since syntax errors are handled in TTF_autohint
140 number_range* x_height_snapping_exceptions;
141 const char* pos = number_set_parse(
142 idata->x_height_snapping_exceptions_string,
143 &x_height_snapping_exceptions,
144 6, 0x7FFF);
145 if (*pos)
147 if (x_height_snapping_exceptions == NUMBERSET_ALLOCATION_ERROR)
148 ret = 1;
149 goto Fail;
152 s = number_set_show(x_height_snapping_exceptions, 6, 0x7FFF);
153 number_set_free(x_height_snapping_exceptions);
155 // ensure UTF16-BE version doesn't get too long
156 if (strlen(s) > 0xFFFF / 2 - sdslen(d))
158 ret = 2;
159 goto Fail;
162 d = sdscatprintf(d, " -X \"%s\"", s);
165 Skip:
166 if (!d)
168 ret = 1;
169 goto Fail;
172 info_string = (unsigned char*)malloc(sdslen(d) + 1);
173 if (!info_string)
175 ret = 1;
176 goto Fail;
178 memcpy(info_string, d, sdslen(d) + 1);
180 idata->info_string = info_string;
181 idata->info_string_len = (unsigned short)sdslen(d);
183 // prepare UTF16-BE version data
184 idata->info_string_wide_len = 2 * idata->info_string_len;
185 info_string_wide = (unsigned char*)realloc(idata->info_string_wide,
186 idata->info_string_wide_len);
187 if (!info_string_wide)
189 ret = 1;
190 goto Fail;
192 idata->info_string_wide = info_string_wide;
194 dt = idata->info_string;
195 dtw = idata->info_string_wide;
196 for (unsigned short i = 0; i < idata->info_string_len; i++)
198 *(dtw++) = '\0';
199 *(dtw++) = *(dt++);
202 Exit:
203 free(s);
204 sdsfree(d);
206 return ret;
208 Fail:
209 free(idata->info_string);
210 free(idata->info_string_wide);
212 idata->info_string = NULL;
213 idata->info_string_wide = NULL;
214 idata->info_string_len = 0;
215 idata->info_string_wide_len = 0;
217 goto Exit;
221 static int
222 info_name_id_5(unsigned short platform_id,
223 unsigned short encoding_id,
224 unsigned short* len,
225 unsigned char** str,
226 Info_Data* idata)
228 unsigned char ttfautohint_string[] = TTFAUTOHINT_STRING;
229 unsigned char ttfautohint_string_wide[] = TTFAUTOHINT_STRING_WIDE;
231 // we use memmem, so don't count the trailing \0 character
232 size_t ttfautohint_string_len = sizeof (TTFAUTOHINT_STRING) - 1;
233 size_t ttfautohint_string_wide_len = sizeof (TTFAUTOHINT_STRING_WIDE) - 1;
235 unsigned char* v;
236 unsigned short v_len;
237 unsigned char* s;
238 size_t s_len;
239 size_t offset;
241 if (platform_id == 1
242 || (platform_id == 3 && !(encoding_id == 1
243 || encoding_id == 10)))
245 // one-byte or multi-byte encodings
246 v = idata->info_string;
247 v_len = idata->info_string_len;
248 s = ttfautohint_string;
249 s_len = ttfautohint_string_len;
250 offset = 2;
252 else
254 // (two-byte) UTF-16BE for everything else
255 v = idata->info_string_wide;
256 v_len = idata->info_string_wide_len;
257 s = ttfautohint_string_wide;
258 s_len = ttfautohint_string_wide_len;
259 offset = 4;
262 // if we already have an ttfautohint info string,
263 // remove it up to a following `;' character (or end of string)
264 unsigned char* s_start = (unsigned char*)memmem(*str, *len, s, s_len);
265 if (s_start)
267 unsigned char* s_end = s_start + offset;
268 unsigned char* limit = *str + *len;
270 while (s_end < limit)
272 if (*s_end == ';')
274 if (offset == 2)
275 break;
276 else
278 if (*(s_end - 1) == '\0') // UTF-16BE
280 s_end--;
281 break;
286 s_end++;
289 while (s_end < limit)
290 *s_start++ = *s_end++;
292 *len -= s_end - s_start;
295 // do nothing if the string would become too long
296 if (*len > 0xFFFF - v_len)
297 return 0;
299 unsigned short len_new = *len + v_len;
300 unsigned char* str_new = (unsigned char*)realloc(*str, len_new);
301 if (!str_new)
302 return 1;
304 *str = str_new;
305 memcpy(*str + *len, v, v_len);
306 *len = len_new;
308 return 0;
312 // a structure to collect family data for a given
313 // (platform_id, encoding_id, language_id) triplet
315 typedef struct Family_
317 unsigned short platform_id;
318 unsigned short encoding_id;
319 unsigned short language_id;
321 unsigned short* name_id_1_len;
322 unsigned char** name_id_1_str;
323 unsigned short* name_id_4_len;
324 unsigned char** name_id_4_str;
325 unsigned short* name_id_6_len;
326 unsigned char** name_id_6_str;
327 unsigned short* name_id_16_len;
328 unsigned char** name_id_16_str;
329 unsigned short* name_id_21_len;
330 unsigned char** name_id_21_str;
332 sds family_name;
333 } Family;
336 // node structure for collected family data
338 typedef struct Node Node;
339 struct Node
341 LLRB_ENTRY(Node) entry;
342 Family family;
346 // comparison function for our red-black tree
348 static int
349 nodecmp(Node *e1,
350 Node *e2)
352 int diff;
354 // sort by platform ID ...
355 diff = e1->family.platform_id - e2->family.platform_id;
356 if (diff)
357 goto Exit;
359 // ... then by encoding ID ...
360 diff = e1->family.encoding_id - e2->family.encoding_id;
361 if (diff)
362 goto Exit;
364 // ... then by language ID
365 diff = e1->family.language_id - e2->family.language_id;
367 Exit:
368 // https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign
369 return (diff > 0) - (diff < 0);
373 // the red-black tree function body
374 typedef struct family_data family_data;
376 LLRB_HEAD(family_data, Node);
378 // no trailing semicolon in the next line
379 LLRB_GENERATE_STATIC(family_data, Node, entry, nodecmp)
382 static void
383 family_data_free(Info_Data* idata)
385 family_data* family_data_head = (family_data*)idata->family_data_head;
387 Node* node;
388 Node* next_node;
390 if (!family_data_head)
391 return;
393 for (node = LLRB_MIN(family_data, family_data_head);
394 node;
395 node = next_node)
397 next_node = LLRB_NEXT(family_data, family_data_head, node);
398 LLRB_REMOVE(family_data, family_data_head, node);
399 sdsfree(node->family.family_name);
400 free(node);
403 free(family_data_head);
407 static int
408 collect_family_data(unsigned short platform_id,
409 unsigned short encoding_id,
410 unsigned short language_id,
411 unsigned short name_id,
412 unsigned short* len,
413 unsigned char** str,
414 Info_Data* idata)
416 family_data* family_data_head = (family_data*)idata->family_data_head;
417 Node* node;
418 Node* val;
420 if (!family_data_head)
422 // first-time initialization
423 family_data_head = (family_data*)malloc(sizeof (family_data));
424 if (!family_data_head)
425 return 1;
427 LLRB_INIT(family_data_head);
428 idata->family_data_head = family_data_head;
431 node = (Node*)malloc(sizeof (Node));
432 if (!node)
433 return 1;
435 node->family.platform_id = platform_id;
436 node->family.encoding_id = encoding_id;
437 node->family.language_id = language_id;
439 val = LLRB_INSERT(family_data, family_data_head, node);
440 if (val)
442 // we already have an entry in the tree for our triplet
443 free(node);
444 node = val;
446 else
448 // initialize remaining fields
449 node->family.name_id_1_len = NULL;
450 node->family.name_id_1_str = NULL;
451 node->family.name_id_4_len = NULL;
452 node->family.name_id_4_str = NULL;
453 node->family.name_id_6_len = NULL;
454 node->family.name_id_6_str = NULL;
455 node->family.name_id_16_len = NULL;
456 node->family.name_id_16_str = NULL;
457 node->family.name_id_21_len = NULL;
458 node->family.name_id_21_str = NULL;
460 node->family.family_name = NULL;
463 if (name_id == 1)
465 node->family.name_id_1_len = len;
466 node->family.name_id_1_str = str;
468 else if (name_id == 4)
470 node->family.name_id_4_len = len;
471 node->family.name_id_4_str = str;
473 else if (name_id == 6)
475 node->family.name_id_6_len = len;
476 node->family.name_id_6_str = str;
478 else if (name_id == 16)
480 node->family.name_id_16_len = len;
481 node->family.name_id_16_str = str;
483 else if (name_id == 21)
485 node->family.name_id_21_len = len;
486 node->family.name_id_21_str = str;
489 return 0;
493 // `info-callback' function
496 info(unsigned short platform_id,
497 unsigned short encoding_id,
498 unsigned short language_id,
499 unsigned short name_id,
500 unsigned short* len,
501 unsigned char** str,
502 void* user)
504 Info_Data* idata = (Info_Data*)user;
506 // if ID is a version string, append our data
507 if (!idata->no_info && name_id == 5)
508 return info_name_id_5(platform_id, encoding_id, len, str, idata);
510 // if ID is related to a family name, collect the data
511 if (*idata->family_suffix
512 && (name_id == 1
513 || name_id == 4
514 || name_id == 6
515 || name_id == 16
516 || name_id == 21))
517 return collect_family_data(platform_id,
518 encoding_id,
519 language_id,
520 name_id,
521 len,
522 str,
523 idata);
525 return 0;
529 // Insert `suffix' to `str', right after substring `name'.
530 // If `name' isn't a substring of `str', append `suffix' to `str'.
531 // Do nothing in case of allocation error or if resulting string is too long.
533 static void
534 insert_suffix(sds suffix,
535 sds name,
536 unsigned short* len,
537 unsigned char** str)
539 if (!len || !*len || !str || !*str)
540 return;
542 sds s = sdsempty();
544 // check whether `name' is a substring of `str'
545 unsigned char* s_start = (unsigned char*)memmem(*str, *len,
546 name, sdslen(name));
548 // construct new string
549 if (s_start)
551 size_t substring_end = size_t(s_start - *str) + sdslen(name);
553 // everything up to the end of the substring
554 s = sdscatlen(s, *str, substring_end);
555 // the suffix
556 s = sdscatsds(s, suffix);
557 // the rest
558 s = sdscatlen(s, *str + substring_end, *len - substring_end);
560 else
562 s = sdscatlen(s, *str, *len);
563 s = sdscatsds(s, suffix);
566 if (!s)
567 return;
569 if (sdslen(s) <= 0xFFFF)
571 unsigned short len_new = (unsigned short)sdslen(s);
572 unsigned char* str_new = (unsigned char*)realloc(*str, len_new);
573 if (str_new)
575 *str = str_new;
576 memcpy(*str, s, len_new);
577 *len = len_new;
581 sdsfree(s);
585 // `info-post-callback' function
588 info_post(void* user)
590 Info_Data* idata = (Info_Data*)user;
591 family_data* family_data_head = (family_data*)idata->family_data_head;
594 // family_suffix + family_suffix_wide
596 sds family_suffix = sdsnew(idata->family_suffix);
597 size_t family_suffix_len = sdslen(family_suffix);
599 sds family_suffix_wide = sdsempty();
600 size_t family_suffix_wide_len = 2 * family_suffix_len;
602 // create sds with given size but uninitialized string data
603 family_suffix_wide = sdsMakeRoomFor(family_suffix_wide,
604 family_suffix_wide_len);
606 char* fs;
608 // construct `family_suffix_wide' by inserting '\0'
609 fs = family_suffix;
610 char *fsw = family_suffix_wide;
611 for (size_t i = 0; i < family_suffix_len; i++)
613 *(fsw++) = '\0';
614 *(fsw++) = *(fs++);
616 sdsIncrLen(family_suffix_wide, (int)family_suffix_wide_len);
619 // family_ps_suffix + family_ps_suffix_wide
621 sds family_ps_suffix = sdsempty();
623 // create sds with estimated size but uninitialized string data;
624 // we later set the size to the actual value
625 family_ps_suffix = sdsMakeRoomFor(family_ps_suffix,
626 family_suffix_len);
628 // construct `family_ps_suffix' by removing all space characters
629 fs = family_suffix;
630 char *fps = family_ps_suffix;
631 for (size_t i = 0; i < family_suffix_len; i++)
633 char c = *(fs++);
634 if (c != ' ')
635 *(fps++) = c;
637 // set correct size
638 sdsIncrLen(family_ps_suffix, fps - family_ps_suffix);
640 size_t family_ps_suffix_len = sdslen(family_ps_suffix);
642 sds family_ps_suffix_wide = sdsempty();
643 size_t family_ps_suffix_wide_len = 2 * family_ps_suffix_len;
645 // create sds with given size but uninitialized string data
646 family_ps_suffix_wide = sdsMakeRoomFor(family_ps_suffix_wide,
647 family_ps_suffix_wide_len);
649 // construct `family_ps_suffix_wide' by inserting '\0'
650 fps = family_ps_suffix;
651 char* fpsw = family_ps_suffix_wide;
652 for (size_t i = 0; i < family_ps_suffix_len; i++)
654 *(fpsw++) = '\0';
655 *(fpsw++) = *(fps++);
657 sdsIncrLen(family_ps_suffix_wide, (int)family_ps_suffix_wide_len);
659 // We try the following algorithm.
661 // 1. If we have a `Preferred Family' (ID 16), use it as the family name,
662 // otherwise use the `Font Family Name' (ID 1). If necessary, search
663 // other language IDs for the current (platform ID, encoding ID) pair
664 // to find a family name.
666 // 2. Append the family suffix to the family substring in the `Font Family
667 // Name' (ID 1), the `Full Font Name' (ID 4), the `Preferred Family'
668 // (ID 16), and the `WWS Family Name' (ID 21). In case the family name
669 // found in step 1 is not a substring, append the suffix to the whole
670 // string.
672 // 3. Remove spaces from the family name and locate this substring in the
673 // `PostScript Name' (ID 6), then append the family suffix, also with
674 // spaces removed. If we don't have a substring, append the stripped
675 // suffix to the whole string.
677 // determine family name for all triplets if available
678 for (Node* node = LLRB_MIN(family_data, family_data_head);
679 node;
680 node = LLRB_NEXT(family_data, family_data_head, node))
682 Family* family = &node->family;
684 if (family->name_id_16_len && *family->name_id_16_len
685 && family->name_id_16_str && *family->name_id_16_str)
686 family->family_name = sdsnewlen(*family->name_id_16_str,
687 *family->name_id_16_len);
688 else if (family->name_id_1_len && *family->name_id_1_len
689 && family->name_id_1_str && *family->name_id_1_str)
690 family->family_name = sdsnewlen(*family->name_id_1_str,
691 *family->name_id_1_len);
694 sds family_name = sdsempty();
695 sds family_ps_name = sdsempty();
697 // process all name ID strings in triplets
698 for (Node* node = LLRB_MIN(family_data, family_data_head);
699 node;
700 node = LLRB_NEXT(family_data, family_data_head, node))
702 Family family = node->family;
703 bool is_wide;
705 sdsclear(family_name);
706 sdsclear(family_ps_name);
708 if (family.family_name)
709 family_name = sdscatsds(family_name, family.family_name);
710 else
712 Node* n;
714 // use family name from a triplet that actually has one
715 for (n = LLRB_MIN(family_data, family_data_head);
717 n = LLRB_NEXT(family_data, family_data_head, n))
719 Family f = n->family;
721 if (f.platform_id == family.platform_id
722 && f.encoding_id == family.encoding_id
723 && f.family_name)
725 family_name = sdscatsds(family_name, f.family_name);
726 break;
730 if (!n)
731 continue; // no valid family name found
734 if (family.platform_id == 1
735 || (family.platform_id == 3 && !(family.encoding_id == 1
736 || family.encoding_id == 10)))
737 is_wide = false; // one-byte or multi-byte encodings
738 else
739 is_wide = true; // (two-byte) UTF-16BE
741 sds suffix = is_wide ? family_suffix_wide : family_suffix;
742 insert_suffix(suffix,
743 family_name,
744 family.name_id_1_len,
745 family.name_id_1_str);
746 insert_suffix(suffix,
747 family_name,
748 family.name_id_4_len,
749 family.name_id_4_str);
750 insert_suffix(suffix,
751 family_name,
752 family.name_id_16_len,
753 family.name_id_16_str);
754 insert_suffix(suffix,
755 family_name,
756 family.name_id_21_len,
757 family.name_id_21_str);
759 size_t family_name_len = sdslen(family_name);
760 if (is_wide)
761 family_name_len &= ~1U; // ensure even value for the loop below
763 // set sds to estimated size;
764 // we later set the size to the actual value
765 family_ps_name = sdsMakeRoomFor(family_ps_name,
766 family_name_len);
768 // construct `family_ps_name' by removing all space characters
769 char *fn = family_name;
770 char *fpn = family_ps_name;
771 if (is_wide)
773 for (size_t i = 0; i < family_name_len; i += 2)
775 char c1 = *(fn++);
776 char c2 = *(fn++);
777 if (!(c1 == '\0' && c2 == ' '))
779 *(fpn++) = c1;
780 *(fpn++) = c2;
784 else
786 for (size_t i = 0; i < family_name_len; i++)
788 char c = *(fn++);
789 if (c != ' ')
790 *(fpn++) = c;
793 // set correct size
794 sdsIncrLen(family_ps_name, fpn - family_ps_name);
796 sds ps_suffix = is_wide ? family_ps_suffix_wide : family_ps_suffix;
797 insert_suffix(ps_suffix,
798 family_ps_name,
799 family.name_id_6_len,
800 family.name_id_6_str);
803 sdsfree(family_suffix);
804 sdsfree(family_suffix_wide);
805 sdsfree(family_ps_suffix);
806 sdsfree(family_ps_suffix_wide);
807 sdsfree(family_name);
808 sdsfree(family_ps_name);
810 family_data_free(idata);
812 return 0;
815 } // extern "C"
817 // end of info.cpp