4 * Copyright (C) 2014 by Werner Lemberg.
6 * This file is part of the ttfautohint library, and may only be used,
7 * modified, and distributed under the terms given in `COPYING'. By
8 * continuing to use, modify, or distribute this file you indicate that you
9 * have read `COPYING' and understand and accept it fully.
11 * The file `COPYING' mentioned in the previous paragraph is distributed
12 * with the ttfautohint library.
22 #include "llrb.h" /* a red-black tree implementation */
25 /* a test to check the validity of the first character */
26 /* in a PostScript glyph name -- for simplicity, we include `.' also */
28 const char* namestart_chars
= "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
29 "abcdefghijklmnopqrstuvwxyz"
35 return strchr(namestart_chars
, c
) != NULL
;
40 get_token(char** string_p
)
42 const char* s
= *string_p
;
43 const char* p
= *string_p
;
46 while (*p
&& !isspace(*p
))
54 /* in the following functions, `*string_p' is never '\0'; */
55 /* in case of error, `*string_p' should be set to a meaningful position */
58 get_font_idx(FONT
* font
,
62 const char* s
= *string_p
;
70 saved_locale
= setlocale(LC_NUMERIC
, "C");
72 font_idx
= strtol(s
, &endptr
, 0);
74 setlocale(LC_NUMERIC
, saved_locale
);
76 if (saved_errno
== ERANGE
)
79 return TA_Err_Deltas_Invalid_Font_Index
;
82 /* there must be a whitespace character after the number */
83 if (saved_errno
|| !isspace(*endptr
))
86 return TA_Err_Deltas_Syntax_Error
;
89 /* we have already tested that the first character in `s' is a digit, */
90 /* so `font_idx' can't be negative */
91 if (font_idx
>= font
->num_sfnts
)
94 return TA_Err_Deltas_Invalid_Font_Index
;
98 *font_idx_p
= font_idx
;
105 get_glyph_idx(FONT
* font
,
110 const char* s
= *string_p
;
114 FT_Face face
= font
->sfnts
[font_idx
].face
;
125 saved_locale
= setlocale(LC_NUMERIC
, "C");
127 glyph_idx
= strtol(s
, &endptr
, 0);
129 setlocale(LC_NUMERIC
, saved_locale
);
131 if (saved_errno
== ERANGE
)
134 return TA_Err_Deltas_Invalid_Glyph_Index
;
137 /* there must be a whitespace character after the number */
138 if (saved_errno
|| !isspace(*endptr
))
141 return TA_Err_Deltas_Syntax_Error
;
144 /* we have already tested that the first character in `s' is a digit, */
145 /* so `glyph_idx' can't be negative */
146 if (glyph_idx
>= face
->num_glyphs
)
149 return TA_Err_Deltas_Invalid_Glyph_Index
;
152 else if (isnamestart(*s
))
160 s
= get_token(&endptr
);
162 token
= strndup(s
, endptr
- s
);
166 return TA_Err_Deltas_Allocation_Error
;
169 /* explicitly compare with `.notdef' */
170 /* since `FT_Get_Name_Index' returns glyph index 0 */
171 /* for both this glyph name and invalid ones */
172 if (!strcmp(token
, ".notdef"))
176 glyph_idx
= FT_Get_Name_Index(face
, token
);
186 return TA_Err_Deltas_Invalid_Glyph_Name
;
194 return TA_Err_Deltas_Syntax_Error
;
198 *glyph_idx_p
= glyph_idx
;
205 get_range(char** string_p
,
206 number_range
** number_set_p
,
211 const char* s
= *string_p
;
212 number_range
* number_set
= *number_set_p
;
214 size_t s_len
= strcspn(s
, delims
);
219 /* there must be a whitespace character before the delimiter */
220 /* if it is not \0 */
222 && (!s_len
|| !isspace(s
[s_len
- 1])))
224 *string_p
= (char*)(s
+ s_len
);
225 return TA_Err_Deltas_Syntax_Error
;
228 token
= strndup(s
, s_len
);
232 return TA_Err_Deltas_Allocation_Error
;
235 endptr
= (char*)number_set_parse(token
, &number_set
, min
, max
);
237 /* use offset relative to `s' */
238 endptr
= (char*)s
+ (endptr
- token
);
241 if (number_set
== NUMBERSET_ALLOCATION_ERROR
)
244 return TA_Err_Deltas_Allocation_Error
;
249 if (number_set
== NUMBERSET_INVALID_CHARACTER
)
250 return TA_Err_Deltas_Invalid_Character
;
251 else if (number_set
== NUMBERSET_OVERFLOW
)
252 return TA_Err_Deltas_Overflow
;
253 else if (number_set
== NUMBERSET_INVALID_RANGE
)
254 /* this error code should be adjusted by the caller if necessary */
255 return TA_Err_Deltas_Invalid_Point_Range
;
256 else if (number_set
== NUMBERSET_OVERLAPPING_RANGES
)
257 return TA_Err_Deltas_Overlapping_Ranges
;
258 else if (number_set
== NUMBERSET_NOT_ASCENDING
)
259 return TA_Err_Deltas_Ranges_Not_Ascending
;
261 *number_set_p
= number_set
;
268 get_shift(char** string_p
,
271 const char* s
= *string_p
;
279 saved_locale
= setlocale(LC_NUMERIC
, "C");
281 shift
= strtod(s
, &endptr
);
283 setlocale(LC_NUMERIC
, saved_locale
);
285 if (saved_errno
== ERANGE
)
288 /* this error code should be adjusted by the caller if necessary */
289 return TA_Err_Deltas_Invalid_X_Range
;
292 /* there must be a whitespace character after the number */
293 if (saved_errno
|| !isspace(*endptr
))
296 return TA_Err_Deltas_Syntax_Error
;
299 if (shift
< DELTA_SHIFT_MIN
|| shift
> DELTA_SHIFT_MAX
)
302 return TA_Err_Deltas_Invalid_X_Range
;
307 /* we round the value to a multiple of 1/(2^DELTA_SHIFT) */
308 *shift_p
= (char)(shift
* DELTA_FACTOR
+ (shift
> 0 ? 0.5 : -0.5));
315 * Parse a delta exceptions line.
317 * In case of success (this is, the delta exceptions description in `s' is
318 * valid), `pos' is a pointer to the final zero byte in string `s'. In case
319 * of an error, it points to the offending character in string `s'.
321 * If s is NULL, the function exits immediately, with NULL as the value of
324 * If the user provides a non-NULL `deltas' value, `deltas_parse' stores the
325 * parsed result in `*deltas', allocating the necessary memory. If there is
326 * no data (for example, an empty string or whitespace only) nothing gets
327 * allocated, and `*deltas' is set to NULL.
332 deltas_parse_line(FONT
* font
,
336 int ppem_min
, int ppem_max
)
358 char* pos
= (char*)s
;
365 number_range
* points
= NULL
;
368 number_range
* ppems
= NULL
;
381 State old_state
= state
;
386 while (isspace(*pos
))
399 next_is_space
= isspace(*(pos
+ 1));
404 token1
= get_token(&pos
);
409 token2
= get_token(&pos
);
416 /* possibility 1: <font idx> <glyph name> `p' */
418 && (isdigit(*token2
) || isnamestart(*token2
))
419 && (c
== 'p' && next_is_space
))
421 if (!(error
= get_font_idx(font
, &pos
, &font_idx
)))
422 state
= HAD_FONT_IDX
;
424 /* possibility 2: <glyph name> `p' */
425 else if ((isdigit(*token1
) || isnamestart(*token1
))
426 && (*token2
== 'p' && isspace(*(token2
+ 1))))
428 if (!(error
= get_glyph_idx(font
, font_idx
,
430 state
= HAD_GLYPH_IDX
;
436 if (!(error
= get_glyph_idx(font
, font_idx
,
438 state
= HAD_GLYPH_IDX
;
443 if (c
== 'p' && next_is_space
)
454 FT_Face face
= font
->sfnts
[font_idx
].face
;
458 ft_error
= FT_Load_Glyph(face
, glyph_idx
, FT_LOAD_NO_SCALE
);
461 error
= TA_Err_Deltas_Invalid_Glyph
;
465 num_points
= face
->glyph
->outline
.n_points
;
466 if (!(error
= get_range(&pos
, deltas_p
? &points
: NULL
,
467 0, num_points
- 1, "xy@")))
473 /* `x' or `y' or `@' */
490 if (!(error
= get_shift(&pos
, &x_shift
)))
510 if (!(error
= get_shift(&pos
, &y_shift
)))
512 if (error
== TA_Err_Deltas_Invalid_X_Range
)
513 error
= TA_Err_Deltas_Invalid_Y_Range
;
518 if (c
== '@' && next_is_space
)
527 if (!(error
= get_range(&pos
, deltas_p
? &ppems
: NULL
,
528 ppem_min
, ppem_max
, "")))
530 if (error
== TA_Err_Deltas_Invalid_Point_Range
)
531 error
= TA_Err_Deltas_Invalid_Ppem_Range
;
535 /* to make compiler happy */
539 if (old_state
== state
)
543 if (state
== HAD_PPEMS
)
548 Deltas
* deltas
= (Deltas
*)malloc(sizeof (Deltas
));
553 number_set_free(points
);
554 number_set_free(ppems
);
558 return TA_Err_Deltas_Allocation_Error
;
561 deltas
->font_idx
= font_idx
;
562 deltas
->glyph_idx
= glyph_idx
;
563 deltas
->points
= points
;
564 deltas
->x_shift
= x_shift
;
565 deltas
->y_shift
= y_shift
;
566 deltas
->ppems
= ppems
;
576 number_set_free(points
);
577 number_set_free(ppems
);
582 if (!error
&& state
!= START
)
583 error
= TA_Err_Deltas_Syntax_Error
;
593 TA_deltas_free(Deltas
* deltas
)
600 number_set_free(deltas
->points
);
601 number_set_free(deltas
->ppems
);
604 deltas
= deltas
->next
;
610 /* `len' is the length of the returned string */
613 deltas_show_line(FONT
* font
,
618 char glyph_name_buf
[64];
619 char* points_buf
= NULL
;
620 char* ppems_buf
= NULL
;
621 char* deltas_buf
= NULL
;
629 if (deltas
->font_idx
>= font
->num_sfnts
)
632 face
= font
->sfnts
[deltas
->font_idx
].face
;
633 glyph_name_buf
[0] = '\0';
634 if (FT_HAS_GLYPH_NAMES(face
))
635 FT_Get_Glyph_Name(face
, deltas
->glyph_idx
, glyph_name_buf
, 64);
637 points_buf
= number_set_show(deltas
->points
, -1, -1);
640 ppems_buf
= number_set_show(deltas
->ppems
, -1, -1);
644 /* display glyph index if we don't have a glyph name */
646 ret
= asprintf(&deltas_buf
, "%ld %s p %s x %.20g y %.20g @ %s",
650 (double)deltas
->x_shift
/ DELTA_FACTOR
,
651 (double)deltas
->y_shift
/ DELTA_FACTOR
,
654 ret
= asprintf(&deltas_buf
, "%ld %ld p %s x %.20g y %.20g @ %s",
658 (double)deltas
->x_shift
/ DELTA_FACTOR
,
659 (double)deltas
->y_shift
/ DELTA_FACTOR
,
676 TA_deltas_show(FONT
* font
,
683 /* we return an empty string if there is no data */
684 s
= (char*)malloc(1);
699 tmp
= deltas_show_line(font
, &tmp_len
, deltas
);
706 /* append current line to buffer, followed by a newline character */
707 s_len_new
= s_len
+ tmp_len
+ 1;
708 s_new
= (char*)realloc(s
, s_len_new
);
716 strcpy(s_new
+ s_len
- 1, tmp
);
717 s_new
[s_len_new
- 2] = '\n';
718 s_new
[s_len_new
- 1] = '\0';
725 deltas
= deltas
->next
;
732 /* Get a line from a buffer, starting at `pos'. The final EOL */
733 /* character (or character pair) of the line (if existing) gets removed. */
734 /* After the call, `pos' points to the position right after the line in */
735 /* the buffer. The line is allocated with `malloc'; it returns NULL for */
736 /* end of data and allocation errors; the latter can be recognized by a */
737 /* changed value of `pos'. */
742 const char* start
= *pos
;
743 const char* p
= start
;
753 if (*p
== '\n' || *p
== '\r')
777 s
= (char*)malloc(len
+ 1);
782 strncpy(s
, start
, len
);
790 TA_deltas_parse(FONT
* font
,
792 unsigned int* errlinenum_p
,
803 unsigned int linenum
;
809 /* nothing to do if no data */
810 if (!font
->deltas_buf
)
816 bufpos
= font
->deltas_buf
;
822 /* parse line by line */
829 line
= get_line(&bufpos
);
832 if (bufpos
== bufpos_old
) /* end of data */
835 *errlinenum_p
= linenum
;
839 return FT_Err_Out_Of_Memory
;
842 error
= deltas_parse_line(font
,
845 deltas
? &new_deltas
: NULL
,
846 DELTA_PPEM_MIN
, DELTA_PPEM_MAX
);
849 *errlinenum_p
= linenum
;
858 if (deltas
&& new_deltas
)
860 new_deltas
->next
= cur
;
869 /* success; now reverse list to have elements in original order */
887 /* node structure for delta exception data */
889 typedef struct Node Node
;
892 LLRB_ENTRY(Node
) entry
;
897 /* comparison function for our red-black tree */
906 /* sort by font index ... */
907 diff
= e1
->delta
.font_idx
- e2
->delta
.font_idx
;
911 /* ... then by glyph index ... */
912 diff
= e1
->delta
.glyph_idx
- e2
->delta
.glyph_idx
;
916 /* ... then by ppem ... */
917 diff
= e1
->delta
.ppem
- e2
->delta
.ppem
;
921 /* ... then by point index */
922 diff
= e1
->delta
.point_idx
- e2
->delta
.point_idx
;
925 /* https://graphics.stanford.edu/~seander/bithacks.html#CopyIntegerSign */
926 return (diff
> 0) - (diff
< 0);
930 /* the red-black tree function body */
931 typedef struct deltas_data deltas_data
;
933 LLRB_HEAD(deltas_data
, Node
);
935 /* no trailing semicolon in the next line */
936 LLRB_GENERATE_STATIC(deltas_data
, Node
, entry
, nodecmp
)
940 TA_deltas_free_tree(FONT
* font
)
942 deltas_data
* deltas_data_head
= (deltas_data
*)font
->deltas_data_head
;
948 if (!deltas_data_head
)
951 for (node
= LLRB_MIN(deltas_data
, deltas_data_head
);
955 next_node
= LLRB_NEXT(deltas_data
, deltas_data_head
, node
);
956 LLRB_REMOVE(deltas_data
, deltas_data_head
, node
);
960 free(deltas_data_head
);
965 TA_deltas_build_tree(FONT
* font
,
968 deltas_data
* deltas_data_head
;
969 int emit_newline
= 0;
972 /* nothing to do if no data */
975 font
->deltas_data_head
= NULL
;
979 deltas_data_head
= (deltas_data
*)malloc(sizeof (deltas_data
));
980 if (!deltas_data_head
)
981 return FT_Err_Out_Of_Memory
;
983 LLRB_INIT(deltas_data_head
);
987 long font_idx
= deltas
->font_idx
;
988 long glyph_idx
= deltas
->glyph_idx
;
989 char x_shift
= deltas
->x_shift
;
990 char y_shift
= deltas
->y_shift
;
992 number_set_iter ppems_iter
;
996 ppems_iter
.range
= deltas
->ppems
;
997 ppem
= number_set_get_first(&ppems_iter
);
999 while (ppems_iter
.range
)
1001 number_set_iter points_iter
;
1005 points_iter
.range
= deltas
->points
;
1006 point_idx
= number_set_get_first(&points_iter
);
1008 while (points_iter
.range
)
1014 node
= (Node
*)malloc(sizeof (Node
));
1016 return FT_Err_Out_Of_Memory
;
1018 node
->delta
.font_idx
= font_idx
;
1019 node
->delta
.glyph_idx
= glyph_idx
;
1020 node
->delta
.ppem
= ppem
;
1021 node
->delta
.point_idx
= point_idx
;
1022 node
->delta
.x_shift
= x_shift
;
1023 node
->delta
.y_shift
= y_shift
;
1025 val
= LLRB_INSERT(deltas_data
, deltas_data_head
, node
);
1028 if (val
&& font
->debug
)
1030 /* entry is already present; we ignore it */
1033 number_range points
;
1039 /* construct Deltas entry for debugging output */
1043 points
.start
= point_idx
;
1044 points
.end
= point_idx
;
1047 deltas
.font_idx
= font_idx
;
1048 deltas
.glyph_idx
= glyph_idx
;
1049 deltas
.points
= &points
;
1050 deltas
.x_shift
= x_shift
;
1051 deltas
.y_shift
= y_shift
;
1052 deltas
.ppems
= &ppems
;
1055 s
= deltas_show_line(font
, &s_len
, &deltas
);
1058 fprintf(stderr
, "Delta exception %s ignored.\n", s
);
1065 point_idx
= number_set_get_next(&points_iter
);
1068 ppem
= number_set_get_next(&ppems_iter
);
1071 deltas
= deltas
->next
;
1074 if (font
->debug
&& emit_newline
)
1075 fprintf(stderr
, "\n");
1077 font
->deltas_data_head
= deltas_data_head
;
1078 font
->deltas_data_cur
= LLRB_MIN(deltas_data
, deltas_data_head
);
1084 /* the next functions are intended to restrict the use of LLRB stuff */
1085 /* related to delta exceptions to this file, */
1086 /* providing a means to access the data sequentially */
1089 TA_deltas_get_next(FONT
* font
)
1091 Node
* node
= (Node
*)font
->deltas_data_cur
;
1097 node
= LLRB_NEXT(deltas_data
, /* unused */, node
);
1099 font
->deltas_data_cur
= node
;
1104 TA_deltas_get_delta(FONT
* font
)
1106 Node
* node
= (Node
*)font
->deltas_data_cur
;
1109 return node
? &node
->delta
: NULL
;
1112 /* end of tadeltas.c */