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