Update copyright year.
[ttfautohint.git] / frontend / info.cpp
blobbb1d9b13c34ef1d0b967f83ca005ece40454309e
1 // info.cpp
3 // Copyright (C) 2012-2015 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->TTFA_info)
132 d = sdscat(d, " -t");
134 if (idata->x_height_snapping_exceptions_string)
136 // only set specific value of `ret' for an allocation error,
137 // since syntax errors are handled in TTF_autohint
138 number_range* x_height_snapping_exceptions;
139 const char* pos = number_set_parse(
140 idata->x_height_snapping_exceptions_string,
141 &x_height_snapping_exceptions,
142 6, 0x7FFF);
143 if (*pos)
145 if (x_height_snapping_exceptions == NUMBERSET_ALLOCATION_ERROR)
146 ret = 1;
147 goto Fail;
150 s = number_set_show(x_height_snapping_exceptions, 6, 0x7FFF);
151 number_set_free(x_height_snapping_exceptions);
153 // ensure UTF16-BE version doesn't get too long
154 if (strlen(s) > 0xFFFF / 2 - sdslen(d))
156 ret = 2;
157 goto Fail;
160 d = sdscatprintf(d, " -X \"%s\"", s);
163 Skip:
164 if (!d)
166 ret = 1;
167 goto Fail;
170 info_string = (unsigned char*)malloc(sdslen(d) + 1);
171 if (!info_string)
173 ret = 1;
174 goto Fail;
176 memcpy(info_string, d, sdslen(d) + 1);
178 idata->info_string = info_string;
179 idata->info_string_len = (unsigned short)sdslen(d);
181 // prepare UTF16-BE version data
182 idata->info_string_wide_len = 2 * idata->info_string_len;
183 info_string_wide = (unsigned char*)realloc(idata->info_string_wide,
184 idata->info_string_wide_len);
185 if (!info_string_wide)
187 ret = 1;
188 goto Fail;
190 idata->info_string_wide = info_string_wide;
192 dt = idata->info_string;
193 dtw = idata->info_string_wide;
194 for (unsigned short i = 0; i < idata->info_string_len; i++)
196 *(dtw++) = '\0';
197 *(dtw++) = *(dt++);
200 Exit:
201 free(s);
202 sdsfree(d);
204 return ret;
206 Fail:
207 free(idata->info_string);
208 free(idata->info_string_wide);
210 idata->info_string = NULL;
211 idata->info_string_wide = NULL;
212 idata->info_string_len = 0;
213 idata->info_string_wide_len = 0;
215 goto Exit;
219 static int
220 info_name_id_5(unsigned short platform_id,
221 unsigned short encoding_id,
222 unsigned short* len,
223 unsigned char** str,
224 Info_Data* idata)
226 unsigned char ttfautohint_string[] = TTFAUTOHINT_STRING;
227 unsigned char ttfautohint_string_wide[] = TTFAUTOHINT_STRING_WIDE;
229 // we use memmem, so don't count the trailing \0 character
230 size_t ttfautohint_string_len = sizeof (TTFAUTOHINT_STRING) - 1;
231 size_t ttfautohint_string_wide_len = sizeof (TTFAUTOHINT_STRING_WIDE) - 1;
233 unsigned char* v;
234 unsigned short v_len;
235 unsigned char* s;
236 size_t s_len;
237 size_t offset;
239 if (platform_id == 1
240 || (platform_id == 3 && !(encoding_id == 1
241 || encoding_id == 10)))
243 // one-byte or multi-byte encodings
244 v = idata->info_string;
245 v_len = idata->info_string_len;
246 s = ttfautohint_string;
247 s_len = ttfautohint_string_len;
248 offset = 2;
250 else
252 // (two-byte) UTF-16BE for everything else
253 v = idata->info_string_wide;
254 v_len = idata->info_string_wide_len;
255 s = ttfautohint_string_wide;
256 s_len = ttfautohint_string_wide_len;
257 offset = 4;
260 // if we already have an ttfautohint info string,
261 // remove it up to a following `;' character (or end of string)
262 unsigned char* s_start = (unsigned char*)memmem(*str, *len, s, s_len);
263 if (s_start)
265 unsigned char* s_end = s_start + offset;
266 unsigned char* limit = *str + *len;
268 while (s_end < limit)
270 if (*s_end == ';')
272 if (offset == 2)
273 break;
274 else
276 if (*(s_end - 1) == '\0') // UTF-16BE
278 s_end--;
279 break;
284 s_end++;
287 while (s_end < limit)
288 *s_start++ = *s_end++;
290 *len -= s_end - s_start;
293 // do nothing if the string would become too long
294 if (*len > 0xFFFF - v_len)
295 return 0;
297 unsigned short len_new = *len + v_len;
298 unsigned char* str_new = (unsigned char*)realloc(*str, len_new);
299 if (!str_new)
300 return 1;
302 *str = str_new;
303 memcpy(*str + *len, v, v_len);
304 *len = len_new;
306 return 0;
310 // a structure to collect family data for a given
311 // (platform_id, encoding_id, language_id) triplet
313 typedef struct Family_
315 unsigned short platform_id;
316 unsigned short encoding_id;
317 unsigned short language_id;
319 unsigned short* name_id_1_len;
320 unsigned char** name_id_1_str;
321 unsigned short* name_id_4_len;
322 unsigned char** name_id_4_str;
323 unsigned short* name_id_6_len;
324 unsigned char** name_id_6_str;
325 unsigned short* name_id_16_len;
326 unsigned char** name_id_16_str;
327 unsigned short* name_id_21_len;
328 unsigned char** name_id_21_str;
330 sds family_name;
331 } Family;
334 // node structure for collected family data
336 typedef struct Node Node;
337 struct Node
339 LLRB_ENTRY(Node) entry;
340 Family family;
344 // comparison function for our red-black tree
346 static int
347 nodecmp(Node *e1,
348 Node *e2)
350 int diff;
352 // sort by platform ID ...
353 diff = e1->family.platform_id - e2->family.platform_id;
354 if (diff)
355 goto Exit;
357 // ... then by encoding ID ...
358 diff = e1->family.encoding_id - e2->family.encoding_id;
359 if (diff)
360 goto Exit;
362 // ... then by language ID
363 diff = e1->family.language_id - e2->family.language_id;
365 Exit:
366 // https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign
367 return (diff > 0) - (diff < 0);
371 // the red-black tree function body
372 typedef struct family_data family_data;
374 LLRB_HEAD(family_data, Node);
376 // no trailing semicolon in the next line
377 LLRB_GENERATE_STATIC(family_data, Node, entry, nodecmp)
380 static void
381 family_data_free(Info_Data* idata)
383 family_data* family_data_head = (family_data*)idata->family_data_head;
385 Node* node;
386 Node* next_node;
388 if (!family_data_head)
389 return;
391 for (node = LLRB_MIN(family_data, family_data_head);
392 node;
393 node = next_node)
395 next_node = LLRB_NEXT(family_data, family_data_head, node);
396 LLRB_REMOVE(family_data, family_data_head, node);
397 sdsfree(node->family.family_name);
398 free(node);
401 free(family_data_head);
405 static int
406 collect_family_data(unsigned short platform_id,
407 unsigned short encoding_id,
408 unsigned short language_id,
409 unsigned short name_id,
410 unsigned short* len,
411 unsigned char** str,
412 Info_Data* idata)
414 family_data* family_data_head = (family_data*)idata->family_data_head;
415 Node* node;
416 Node* val;
418 if (!family_data_head)
420 // first-time initialization
421 family_data_head = (family_data*)malloc(sizeof (family_data));
422 if (!family_data_head)
423 return 1;
425 LLRB_INIT(family_data_head);
426 idata->family_data_head = family_data_head;
429 node = (Node*)malloc(sizeof (Node));
430 if (!node)
431 return 1;
433 node->family.platform_id = platform_id;
434 node->family.encoding_id = encoding_id;
435 node->family.language_id = language_id;
437 val = LLRB_INSERT(family_data, family_data_head, node);
438 if (val)
440 // we already have an entry in the tree for our triplet
441 free(node);
442 node = val;
444 else
446 // initialize remaining fields
447 node->family.name_id_1_len = NULL;
448 node->family.name_id_1_str = NULL;
449 node->family.name_id_4_len = NULL;
450 node->family.name_id_4_str = NULL;
451 node->family.name_id_6_len = NULL;
452 node->family.name_id_6_str = NULL;
453 node->family.name_id_16_len = NULL;
454 node->family.name_id_16_str = NULL;
455 node->family.name_id_21_len = NULL;
456 node->family.name_id_21_str = NULL;
458 node->family.family_name = NULL;
461 if (name_id == 1)
463 node->family.name_id_1_len = len;
464 node->family.name_id_1_str = str;
466 else if (name_id == 4)
468 node->family.name_id_4_len = len;
469 node->family.name_id_4_str = str;
471 else if (name_id == 6)
473 node->family.name_id_6_len = len;
474 node->family.name_id_6_str = str;
476 else if (name_id == 16)
478 node->family.name_id_16_len = len;
479 node->family.name_id_16_str = str;
481 else if (name_id == 21)
483 node->family.name_id_21_len = len;
484 node->family.name_id_21_str = str;
487 return 0;
491 // `info-callback' function
494 info(unsigned short platform_id,
495 unsigned short encoding_id,
496 unsigned short language_id,
497 unsigned short name_id,
498 unsigned short* len,
499 unsigned char** str,
500 void* user)
502 Info_Data* idata = (Info_Data*)user;
504 // if ID is a version string, append our data
505 if (!idata->no_info && name_id == 5)
506 return info_name_id_5(platform_id, encoding_id, len, str, idata);
508 // if ID is related to a family name, collect the data
509 if (*idata->family_suffix
510 && (name_id == 1
511 || name_id == 4
512 || name_id == 6
513 || name_id == 16
514 || name_id == 21))
515 return collect_family_data(platform_id,
516 encoding_id,
517 language_id,
518 name_id,
519 len,
520 str,
521 idata);
523 return 0;
527 // Insert `suffix' to `str', right after substring `name'.
528 // If `name' isn't a substring of `str', append `suffix' to `str'.
529 // Do nothing in case of allocation error or if resulting string is too long.
531 static void
532 insert_suffix(sds suffix,
533 sds name,
534 unsigned short* len,
535 unsigned char** str)
537 if (!len || !*len || !str || !*str)
538 return;
540 sds s = sdsempty();
542 // check whether `name' is a substring of `str'
543 unsigned char* s_start = (unsigned char*)memmem(*str, *len,
544 name, sdslen(name));
546 // construct new string
547 if (s_start)
549 size_t substring_end = s_start - *str + sdslen(name);
551 // everything up to the end of the substring
552 s = sdscatlen(s, *str, substring_end);
553 // the suffix
554 s = sdscatsds(s, suffix);
555 // the rest
556 s = sdscatlen(s, *str + substring_end, *len - substring_end);
558 else
560 s = sdscatlen(s, *str, *len);
561 s = sdscatsds(s, suffix);
564 if (!s)
565 return;
567 if (sdslen(s) <= 0xFFFF)
569 unsigned short len_new = sdslen(s);
570 unsigned char* str_new = (unsigned char*)realloc(*str, len_new);
571 if (str_new)
573 *str = str_new;
574 memcpy(*str, s, len_new);
575 *len = len_new;
579 sdsfree(s);
583 // `info-post-callback' function
586 info_post(void* user)
588 Info_Data* idata = (Info_Data*)user;
589 family_data* family_data_head = (family_data*)idata->family_data_head;
592 // family_suffix + family_suffix_wide
594 sds family_suffix = sdsnew(idata->family_suffix);
595 size_t family_suffix_len = sdslen(family_suffix);
597 sds family_suffix_wide = sdsempty();
598 size_t family_suffix_wide_len = 2 * family_suffix_len;
600 // create sds with given size but uninitialized string data
601 family_suffix_wide = sdsMakeRoomFor(family_suffix_wide,
602 family_suffix_wide_len);
604 char* fs;
606 // construct `family_suffix_wide' by inserting '\0'
607 fs = family_suffix;
608 char *fsw = family_suffix_wide;
609 for (size_t i = 0; i < family_suffix_len; i++)
611 *(fsw++) = '\0';
612 *(fsw++) = *(fs++);
614 sdsIncrLen(family_suffix_wide, family_suffix_wide_len);
617 // family_ps_suffix + family_ps_suffix_wide
619 sds family_ps_suffix = sdsempty();
621 // create sds with estimated size but uninitialized string data;
622 // we later set the size to the actual value
623 family_ps_suffix = sdsMakeRoomFor(family_ps_suffix,
624 family_suffix_len);
626 // construct `family_ps_suffix' by removing all space characters
627 fs = family_suffix;
628 char *fps = family_ps_suffix;
629 for (size_t i = 0; i < family_suffix_len; i++)
631 char c = *(fs++);
632 if (c != ' ')
633 *(fps++) = c;
635 // set correct size
636 sdsIncrLen(family_ps_suffix, fps - family_ps_suffix);
638 size_t family_ps_suffix_len = sdslen(family_ps_suffix);
640 sds family_ps_suffix_wide = sdsempty();
641 size_t family_ps_suffix_wide_len = 2 * family_ps_suffix_len;
643 // create sds with given size but uninitialized string data
644 family_ps_suffix_wide = sdsMakeRoomFor(family_ps_suffix_wide,
645 family_ps_suffix_wide_len);
647 // construct `family_ps_suffix_wide' by inserting '\0'
648 fps = family_ps_suffix;
649 char* fpsw = family_ps_suffix_wide;
650 for (size_t i = 0; i < family_ps_suffix_len; i++)
652 *(fpsw++) = '\0';
653 *(fpsw++) = *(fps++);
655 sdsIncrLen(family_ps_suffix_wide, family_ps_suffix_wide_len);
657 // We try the following algorithm.
659 // 1. If we have a `Preferred Family' (ID 16), use it as the family name,
660 // otherwise use the `Font Family Name' (ID 1). If necessary, search
661 // other language IDs for the current (platform ID, encoding ID) pair
662 // to find a family name.
664 // 2. Append the family suffix to the family substring in the `Font Family
665 // Name' (ID 1), the `Full Font Name' (ID 4), the `Preferred Family'
666 // (ID 16), and the `WWS Family Name' (ID 21). In case the family name
667 // found in step 1 is not a substring, append the suffix to the whole
668 // string.
670 // 3. Remove spaces from the family name and locate this substring in the
671 // `PostScript Name' (ID 6), then append the family suffix, also with
672 // spaces removed. If we don't have a substring, append the stripped
673 // suffix to the whole string.
675 // determine family name for all triplets if available
676 for (Node* node = LLRB_MIN(family_data, family_data_head);
677 node;
678 node = LLRB_NEXT(family_data, family_data_head, node))
680 Family* family = &node->family;
682 if (family->name_id_16_len && *family->name_id_16_len
683 && family->name_id_16_str && *family->name_id_16_str)
684 family->family_name = sdsnewlen(*family->name_id_16_str,
685 *family->name_id_16_len);
686 else if (family->name_id_1_len && *family->name_id_1_len
687 && family->name_id_1_str && *family->name_id_1_str)
688 family->family_name = sdsnewlen(*family->name_id_1_str,
689 *family->name_id_1_len);
692 sds family_name = sdsempty();
693 sds family_ps_name = sdsempty();
695 // process all name ID strings in triplets
696 for (Node* node = LLRB_MIN(family_data, family_data_head);
697 node;
698 node = LLRB_NEXT(family_data, family_data_head, node))
700 Family family = node->family;
701 bool is_wide;
703 sdsclear(family_name);
704 sdsclear(family_ps_name);
706 if (family.family_name)
707 family_name = sdscatsds(family_name, family.family_name);
708 else
710 Node* n;
712 // use family name from a triplet that actually has one
713 for (n = LLRB_MIN(family_data, family_data_head);
715 n = LLRB_NEXT(family_data, family_data_head, n))
717 Family f = n->family;
719 if (f.platform_id == family.platform_id
720 && f.encoding_id == family.encoding_id
721 && f.family_name)
723 family_name = sdscatsds(family_name, f.family_name);
724 break;
728 if (!n)
729 continue; // no valid family name found
732 if (family.platform_id == 1
733 || (family.platform_id == 3 && !(family.encoding_id == 1
734 || family.encoding_id == 10)))
735 is_wide = false; // one-byte or multi-byte encodings
736 else
737 is_wide = true; // (two-byte) UTF-16BE
739 sds suffix = is_wide ? family_suffix_wide : family_suffix;
740 insert_suffix(suffix,
741 family_name,
742 family.name_id_1_len,
743 family.name_id_1_str);
744 insert_suffix(suffix,
745 family_name,
746 family.name_id_4_len,
747 family.name_id_4_str);
748 insert_suffix(suffix,
749 family_name,
750 family.name_id_16_len,
751 family.name_id_16_str);
752 insert_suffix(suffix,
753 family_name,
754 family.name_id_21_len,
755 family.name_id_21_str);
757 size_t family_name_len = sdslen(family_name);
758 if (is_wide)
759 family_name_len &= ~1; // ensure even value for the loop below
761 // set sds to estimated size;
762 // we later set the size to the actual value
763 family_ps_name = sdsMakeRoomFor(family_ps_name,
764 family_name_len);
766 // construct `family_ps_name' by removing all space characters
767 char *fn = family_name;
768 char *fpn = family_ps_name;
769 if (is_wide)
771 for (size_t i = 0; i < family_name_len; i += 2)
773 char c1 = *(fn++);
774 char c2 = *(fn++);
775 if (!(c1 == '\0' && c2 == ' '))
777 *(fpn++) = c1;
778 *(fpn++) = c2;
782 else
784 for (size_t i = 0; i < family_name_len; i++)
786 char c = *(fn++);
787 if (c != ' ')
788 *(fpn++) = c;
791 // set correct size
792 sdsIncrLen(family_ps_name, fpn - family_ps_name);
794 sds ps_suffix = is_wide ? family_ps_suffix_wide : family_ps_suffix;
795 insert_suffix(ps_suffix,
796 family_ps_name,
797 family.name_id_6_len,
798 family.name_id_6_str);
801 sdsfree(family_suffix);
802 sdsfree(family_suffix_wide);
803 sdsfree(family_ps_suffix);
804 sdsfree(family_ps_suffix_wide);
805 sdsfree(family_name);
806 sdsfree(family_ps_name);
808 family_data_free(idata);
810 return 0;
813 } // extern "C"
815 // end of info.cpp