Fix OTS warning about `maxp.maxSizeOfInstructions`.
[ttfautohint.git] / frontend / info.cpp
blob163fb6f0aa381f7fcb6e98d72cf9a65a3303d0bc
1 // info.cpp
3 // Copyright (C) 2012-2022 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 `last_component',
19 // which is a replacement for `basename' (but just returning a pointer
20 // without allocating a string) that works on Windows also
21 #include "dirname.h"
23 #include "info.h"
24 #include <sds.h>
25 #include <numberset.h>
28 #define TTFAUTOHINT_STRING "; ttfautohint"
29 #define TTFAUTOHINT_STRING_WIDE "\0;\0 \0t\0t\0f\0a\0u\0t\0o\0h\0i\0n\0t"
32 extern "C" {
34 const char invalid_ps_chars[96] =
36 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, // 0x20 %, (, ), /
37 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, // 0x30 <, >
38 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40
39 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, // 0x50 [, ]
40 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60
41 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, // 0x70 {, }, DEL
45 char*
46 check_family_suffix(const char* s)
48 while (*s)
50 int c = (signed char)*s - 0x20;
52 // valid range is 0x20-0x7E minus characters %()/<>[]{}
53 // (well, the space character 0x20 is not valid within a PS name,
54 // but the validity test gets executed before constructing a PS name,
55 // which has all space characters removed)
56 if (c < 0 || invalid_ps_chars[c])
57 return (char*)s;
59 s++;
62 return NULL;
66 // build string that gets appended to the `Version' field(s)
67 // return value 1 means allocation error, value 2 too long a string
68 int
69 build_version_string(Info_Data* idata)
71 // since we use `goto' we have to declare variables before the jumps
72 unsigned char* info_string;
73 unsigned char* info_string_wide;
74 unsigned char* dt;
75 unsigned char* dtw;
76 char* s = NULL;
77 char mode[4];
78 char mode_letters[] = "nqs";
79 int ret = 0;
80 sds d;
82 d = sdsempty();
84 d = sdscatprintf(d, TTFAUTOHINT_STRING " (v%s)", VERSION);
85 if (!idata->detailed_info)
86 goto Skip;
88 if (idata->dehint)
90 d = sdscat(d, " -d");
91 goto Skip;
93 d = sdscatprintf(d, " -l %d", idata->hinting_range_min);
94 d = sdscatprintf(d, " -r %d", idata->hinting_range_max);
95 d = sdscatprintf(d, " -G %d", idata->hinting_limit);
96 d = sdscatprintf(d, " -x %d", idata->increase_x_height);
97 if (idata->fallback_stem_width)
98 d = sdscatprintf(d, " -H %d", idata->fallback_stem_width);
99 d = sdscatprintf(d, " -D %s", idata->default_script);
100 d = sdscatprintf(d, " -f %s", idata->fallback_script);
101 if (idata->control_name)
103 char* bn = last_component(idata->control_name);
104 d = sdscatprintf(d, " -m \"%s\"", bn ? bn : idata->control_name);
106 if (idata->reference_name)
108 char* bn = last_component(idata->reference_name);
109 d = sdscatprintf(d, " -R \"%s\"", bn ? bn : idata->reference_name);
111 d = sdscatprintf(d, " -Z %d", idata->reference_index);
113 // `*_stem_width_mode' can have values -1, 0, and 1
114 mode[0] = mode_letters[idata->gray_stem_width_mode + 1];
115 mode[1] = mode_letters[idata->gdi_cleartype_stem_width_mode + 1];
116 mode[2] = mode_letters[idata->dw_cleartype_stem_width_mode + 1];
117 mode[3] = '\0';
118 d = sdscatprintf(d, " -a %s", mode);
120 if (idata->windows_compatibility)
121 d = sdscat(d, " -W");
122 if (idata->adjust_subglyphs)
123 d = sdscat(d, " -p");
124 if (idata->hint_composites)
125 d = sdscat(d, " -c");
126 if (idata->symbol)
127 d = sdscat(d, " -s");
128 if (idata->fallback_scaling)
129 d = sdscat(d, " -S");
130 if (idata->TTFA_info)
131 d = sdscat(d, " -t");
133 if (idata->x_height_snapping_exceptions_string)
135 // only set specific value of `ret' for an allocation error,
136 // since syntax errors are handled in TTF_autohint
137 number_range* x_height_snapping_exceptions;
138 const char* pos = number_set_parse(
139 idata->x_height_snapping_exceptions_string,
140 &x_height_snapping_exceptions,
141 6, 0x7FFF);
142 if (*pos)
144 if (x_height_snapping_exceptions == NUMBERSET_ALLOCATION_ERROR)
145 ret = 1;
146 goto Fail;
149 s = number_set_show(x_height_snapping_exceptions, 6, 0x7FFF);
150 number_set_free(x_height_snapping_exceptions);
152 // ensure UTF16-BE version doesn't get too long
153 if (strlen(s) > 0xFFFF / 2 - sdslen(d))
155 ret = 2;
156 goto Fail;
159 d = sdscatprintf(d, " -X \"%s\"", s);
162 Skip:
163 if (!d)
165 ret = 1;
166 goto Fail;
169 info_string = (unsigned char*)malloc(sdslen(d) + 1);
170 if (!info_string)
172 ret = 1;
173 goto Fail;
175 memcpy(info_string, d, sdslen(d) + 1);
177 idata->info_string = info_string;
178 idata->info_string_len = (unsigned short)sdslen(d);
180 // prepare UTF16-BE version data
181 idata->info_string_wide_len = 2 * idata->info_string_len;
182 info_string_wide = (unsigned char*)realloc(idata->info_string_wide,
183 idata->info_string_wide_len);
184 if (!info_string_wide)
186 ret = 1;
187 goto Fail;
189 idata->info_string_wide = info_string_wide;
191 dt = idata->info_string;
192 dtw = idata->info_string_wide;
193 for (unsigned short i = 0; i < idata->info_string_len; i++)
195 *(dtw++) = '\0';
196 *(dtw++) = *(dt++);
199 Exit:
200 free(s);
201 sdsfree(d);
203 return ret;
205 Fail:
206 free(idata->info_string);
207 free(idata->info_string_wide);
209 idata->info_string = NULL;
210 idata->info_string_wide = NULL;
211 idata->info_string_len = 0;
212 idata->info_string_wide_len = 0;
214 goto Exit;
218 static int
219 info_name_id_5(unsigned short platform_id,
220 unsigned short encoding_id,
221 unsigned short* len,
222 unsigned char** str,
223 Info_Data* idata)
225 unsigned char ttfautohint_string[] = TTFAUTOHINT_STRING;
226 unsigned char ttfautohint_string_wide[] = TTFAUTOHINT_STRING_WIDE;
228 // we use memmem, so don't count the trailing \0 character
229 size_t ttfautohint_string_len = sizeof (TTFAUTOHINT_STRING) - 1;
230 size_t ttfautohint_string_wide_len = sizeof (TTFAUTOHINT_STRING_WIDE) - 1;
232 unsigned char* v;
233 unsigned short v_len;
234 unsigned char* s;
235 size_t s_len;
236 size_t offset;
238 if (platform_id == 1
239 || (platform_id == 3 && !(encoding_id == 1
240 || encoding_id == 10)))
242 // one-byte or multi-byte encodings
243 v = idata->info_string;
244 v_len = idata->info_string_len;
245 s = ttfautohint_string;
246 s_len = ttfautohint_string_len;
247 offset = 2;
249 else
251 // (two-byte) UTF-16BE for everything else
252 v = idata->info_string_wide;
253 v_len = idata->info_string_wide_len;
254 s = ttfautohint_string_wide;
255 s_len = ttfautohint_string_wide_len;
256 offset = 4;
259 // if we already have an ttfautohint info string,
260 // remove it up to a following `;' character (or end of string)
261 unsigned char* s_start = (unsigned char*)memmem(*str, *len, s, s_len);
262 if (s_start)
264 unsigned char* s_end = s_start + offset;
265 unsigned char* limit = *str + *len;
267 while (s_end < limit)
269 if (*s_end == ';')
271 if (offset == 2)
272 break;
273 else
275 if (*(s_end - 1) == '\0') // UTF-16BE
277 s_end--;
278 break;
283 s_end++;
286 while (s_end < limit)
287 *s_start++ = *s_end++;
289 *len -= s_end - s_start;
292 // do nothing if the string would become too long
293 if (*len > 0xFFFF - v_len)
294 return 0;
296 unsigned short len_new = *len + v_len;
297 unsigned char* str_new = (unsigned char*)realloc(*str, len_new);
298 if (!str_new)
299 return 1;
301 *str = str_new;
302 memcpy(*str + *len, v, v_len);
303 *len = len_new;
305 return 0;
309 // a structure to collect family data for a given
310 // (platform_id, encoding_id, language_id) triplet
312 typedef struct Family_
314 unsigned short platform_id;
315 unsigned short encoding_id;
316 unsigned short language_id;
318 unsigned short* name_id_1_len;
319 unsigned char** name_id_1_str;
320 unsigned short* name_id_4_len;
321 unsigned char** name_id_4_str;
322 unsigned short* name_id_6_len;
323 unsigned char** name_id_6_str;
324 unsigned short* name_id_16_len;
325 unsigned char** name_id_16_str;
326 unsigned short* name_id_21_len;
327 unsigned char** name_id_21_str;
329 sds family_name;
330 } Family;
333 // node structure for collected family data
335 typedef struct Node Node;
336 struct Node
338 LLRB_ENTRY(Node) entry;
339 Family family;
343 // comparison function for our red-black tree
345 static int
346 nodecmp(Node *e1,
347 Node *e2)
349 int diff;
351 // sort by platform ID ...
352 diff = e1->family.platform_id - e2->family.platform_id;
353 if (diff)
354 goto Exit;
356 // ... then by encoding ID ...
357 diff = e1->family.encoding_id - e2->family.encoding_id;
358 if (diff)
359 goto Exit;
361 // ... then by language ID
362 diff = e1->family.language_id - e2->family.language_id;
364 Exit:
365 // https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign
366 return (diff > 0) - (diff < 0);
370 // the red-black tree function body
371 typedef struct family_data family_data;
373 LLRB_HEAD(family_data, Node);
375 // no trailing semicolon in the next line
376 LLRB_GENERATE_STATIC(family_data, Node, entry, nodecmp)
379 static void
380 family_data_free(Info_Data* idata)
382 family_data* family_data_head = (family_data*)idata->family_data_head;
384 Node* node;
385 Node* next_node;
387 if (!family_data_head)
388 return;
390 for (node = LLRB_MIN(family_data, family_data_head);
391 node;
392 node = next_node)
394 next_node = LLRB_NEXT(family_data, family_data_head, node);
395 LLRB_REMOVE(family_data, family_data_head, node);
396 sdsfree(node->family.family_name);
397 free(node);
400 free(family_data_head);
404 static int
405 collect_family_data(unsigned short platform_id,
406 unsigned short encoding_id,
407 unsigned short language_id,
408 unsigned short name_id,
409 unsigned short* len,
410 unsigned char** str,
411 Info_Data* idata)
413 family_data* family_data_head = (family_data*)idata->family_data_head;
414 Node* node;
415 Node* val;
417 if (!family_data_head)
419 // first-time initialization
420 family_data_head = (family_data*)malloc(sizeof (family_data));
421 if (!family_data_head)
422 return 1;
424 LLRB_INIT(family_data_head);
425 idata->family_data_head = family_data_head;
428 node = (Node*)malloc(sizeof (Node));
429 if (!node)
430 return 1;
432 node->family.platform_id = platform_id;
433 node->family.encoding_id = encoding_id;
434 node->family.language_id = language_id;
436 val = LLRB_INSERT(family_data, family_data_head, node);
437 if (val)
439 // we already have an entry in the tree for our triplet
440 free(node);
441 node = val;
443 else
445 // initialize remaining fields
446 node->family.name_id_1_len = NULL;
447 node->family.name_id_1_str = NULL;
448 node->family.name_id_4_len = NULL;
449 node->family.name_id_4_str = NULL;
450 node->family.name_id_6_len = NULL;
451 node->family.name_id_6_str = NULL;
452 node->family.name_id_16_len = NULL;
453 node->family.name_id_16_str = NULL;
454 node->family.name_id_21_len = NULL;
455 node->family.name_id_21_str = NULL;
457 node->family.family_name = NULL;
460 if (name_id == 1)
462 node->family.name_id_1_len = len;
463 node->family.name_id_1_str = str;
465 else if (name_id == 4)
467 node->family.name_id_4_len = len;
468 node->family.name_id_4_str = str;
470 else if (name_id == 6)
472 node->family.name_id_6_len = len;
473 node->family.name_id_6_str = str;
475 else if (name_id == 16)
477 node->family.name_id_16_len = len;
478 node->family.name_id_16_str = str;
480 else if (name_id == 21)
482 node->family.name_id_21_len = len;
483 node->family.name_id_21_str = str;
486 return 0;
490 // `info-callback' function
493 info(unsigned short platform_id,
494 unsigned short encoding_id,
495 unsigned short language_id,
496 unsigned short name_id,
497 unsigned short* len,
498 unsigned char** str,
499 void* user)
501 Info_Data* idata = (Info_Data*)user;
503 // if ID is a version string, append our data
504 if (!idata->no_info && name_id == 5)
505 return info_name_id_5(platform_id, encoding_id, len, str, idata);
507 // if ID is related to a family name, collect the data
508 if (*idata->family_suffix
509 && (name_id == 1
510 || name_id == 4
511 || name_id == 6
512 || name_id == 16
513 || name_id == 21))
514 return collect_family_data(platform_id,
515 encoding_id,
516 language_id,
517 name_id,
518 len,
519 str,
520 idata);
522 return 0;
526 // Insert `suffix' to `str', right after substring `name'.
527 // If `name' isn't a substring of `str', append `suffix' to `str'.
528 // Do nothing in case of allocation error or if resulting string is too long.
530 static void
531 insert_suffix(sds suffix,
532 sds name,
533 unsigned short* len,
534 unsigned char** str)
536 if (!len || !*len || !str || !*str)
537 return;
539 sds s = sdsempty();
541 // check whether `name' is a substring of `str'
542 unsigned char* s_start = (unsigned char*)memmem(*str, *len,
543 name, sdslen(name));
545 // construct new string
546 if (s_start)
548 size_t substring_end = size_t(s_start - *str) + sdslen(name);
550 // everything up to the end of the substring
551 s = sdscatlen(s, *str, substring_end);
552 // the suffix
553 s = sdscatsds(s, suffix);
554 // the rest
555 s = sdscatlen(s, *str + substring_end, *len - substring_end);
557 else
559 s = sdscatlen(s, *str, *len);
560 s = sdscatsds(s, suffix);
563 if (!s)
564 return;
566 if (sdslen(s) <= 0xFFFF)
568 unsigned short len_new = (unsigned short)sdslen(s);
569 unsigned char* str_new = (unsigned char*)realloc(*str, len_new);
570 if (str_new)
572 *str = str_new;
573 memcpy(*str, s, len_new);
574 *len = len_new;
578 sdsfree(s);
582 // `info-post-callback' function
585 info_post(void* user)
587 Info_Data* idata = (Info_Data*)user;
588 family_data* family_data_head = (family_data*)idata->family_data_head;
591 // family_suffix + family_suffix_wide
593 sds family_suffix = sdsnew(idata->family_suffix);
594 size_t family_suffix_len = sdslen(family_suffix);
596 sds family_suffix_wide = sdsempty();
597 size_t family_suffix_wide_len = 2 * family_suffix_len;
599 // create sds with given size but uninitialized string data
600 family_suffix_wide = sdsMakeRoomFor(family_suffix_wide,
601 family_suffix_wide_len);
603 char* fs;
605 // construct `family_suffix_wide' by inserting '\0'
606 fs = family_suffix;
607 char *fsw = family_suffix_wide;
608 for (size_t i = 0; i < family_suffix_len; i++)
610 *(fsw++) = '\0';
611 *(fsw++) = *(fs++);
613 sdsIncrLen(family_suffix_wide, (int)family_suffix_wide_len);
616 // family_ps_suffix + family_ps_suffix_wide
618 sds family_ps_suffix = sdsempty();
620 // create sds with estimated size but uninitialized string data;
621 // we later set the size to the actual value
622 family_ps_suffix = sdsMakeRoomFor(family_ps_suffix,
623 family_suffix_len);
625 // construct `family_ps_suffix' by removing all space characters
626 fs = family_suffix;
627 char *fps = family_ps_suffix;
628 for (size_t i = 0; i < family_suffix_len; i++)
630 char c = *(fs++);
631 if (c != ' ')
632 *(fps++) = c;
634 // set correct size
635 sdsIncrLen(family_ps_suffix, (int)(fps - family_ps_suffix));
637 size_t family_ps_suffix_len = sdslen(family_ps_suffix);
639 sds family_ps_suffix_wide = sdsempty();
640 size_t family_ps_suffix_wide_len = 2 * family_ps_suffix_len;
642 // create sds with given size but uninitialized string data
643 family_ps_suffix_wide = sdsMakeRoomFor(family_ps_suffix_wide,
644 family_ps_suffix_wide_len);
646 // construct `family_ps_suffix_wide' by inserting '\0'
647 fps = family_ps_suffix;
648 char* fpsw = family_ps_suffix_wide;
649 for (size_t i = 0; i < family_ps_suffix_len; i++)
651 *(fpsw++) = '\0';
652 *(fpsw++) = *(fps++);
654 sdsIncrLen(family_ps_suffix_wide, (int)family_ps_suffix_wide_len);
656 // We try the following algorithm.
658 // 1. If we have a `Preferred Family' (ID 16), use it as the family name,
659 // otherwise use the `Font Family Name' (ID 1). If necessary, search
660 // other language IDs for the current (platform ID, encoding ID) pair
661 // to find a family name.
663 // 2. Append the family suffix to the family substring in the `Font Family
664 // Name' (ID 1), the `Full Font Name' (ID 4), the `Preferred Family'
665 // (ID 16), and the `WWS Family Name' (ID 21). In case the family name
666 // found in step 1 is not a substring, append the suffix to the whole
667 // string.
669 // 3. Remove spaces from the family name and locate this substring in the
670 // `PostScript Name' (ID 6), then append the family suffix, also with
671 // spaces removed. If we don't have a substring, append the stripped
672 // suffix to the whole string.
674 // determine family name for all triplets if available
675 for (Node* node = LLRB_MIN(family_data, family_data_head);
676 node;
677 node = LLRB_NEXT(family_data, family_data_head, node))
679 Family* family = &node->family;
681 if (family->name_id_16_len && *family->name_id_16_len
682 && family->name_id_16_str && *family->name_id_16_str)
683 family->family_name = sdsnewlen(*family->name_id_16_str,
684 *family->name_id_16_len);
685 else if (family->name_id_1_len && *family->name_id_1_len
686 && family->name_id_1_str && *family->name_id_1_str)
687 family->family_name = sdsnewlen(*family->name_id_1_str,
688 *family->name_id_1_len);
691 sds family_name = sdsempty();
692 sds family_ps_name = sdsempty();
694 // process all name ID strings in triplets
695 for (Node* node = LLRB_MIN(family_data, family_data_head);
696 node;
697 node = LLRB_NEXT(family_data, family_data_head, node))
699 Family family = node->family;
700 bool is_wide;
702 sdsclear(family_name);
703 sdsclear(family_ps_name);
705 if (family.family_name)
706 family_name = sdscatsds(family_name, family.family_name);
707 else
709 Node* n;
711 // use family name from a triplet that actually has one
712 for (n = LLRB_MIN(family_data, family_data_head);
714 n = LLRB_NEXT(family_data, family_data_head, n))
716 Family f = n->family;
718 if (f.platform_id == family.platform_id
719 && f.encoding_id == family.encoding_id
720 && f.family_name)
722 family_name = sdscatsds(family_name, f.family_name);
723 break;
727 if (!n)
728 continue; // no valid family name found
731 if (family.platform_id == 1
732 || (family.platform_id == 3 && !(family.encoding_id == 1
733 || family.encoding_id == 10)))
734 is_wide = false; // one-byte or multi-byte encodings
735 else
736 is_wide = true; // (two-byte) UTF-16BE
738 sds suffix = is_wide ? family_suffix_wide : family_suffix;
739 insert_suffix(suffix,
740 family_name,
741 family.name_id_1_len,
742 family.name_id_1_str);
743 insert_suffix(suffix,
744 family_name,
745 family.name_id_4_len,
746 family.name_id_4_str);
747 insert_suffix(suffix,
748 family_name,
749 family.name_id_16_len,
750 family.name_id_16_str);
751 insert_suffix(suffix,
752 family_name,
753 family.name_id_21_len,
754 family.name_id_21_str);
756 size_t family_name_len = sdslen(family_name);
757 if (is_wide)
758 family_name_len &= ~1U; // ensure even value for the loop below
760 // set sds to estimated size;
761 // we later set the size to the actual value
762 family_ps_name = sdsMakeRoomFor(family_ps_name,
763 family_name_len);
765 // construct `family_ps_name' by removing all space characters
766 char *fn = family_name;
767 char *fpn = family_ps_name;
768 if (is_wide)
770 for (size_t i = 0; i < family_name_len; i += 2)
772 char c1 = *(fn++);
773 char c2 = *(fn++);
774 if (!(c1 == '\0' && c2 == ' '))
776 *(fpn++) = c1;
777 *(fpn++) = c2;
781 else
783 for (size_t i = 0; i < family_name_len; i++)
785 char c = *(fn++);
786 if (c != ' ')
787 *(fpn++) = c;
790 // set correct size
791 sdsIncrLen(family_ps_name, (int)(fpn - family_ps_name));
793 sds ps_suffix = is_wide ? family_ps_suffix_wide : family_ps_suffix;
794 insert_suffix(ps_suffix,
795 family_ps_name,
796 family.name_id_6_len,
797 family.name_id_6_str);
800 sdsfree(family_suffix);
801 sdsfree(family_suffix_wide);
802 sdsfree(family_ps_suffix);
803 sdsfree(family_ps_suffix_wide);
804 sdsfree(family_name);
805 sdsfree(family_ps_name);
807 family_data_free(idata);
809 return 0;
812 } // extern "C"
814 // end of info.cpp