1 /*
2 svgxdump.h
3
4 conversion of svg structures into other formats
5 and dumping them into files. This is where the
6 work is done in svgx
7
8 Copyright (c) J.J. Green 2012
9 */
10
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <stdbool.h>
14 #include <string.h>
15 #include <math.h>
16
17 #include <btrace.h>
18 #include <cpt.h>
19 #include <cptwrite.h>
20 #include <ggr.h>
21 #include <povwrite.h>
22 #include <gptwrite.h>
23 #include <css3write.h>
24 #include <grd3write.h>
25 #include <saowrite.h>
26 #include <svgwrite.h>
27 #include <qgswrite.h>
28 #include <tpmwrite.h>
29 #include <utf8.h>
30
31 #include "pngwrite.h"
32 #include "svgxdump.h"
33 #include "utf8x.h"
34
35 /*
36 given a utf8 string, modify it in-place so that is is
37 a reasonable filename on a modern Unix system.
38 */
39
posixify(char * str)40 static int posixify(char *str)
41 {
42 /* cannot have an empty string */
43
44 if (*str == '\0')
45 return 1;
46
47 /* convert leading dot to underscore */
48
49 if ((u8_seqlen(str) == 1) && (*str == '.'))
50 *str = '_';
51
52 /* convert forward slash to underscore */
53
54 char *p = str;
55 int cn;
56
57 while ((p = u8_strchr(p, '/', &cn)) != NULL)
58 *p = '_';
59
60 return 0;
61 }
62
63 /*
64 create a new opt with an autogenerated filename and call
65 f(svg, opt), this is common code for the dump function in
66 the case opt.job == job_all;
67 */
68
call_autonamed(const svg_t * svg,svgx_opt_t * opt,const char * suffix,int (* f)(const svg_t *,svgx_opt_t *))69 static int call_autonamed(const svg_t *svg,
70 svgx_opt_t *opt,
71 const char *suffix,
72 int (*f)(const svg_t*, svgx_opt_t*))
73 {
74 const char *name = (char*)svg->name;
75 size_t n = strlen(name) + strlen(suffix) + 2;
76 char file[n];
77
78 if (snprintf(file, n, "%s.%s", name, suffix) >= n)
79 {
80 btrace("filename truncated! %s", file);
81 return 1;
82 }
83
84 if (posixify(file) != 0)
85 {
86 btrace("failed to create POSIX filename");
87 return 0;
88 }
89
90 svgx_opt_t opt2 = *opt;
91
92 opt2.job = job_named;
93 int err = 0;
94
95 if (opt->output.file)
96 {
97 size_t m = strlen(opt->output.file) + 1 + n;
98 char path[m];
99
100 if (snprintf(path, m, "%s/%s", opt->output.file, file) >= m)
101 {
102 btrace("filename truncated! %s", path);
103 return 1;
104 }
105
106 opt2.output.file = path;
107 err = f(svg, &opt2);
108 if (opt->verbose) printf(" %s\n", path);
109 }
110 else
111 {
112 opt2.output.file = file;
113 err = f(svg, &opt2);
114 if (opt->verbose) printf(" %s\n", file);
115 }
116
117 return err;
118 }
119
120 /*
121 here there are blocks for each svgx_dump() function,
122 which converts the svg structure to type x by a call
123 to the file static svgx() function. For some x there
124 are a few helper functions too.
125
126 Each block could (perhaps should) be moved into a
127 seperate file.
128 */
129
130 /* cpt */
131
svgcpt(const svg_t * svg,cpt_t * cpt)132 static int svgcpt(const svg_t *svg, cpt_t *cpt)
133 {
134 svg_node_t *node, *next;
135
136 node = svg->nodes;
137 next = node->r;
138
139 while (next)
140 {
141 double z1, z2;
142
143 z1 = node->stop.value;
144 z2 = next->stop.value;
145
146 if (z1 < z2)
147 {
148 rgb_t c1, c2;
149 cpt_seg_t *seg;
150
151 c1 = node->stop.colour;
152 c2 = next->stop.colour;
153
154 if ((seg = cpt_seg_new()) == NULL)
155 {
156 btrace("failed to create cpt segment");
157 return 1;
158 }
159
160 seg->lsmp.val = z1;
161 seg->rsmp.val = z2;
162
163 seg->lsmp.fill.type = fill_colour;
164 seg->lsmp.fill.u.colour.rgb = c1;
165
166 seg->rsmp.fill.type = fill_colour;
167 seg->rsmp.fill.u.colour.rgb = c2;
168
169 if (cpt_append(seg, cpt) != 0)
170 {
171 btrace("failed to append segment");
172 return 1;
173 }
174 }
175
176 node = next;
177 next = node->r;
178 }
179
180 return 0;
181 }
182
svgcpt_dump(const svg_t * svg,svgx_opt_t * opt)183 extern int svgcpt_dump(const svg_t *svg, svgx_opt_t *opt)
184 {
185 if (opt->job == job_all)
186 return call_autonamed(svg, opt, "cpt", svgcpt_dump);
187
188 const char *name = (char*)svg->name;
189 const char *file = opt->output.file;
190 cpt_t *cpt;
191
192 if ((cpt = cpt_new()) == NULL)
193 {
194 btrace("failed to create cpt structure");
195 return 1;
196 }
197
198 cpt->model = model_rgb;
199
200 cpt->fg.type = cpt->bg.type = cpt->nan.type = fill_colour;
201
202 cpt->bg.u.colour.rgb = opt->format.cpt.bg;
203 cpt->fg.u.colour.rgb = opt->format.cpt.fg;
204 cpt->nan.u.colour.rgb = opt->format.cpt.nan;
205
206 cpt->name = strdup(name);
207
208 int err = 0;
209
210 if (svgcpt(svg, cpt) != 0)
211 {
212 btrace("failed to convert %s to cpt", name);
213 err++;
214 }
215 else
216 {
217 if (cpt_write(file, cpt) != 0)
218 {
219 btrace("failed to write to %s", file);
220 err++;
221 }
222 }
223
224 cpt_destroy(cpt);
225
226 return err;
227 }
228
229 /* ggr */
230
svgggr(const svg_t * svg,gradient_t * ggr)231 static int svgggr(const svg_t *svg, gradient_t *ggr)
232 {
233 svg_node_t *node, *next;
234 grad_segment_t *gseg, *prev = NULL;
235
236 ggr->name = strdup((char*)svg->name);
237
238 node = svg->nodes;
239 next = node->r;
240
241 while (next)
242 {
243 double z1, z2;
244
245 z1 = node->stop.value;
246 z2 = next->stop.value;
247
248 if (z1 < z2)
249 {
250 rgb_t c1, c2;
251 double o1, o2;
252 double lcol[3], rcol[3];
253
254 c1 = node->stop.colour;
255 c2 = next->stop.colour;
256
257 o1 = node->stop.opacity;
258 o2 = next->stop.opacity;
259
260 if ((gseg = seg_new_segment()) == NULL) return 1;
261
262 gseg->prev = prev;
263
264 if (prev)
265 prev->next = gseg;
266 else
267 ggr->segments = gseg;
268
269 gseg->left = z1/100.0;
270 gseg->middle = (z1+z2)/200.0;
271 gseg->right = z2/100.0;
272
273 lcol[0] = lcol[1] = lcol[2] = 0.0;
274 rcol[0] = rcol[1] = rcol[2] = 0.0;
275
276 gseg->a0 = o1;
277 gseg->a1 = o2;
278
279 rgb_to_rgbD(c1, lcol);
280 rgb_to_rgbD(c2, rcol);
281
282 gseg->r0 = lcol[0];
283 gseg->g0 = lcol[1];
284 gseg->b0 = lcol[2];
285
286 gseg->r1 = rcol[0];
287 gseg->g1 = rcol[1];
288 gseg->b1 = rcol[2];
289
290 gseg->type = GRAD_LINEAR;
291 gseg->color = GRAD_RGB;
292
293 gseg->ect_left = GRAD_FIXED;
294 gseg->ect_right = GRAD_FIXED;
295
296 prev = gseg;
297 }
298
299 node = next;
300 next = node->r;
301 }
302
303 return 0;
304 }
305
svgggr_dump(const svg_t * svg,svgx_opt_t * opt)306 extern int svgggr_dump(const svg_t *svg, svgx_opt_t *opt)
307 {
308 if (opt->job == job_all)
309 return call_autonamed(svg, opt, "ggr", svgggr_dump);
310
311 const char *name = (char*)svg->name;
312 const char *file = opt->output.file;
313 gradient_t *ggr;
314
315 if ((ggr = grad_new_gradient()) == NULL)
316 {
317 btrace("failed to create ggr structure");
318 return 1;
319 }
320
321 int err = 0;
322
323 if (svgggr(svg, ggr) != 0)
324 {
325 btrace("failed to convert %s to cpt", name);
326 err++;
327 }
328 else
329 {
330 if (grad_save_gradient(ggr, file) != 0)
331 {
332 btrace("failed to write to %s", file);
333 err++;
334 }
335 }
336
337 grad_free_gradient(ggr);
338
339 return err;
340 }
341
342 /* tecplot map (tpm) */
343
svgmap(const svg_t * svg,tpm_t * tpm)344 static int svgmap(const svg_t *svg, tpm_t *tpm)
345 {
346 tpm_point_t point;
347 svg_node_t *node = svg->nodes;
348
349 while (node)
350 {
351 point.fraction = node->stop.value / 100.0;
352 point.lead = point.trail = node->stop.colour;
353
354 if (tpm_push(tpm, &point) != 0)
355 {
356 btrace("failed push of point %g", point.fraction);
357 return 1;
358 }
359
360 node = node->r;
361 }
362
363 if (tpm_manicure(tpm) != 0)
364 {
365 btrace("failed tpm manicure");
366 return 1;
367 }
368
369 return 0;
370 }
371
svgmap_valid(const svg_t * svg,const svgx_opt_t * opt)372 static int svgmap_valid(const svg_t *svg, const svgx_opt_t *opt)
373 {
374 if (opt->permissive)
375 return 0;
376
377 int m;
378
379 if ((m = svg_num_stops(svg)) > TPM_STOPS_MAX)
380 {
381 if (opt->verbose)
382 printf("Tecplot can only manage %i stops\n", TPM_STOPS_MAX);
383 btrace("too many stops (%i)", m);
384 return 1;
385 }
386
387 return 0;
388 }
389
svgmap_dump(const svg_t * svg,svgx_opt_t * opt)390 extern int svgmap_dump(const svg_t *svg, svgx_opt_t *opt)
391 {
392 if (opt->job == job_all)
393 return call_autonamed(svg, opt, "map", svgmap_dump);
394
395 if (svgmap_valid(svg, opt) != 0)
396 {
397 btrace("cannot create valid map file");
398 return 1;
399 }
400
401 const char *name = (char*)svg->name;
402 const char *path = opt->output.file;
403 tpm_t *tpm;
404
405 if ((tpm = tpm_new()) == NULL)
406 {
407 btrace("failed to create new tpm (map)");
408 return 1;
409 }
410
411 int err = 0;
412
413 if (svgmap(svg, tpm) != 0)
414 {
415 btrace("failed to convert %s to map", name);
416 err++;
417 }
418 else
419 {
420 if (tpm_write(tpm, path) != 0)
421 {
422 btrace("failed to write to %s", path);
423 err++;
424 }
425 }
426
427 tpm_destroy(tpm);
428
429 return err;
430 }
431
432 /* povray */
433
svgpov_valid(const svg_t * svg,int permissive,int verbose)434 static int svgpov_valid(const svg_t *svg, int permissive, int verbose)
435 {
436 int m = svg_num_stops(svg);
437
438 if (m > POV_STOPS_MAX)
439 {
440 if (permissive)
441 {
442 if (verbose)
443 printf("warning : format limit broken %i stops (max is %i)\n",
444 m, POV_STOPS_MAX);
445 }
446 else
447 {
448 btrace("format limit: POV-ray allows no more than %i stops, "
449 "but this gradient has %i", POV_STOPS_MAX, m);
450
451 return 0;
452 }
453 }
454
455 if (m < 2)
456 {
457 btrace("found %i stops, but at least 2 required", m);
458 return 0;
459 }
460
461 return 1;
462 }
463
has_name(const svg_t * svg)464 static bool has_name(const svg_t *svg)
465 {
466 return svg->name[0] != '\0';
467 }
468
svgpov(const svg_t * svg,pov_t * pov)469 static int svgpov(const svg_t *svg, pov_t *pov)
470 {
471 int n, m, nmod;
472 svg_node_t *node;
473
474 /* count & allocate */
475
476 m = svg_num_stops(svg);
477
478 if (m < 2)
479 {
480 btrace("bad number of stops : %i", m);
481 return 1;
482 }
483
484 if (pov_stops_alloc(pov, m) != 0)
485 {
486 btrace("failed alloc for %i stops", m);
487 return 1;
488 }
489
490 /* convert */
491
492 for (n=0, node = svg->nodes ; node ; n++, node = node->r)
493 {
494 pov_stop_t stop;
495 rgb_t rgb;
496 double c[3], t, z;
497
498 rgb = node->stop.colour;
499
500 if (rgb_to_rgbD(rgb, c) != 0)
501 {
502 btrace("failed conversion to rgbD");
503 return 1;
504 }
505
506 t = 1.0 - node->stop.opacity;
507
508 if ((t < 0.0) || (t > 1.0))
509 {
510 btrace("bad value for transparency : %f", t);
511 return 1;
512 }
513
514 z = node->stop.value/100.0;
515
516 if ((z < 0.0) || (z > 1.0))
517 {
518 btrace("bad z value : %f", t);
519 return 1;
520 }
521
522 stop.z = z;
523
524 stop.rgbt[0] = c[0];
525 stop.rgbt[1] = c[1];
526 stop.rgbt[2] = c[2];
527 stop.rgbt[3] = t;
528
529 pov->stop[n] = stop;
530 }
531
532 if (n != m)
533 {
534 btrace("missmatch between stops expected (%i) and found (%i)", m, n);
535 return 1;
536 }
537
538 pov->n = n;
539
540 /* povray names need to be alphanumeric ascii */
541
542 if (has_name(svg))
543 {
544 char aname[SVG_NAME_LEN];
545
546 if (utf8_to_x("ASCII", svg->name, aname, SVG_NAME_LEN) != 0)
547 {
548 btrace("failed to convert name %s to ascii", aname);
549 return 1;
550 }
551
552 if (pov_set_name(pov, aname, &nmod) != 0)
553 {
554 btrace("failed to assign povray name (%s)", aname);
555 return 1;
556 }
557
558 /* warn if name was modified */
559
560 if (nmod > 0)
561 btrace("name modified : %s to %s", svg->name, pov->name);
562 }
563 else
564 {
565 const char aname[] = "unassigned";
566
567 if (pov_set_name(pov, aname, &nmod) != 0)
568 {
569 btrace("failed to assign povray name (%s)", aname);
570 return 1;
571 }
572 }
573
574 return 0;
575 }
576
svgpov_dump(const svg_t * svg,svgx_opt_t * opt)577 extern int svgpov_dump(const svg_t *svg, svgx_opt_t *opt)
578 {
579 if (opt->job == job_all)
580 return call_autonamed(svg, opt, "inc", svgpov_dump);
581
582 const char *name = (char*)svg->name;
583 const char *file = opt->output.file;
584 pov_t *pov;
585
586 if (! svgpov_valid(svg, opt->permissive, opt->verbose))
587 {
588 btrace("cannot create valid povray file");
589 return 1;
590 }
591
592 if ((pov = pov_new()) == NULL)
593 {
594 btrace("failed to create pov structure");
595 return 1;
596 }
597
598 int err = 0;
599
600 if (svgpov(svg, pov) != 0)
601 {
602 btrace("failed to convert %s to pov", name);
603 err++;
604 }
605 else
606 {
607 if (pov_write(file, pov) != 0)
608 {
609 btrace("failed to write to %s", file);
610 err++;
611 }
612 }
613
614 pov_destroy(pov);
615
616 return err;
617 }
618
619 /* qgs */
620
unit_uchar(double x)621 static unsigned char unit_uchar(double x)
622 {
623 return nearbyint(x * 255);
624 }
625
svg_stop_to_qgs_entry(svg_stop_t stop,qgs_entry_t * entry)626 static void svg_stop_to_qgs_entry(svg_stop_t stop, qgs_entry_t *entry)
627 {
628 entry->rgb = stop.colour;
629 entry->opacity = unit_uchar(stop.opacity);
630 entry->value = stop.value/100.0;
631 }
632
svgqgs2(const svg_t * svg,qgs_t * qgs)633 static int svgqgs2(const svg_t *svg, qgs_t *qgs)
634 {
635 int m = svg_num_stops(svg);
636
637 if (m < 2)
638 {
639 btrace("bad number of stops : %i", m);
640 return 1;
641 }
642
643 if (qgs_set_name(qgs, (const char*)svg->name) != 0)
644 {
645 btrace("failed to set name for qgs");
646 return 1;
647 }
648
649 if (qgs_set_type(qgs, QGS_TYPE_INTERPOLATED) != 0)
650 {
651 btrace("failed to set type for qgs");
652 return 1;
653 }
654
655 if (qgs_alloc_entries(qgs, m) != 0)
656 {
657 btrace("failed qgs allocate for %i stops", m);
658 return 1;
659 }
660
661 svg_node_t *node;
662 int n;
663
664 for (n = 0, node = svg->nodes ; node ; n++, node = node->r)
665 {
666 qgs_entry_t entry;
667
668 svg_stop_to_qgs_entry(node->stop, &entry);
669
670 if (qgs_set_entry(qgs, n, &entry) != 0)
671 {
672 btrace("failed to set qgs entry %zi", n);
673 return 1;
674 }
675 }
676
677 if (n != m)
678 {
679 btrace("missmatch between stops expected (%i) and found (%i)", m, n);
680 return 1;
681 }
682
683 return 0;
684 }
685
svgqgs(const svg_t * svg)686 static qgs_t* svgqgs(const svg_t *svg)
687 {
688 qgs_t *qgs;
689
690 if ((qgs = qgs_new()) == NULL)
691 {
692 btrace("failed to allocate qgs");
693 return NULL;
694 }
695
696 if (svgqgs2(svg, qgs) == 0)
697 return qgs;
698
699 qgs_destroy(qgs);
700
701 return NULL;
702 }
703
svgqgs_dump(const svg_t * svg,svgx_opt_t * opt)704 extern int svgqgs_dump(const svg_t *svg, svgx_opt_t *opt)
705 {
706 if (opt->job == job_all)
707 return call_autonamed(svg, opt, "gpt", svggpt_dump);
708
709 qgs_t *qgs;
710
711 if ((qgs = svgqgs(svg)) == NULL)
712 {
713 btrace("failed to convert %s to qgs", (char*)svg->name);
714 return 1;
715 }
716
717 const char *file = opt->output.file;
718 int err = 0;
719
720 if (qgs_write(file, qgs) != 0)
721 {
722 btrace("failed to write to %s", file);
723 err++;
724 }
725
726 qgs_destroy(qgs);
727
728 return err;
729 }
730
731 /* sao */
732
svgsao(const svg_t * svg,sao_t * sao)733 static int svgsao(const svg_t *svg, sao_t *sao)
734 {
735 svg_node_t *node;
736 int n=0;
737
738 for (node = svg->nodes ; node ; node = node->r, n++)
739 {
740 double rgbD[3], val = node->stop.value / 100.0;
741
742 if (rgb_to_rgbD(node->stop.colour, rgbD) != 0)
743 {
744 btrace("error converting colour of stop %i", n+1);
745 return 1;
746 }
747
748 int err = 0;
749
750 err += sao_red_push(sao, val, rgbD[0]);
751 err += sao_green_push(sao, val, rgbD[1]);
752 err += sao_blue_push(sao, val, rgbD[2]);
753
754 if (err)
755 {
756 btrace("error adding sao stop %i", n+1);
757 return 1;
758 }
759 }
760
761 return 0;
762 }
763
svgsao_dump(const svg_t * svg,svgx_opt_t * opt)764 extern int svgsao_dump(const svg_t *svg, svgx_opt_t *opt)
765 {
766 if (opt->job == job_all)
767 return call_autonamed(svg, opt, "sao", svgsao_dump);
768
769 const char *name = (char*)svg->name;
770 const char *file = opt->output.file;
771 sao_t *sao;
772
773 if ((sao = sao_new()) == NULL)
774 {
775 btrace("failed to create sao structure");
776 return 1;
777 }
778
779 int err = 0;
780
781 if (svgsao(svg, sao) != 0)
782 {
783 btrace("failed to convert %s to sao", opt->name);
784 err++;
785 }
786 else
787 {
788 if (sao_write(file, sao, name) != 0)
789 {
790 btrace("failed to write to %s", file);
791 err++;
792 }
793 }
794
795 sao_destroy(sao);
796
797 return err;
798 }
799
800 /* gpt */
801
svggpt(const svg_t * svg,gpt_t * gpt)802 static int svggpt(const svg_t *svg, gpt_t *gpt)
803 {
804 int n, m;
805 svg_node_t *node;
806
807 /* count & allocate */
808
809 m = svg_num_stops(svg);
810
811 if (m < 2)
812 {
813 btrace("bad number of stops : %i", m);
814 return 1;
815 }
816
817 if (gpt_stops_alloc(gpt, m) != 0)
818 {
819 btrace("failed alloc for %i stops", m);
820 return 1;
821 }
822
823 /* convert */
824
825 for (n=0, node = svg->nodes ; node ; n++, node = node->r)
826 {
827 gpt_stop_t stop;
828 rgb_t rgb;
829 double c[3];
830
831 rgb = node->stop.colour;
832
833 if (rgb_to_rgbD(rgb, c) != 0)
834 {
835 btrace("failed conversion to rgbD");
836 return 1;
837 }
838
839 stop.z = node->stop.value/100.0;
840
841 stop.rgb[0] = c[0];
842 stop.rgb[1] = c[1];
843 stop.rgb[2] = c[2];
844
845 gpt->stop[n] = stop;
846 }
847
848 if (n != m)
849 {
850 btrace("missmatch between stops expected (%i) and found (%i)", m, n);
851 return 1;
852 }
853
854 gpt->n = n;
855
856 return 0;
857 }
858
svggpt_dump(const svg_t * svg,svgx_opt_t * opt)859 extern int svggpt_dump(const svg_t *svg, svgx_opt_t *opt)
860 {
861 if (opt->job == job_all)
862 return call_autonamed(svg, opt, "gpt", svggpt_dump);
863
864 const char *name = (char*)svg->name;
865 const char *file = opt->output.file;
866 gpt_t *gpt;
867
868 if ((gpt = gpt_new()) == NULL)
869 {
870 btrace("failed to create gpt structure");
871 return 1;
872 }
873
874 int err = 0;
875
876 if (svggpt(svg, gpt) != 0)
877 {
878 btrace("failed to convert %s to gpt", name);
879 err++;
880 }
881 else
882 {
883 if (gpt_write(file, gpt) != 0)
884 {
885 btrace("failed to write to %s", file);
886 err++;
887 }
888 }
889
890 gpt_destroy(gpt);
891
892 return err;
893 }
894
895 /* css3 */
896
svgcss3(const svg_t * svg,css3_t * css3)897 static int svgcss3(const svg_t *svg, css3_t *css3)
898 {
899 int n, m;
900 svg_node_t *node;
901
902 /* count & allocate */
903
904 m = svg_num_stops(svg);
905
906 if (m < 2)
907 {
908 btrace("bad number of stops : %i", m);
909 return 1;
910 }
911
912 if (css3_stops_alloc(css3, m) != 0)
913 {
914 btrace("failed alloc for %i stops", m);
915 return 1;
916 }
917
918 /* convert */
919
920 for (n=0, node = svg->nodes ; node ; n++, node = node->r)
921 {
922 css3_stop_t stop;
923
924 stop.rgb = node->stop.colour;
925 stop.z = node->stop.value;
926 stop.alpha = node->stop.opacity;
927
928 css3->stop[n] = stop;
929 }
930
931 if (n != m)
932 {
933 btrace("missmatch between stops expected (%i) and found (%i)", m, n);
934 return 1;
935 }
936
937 css3->n = n;
938
939 return 0;
940 }
941
svgcss3_dump(const svg_t * svg,svgx_opt_t * opt)942 extern int svgcss3_dump(const svg_t *svg, svgx_opt_t *opt)
943 {
944 if (opt->job == job_all)
945 return call_autonamed(svg, opt, "c3g", svgcss3_dump);
946
947 const char *name = (char*)svg->name;
948 const char *file = opt->output.file;
949 css3_t *css3;
950
951 if ((css3 = css3_new()) == NULL)
952 {
953 btrace("failed to create css3 structure");
954 return 1;
955 }
956
957 int err = 0;
958
959 if (svgcss3(svg, css3) != 0)
960 {
961 btrace("failed to convert %s to css3", name);
962 err++;
963 }
964 else
965 {
966 if (css3_write(file, css3) != 0)
967 {
968 btrace("failed to write to %s", file);
969 err++;
970 }
971 }
972
973 css3_destroy(css3);
974
975 return err;
976 }
977
978 /* grd3 */
979
clampd(double z,double min,double max)980 static double clampd(double z, double min, double max)
981 {
982 if (z < min) z = min;
983 if (z > max) z = max;
984
985 return z;
986 }
987
clampi(int z,int min,int max)988 static int clampi(int z, int min, int max)
989 {
990 if (z < min) z = min;
991 if (z > max) z = max;
992
993 return z;
994 }
995
svggrd3(const svg_t * svg,grd3_t * grd3)996 static int svggrd3(const svg_t *svg, grd3_t *grd3)
997 {
998 int m = svg_num_stops(svg);
999
1000 if (m < 2)
1001 {
1002 btrace("bad number of stops : %i", m);
1003 return 1;
1004 }
1005
1006 grd3_rgbseg_t *pcseg = malloc(m*sizeof(grd3_rgbseg_t));
1007
1008 if (pcseg == NULL)
1009 btrace("failed to allocate segments");
1010 else
1011 {
1012 grd3_opseg_t *poseg = malloc(m*sizeof(grd3_opseg_t));
1013
1014 if (poseg == NULL)
1015 btrace("failed to allocate segments");
1016 else
1017 {
1018 int n;
1019 svg_node_t *node;
1020
1021 for (n=0, node = svg->nodes ; node ; n++, node = node->r)
1022 {
1023 rgb_t rgb;
1024 double op, z;
1025
1026 rgb = node->stop.colour;
1027 op = node->stop.opacity;
1028 z = node->stop.value;
1029
1030 pcseg[n].z = clampd(4096*z/100.0, 0, 4096);
1031 pcseg[n].midpoint = 50;
1032 pcseg[n].r = clampi(rgb.red*257, 0, 65535);
1033 pcseg[n].g = clampi(rgb.green*257, 0, 65535);;
1034 pcseg[n].b = clampi(rgb.blue*257, 0, 65535);;
1035
1036 poseg[n].z = clampd(4096*z/100.0, 0, 4096);
1037 poseg[n].midpoint = 50;
1038 poseg[n].opacity = clampi(op*256, 0, 255);
1039 }
1040
1041 char buffer[SVG_NAME_LEN];
1042
1043 if (utf8_to_x("LATIN1", svg->name, buffer, SVG_NAME_LEN) != 0)
1044 btrace("failed to convert utf name to latin1");
1045 else
1046 {
1047 grd3->name = (unsigned char*)strdup(buffer);
1048
1049 grd3->rgb.n = m;
1050 grd3->rgb.seg = pcseg;
1051
1052 grd3->op.n = m;
1053 grd3->op.seg = poseg;
1054
1055 return 0;
1056 }
1057 }
1058 free(poseg);
1059 }
1060 free(pcseg);
1061
1062 return 1;
1063 }
1064
svggrd3_dump(const svg_t * svg,svgx_opt_t * opt)1065 extern int svggrd3_dump(const svg_t *svg, svgx_opt_t *opt)
1066 {
1067 if (opt->job == job_all)
1068 return call_autonamed(svg, opt, "grd", svggrd3_dump);
1069
1070 const char *name = (char*)svg->name;
1071 const char *file = opt->output.file;
1072 grd3_t *grd3;
1073
1074 if ((grd3 = grd3_new()) == NULL)
1075 {
1076 btrace("failed to create grd3 structure");
1077 return 1;
1078 }
1079
1080 int err = 0;
1081
1082 if (svggrd3(svg, grd3) != 0)
1083 {
1084 btrace("failed to convert %s to grd3", name);
1085 err++;
1086 }
1087 else
1088 {
1089 if (grd3_write(file, grd3) != 0)
1090 {
1091 btrace("failed to write to %s", file);
1092 err++;
1093 }
1094 }
1095
1096 grd3_destroy(grd3);
1097
1098 return err;
1099 }
1100
1101 /* png */
1102
svgpng(const svg_t * svg,png_t * png)1103 static int svgpng(const svg_t *svg, png_t *png)
1104 {
1105
1106 size_t nz = png->width;
1107 unsigned char *row = png->row;
1108 int i;
1109
1110 for (i=0 ; i<nz ; i++)
1111 {
1112 rgb_t rgb;
1113 double op, z = 100.0 * (double)i/(double)(nz-1);
1114
1115 if (svg_interpolate(svg, z, &rgb, &op) != 0)
1116 {
1117 btrace("failed svg interpolate at %.3g", z);
1118 return 1;
1119 }
1120
1121 op *= 256;
1122
1123 if (op > 255) op = 255;
1124
1125 row[4*i] = rgb.red;
1126 row[4*i+1] = rgb.green;
1127 row[4*i+2] = rgb.blue;
1128 row[4*i+3] = (unsigned char)op;
1129 }
1130
1131 return 0;
1132 }
1133
svgpng_dump(const svg_t * svg,svgx_opt_t * opt)1134 extern int svgpng_dump(const svg_t *svg, svgx_opt_t *opt)
1135 {
1136 if (opt->job == job_all)
1137 return call_autonamed(svg, opt, "png", svgpng_dump);
1138
1139 const char *name = (char*)svg->name;
1140 const char *file = opt->output.file;
1141 png_t *png;
1142
1143 if ((png = png_new(opt->format.png.width,
1144 opt->format.png.height)) == NULL)
1145 {
1146 btrace("failed to create png structure");
1147 return 1;
1148 }
1149
1150 int err = 0;
1151
1152 if (svgpng(svg, png) != 0)
1153 {
1154 btrace("failed to convert %s to png", name);
1155 err++;
1156 }
1157 else
1158 {
1159 if (png_write(file, png, name) != 0)
1160 {
1161 btrace("failed to write to %s", file);
1162 err++;
1163 }
1164 }
1165
1166 png_destroy(png);
1167
1168 return err;
1169 }
1170
1171 /* svg */
1172
svgsvg_dump(const svg_t * svg,svgx_opt_t * opt)1173 extern int svgsvg_dump(const svg_t *svg, svgx_opt_t *opt)
1174 {
1175 if (opt->job == job_all)
1176 return call_autonamed(svg, opt, "svg", svgsvg_dump);
1177
1178 const char *file = opt->output.file;
1179
1180 if (svg_write(file, 1,
1181 (const svg_t**)(&svg),
1182 &(opt->format.svg.preview)) != 0)
1183 {
1184 btrace("failed to write to %s", file);
1185 return 1;
1186 }
1187
1188 return 0;
1189 }
1190