bump $Imager::Font::FT2::VERSION
[imager.git] / tags.c
CommitLineData
faa9b3e7
TC
1/*
2=head1 NAME
3
4tags.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);
2e41e30b
TC
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);
faa9b3e7
TC
22
23=head1 DESCRIPTION
24
25Provides functions which give write access to the tags list of an image.
26
27For read access directly access the fields (do not write any fields
28directly).
29
30A 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
92bda632 51#include "imager.h"
faa9b3e7
TC
52#include <string.h>
53#include <stdlib.h>
97c4effc
TC
54#include <errno.h>
55#include <limits.h>
faa9b3e7
TC
56
57/* useful for debugging */
58void i_tags_print(i_img_tags *tags);
59
60/*
61=item i_tags_new(i_img_tags *tags)
62
92bda632
TC
63=category Tags
64
faa9b3e7
TC
65Initialize a tags structure. Should not be used if the tags structure
66has been previously used.
67
92bda632
TC
68This should be called tags member of an i_img object on creation (in
69i_img_*_new() functions).
70
faa9b3e7
TC
71To destroy the contents use i_tags_destroy()
72
73=cut
74*/
75
76void i_tags_new(i_img_tags *tags) {
77 tags->count = tags->alloc = 0;
78 tags->tags = NULL;
79}
80
81/*
82=item i_tags_addn(i_img_tags *tags, char *name, int code, int idata)
83
84Adds a tag that has an integer value. A simple wrapper around i_tags_add().
85
6cfee9d1 86Use i_tags_setn() instead, this function may be removed in the future.
faa9b3e7
TC
87
88Returns non-zero on success.
89
90=cut
91*/
92
97c4effc 93int i_tags_addn(i_img_tags *tags, char const *name, int code, int idata) {
faa9b3e7
TC
94 return i_tags_add(tags, name, code, NULL, 0, idata);
95}
96
97/*
98=item i_tags_add(i_img_tags *tags, char *name, int code, char *data, int size, i_tag_type type, int idata)
99
100Adds a tag to the tags list.
101
6cfee9d1 102Use i_tags_set() instead, this function may be removed in the future.
faa9b3e7
TC
103
104Returns non-zero on success.
105
106=cut
107*/
108
97c4effc
TC
109int i_tags_add(i_img_tags *tags, char const *name, int code, char const *data,
110 int size, int idata) {
faa9b3e7 111 i_img_tag work = {0};
97c4effc
TC
112 /*printf("i_tags_add(tags %p [count %d], name %s, code %d, data %p, size %d, idata %d)\n",
113 tags, tags->count, name, code, data, size, idata);*/
faa9b3e7
TC
114 if (tags->tags == NULL) {
115 int alloc = 10;
4dfa5522 116 tags->tags = mymalloc(sizeof(i_img_tag) * alloc);
faa9b3e7
TC
117 if (!tags->tags)
118 return 0;
119 tags->alloc = alloc;
120 }
121 else if (tags->count == tags->alloc) {
122 int newalloc = tags->alloc + 10;
7f882a01 123 void *newtags = myrealloc(tags->tags, sizeof(i_img_tag) * newalloc);
faa9b3e7
TC
124 if (!newtags) {
125 return 0;
126 }
127 tags->tags = newtags;
128 tags->alloc = newalloc;
129 }
130 if (name) {
4dfa5522 131 work.name = mymalloc(strlen(name)+1);
faa9b3e7
TC
132 if (!work.name)
133 return 0;
134 strcpy(work.name, name);
135 }
136 if (data) {
403946c6
TC
137 if (size == -1)
138 size = strlen(data);
4dfa5522 139 work.data = mymalloc(size+1);
faa9b3e7 140 if (!work.data) {
4dfa5522 141 if (work.name) myfree(work.name);
faa9b3e7
TC
142 return 0;
143 }
144 memcpy(work.data, data, size);
145 work.data[size] = '\0'; /* convenience */
146 work.size = size;
147 }
148 work.code = code;
149 work.idata = idata;
150 tags->tags[tags->count++] = work;
151
97c4effc
TC
152 /*i_tags_print(tags);*/
153
faa9b3e7
TC
154 return 1;
155}
156
92bda632
TC
157/*
158=item i_tags_destroy(tags)
159
160=category Tags
161
162Destroys the given tags structure. Called by i_img_destroy().
163
164=cut
165*/
166
faa9b3e7
TC
167void i_tags_destroy(i_img_tags *tags) {
168 if (tags->tags) {
169 int i;
170 for (i = 0; i < tags->count; ++i) {
171 if (tags->tags[i].name)
4dfa5522 172 myfree(tags->tags[i].name);
faa9b3e7 173 if (tags->tags[i].data)
4dfa5522 174 myfree(tags->tags[i].data);
faa9b3e7 175 }
4dfa5522 176 myfree(tags->tags);
faa9b3e7
TC
177 }
178}
179
92bda632
TC
180/*
181=item i_tags_find(tags, name, start, &entry)
182
183=category Tags
184
7468f3fa 185Searches for a tag of the given I<name> starting from index I<start>.
92bda632
TC
186
187On success returns true and sets *I<entry>.
188
189On failure returns false.
190
191=cut
192*/
193
97c4effc 194int i_tags_find(i_img_tags *tags, char const *name, int start, int *entry) {
faa9b3e7
TC
195 if (tags->tags) {
196 while (start < tags->count) {
197 if (tags->tags[start].name && strcmp(name, tags->tags[start].name) == 0) {
198 *entry = start;
199 return 1;
200 }
201 ++start;
202 }
203 }
204 return 0;
205}
206
92bda632
TC
207/*
208=item i_tags_findn(tags, code, start, &entry)
209
210=category Tags
211
5715f7c3 212Searches for a tag of the given I<code> starting from index I<start>.
92bda632
TC
213
214On success returns true and sets *I<entry>.
215
216On failure returns false.
217
218=cut
219*/
220
faa9b3e7
TC
221int i_tags_findn(i_img_tags *tags, int code, int start, int *entry) {
222 if (tags->tags) {
223 while (start < tags->count) {
224 if (tags->tags[start].code == code) {
225 *entry = start;
226 return 1;
227 }
228 ++start;
229 }
230 }
231 return 0;
232}
233
92bda632
TC
234/*
235=item i_tags_delete(tags, index)
236
237=category Tags
238
239Delete a tag by index.
240
241Returns true on success.
242
243=cut
244*/
faa9b3e7 245int i_tags_delete(i_img_tags *tags, int entry) {
97c4effc
TC
246 /*printf("i_tags_delete(tags %p [count %d], entry %d)\n",
247 tags, tags->count, entry);*/
faa9b3e7
TC
248 if (tags->tags && entry >= 0 && entry < tags->count) {
249 i_img_tag old = tags->tags[entry];
250 memmove(tags->tags+entry, tags->tags+entry+1,
97c4effc 251 (tags->count-entry-1) * sizeof(i_img_tag));
faa9b3e7 252 if (old.name)
4dfa5522 253 myfree(old.name);
faa9b3e7 254 if (old.data)
4dfa5522 255 myfree(old.data);
faa9b3e7 256 --tags->count;
97c4effc 257
faa9b3e7
TC
258 return 1;
259 }
260 return 0;
261}
262
92bda632
TC
263/*
264=item i_tags_delbyname(tags, name)
265
266=category Tags
267
268Delete any tags with the given name.
269
270Returns the number of tags deleted.
271
272=cut
273*/
274
97c4effc 275int i_tags_delbyname(i_img_tags *tags, char const *name) {
faa9b3e7
TC
276 int count = 0;
277 int i;
97c4effc
TC
278 /*printf("i_tags_delbyname(tags %p [count %d], name %s)\n",
279 tags, tags->count, name);*/
faa9b3e7
TC
280 if (tags->tags) {
281 for (i = tags->count-1; i >= 0; --i) {
282 if (tags->tags[i].name && strcmp(name, tags->tags[i].name) == 0) {
283 ++count;
284 i_tags_delete(tags, i);
285 }
286 }
287 }
97c4effc
TC
288 /*i_tags_print(tags);*/
289
faa9b3e7
TC
290 return count;
291}
292
92bda632
TC
293/*
294=item i_tags_delbycode(tags, code)
295
296=category Tags
297
298Delete any tags with the given code.
299
300Returns the number of tags deleted.
301
302=cut
303*/
304
faa9b3e7
TC
305int i_tags_delbycode(i_img_tags *tags, int code) {
306 int count = 0;
307 int i;
308 if (tags->tags) {
309 for (i = tags->count-1; i >= 0; --i) {
310 if (tags->tags[i].code == code) {
311 ++count;
312 i_tags_delete(tags, i);
313 }
314 }
315 }
316 return count;
317}
318
92bda632
TC
319/*
320=item i_tags_get_float(tags, name, code, value)
321
322=category Tags
323
324Retrieves a tag as a floating point value.
325
326If the tag has a string value then that is parsed as a floating point
327number, otherwise the integer value of the tag is used.
328
329On success sets *I<value> and returns true.
330
331On failure returns false.
332
333=cut
334*/
335
97c4effc
TC
336int i_tags_get_float(i_img_tags *tags, char const *name, int code,
337 double *value) {
faa9b3e7
TC
338 int index;
339 i_img_tag *entry;
340
341 if (name) {
342 if (!i_tags_find(tags, name, 0, &index))
343 return 0;
344 }
345 else {
346 if (!i_tags_findn(tags, code, 0, &index))
347 return 0;
348 }
349 entry = tags->tags+index;
350 if (entry->data)
351 *value = atof(entry->data);
352 else
353 *value = entry->idata;
354
355 return 1;
356}
357
92bda632
TC
358/*
359=item i_tags_set_float(tags, name, code, value)
360
361=category Tags
362
363Equivalent to i_tags_set_float2(tags, name, code, value, 30).
364
365=cut
366*/
367
97c4effc
TC
368int i_tags_set_float(i_img_tags *tags, char const *name, int code,
369 double value) {
2e41e30b
TC
370 return i_tags_set_float2(tags, name, code, value, 30);
371}
372
373/*
374=item i_tags_set_float2(tags, name, code, value, places)
375
92bda632
TC
376=category Tags
377
2e41e30b
TC
378Sets the tag with the given name and code to the given floating point
379value.
380
381Since tags are strings or ints, we convert the value to a string before
382storage at the precision specified by C<places>.
383
384=cut
385*/
386
387int i_tags_set_float2(i_img_tags *tags, char const *name, int code,
388 double value, int places) {
faa9b3e7
TC
389 char temp[40];
390
2e41e30b
TC
391 if (places < 0)
392 places = 30;
393 else if (places > 30)
394 places = 30;
395
396 sprintf(temp, "%.*g", places, value);
faa9b3e7
TC
397 if (name)
398 i_tags_delbyname(tags, name);
399 else
400 i_tags_delbycode(tags, code);
401
402 return i_tags_add(tags, name, code, temp, strlen(temp), 0);
403}
404
92bda632
TC
405/*
406=item i_tags_get_int(tags, name, code, &value)
407
408=category Tags
409
410Retrieve a tag specified by name or code as an integer.
411
743e98e4 412On success sets the int *I<value> to the integer and returns true.
92bda632
TC
413
414On failure returns false.
415
416=cut
417*/
418
97c4effc 419int i_tags_get_int(i_img_tags *tags, char const *name, int code, int *value) {
faa9b3e7
TC
420 int index;
421 i_img_tag *entry;
422
423 if (name) {
424 if (!i_tags_find(tags, name, 0, &index))
425 return 0;
426 }
427 else {
428 if (!i_tags_findn(tags, code, 0, &index))
429 return 0;
430 }
431 entry = tags->tags+index;
432 if (entry->data)
433 *value = atoi(entry->data);
434 else
435 *value = entry->idata;
436
437 return 1;
438}
439
97c4effc 440static int parse_long(char *data, char **end, long *out) {
97c4effc
TC
441 long result;
442 int savederr = errno;
443 char *myend;
444
445 errno = 0;
446 result = strtol(data, &myend, 10);
af070d99 447 if (((result == LONG_MIN || result == LONG_MAX) && errno == ERANGE)
97c4effc 448 || myend == data) {
a659442a 449 errno = savederr;
97c4effc
TC
450 return 0;
451 }
452
a659442a 453 errno = savederr;
97c4effc
TC
454 *out = result;
455 *end = myend;
456
457 return 1;
97c4effc
TC
458}
459
460/* parse a comma-separated list of integers
461 returns when it has maxcount numbers, finds a non-comma after a number
462 or can't parse a number
463 if it can't parse a number after a comma, that's considered an error
464*/
465static int parse_long_list(char *data, char **end, int maxcount, long *out) {
466 int i;
467
26fd367b 468 i = 0;
97c4effc
TC
469 while (i < maxcount-1) {
470 if (!parse_long(data, &data, out))
471 return 0;
472 out++;
473 i++;
474 if (*data != ',')
475 return i;
476 ++data;
477 }
478 if (!parse_long(data, &data, out))
479 return 0;
480 ++i;
481 *end = data;
482 return i;
483}
484
485/* parse "color(red,green,blue,alpha)" */
486static int parse_color(char *data, char **end, i_color *value) {
487 long n[4];
488 int count, i;
489
490 if (memcmp(data, "color(", 6))
491 return 0; /* not a color */
492 data += 6;
493 count = parse_long_list(data, &data, 4, n);
494 if (count < 3)
495 return 0;
496 for (i = 0; i < count; ++i)
497 value->channel[i] = n[i];
498 if (count < 4)
499 value->channel[3] = 255;
500
501 return 1;
502}
503
92bda632
TC
504/*
505=item i_tags_get_color(tags, name, code, &value)
506
507=category Tags
508
509Retrieve a tag specified by name or code as color.
510
511On success sets the i_color *I<value> to the color and returns true.
512
513On failure returns false.
514
515=cut
516*/
517
97c4effc
TC
518int i_tags_get_color(i_img_tags *tags, char const *name, int code,
519 i_color *value) {
520 int index;
521 i_img_tag *entry;
522 char *end;
523
524 if (name) {
525 if (!i_tags_find(tags, name, 0, &index))
526 return 0;
527 }
528 else {
529 if (!i_tags_findn(tags, code, 0, &index))
530 return 0;
531 }
532 entry = tags->tags+index;
533 if (!entry->data)
534 return 0;
535
536 if (!parse_color(entry->data, &end, value))
537 return 0;
538
539 /* for now we're sloppy about the end */
540
541 return 1;
542}
543
92bda632
TC
544/*
545=item i_tags_set_color(tags, name, code, &value)
546
547=category Tags
548
549Stores the given color as a tag with the given name and code.
550
551=cut
552*/
553
97c4effc
TC
554int i_tags_set_color(i_img_tags *tags, char const *name, int code,
555 i_color const *value) {
556 char temp[80];
557
558 sprintf(temp, "color(%d,%d,%d,%d)", value->channel[0], value->channel[1],
559 value->channel[2], value->channel[3]);
560 if (name)
561 i_tags_delbyname(tags, name);
562 else
563 i_tags_delbycode(tags, code);
564
565 return i_tags_add(tags, name, code, temp, strlen(temp), 0);
566}
567
92bda632
TC
568/*
569=item i_tags_get_string(tags, name, code, value, value_size)
570
571=category Tags
572
573Retrieves a tag by name or code as a string.
574
575On success copies the string to value for a max of value_size and
576returns true.
577
578On failure returns false.
579
580value_size must be at least large enough for a string representation
581of an integer.
582
5715f7c3 583The copied value is always C<NUL> terminated.
92bda632
TC
584
585=cut
586*/
587
97c4effc 588int i_tags_get_string(i_img_tags *tags, char const *name, int code,
faa9b3e7
TC
589 char *value, size_t value_size) {
590 int index;
591 i_img_tag *entry;
592
593 if (name) {
594 if (!i_tags_find(tags, name, 0, &index))
595 return 0;
596 }
597 else {
598 if (!i_tags_findn(tags, code, 0, &index))
599 return 0;
600 }
601 entry = tags->tags+index;
602 if (entry->data) {
603 size_t cpsize = value_size < entry->size ? value_size : entry->size;
604 memcpy(value, entry->data, cpsize);
605 if (cpsize == value_size)
606 --cpsize;
607 value[cpsize] = '\0';
608 }
609 else {
241defe8 610 sprintf(value, "%d", entry->idata);
faa9b3e7
TC
611 }
612
613 return 1;
614}
615
92bda632
TC
616/*
617=item i_tags_set(tags, name, data, size)
6cfee9d1 618=synopsis i_tags_set(&img->tags, "i_comment", -1);
92bda632
TC
619=category Tags
620
621Sets the given tag to the string I<data>
622
6cfee9d1
TC
623If size is -1 then the strlen(I<data>) bytes are stored.
624
625Even on failure, if an existing tag I<name> exists, it will be
626removed.
627
92bda632
TC
628=cut
629*/
630
631int
632i_tags_set(i_img_tags *tags, char const *name, char const *data, int size) {
633 i_tags_delbyname(tags, name);
634
635 return i_tags_add(tags, name, 0, data, size, 0);
636}
637
638/*
5715f7c3 639=item i_tags_setn(C<tags>, C<name>, C<idata>)
6cfee9d1
TC
640=synopsis i_tags_setn(&img->tags, "i_xres", 204);
641=synopsis i_tags_setn(&img->tags, "i_yres", 196);
92bda632
TC
642=category Tags
643
5715f7c3 644Sets the given tag to the integer C<idata>
92bda632 645
5715f7c3 646Even on failure, if an existing tag C<name> exists, it will be
6cfee9d1
TC
647removed.
648
92bda632
TC
649=cut
650*/
651
652int
653i_tags_setn(i_img_tags *tags, char const *name, int idata) {
654 i_tags_delbyname(tags, name);
655
656 return i_tags_addn(tags, name, 0, idata);
657}
658
faa9b3e7
TC
659void i_tags_print(i_img_tags *tags) {
660 int i;
661 printf("Alloc %d\n", tags->alloc);
662 printf("Count %d\n", tags->count);
663 for (i = 0; i < tags->count; ++i) {
664 i_img_tag *tag = tags->tags + i;
665 printf("Tag %d\n", i);
666 if (tag->name)
97c4effc 667 printf(" Name : %s (%p)\n", tag->name, tag->name);
faa9b3e7
TC
668 printf(" Code : %d\n", tag->code);
669 if (tag->data) {
670 int pos;
97c4effc 671 printf(" Data : %d (%p) => '", tag->size, tag->data);
faa9b3e7
TC
672 for (pos = 0; pos < tag->size; ++pos) {
673 if (tag->data[pos] == '\\' || tag->data[pos] == '\'') {
674 putchar('\\');
675 putchar(tag->data[pos]);
676 }
677 else if (tag->data[pos] < ' ' || tag->data[pos] >= '\x7E')
678 printf("\\x%02X", tag->data[pos]);
679 else
680 putchar(tag->data[pos]);
681 }
682 printf("'\n");
683 printf(" Idata: %d\n", tag->idata);
684 }
685 }
686}
b8c2033e
AMH
687
688/*
689=back
690
691=head1 AUTHOR
692
693Tony Cook <tony@develop-help.com>
694
695=head1 SEE ALSO
696
697Imager(3)
698
699=cut
700*/