[gui] Add file type classes to drag & drop support.
[ttfautohint.git] / lib / tadeltas.c
blob2ad28f23fc82d902d591f4b11751e815aedb25d8
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 const char*
37 get_token(const 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 = p;
47 return 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 Deltas*
55 get_font_idx(FONT* font,
56 const 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_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_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_DELTAS_INVALID_FONT_INDEX;
88 *string_p = endptr;
89 *font_idx_p = font_idx;
91 return 0;
95 static Deltas*
96 get_glyph_idx(FONT* font,
97 long font_idx,
98 const 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_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_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_DELTAS_INVALID_GLYPH_INDEX;
136 else if (isnamestart(*s))
138 /* glyph name */
140 char* token;
143 endptr = (char*)s;
144 s = get_token((const char**)&endptr);
146 token = strndup(s, endptr - s);
147 if (!token)
149 /* *string_p = s; */
150 return TA_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_DELTAS_INVALID_GLYPH_NAME;
173 else
175 /* invalid */
177 /* *string_p = s; */
178 return TA_DELTAS_SYNTAX_ERROR;
181 *string_p = endptr;
182 *glyph_idx_p = glyph_idx;
184 return 0;
188 static Deltas*
189 get_range(const 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 = s + s_len;
209 return TA_DELTAS_SYNTAX_ERROR;
212 token = strndup(s, s_len);
213 if (!token)
215 /* *string_p = s; */
216 return TA_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_DELTAS_ALLOCATION_ERROR;
231 *string_p = endptr;
233 if (number_set == NUMBERSET_INVALID_CHARACTER)
234 return TA_DELTAS_INVALID_CHARACTER;
235 else if (number_set == NUMBERSET_OVERFLOW)
236 return TA_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_DELTAS_INVALID_POINT_RANGE;
240 else if (number_set == NUMBERSET_OVERLAPPING_RANGES)
241 return TA_DELTAS_OVERLAPPING_RANGES;
242 else if (number_set == NUMBERSET_NOT_ASCENDING)
243 return TA_DELTAS_NOT_ASCENDING;
245 *number_set_p = number_set;
247 return 0;
251 static Deltas*
252 get_shift(const char** string_p,
253 double* shift_p,
254 int min,
255 int max)
257 const char* s = *string_p;
258 char* endptr;
259 double shift;
262 errno = 0;
263 shift = strtod(s, &endptr);
265 if (errno == ERANGE)
267 /* *string_p = s; */
268 /* this error code should be adjusted by the caller if necessary */
269 return TA_DELTAS_INVALID_X_RANGE;
272 /* there must be a whitespace character after the number */
273 if (errno || !isspace(*endptr))
275 *string_p = endptr;
276 return TA_DELTAS_SYNTAX_ERROR;
279 if (shift < min || shift > max)
281 /* *string_p = s; */
282 return TA_DELTAS_INVALID_X_RANGE;
285 /* we round the value to a multiple of 1/(2^DELTA_SHIFT) */
286 shift = floor(shift * DELTA_FACTOR + 0.5) / DELTA_FACTOR;
288 *string_p = endptr;
289 *shift_p = shift;
291 return 0;
295 const char*
296 TA_deltas_parse(FONT* font,
297 const char* s,
298 Deltas** deltas_p,
299 int x_min, int x_max,
300 int y_min, int y_max,
301 int ppem_min, int ppem_max)
303 typedef enum State_
304 { START,
305 HAD_TOKEN1,
306 HAD_TOKEN2,
307 HAD_FONT_IDX,
308 HAD_GLYPH_IDX,
309 HAD_P,
310 HAD_POINTS,
311 HAD_X,
312 HAD_X_SHIFT,
313 HAD_Y,
314 HAD_Y_SHIFT,
315 HAD_AT,
316 HAD_PPEMS
317 } State;
319 State state = START;
321 Deltas* deltas = NULL;
322 Deltas* error;
323 const char* pos = s;
325 const char* token1;
326 const char* token2;
329 if (!s)
330 return NULL;
332 deltas = (Deltas*)malloc(sizeof (Deltas));
333 if (!deltas)
335 if (deltas_p)
336 *deltas_p = TA_DELTAS_ALLOCATION_ERROR;
337 return s;
340 deltas->font_idx = 0;
341 deltas->glyph_idx = -1;
342 deltas->points = NULL;
343 deltas->x_shift = 0.0;
344 deltas->y_shift = 0.0;
345 deltas->ppems = NULL;
347 for (;;)
349 char c;
350 int next_is_space;
351 State old_state = state;
354 error = 0;
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 /* possibility 1: <font idx> <glyph name> `p' */
385 if (isdigit(*token1)
386 && (isdigit(*token2) || isnamestart(*token2))
387 && (c == 'p' && next_is_space))
389 pos = token1;
390 if (!(error = get_font_idx(font, &pos, &deltas->font_idx)))
391 state = HAD_FONT_IDX;
393 /* possibility 2: <glyph name> `p' */
394 else if ((isdigit(*token1) || isnamestart(*token1))
395 && (c == 'p' && next_is_space))
397 pos = token1;
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_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, x_min, x_max)))
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, y_min, y_max)))
481 state = HAD_Y_SHIFT;
482 if (error == TA_DELTAS_INVALID_X_RANGE)
483 error = TA_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_DELTAS_INVALID_POINT_RANGE)
501 error = TA_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 *deltas_p = deltas;
518 else
520 TA_deltas_free(deltas);
522 *deltas_p = error;
525 return pos;
529 void
530 TA_deltas_free(Deltas* deltas)
532 if (!deltas)
533 return;
535 number_set_free(deltas->points);
536 number_set_free(deltas->ppems);
538 free(deltas);
541 /* end of tadeltas.c */