]> git.imager.perl.org - imager.git/blob - tags.c
- Makefile.PL now attempts to use freetype-config to configure freetype 2
[imager.git] / tags.c
1 /*
2 =head1 NAME
3
4 tags.c - functions for manipulating an images tags list
5
6 =head1 SYNOPSIS
7
8   i_img_tags tags;
9   i_tags_new(&tags);
10   i_tags_destroy(&tags);
11   i_tags_addn(&tags, "name", code, idata);
12   i_tags_add(&tags, "name", code, data, data_size, idata);
13   if (i_tags_find(&tags, name, start, &entry)) { found }
14   if (i_tags_findn(&tags, code, start, &entry)) { found }
15   i_tags_delete(&tags, index);
16   count = i_tags_delbyname(tags, name);
17   count = i_tags_delbycode(tags, code);
18   if (i_tags_get_float(&tags, name, code, &float_value)) { found }
19   i_tags_set_float(&tags, name, code, value);
20   i_tags_set_float2(&tags, name, code, value, sig_digits);
21   i_tags_get_int(&tags, name, code, &int_value);
22
23 =head1 DESCRIPTION
24
25 Provides functions which give write access to the tags list of an image.
26
27 For read access directly access the fields (do not write any fields
28 directly).
29
30 A tag is represented by an i_img_tag structure:
31
32   typedef enum {
33     itt_double,
34     iit_text
35   } i_tag_type;
36
37   typedef struct {
38     char *name; // name of a given tag, might be NULL 
39     int code; // number of a given tag, -1 if it has no meaning 
40     char *data; // value of a given tag if it's not an int, may be NULL 
41     int size; // size of the data 
42     int idata; // value of a given tag if data is NULL 
43   } i_img_tag;
44
45
46 =over
47
48 =cut
49 */
50
51 #include "image.h"
52 #include <string.h>
53 #include <stdlib.h>
54 #include <errno.h>
55 #include <limits.h>
56
57 /* useful for debugging */
58 void i_tags_print(i_img_tags *tags);
59
60 /*
61 =item i_tags_new(i_img_tags *tags)
62
63 Initialize a tags structure.  Should not be used if the tags structure
64 has been previously used.
65
66 To destroy the contents use i_tags_destroy()
67
68 =cut
69 */
70
71 void i_tags_new(i_img_tags *tags) {
72   tags->count = tags->alloc = 0;
73   tags->tags = NULL;
74 }
75
76 /*
77 =item i_tags_addn(i_img_tags *tags, char *name, int code, int idata)
78
79 Adds a tag that has an integer value.  A simple wrapper around i_tags_add().
80
81 Duplicate tags can be added.
82
83 Returns non-zero on success.
84
85 =cut
86 */
87
88 int i_tags_addn(i_img_tags *tags, char const *name, int code, int idata) {
89   return i_tags_add(tags, name, code, NULL, 0, idata);
90 }
91
92 /*
93 =item i_tags_add(i_img_tags *tags, char *name, int code, char *data, int size, i_tag_type type, int idata)
94
95 Adds a tag to the tags list.
96
97 Duplicate tags can be added.
98
99 Returns non-zero on success.
100
101 =cut
102 */
103
104 int i_tags_add(i_img_tags *tags, char const *name, int code, char const *data, 
105                int size, int idata) {
106   i_img_tag work = {0};
107   /*printf("i_tags_add(tags %p [count %d], name %s, code %d, data %p, size %d, idata %d)\n",
108     tags, tags->count, name, code, data, size, idata);*/
109   if (tags->tags == NULL) {
110     int alloc = 10;
111     tags->tags = mymalloc(sizeof(i_img_tag) * alloc);
112     if (!tags->tags)
113       return 0;
114     tags->alloc = alloc;
115   }
116   else if (tags->count == tags->alloc) {
117     int newalloc = tags->alloc + 10;
118     void *newtags = myrealloc(tags->tags, sizeof(i_img_tag) * newalloc);
119     if (!newtags) {
120       return 0;
121     }
122     tags->tags = newtags;
123     tags->alloc = newalloc;
124   }
125   if (name) {
126     work.name = mymalloc(strlen(name)+1);
127     if (!work.name)
128       return 0;
129     strcpy(work.name, name);
130   }
131   if (data) {
132     if (size == -1)
133       size = strlen(data);
134     work.data = mymalloc(size+1);
135     if (!work.data) {
136       if (work.name) myfree(work.name);
137       return 0;
138     }
139     memcpy(work.data, data, size);
140     work.data[size] = '\0'; /* convenience */
141     work.size = size;
142   }
143   work.code = code;
144   work.idata = idata;
145   tags->tags[tags->count++] = work;
146
147   /*i_tags_print(tags);*/
148
149   return 1;
150 }
151
152 void i_tags_destroy(i_img_tags *tags) {
153   if (tags->tags) {
154     int i;
155     for (i = 0; i < tags->count; ++i) {
156       if (tags->tags[i].name)
157         myfree(tags->tags[i].name);
158       if (tags->tags[i].data)
159         myfree(tags->tags[i].data);
160     }
161     myfree(tags->tags);
162   }
163 }
164
165 int i_tags_find(i_img_tags *tags, char const *name, int start, int *entry) {
166   if (tags->tags) {
167     while (start < tags->count) {
168       if (tags->tags[start].name && strcmp(name, tags->tags[start].name) == 0) {
169         *entry = start;
170         return 1;
171       }
172       ++start;
173     }
174   }
175   return 0;
176 }
177
178 int i_tags_findn(i_img_tags *tags, int code, int start, int *entry) {
179   if (tags->tags) {
180     while (start < tags->count) {
181       if (tags->tags[start].code == code) {
182         *entry = start;
183         return 1;
184       }
185       ++start;
186     }
187   }
188   return 0;
189 }
190
191 int i_tags_delete(i_img_tags *tags, int entry) {
192   /*printf("i_tags_delete(tags %p [count %d], entry %d)\n",
193     tags, tags->count, entry);*/
194   if (tags->tags && entry >= 0 && entry < tags->count) {
195     i_img_tag old = tags->tags[entry];
196     memmove(tags->tags+entry, tags->tags+entry+1,
197             (tags->count-entry-1) * sizeof(i_img_tag));
198     if (old.name)
199       myfree(old.name);
200     if (old.data)
201       myfree(old.data);
202     --tags->count;
203
204     return 1;
205   }
206   return 0;
207 }
208
209 int i_tags_delbyname(i_img_tags *tags, char const *name) {
210   int count = 0;
211   int i;
212   /*printf("i_tags_delbyname(tags %p [count %d], name %s)\n",
213     tags, tags->count, name);*/
214   if (tags->tags) {
215     for (i = tags->count-1; i >= 0; --i) {
216       if (tags->tags[i].name && strcmp(name, tags->tags[i].name) == 0) {
217         ++count;
218         i_tags_delete(tags, i);
219       }
220     }
221   }
222   /*i_tags_print(tags);*/
223
224   return count;
225 }
226
227 int i_tags_delbycode(i_img_tags *tags, int code) {
228   int count = 0;
229   int i;
230   if (tags->tags) {
231     for (i = tags->count-1; i >= 0; --i) {
232       if (tags->tags[i].code == code) {
233         ++count;
234         i_tags_delete(tags, i);
235       }
236     }
237   }
238   return count;
239 }
240
241 int i_tags_get_float(i_img_tags *tags, char const *name, int code, 
242                      double *value) {
243   int index;
244   i_img_tag *entry;
245
246   if (name) {
247     if (!i_tags_find(tags, name, 0, &index))
248       return 0;
249   }
250   else {
251     if (!i_tags_findn(tags, code, 0, &index))
252       return 0;
253   }
254   entry = tags->tags+index;
255   if (entry->data)
256     *value = atof(entry->data);
257   else
258     *value = entry->idata;
259
260   return 1;
261 }
262
263 int i_tags_set_float(i_img_tags *tags, char const *name, int code, 
264                      double value) {
265   return i_tags_set_float2(tags, name, code, value, 30);
266 }
267
268 /*
269 =item i_tags_set_float2(tags, name, code, value, places)
270
271 Sets the tag with the given name and code to the given floating point
272 value.
273
274 Since tags are strings or ints, we convert the value to a string before
275 storage at the precision specified by C<places>.
276
277 =cut
278 */
279
280 int i_tags_set_float2(i_img_tags *tags, char const *name, int code, 
281                       double value, int places) {
282   char temp[40];
283
284   if (places < 0) 
285     places = 30;
286   else if (places > 30) 
287     places = 30;
288
289   sprintf(temp, "%.*g", places, value);
290   if (name)
291     i_tags_delbyname(tags, name);
292   else
293     i_tags_delbycode(tags, code);
294
295   return i_tags_add(tags, name, code, temp, strlen(temp), 0);
296 }
297
298 int i_tags_get_int(i_img_tags *tags, char const *name, int code, int *value) {
299   int index;
300   i_img_tag *entry;
301
302   if (name) {
303     if (!i_tags_find(tags, name, 0, &index))
304       return 0;
305   }
306   else {
307     if (!i_tags_findn(tags, code, 0, &index))
308       return 0;
309   }
310   entry = tags->tags+index;
311   if (entry->data)
312     *value = atoi(entry->data);
313   else
314     *value = entry->idata;
315
316   return 1;
317 }
318
319 static int parse_long(char *data, char **end, long *out) {
320 #if 0
321   /* I wrote this without thinking about strtol */
322   long x = 0;
323   int neg = *data == '-';
324
325   if (neg)
326     ++data;
327   if (!isdigit(*data))
328     return 0;
329   while (isdigit(*data)) {
330     /* this check doesn't guarantee we don't overflow, but it helps */
331     if (x > LONG_MAX / 10)
332       return 0; 
333     x = x * 10 + *data - '0';
334     ++data;
335   }
336   if (neg)
337     x = -x;
338
339   *end = data;
340
341   return 1;
342 #else
343   long result;
344   int savederr = errno;
345   char *myend;
346
347   errno = 0;
348   result = strtol(data, &myend, 10);
349   if ((result == LONG_MIN || result == LONG_MAX) && errno == ERANGE
350       || myend == data) {
351     return 0;
352   }
353
354   *out = result;
355   *end = myend;
356
357   return 1;
358 #endif
359 }
360
361 /* parse a comma-separated list of integers
362    returns when it has maxcount numbers, finds a non-comma after a number
363    or can't parse a number
364    if it can't parse a number after a comma, that's considered an error
365 */
366 static int parse_long_list(char *data, char **end, int maxcount, long *out) {
367   int i;
368
369   i = 0;
370   while (i < maxcount-1) {
371     if (!parse_long(data, &data, out))
372       return 0;
373     out++;
374     i++;
375     if (*data != ',')
376       return i;
377     ++data;
378   }
379   if (!parse_long(data, &data, out))
380     return 0;
381   ++i;
382   *end = data;
383   return i;
384 }
385
386 /* parse "color(red,green,blue,alpha)" */
387 static int parse_color(char *data, char **end, i_color *value) {
388   long n[4];
389   int count, i;
390   
391   if (memcmp(data, "color(", 6))
392     return 0; /* not a color */
393   data += 6;
394   count = parse_long_list(data, &data, 4, n);
395   if (count < 3)
396     return 0;
397   for (i = 0; i < count; ++i)
398     value->channel[i] = n[i];
399   if (count < 4)
400     value->channel[3] = 255;
401
402   return 1;
403 }
404
405 int i_tags_get_color(i_img_tags *tags, char const *name, int code, 
406                      i_color *value) {
407   int index;
408   i_img_tag *entry;
409   char *end;
410
411   if (name) {
412     if (!i_tags_find(tags, name, 0, &index))
413       return 0;
414   }
415   else {
416     if (!i_tags_findn(tags, code, 0, &index))
417       return 0;
418   }
419   entry = tags->tags+index;
420   if (!entry->data) 
421     return 0;
422
423   if (!parse_color(entry->data, &end, value))
424     return 0;
425   
426   /* for now we're sloppy about the end */
427
428   return 1;
429 }
430
431 int i_tags_set_color(i_img_tags *tags, char const *name, int code, 
432                      i_color const *value) {
433   char temp[80];
434
435   sprintf(temp, "color(%d,%d,%d,%d)", value->channel[0], value->channel[1],
436           value->channel[2], value->channel[3]);
437   if (name)
438     i_tags_delbyname(tags, name);
439   else
440     i_tags_delbycode(tags, code);
441
442   return i_tags_add(tags, name, code, temp, strlen(temp), 0);
443 }
444
445 int i_tags_get_string(i_img_tags *tags, char const *name, int code, 
446                       char *value, size_t value_size) {
447   int index;
448   i_img_tag *entry;
449
450   if (name) {
451     if (!i_tags_find(tags, name, 0, &index))
452       return 0;
453   }
454   else {
455     if (!i_tags_findn(tags, code, 0, &index))
456       return 0;
457   }
458   entry = tags->tags+index;
459   if (entry->data) {
460     size_t cpsize = value_size < entry->size ? value_size : entry->size;
461     memcpy(value, entry->data, cpsize);
462     if (cpsize == value_size)
463       --cpsize;
464     value[cpsize] = '\0';
465   }
466   else {
467     sprintf(value, "%d", entry->data);
468   }
469
470   return 1;
471 }
472
473 void i_tags_print(i_img_tags *tags) {
474   int i;
475   printf("Alloc %d\n", tags->alloc);
476   printf("Count %d\n", tags->count);
477   for (i = 0; i < tags->count; ++i) {
478     i_img_tag *tag = tags->tags + i;
479     printf("Tag %d\n", i);
480     if (tag->name)
481       printf(" Name : %s (%p)\n", tag->name, tag->name);
482     printf(" Code : %d\n", tag->code);
483     if (tag->data) {
484       int pos;
485       printf(" Data : %d (%p) => '", tag->size, tag->data);
486       for (pos = 0; pos < tag->size; ++pos) {
487         if (tag->data[pos] == '\\' || tag->data[pos] == '\'') {
488           putchar('\\');
489           putchar(tag->data[pos]);
490         }
491         else if (tag->data[pos] < ' ' || tag->data[pos] >= '\x7E')
492           printf("\\x%02X", tag->data[pos]);
493         else
494           putchar(tag->data[pos]);
495       }
496       printf("'\n");
497       printf(" Idata: %d\n", tag->idata);
498     }
499   }
500 }
501
502 /*
503 =back
504
505 =head1 AUTHOR
506
507 Tony Cook <tony@develop-help.com>
508
509 =head1 SEE ALSO
510
511 Imager(3)
512
513 =cut
514 */