Minor renamings and type fiddling.
[ttfautohint.git] / lib / tadeltas.c
blobe80882113283b9e20315c84ee61a74fbffe790e0
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;
292 TA_Error
293 TA_deltas_parse(FONT* font,
294 const char* s,
295 char** err_pos,
296 Deltas** deltas_p,
297 int ppem_min, int ppem_max)
299 TA_Error error;
301 typedef enum State_
302 { START,
303 HAD_TOKEN1,
304 HAD_TOKEN2,
305 HAD_FONT_IDX,
306 HAD_GLYPH_IDX,
307 HAD_P,
308 HAD_POINTS,
309 HAD_X,
310 HAD_X_SHIFT,
311 HAD_Y,
312 HAD_Y_SHIFT,
313 HAD_AT,
314 HAD_PPEMS
315 } State;
317 State state = START;
319 Deltas* deltas = NULL;
320 char* pos = (char*)s;
322 const char* token1;
323 const char* token2;
326 if (!s)
328 *err_pos = NULL;
329 return TA_Err_Ok;
332 deltas = (Deltas*)malloc(sizeof (Deltas));
333 if (!deltas)
335 *err_pos = (char*)s;
337 return TA_Err_Deltas_Allocation_Error;
340 deltas->font_idx = 0;
341 deltas->glyph_idx = -1;
342 deltas->points = NULL;
343 deltas->x_shift = 0;
344 deltas->y_shift = 0;
345 deltas->ppems = NULL;
347 for (;;)
349 char c;
350 int next_is_space;
351 State old_state = state;
354 error = TA_Err_Ok;
356 while (isspace(*pos))
357 pos++;
359 /* skip comment */
360 if (*pos == '#')
361 while (*pos)
362 pos++;
364 /* EOL */
365 if (!*pos)
366 break;
368 c = *pos;
369 next_is_space = isspace(*(pos + 1));
371 switch (state)
373 case START:
374 token1 = get_token(&pos);
375 state = HAD_TOKEN1;
376 break;
378 case HAD_TOKEN1:
379 token2 = get_token(&pos);
380 state = HAD_TOKEN2;
381 break;
383 case HAD_TOKEN2:
384 pos = (char*)token1;
386 /* possibility 1: <font idx> <glyph name> `p' */
387 if (isdigit(*token1)
388 && (isdigit(*token2) || isnamestart(*token2))
389 && (c == 'p' && next_is_space))
391 if (!(error = get_font_idx(font, &pos, &deltas->font_idx)))
392 state = HAD_FONT_IDX;
394 /* possibility 2: <glyph name> `p' */
395 else if ((isdigit(*token1) || isnamestart(*token1))
396 && (c == 'p' && next_is_space))
398 if (!(error = get_glyph_idx(font, deltas->font_idx,
399 &pos, &deltas->glyph_idx)))
400 state = HAD_GLYPH_IDX;
402 break;
404 case HAD_FONT_IDX:
405 /* <glyph idx> */
406 if (!(error = get_glyph_idx(font, deltas->font_idx,
407 &pos, &deltas->glyph_idx)))
408 state = HAD_GLYPH_IDX;
409 break;
411 case HAD_GLYPH_IDX:
412 /* `p' */
413 if (c == 'p' && next_is_space)
415 state = HAD_P;
416 pos += 2;
418 break;
420 case HAD_P:
421 /* <points> */
423 FT_Error ft_error;
424 FT_Face face = font->sfnts[deltas->font_idx].face;
425 int num_points;
428 ft_error = FT_Load_Glyph(face, deltas->glyph_idx, FT_LOAD_NO_SCALE);
429 if (ft_error)
431 error = TA_Err_Deltas_Invalid_Glyph;
432 break;
435 num_points = face->glyph->outline.n_points;
436 if (!(error = get_range(&pos, &deltas->points,
437 0, num_points - 1, "xy@")))
438 state = HAD_POINTS;
440 break;
442 case HAD_POINTS:
443 /* `x' or `y' or `@' */
444 if (next_is_space)
446 pos += 2;
447 if (c == 'x')
448 state = HAD_X;
449 else if (c == 'y')
450 state = HAD_Y;
451 else if (c == '@')
452 state = HAD_AT;
453 else
454 pos -= 2;
456 break;
458 case HAD_X:
459 /* <x_shift> */
460 if (!(error = get_shift(&pos, &deltas->x_shift)))
461 state = HAD_X_SHIFT;
462 break;
464 case HAD_X_SHIFT:
465 /* 'y' or '@' */
466 if (next_is_space)
468 pos += 2;
469 if (c == 'y')
470 state = HAD_Y;
471 else if (c == '@')
472 state = HAD_AT;
473 else
474 pos -= 2;
476 break;
478 case HAD_Y:
479 /* <y shift> */
480 if (!(error = get_shift(&pos, &deltas->y_shift)))
481 state = HAD_Y_SHIFT;
482 if (error == TA_Err_Deltas_Invalid_X_Range)
483 error = TA_Err_Deltas_Invalid_Y_Range;
484 break;
486 case HAD_Y_SHIFT:
487 /* '@' */
488 if (c == '@' && next_is_space)
490 state = HAD_AT;
491 pos += 2;
493 break;
495 case HAD_AT:
496 /* <ppems> */
497 if (!(error = get_range(&pos, &deltas->ppems,
498 ppem_min, ppem_max, "")))
499 state = HAD_PPEMS;
500 if (error == TA_Err_Deltas_Invalid_Point_Range)
501 error = TA_Err_Deltas_Invalid_Ppem_Range;
502 break;
504 case HAD_PPEMS:
505 /* to make compiler happy */
506 break;
509 if (old_state == state)
510 break;
513 if (state == HAD_PPEMS)
515 /* success */
516 if (deltas_p)
517 *deltas_p = deltas;
518 else
519 TA_deltas_free(deltas);
521 else
523 TA_deltas_free(deltas);
524 *deltas_p = NULL;
526 if (!error && state != START)
527 error = TA_Err_Deltas_Syntax_Error;
530 *err_pos = pos;
532 return error;
536 void
537 TA_deltas_free(Deltas* deltas)
539 if (!deltas)
540 return;
542 number_set_free(deltas->points);
543 number_set_free(deltas->ppems);
545 free(deltas);
549 char*
550 TA_deltas_show(FONT* font,
551 Deltas* deltas)
553 int ret;
554 char glyph_name_buf[64];
555 char* points_buf = NULL;
556 char* ppems_buf = NULL;
557 char* deltas_buf = NULL;
559 FT_Face face;
562 if (!deltas)
563 return NULL;
565 if (deltas->font_idx >= font->num_sfnts)
566 return NULL;
568 face = font->sfnts[deltas->font_idx].face;
569 glyph_name_buf[0] = '\0';
570 if (FT_HAS_GLYPH_NAMES(face))
571 FT_Get_Glyph_Name(face, deltas->glyph_idx, glyph_name_buf, 64);
573 points_buf = number_set_show(deltas->points, -1, -1);
574 if (!points_buf)
575 goto Exit;
576 ppems_buf = number_set_show(deltas->ppems, -1, -1);
577 if (!ppems_buf)
578 goto Exit;
580 /* display glyph index if we don't have a glyph name */
581 if (*glyph_name_buf)
582 ret = asprintf(&deltas_buf, "%ld %s p %s x %.20g y %.20g @ %s",
583 deltas->font_idx,
584 glyph_name_buf,
585 points_buf,
586 (double)deltas->x_shift / DELTA_FACTOR,
587 (double)deltas->y_shift / DELTA_FACTOR,
588 ppems_buf);
589 else
590 ret = asprintf(&deltas_buf, "%ld %ld p %s x %.20g y %.20g @ %s",
591 deltas->font_idx,
592 deltas->glyph_idx,
593 points_buf,
594 (double)deltas->x_shift / DELTA_FACTOR,
595 (double)deltas->y_shift / DELTA_FACTOR,
596 ppems_buf);
598 Exit:
599 free(points_buf);
600 free(ppems_buf);
602 if (ret == -1)
603 return NULL;
605 return deltas_buf;
608 /* end of tadeltas.c */