Parse delta exceptions file completely.
[ttfautohint.git] / lib / tadeltas.c
blob37483d0b4e666fff4f24112c0b2140ce54396d05
1 /* tadeltas.c */
3 /*
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.
15 #include <ta.h>
16 #include <tadeltas.h>
17 #include <errno.h>
18 #include <ctype.h>
19 #include <math.h>
22 /* a test to check the validity of the first character */
23 /* in a PostScript glyph name -- for simplicity, we include `.' also */
25 const char* namestart_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
26 "abcdefghijklmnopqrstuvwxyz"
27 "._";
29 static int
30 isnamestart(int c)
32 return strchr(namestart_chars, c) != NULL;
36 static char*
37 get_token(char** string_p)
39 const char* s = *string_p;
40 const char* p = *string_p;
43 while (*p && !isspace(*p))
44 p++;
46 *string_p = (char*)p;
47 return (char*)s;
51 /* in the following functions, `*string_p' is never '\0'; */
52 /* in case of error, `*string_p' should be set to a meaningful position */
54 static TA_Error
55 get_font_idx(FONT* font,
56 char** string_p,
57 long* font_idx_p)
59 const char* s = *string_p;
60 char* endptr;
61 long font_idx;
64 errno = 0;
65 font_idx = strtol(s, &endptr, 0);
67 if (errno == ERANGE)
69 /* *string_p = s; */
70 return TA_Err_Deltas_Invalid_Font_Index;
73 /* there must be a whitespace character after the number */
74 if (errno || !isspace(*endptr))
76 *string_p = endptr;
77 return TA_Err_Deltas_Syntax_Error;
80 /* we have already tested that the first character in `s' is a digit, */
81 /* so `font_idx' can't be negative */
82 if (font_idx >= font->num_sfnts)
84 /* *string_p = s; */
85 return TA_Err_Deltas_Invalid_Font_Index;
88 *string_p = endptr;
89 *font_idx_p = font_idx;
91 return TA_Err_Ok;
95 static TA_Error
96 get_glyph_idx(FONT* font,
97 long font_idx,
98 char** string_p,
99 long* glyph_idx_p)
101 const char* s = *string_p;
102 char* endptr;
103 long glyph_idx;
105 FT_Face face = font->sfnts[font_idx].face;
108 if (isdigit(*s))
110 /* glyph index */
112 errno = 0;
113 glyph_idx = strtol(s, &endptr, 0);
115 if (errno == ERANGE)
117 /* *string_p = s; */
118 return TA_Err_Deltas_Invalid_Glyph_Index;
121 /* there must be a whitespace character after the number */
122 if (errno || !isspace(*endptr))
124 *string_p = endptr;
125 return TA_Err_Deltas_Syntax_Error;
128 /* we have already tested that the first character in `s' is a digit, */
129 /* so `glyph_idx' can't be negative */
130 if (glyph_idx >= face->num_glyphs)
132 /* *string_p = s; */
133 return TA_Err_Deltas_Invalid_Glyph_Index;
136 else if (isnamestart(*s))
138 /* glyph name */
140 char* token;
143 endptr = (char*)s;
144 s = get_token(&endptr);
146 token = strndup(s, endptr - s);
147 if (!token)
149 /* *string_p = s; */
150 return TA_Err_Deltas_Allocation_Error;
153 /* explicitly compare with `.notdef' */
154 /* since `FT_Get_Name_Index' returns glyph index 0 */
155 /* for both this glyph name and invalid ones */
156 if (!strcmp(token, ".notdef"))
157 glyph_idx = 0;
158 else
160 glyph_idx = FT_Get_Name_Index(face, token);
161 if (glyph_idx == 0)
162 glyph_idx = -1;
165 free(token);
167 if (glyph_idx < 0)
169 /* *string_p = s; */
170 return TA_Err_Deltas_Invalid_Glyph_Name;
173 else
175 /* invalid */
177 /* *string_p = s; */
178 return TA_Err_Deltas_Syntax_Error;
181 *string_p = endptr;
182 *glyph_idx_p = glyph_idx;
184 return TA_Err_Ok;
188 static TA_Error
189 get_range(char** string_p,
190 number_range** number_set_p,
191 int min,
192 int max,
193 const char* delims)
195 const char* s = *string_p;
196 number_range* number_set = *number_set_p;
198 size_t s_len = strcspn(s, delims);
199 char* token;
200 char* endptr;
203 /* there must be a whitespace character before the delimiter */
204 /* if it is not \0 */
205 if (*delims
206 && (!s_len || !isspace(s[s_len - 1])))
208 *string_p = (char*)(s + s_len);
209 return TA_Err_Deltas_Syntax_Error;
212 token = strndup(s, s_len);
213 if (!token)
215 /* *string_p = s; */
216 return TA_Err_Deltas_Allocation_Error;
219 endptr = (char*)number_set_parse(token, &number_set, min, max);
221 /* use offset relative to `s' */
222 endptr = (char*)s + (endptr - token);
223 free(token);
225 if (number_set == NUMBERSET_ALLOCATION_ERROR)
227 /* *string_p = s; */
228 return TA_Err_Deltas_Allocation_Error;
231 *string_p = endptr;
233 if (number_set == NUMBERSET_INVALID_CHARACTER)
234 return TA_Err_Deltas_Invalid_Character;
235 else if (number_set == NUMBERSET_OVERFLOW)
236 return TA_Err_Deltas_Overflow;
237 else if (number_set == NUMBERSET_INVALID_RANGE)
238 /* this error code should be adjusted by the caller if necessary */
239 return TA_Err_Deltas_Invalid_Point_Range;
240 else if (number_set == NUMBERSET_OVERLAPPING_RANGES)
241 return TA_Err_Deltas_Overlapping_Ranges;
242 else if (number_set == NUMBERSET_NOT_ASCENDING)
243 return TA_Err_Deltas_Ranges_Not_Ascending;
245 *number_set_p = number_set;
247 return TA_Err_Ok;
251 static TA_Error
252 get_shift(char** string_p,
253 char* shift_p)
255 const char* s = *string_p;
256 char* endptr;
257 double shift;
260 errno = 0;
261 shift = strtod(s, &endptr);
263 if (errno == ERANGE)
265 /* *string_p = s; */
266 /* this error code should be adjusted by the caller if necessary */
267 return TA_Err_Deltas_Invalid_X_Range;
270 /* there must be a whitespace character after the number */
271 if (errno || !isspace(*endptr))
273 *string_p = endptr;
274 return TA_Err_Deltas_Syntax_Error;
277 if (shift < DELTA_SHIFT_MIN || shift > DELTA_SHIFT_MAX)
279 /* *string_p = s; */
280 return TA_Err_Deltas_Invalid_X_Range;
283 *string_p = endptr;
285 /* we round the value to a multiple of 1/(2^DELTA_SHIFT) */
286 *shift_p = (char)(shift * DELTA_FACTOR + (shift > 0 ? 0.5 : -0.5));
288 return TA_Err_Ok;
293 * Parse a delta exceptions line.
295 * In case of success (this is, the delta exceptions description in `s' is
296 * valid), `pos' is a pointer to the final zero byte in string `s'. In case
297 * of an error, it points to the offending character in string `s'.
299 * If s is NULL, the function exits immediately, with NULL as the value of
300 * `pos'.
302 * If the user provides a non-NULL `deltas' value, `deltas_parse' stores the
303 * parsed result in `*deltas', allocating the necessary memory. If there is
304 * no data (for example, an empty string or whitespace only) nothing gets
305 * allocated, and `*deltas' is set to NULL.
309 static TA_Error
310 deltas_parse_line(FONT* font,
311 const char* s,
312 char** err_pos,
313 Deltas** deltas_p,
314 int ppem_min, int ppem_max)
316 TA_Error error;
318 typedef enum State_
319 { START,
320 HAD_TOKEN1,
321 HAD_TOKEN2,
322 HAD_FONT_IDX,
323 HAD_GLYPH_IDX,
324 HAD_P,
325 HAD_POINTS,
326 HAD_X,
327 HAD_X_SHIFT,
328 HAD_Y,
329 HAD_Y_SHIFT,
330 HAD_AT,
331 HAD_PPEMS
332 } State;
334 State state = START;
336 char* pos = (char*)s;
338 const char* token1;
339 const char* token2;
341 long font_idx = 0;
342 long glyph_idx = -1;
343 number_range* points = NULL;
344 char x_shift = 0;
345 char y_shift = 0;
346 number_range* ppems = NULL;
349 if (!s)
351 *err_pos = NULL;
352 return TA_Err_Ok;
355 for (;;)
357 char c;
358 int next_is_space;
359 State old_state = state;
362 error = TA_Err_Ok;
364 while (isspace(*pos))
365 pos++;
367 /* skip comment */
368 if (*pos == '#')
369 while (*pos)
370 pos++;
372 /* EOL */
373 if (!*pos)
374 break;
376 c = *pos;
377 next_is_space = isspace(*(pos + 1));
379 switch (state)
381 case START:
382 token1 = get_token(&pos);
383 state = HAD_TOKEN1;
384 break;
386 case HAD_TOKEN1:
387 token2 = get_token(&pos);
388 state = HAD_TOKEN2;
389 break;
391 case HAD_TOKEN2:
392 pos = (char*)token1;
394 /* possibility 1: <font idx> <glyph name> `p' */
395 if (isdigit(*token1)
396 && (isdigit(*token2) || isnamestart(*token2))
397 && (c == 'p' && next_is_space))
399 if (!(error = get_font_idx(font, &pos, &font_idx)))
400 state = HAD_FONT_IDX;
402 /* possibility 2: <glyph name> `p' */
403 else if ((isdigit(*token1) || isnamestart(*token1))
404 && (c == 'p' && next_is_space))
406 if (!(error = get_glyph_idx(font, font_idx,
407 &pos, &glyph_idx)))
408 state = HAD_GLYPH_IDX;
410 break;
412 case HAD_FONT_IDX:
413 /* <glyph idx> */
414 if (!(error = get_glyph_idx(font, font_idx,
415 &pos, &glyph_idx)))
416 state = HAD_GLYPH_IDX;
417 break;
419 case HAD_GLYPH_IDX:
420 /* `p' */
421 if (c == 'p' && next_is_space)
423 state = HAD_P;
424 pos += 2;
426 break;
428 case HAD_P:
429 /* <points> */
431 FT_Error ft_error;
432 FT_Face face = font->sfnts[font_idx].face;
433 int num_points;
436 ft_error = FT_Load_Glyph(face, glyph_idx, FT_LOAD_NO_SCALE);
437 if (ft_error)
439 error = TA_Err_Deltas_Invalid_Glyph;
440 break;
443 num_points = face->glyph->outline.n_points;
444 if (!(error = get_range(&pos, deltas_p ? &points : NULL,
445 0, num_points - 1, "xy@")))
446 state = HAD_POINTS;
448 break;
450 case HAD_POINTS:
451 /* `x' or `y' or `@' */
452 if (next_is_space)
454 pos += 2;
455 if (c == 'x')
456 state = HAD_X;
457 else if (c == 'y')
458 state = HAD_Y;
459 else if (c == '@')
460 state = HAD_AT;
461 else
462 pos -= 2;
464 break;
466 case HAD_X:
467 /* <x_shift> */
468 if (!(error = get_shift(&pos, &x_shift)))
469 state = HAD_X_SHIFT;
470 break;
472 case HAD_X_SHIFT:
473 /* 'y' or '@' */
474 if (next_is_space)
476 pos += 2;
477 if (c == 'y')
478 state = HAD_Y;
479 else if (c == '@')
480 state = HAD_AT;
481 else
482 pos -= 2;
484 break;
486 case HAD_Y:
487 /* <y shift> */
488 if (!(error = get_shift(&pos, &y_shift)))
489 state = HAD_Y_SHIFT;
490 if (error == TA_Err_Deltas_Invalid_X_Range)
491 error = TA_Err_Deltas_Invalid_Y_Range;
492 break;
494 case HAD_Y_SHIFT:
495 /* '@' */
496 if (c == '@' && next_is_space)
498 state = HAD_AT;
499 pos += 2;
501 break;
503 case HAD_AT:
504 /* <ppems> */
505 if (!(error = get_range(&pos, deltas_p ? &ppems : NULL,
506 ppem_min, ppem_max, "")))
507 state = HAD_PPEMS;
508 if (error == TA_Err_Deltas_Invalid_Point_Range)
509 error = TA_Err_Deltas_Invalid_Ppem_Range;
510 break;
512 case HAD_PPEMS:
513 /* to make compiler happy */
514 break;
517 if (old_state == state)
518 break;
521 if (state == HAD_PPEMS)
523 /* success */
524 if (deltas_p)
526 Deltas* deltas = (Deltas*)malloc(sizeof (Deltas));
529 if (!deltas)
531 number_set_free(points);
532 number_set_free(ppems);
534 *err_pos = (char*)s;
536 return TA_Err_Deltas_Allocation_Error;
539 deltas->font_idx = font_idx;
540 deltas->glyph_idx = glyph_idx;
541 deltas->points = points;
542 deltas->x_shift = x_shift;
543 deltas->y_shift = y_shift;
544 deltas->ppems = ppems;
545 deltas->next = NULL;
547 *deltas_p = deltas;
550 else
552 if (deltas_p)
554 number_set_free(points);
555 number_set_free(ppems);
557 *deltas_p = NULL;
560 if (!error && state != START)
561 error = TA_Err_Deltas_Syntax_Error;
564 *err_pos = pos;
566 return error;
570 void
571 TA_deltas_free(Deltas* deltas)
573 while (deltas)
575 Deltas* tmp;
578 number_set_free(deltas->points);
579 number_set_free(deltas->ppems);
581 tmp = deltas;
582 deltas = deltas->next;
583 free(tmp);
588 /* `len' is the length of the returned string */
590 char*
591 deltas_show_line(FONT* font,
592 int* len,
593 Deltas* deltas)
595 int ret;
596 char glyph_name_buf[64];
597 char* points_buf = NULL;
598 char* ppems_buf = NULL;
599 char* deltas_buf = NULL;
601 FT_Face face;
604 if (!deltas)
605 return NULL;
607 if (deltas->font_idx >= font->num_sfnts)
608 return NULL;
610 face = font->sfnts[deltas->font_idx].face;
611 glyph_name_buf[0] = '\0';
612 if (FT_HAS_GLYPH_NAMES(face))
613 FT_Get_Glyph_Name(face, deltas->glyph_idx, glyph_name_buf, 64);
615 points_buf = number_set_show(deltas->points, -1, -1);
616 if (!points_buf)
617 goto Exit;
618 ppems_buf = number_set_show(deltas->ppems, -1, -1);
619 if (!ppems_buf)
620 goto Exit;
622 /* display glyph index if we don't have a glyph name */
623 if (*glyph_name_buf)
624 ret = asprintf(&deltas_buf, "%ld %s p %s x %.20g y %.20g @ %s",
625 deltas->font_idx,
626 glyph_name_buf,
627 points_buf,
628 (double)deltas->x_shift / DELTA_FACTOR,
629 (double)deltas->y_shift / DELTA_FACTOR,
630 ppems_buf);
631 else
632 ret = asprintf(&deltas_buf, "%ld %ld p %s x %.20g y %.20g @ %s",
633 deltas->font_idx,
634 deltas->glyph_idx,
635 points_buf,
636 (double)deltas->x_shift / DELTA_FACTOR,
637 (double)deltas->y_shift / DELTA_FACTOR,
638 ppems_buf);
640 Exit:
641 free(points_buf);
642 free(ppems_buf);
644 if (ret == -1)
645 return NULL;
647 *len = ret;
649 return deltas_buf;
653 char*
654 TA_deltas_show(FONT* font,
655 Deltas* deltas)
657 char* s;
658 int s_len;
661 /* we return an empty string if there is no data */
662 s = (char*)malloc(1);
663 if (!s)
664 return NULL;
666 *s = '\0';
667 s_len = 1;
669 while (deltas)
671 char* tmp;
672 int tmp_len;
673 char* s_new;
674 int s_len_new;
677 tmp = deltas_show_line(font, &tmp_len, deltas);
678 if (!tmp)
680 free(s);
681 return NULL;
684 /* append current line to buffer, followed by a newline character */
685 s_len_new = s_len + tmp_len + 1;
686 s_new = (char*)realloc(s, s_len_new);
687 if (!s_new)
689 free(s);
690 free(tmp);
691 return NULL;
694 strcpy(s_new + s_len - 1, tmp);
695 s_new[s_len_new - 2] = '\n';
696 s_new[s_len_new - 1] = '\0';
698 s = s_new;
699 s_len = s_len_new;
701 free(tmp);
703 deltas = deltas->next;
706 return s;
710 /* Get a line from a buffer, starting at `pos'. The final EOL */
711 /* character (or character pair) of the line (if existing) gets removed. */
712 /* After the call, `pos' points to the position right after the line in */
713 /* the buffer. The line is allocated with `malloc'; it returns NULL for */
714 /* end of data and allocation errors; the latter can be recognized by a */
715 /* changed value of `pos'. */
717 static char*
718 get_line(char** pos)
720 const char* start = *pos;
721 const char* p = start;
722 size_t len;
723 char* s;
726 if (!*p)
727 return NULL;
729 while (*p)
731 if (*p == '\n' || *p == '\r')
732 break;
734 p++;
737 len = p - start + 1;
739 if (*p == '\r')
741 len--;
742 p++;
744 if (*p == '\n')
745 p++;
747 else if (*p == '\n')
749 len--;
750 p++;
753 *pos = (char*)p;
755 s = (char*)malloc(len + 1);
756 if (!s)
757 return NULL;
759 if (len)
760 strncpy(s, start, len);
761 s[len] = '\0';
763 return s;
767 TA_Error
768 TA_deltas_parse(FONT* font,
769 Deltas** deltas,
770 unsigned int* errlinenum_p,
771 char** errline_p,
772 char** errpos_p)
774 FT_Error error;
776 Deltas* cur;
777 Deltas* new_deltas;
778 Deltas* tmp;
779 Deltas* list;
781 unsigned int linenum;
783 char* bufpos;
784 char* bufpos_old;
787 /* nothing to do if no data */
788 if (!font->deltas_buf)
789 return TA_Err_Ok;
791 bufpos = font->deltas_buf;
792 bufpos_old = bufpos;
794 linenum = 0;
795 cur = NULL;
797 /* parse line by line */
798 for (;;)
800 char* line;
801 char* errpos;
804 line = get_line(&bufpos);
805 if (!line)
807 if (bufpos == bufpos_old) /* end of data */
808 break;
810 *errlinenum_p = linenum;
811 *errline_p = NULL;
812 *errpos_p = NULL;
814 return FT_Err_Out_Of_Memory;
817 error = deltas_parse_line(font,
818 line,
819 &errpos,
820 deltas ? &new_deltas : NULL,
821 DELTA_PPEM_MIN, DELTA_PPEM_MAX);
822 if (error)
824 *errlinenum_p = linenum;
825 *errline_p = line;
826 *errpos_p = errpos;
828 TA_deltas_free(cur);
830 return error;
833 if (deltas && new_deltas)
835 new_deltas->next = cur;
836 cur = new_deltas;
839 free(line);
840 linenum++;
841 bufpos_old = bufpos;
844 /* success; now reverse list to have elements in original order */
845 list = NULL;
847 while (cur)
849 tmp = cur;
850 cur = cur->next;
851 tmp->next = list;
852 list = tmp;
855 if (deltas)
856 *deltas = list;
858 return TA_Err_Ok;
864 * Using
866 * delta_shift = 3 ,
868 * the possible shift values in the instructions are indexed as follows:
870 * 0 -1px
871 * 1 -7/8px
872 * ...
873 * 7 -1/8px
874 * 8 1/8px
875 * ...
876 * 14 7/8px
877 * 15 1px
879 * (note that there is no index for a zero shift).
883 /* end of tadeltas.c */