0a1108b1ebbafb5eeaa70067ac7cee061fbe49ab
1 /*
2 * A simple utility for exporting Inkscape svg Shapes as JavaFX paths.
3 *
4 * For information on the JavaFX file format, see:
5 * https://openjfx.dev.java.net/
6 *
7 * Authors:
8 * Bob Jamison <ishmal@inkscape.org>
9 * Silveira Neto <silveiraneto@gmail.com>
10 * Jim Clarke <Jim.Clarke@sun.com>
11 *
12 * Copyright (C) 2008 Authors
13 *
14 * Released under GNU GPL, read the file 'COPYING' for more information
15 */
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 #include "javafx-out.h"
22 #include <inkscape.h>
23 #include <inkscape-version.h>
24 #include <sp-path.h>
25 #include <sp-linear-gradient.h>
26 #include <sp-radial-gradient.h>
27 #include <style.h>
28 #include <display/curve.h>
29 #include <display/canvas-bpath.h>
30 #include <svg/svg.h>
31 #include <extension/system.h>
32 #include <2geom/pathvector.h>
33 #include <2geom/rect.h>
34 #include <2geom/bezier-curve.h>
35 #include <2geom/hvlinesegment.h>
36 #include "helper/geom.h"
37 #include "helper/geom-curves.h"
38 #include <io/sys.h>
41 #include <string>
42 #include <stdio.h>
43 #include <stdarg.h>
46 namespace Inkscape
47 {
48 namespace Extension
49 {
50 namespace Internal
51 {
56 //########################################################################
57 //# M E S S A G E S
58 //########################################################################
60 static void err(const char *fmt, ...)
61 {
62 va_list args;
63 g_log(NULL, G_LOG_LEVEL_WARNING, "javafx-out err: ");
64 va_start(args, fmt);
65 g_logv(NULL, G_LOG_LEVEL_WARNING, fmt, args);
66 va_end(args);
67 g_log(NULL, G_LOG_LEVEL_WARNING, "\n");
68 }
71 //########################################################################
72 //# U T I L I T Y
73 //########################################################################
75 /**
76 * Got this method from Bulia, and modified it a bit. It basically
77 * starts with this style, gets its SPObject parent, walks up the object
78 * tree and finds all of the opacities and multiplies them.
79 *
80 * We use this for our "flat" object output. If the code is modified
81 * to reflect a tree of <groups>, then this will be unneccessary.
82 */
83 static double effective_opacity(const SPStyle *style)
84 {
85 double val = 1.0;
86 for (SPObject const *obj = style->object; obj ; obj = obj->parent)
87 {
88 style = SP_OBJECT_STYLE(obj);
89 if (style)
90 val *= SP_SCALE24_TO_FLOAT(style->opacity.value);
91 }
92 return val;
93 }
95 //########################################################################
96 //# OUTPUT FORMATTING
97 //########################################################################
100 /**
101 * We want to control floating output format.
102 * Especially to avoid localization. (decimal ',' for example)
103 */
104 static JavaFXOutput::String dstr(double d)
105 {
106 char dbuf[G_ASCII_DTOSTR_BUF_SIZE+1];
107 g_ascii_formatd(dbuf, G_ASCII_DTOSTR_BUF_SIZE,
108 "%.8f", (gdouble)d);
109 JavaFXOutput::String s = dbuf;
110 return s;
111 }
113 #define DSTR(d) (dstr(d).c_str())
116 /**
117 * Format a double as an integer
118 */
119 static JavaFXOutput::String istr(double d)
120 {
121 char dbuf[G_ASCII_DTOSTR_BUF_SIZE+1];
122 g_ascii_formatd(dbuf, G_ASCII_DTOSTR_BUF_SIZE,
123 "%.0f", (gdouble)d);
124 JavaFXOutput::String s = dbuf;
125 return s;
126 }
128 #define ISTR(d) (istr(d).c_str())
131 /**
132 * Format an rgba() string
133 */
134 static JavaFXOutput::String rgba(guint32 rgba)
135 {
136 unsigned int r = SP_RGBA32_R_U(rgba);
137 unsigned int g = SP_RGBA32_G_U(rgba);
138 unsigned int b = SP_RGBA32_B_U(rgba);
139 unsigned int a = SP_RGBA32_A_U(rgba);
140 char buf[80];
141 snprintf(buf, 79, "Color.rgb(0x%02x, 0x%02x, 0x%02x, %s)",
142 r, g, b, DSTR((double)a/256.0));
143 JavaFXOutput::String s = buf;
144 return s;
145 }
148 /**
149 * Format an rgba() string for a color and a 0.0-1.0 alpha
150 */
151 static JavaFXOutput::String rgba(SPColor color, gdouble alpha)
152 {
153 return rgba(color.toRGBA32(alpha));
154 }
156 /**
157 * Map Inkscape linecap styles to JavaFX
158 */
159 static JavaFXOutput::String getStrokeLineCap(unsigned value) {
160 switch(value) {
161 case SP_STROKE_LINECAP_BUTT:
162 return "StrokeLineCap.BUTT";
163 case SP_STROKE_LINECAP_ROUND:
164 return "StrokeLineCap.ROUND";
165 case SP_STROKE_LINECAP_SQUARE:
166 return "StrokeLineCap.SQUARE";
167 default:
168 return "INVALID LINE CAP";
169 }
170 }
173 /**
174 * Map Inkscape linejoin styles to JavaFX
175 */
176 static JavaFXOutput::String getStrokeLineJoin(unsigned value) {
177 switch(value) {
178 case SP_STROKE_LINEJOIN_MITER:
179 return "StrokeLineJoin.MITER";
180 case SP_STROKE_LINEJOIN_ROUND:
181 return "StrokeLineJoin.ROUND";
182 case SP_STROKE_LINEJOIN_BEVEL:
183 return "StrokeLineJoin.BEVEL";
184 default:
185 return "INVALID LINE JOIN";
186 }
187 }
190 /**
191 * Replace illegal characters for JavaFX for a underscore.
192 */
193 static JavaFXOutput::String sanatize(const JavaFXOutput::String &badstr){
194 JavaFXOutput::String good(badstr);
195 for (int pos = 0; pos < badstr.length(); ++pos )
196 if((badstr.at(pos)=='-')||(badstr.at(pos)==' '))
197 good.replace(pos, 1, "_");
198 return good;
199 }
201 /**
202 * Output data to the buffer, printf()-style
203 */
204 void JavaFXOutput::out(const char *fmt, ...)
205 {
206 va_list args;
207 va_start(args, fmt);
208 gchar *output = g_strdup_vprintf(fmt, args);
209 va_end(args);
210 outbuf.append(output);
211 g_free(output);
212 }
216 /**
217 * Output the file header
218 */
219 bool JavaFXOutput::doHeader()
220 {
221 time_t tim = time(NULL);
222 out("/*###################################################################\n");
223 out("### This JavaFX document was generated by Inkscape\n");
224 out("### http://www.inkscape.org\n");
225 out("### Created: %s", ctime(&tim));
226 out("### Version: %s\n", Inkscape::version_string);
227 out("#####################################################################\n");
228 out("### NOTES:\n");
229 out("### ============\n");
230 out("### JavaFX information can be found at\n");
231 out("### hhttps://openjfx.dev.java.net\n");
232 out("###\n");
233 out("### If you have any problems with this output, please see the\n");
234 out("### Inkscape project at http://www.inkscape.org, or visit\n");
235 out("### the #inkscape channel on irc.freenode.net . \n");
236 out("###\n");
237 out("###################################################################*/\n");
238 out("\n\n");
239 out("/*###################################################################\n");
240 out("## Exports in this file\n");
241 out("##==========================\n");
242 out("## Shapes : %d\n", nrShapes);
243 out("## Nodes : %d\n", nrNodes);
244 out("###################################################################*/\n");
245 out("\n\n");
247 // import javafx libraries we can need
248 out("import javafx.application.*;\n");
249 out("import javafx.scene.*;\n");
250 out("import javafx.scene.geometry.*;\n");
251 out("import javafx.scene.transform.*;\n");
252 out("import javafx.scene.paint.*;\n");
253 out("\n");
255 out("\n\n");
257 // Creates a class extended from CustomNode
258 out("public class %s extends CustomNode {\n", name.c_str());
260 return true;
261 }
265 /**
266 * Output the file footer
267 */
268 bool JavaFXOutput::doTail()
269 {
270 float border = 25.0;
272 // Write the tail of CustomNode
273 out(" ] // content\n");
274 out(" transform: Translate { x : %s, y : %s }\n",
275 DSTR((-minx) + border), DSTR((-miny) + border) );
276 out(" } // Group\n");
277 out(" } // function create()\n");
278 out("} // class %s\n", name.c_str());
279 out("\n");
281 // Frame
282 out("Frame {\n");
283 out(" title: \"%s\"\n", name.c_str());
284 out(" width: %s\n", ISTR(maxx-minx + border * 2.0));
285 out(" height: %s\n", ISTR(maxy-miny + border * 2.0));
286 out(" visible: true\n");
288 // Stage
289 out(" stage: Stage {\n");
290 out(" content: %s{}\n", name.c_str());
291 out(" } // Stage\n");
293 out("} // Frame\n");
295 out("\n");
297 out("/*###################################################################\n");
298 out("### E N D C L A S S %s\n", name.c_str());
299 out("###################################################################*/\n");
300 out("\n\n");
301 return true;
302 }
306 /**
307 * Output gradient information to the buffer
308 */
309 bool JavaFXOutput::doGradient(SPGradient *grad, const String &id)
310 {
311 String jfxid = sanatize(id);
313 if (SP_IS_LINEARGRADIENT(grad))
314 {
315 SPLinearGradient *g = SP_LINEARGRADIENT(grad);
316 out(" /* create LinearGradient for %s */\n", jfxid.c_str());
317 out(" private function %s(): LinearGradient {\n", jfxid.c_str());
318 out(" LinearGradient {\n");
319 std::vector<SPGradientStop> stops = g->vector.stops;
320 if (stops.size() > 0)
321 {
322 out(" stops:\n");
323 out(" [\n");
324 for (unsigned int i = 0 ; i<stops.size() ; i++)
325 {
326 SPGradientStop stop = stops[i];
327 out(" Stop {\n");
328 out(" offset: %s\n", DSTR(stop.offset));
329 out(" color: %s\n", rgba(stop.color, stop.opacity).c_str());
330 out(" },\n");
331 }
332 out(" ]\n");
333 }
334 out(" };\n");
335 out(" } // end LinearGradient: %s\n", jfxid.c_str());
336 out("\n\n");
337 }
338 else if (SP_IS_RADIALGRADIENT(grad))
339 {
340 SPRadialGradient *g = SP_RADIALGRADIENT(grad);
341 out(" /* create RadialGradient for %s */\n", jfxid.c_str());
342 out(" private function %s() {\n", jfxid.c_str());
343 out(" RadialGradient {\n");
344 out(" centerX: %s\n", DSTR(g->cx.value));
345 out(" centerY: %s\n", DSTR(g->cy.value));
346 out(" focusX: %s\n", DSTR(g->fx.value));
347 out(" focusY: %s\n", DSTR(g->fy.value));
348 out(" radius: %s\n", DSTR(g->r.value ));
349 std::vector<SPGradientStop> stops = g->vector.stops;
350 if (stops.size() > 0)
351 {
352 out(" stops:\n");
353 out(" [\n");
354 for (unsigned int i = 0 ; i<stops.size() ; i++)
355 {
356 SPGradientStop stop = stops[i];
357 out(" Stop {\n");
358 out(" offset: %s\n", DSTR(stop.offset));
359 out(" color: %s\n", rgba(stop.color, stop.opacity).c_str());
360 out(" },\n");
361 }
362 out(" ]\n");
363 }
364 out(" };\n");
365 out(" } // end RadialGradient: %s\n", jfxid.c_str());
366 out("\n\n");
367 }
368 else
369 {
370 err("Unknown gradient type for '%s'\n", jfxid.c_str());
371 return false;
372 }
375 return true;
376 }
381 /**
382 * Output an element's style attribute
383 */
384 bool JavaFXOutput::doStyle(SPStyle *style)
385 {
386 if (!style)
387 return true;
389 out(" opacity: %s\n", DSTR(effective_opacity(style)));
391 /**
392 * Fill
393 */
394 SPIPaint fill = style->fill;
395 if (fill.isColor())
396 {
397 // see color.h for how to parse SPColor
398 out(" fill: %s\n",
399 rgba(fill.value.color, SP_SCALE24_TO_FLOAT(style->fill_opacity.value)).c_str());
400 }
401 else if (fill.isPaintserver()){
402 if (fill.value.href && fill.value.href->getURI() ){
403 String uri = fill.value.href->getURI()->toString();
404 /* trim the anchor '#' from the front */
405 if (uri.size() > 0 && uri[0]=='#')
406 uri = uri.substr(1);
407 out(" fill: %s()\n", sanatize(uri).c_str());
408 }
409 }
412 /**
413 * Stroke
414 */
415 /**
416 *NOTE: Things in style we can use:
417 * SPIPaint stroke;
418 * SPILength stroke_width;
419 * SPIEnum stroke_linecap;
420 * SPIEnum stroke_linejoin;
421 * SPIFloat stroke_miterlimit;
422 * NRVpathDash stroke_dash;
423 * unsigned stroke_dasharray_set : 1;
424 * unsigned stroke_dasharray_inherit : 1;
425 * unsigned stroke_dashoffset_set : 1;
426 * SPIScale24 stroke_opacity;
427 */
428 if (style->stroke_opacity.value > 0)
429 {
430 SPIPaint stroke = style->stroke;
431 out(" stroke: %s\n",
432 rgba(stroke.value.color, SP_SCALE24_TO_FLOAT(style->stroke_opacity.value)).c_str());
433 double strokewidth = style->stroke_width.value;
434 unsigned linecap = style->stroke_linecap.value;
435 unsigned linejoin = style->stroke_linejoin.value;
436 out(" strokeWidth: %s\n", DSTR(strokewidth));
437 out(" strokeLineCap: %s\n", getStrokeLineCap(linecap).c_str());
438 out(" strokeLineJoin: %s\n", getStrokeLineJoin(linejoin).c_str());
439 out(" strokeMiterLimit: %s\n", DSTR(style->stroke_miterlimit.value));
440 if(style->stroke_dasharray_set) {
441 if(style->stroke_dashoffset_set) {
442 out(" strokeDashOffset: %s\n", DSTR(style->stroke_dash.offset));
443 }
444 out(" strokeDashArray: [ ");
445 for(int i = 0; i < style->stroke_dash.n_dash; i++ ) {
446 if(i > 0) {
447 out(", %.2lf", style->stroke_dash.dash[i]);
448 }else {
449 out(" %.2lf", style->stroke_dash.dash[i]);
450 }
451 }
452 out(" ]\n");
453 }
455 }
457 return true;
458 }
461 #if 1
463 /**
464 * Output the curve data to buffer
465 */
466 bool JavaFXOutput::doCurve(SPItem *item, const String &id)
467 {
468 using Geom::X;
469 using Geom::Y;
471 String jfxid = sanatize(id);
473 //### Get the Shape
474 if (!SP_IS_SHAPE(item))//Bulia's suggestion. Allow all shapes
475 return true;
477 SPShape *shape = SP_SHAPE(item);
478 SPCurve *curve = shape->curve;
479 if (curve->is_empty())
480 return true;
482 nrShapes++;
484 out(" /** path %s */\n", jfxid.c_str());
485 out(" private function %s() : Path {\n",jfxid.c_str());
486 out(" Path {\n");
487 out(" id: \"%s\"\n", jfxid.c_str());
489 /**
490 * Output the style information
491 */
492 if (!doStyle(SP_OBJECT_STYLE(shape)))
493 return false;
495 // convert the path to only lineto's and cubic curveto's:
496 Geom::Scale yflip(1.0, -1.0);
497 Geom::Matrix tf = sp_item_i2d_affine(item) * yflip;
498 Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() * tf );
500 //Count the NR_CURVETOs/LINETOs (including closing line segment)
501 guint segmentCount = 0;
502 for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {
503 segmentCount += (*it).size();
504 if (it->closed())
505 segmentCount += 1;
506 }
508 out(" elements: [\n");
510 unsigned int segmentNr = 0;
512 nrNodes += segmentCount;
514 Geom::Rect cminmax( pathv.front().initialPoint(), pathv.front().initialPoint() );
516 /**
517 * For all Subpaths in the <path>
518 */
519 for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit)
520 {
521 Geom::Point p = pit->front().initialPoint();
522 cminmax.expandTo(p);
523 out(" MoveTo {\n");
524 out(" x: %s\n", DSTR(p[X]));
525 out(" y: %s\n", DSTR(p[Y]));
526 out(" },\n");
528 /**
529 * For all segments in the subpath
530 */
531 for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit)
532 {
533 //### LINE
534 if( dynamic_cast<Geom::LineSegment const *> (&*cit) ||
535 dynamic_cast<Geom::HLineSegment const *> (&*cit) ||
536 dynamic_cast<Geom::VLineSegment const *> (&*cit) )
537 {
538 Geom::Point p = cit->finalPoint();
539 out(" LineTo {\n");
540 out(" x: %s\n", DSTR(p[X]));
541 out(" y: %s\n", DSTR(p[Y]));
542 out(" },\n");
543 nrNodes++;
544 }
545 //### BEZIER
546 else if(Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit))
547 {
548 std::vector<Geom::Point> points = cubic->points();
549 Geom::Point p1 = points[1];
550 Geom::Point p2 = points[2];
551 Geom::Point p3 = points[3];
552 out(" CurveTo {\n");
553 out(" controlX1: %s\n", DSTR(p1[X]));
554 out(" controlY1: %s\n", DSTR(p1[Y]));
555 out(" controlX2: %s\n", DSTR(p2[X]));
556 out(" controlY2: %s\n", DSTR(p2[Y]));
557 out(" x: %s\n", DSTR(p3[X]));
558 out(" y: %s\n", DSTR(p3[Y]));
559 out(" },\n");
560 nrNodes++;
561 }
562 else
563 {
564 g_error ("logical error, because pathv_to_linear_and_cubic_beziers was used");
565 }
566 segmentNr++;
567 cminmax.expandTo(cit->finalPoint());
568 }
569 if (pit->closed())
570 {
571 out(" ClosePath {},\n");
572 }
573 }
575 out(" ] // elements\n");
576 out(" }; // Path\n");
577 out(" } // end path %s\n\n", jfxid.c_str());
579 double cminx = cminmax.min()[X];
580 double cmaxx = cminmax.max()[X];
581 double cminy = cminmax.min()[Y];
582 double cmaxy = cminmax.max()[Y];
584 if (cminx < minx)
585 minx = cminx;
586 if (cmaxx > maxx)
587 maxx = cmaxx;
588 if (cminy < miny)
589 miny = cminy;
590 if (cmaxy > maxy)
591 maxy = cmaxy;
593 return true;
594 }
598 #else
600 /**
601 * Output the curve data to buffer
602 */
603 bool JavaFXOutput::doCurve(SPItem *item, const String &id)
604 {
605 using Geom::X;
606 using Geom::Y;
608 //### Get the Shape
609 if (!SP_IS_SHAPE(item))//Bulia's suggestion. Allow all shapes
610 return true;
612 SPShape *shape = SP_SHAPE(item);
613 SPCurve *curve = shape->curve;
614 if (curve->is_empty())
615 return true;
617 nrShapes++;
619 out(" SVGPath \n");
620 out(" {\n");
621 out(" id: \"%s\"\n", id.c_str());
623 /**
624 * Output the style information
625 */
626 if (!doStyle(SP_OBJECT_STYLE(shape)))
627 return false;
629 // convert the path to only lineto's and cubic curveto's:
630 Geom::Scale yflip(1.0, -1.0);
631 Geom::Matrix tf = sp_item_i2d_affine(item) * yflip;
632 Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() * tf );
634 //Count the NR_CURVETOs/LINETOs (including closing line segment)
635 nrNodes = 0;
636 for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {
637 nrNodes += (*it).size();
638 if (it->closed())
639 nrNodes += 1;
640 }
642 char *dataStr = sp_svg_write_path(pathv);
643 out(" content: \"%s\"\n", dataStr);
644 free(dataStr);
646 Geom::Rect cminmax( pathv.front().initialPoint(), pathv.front().initialPoint() );
648 /**
649 * Get the Min and Max X and Y extends for the Path.
650 * ....For all Subpaths in the <path>
651 */
652 for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit)
653 {
654 cminmax.expandTo(pit->front().initialPoint());
655 /**
656 * For all segments in the subpath
657 */
658 for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit)
659 {
660 cminmax.expandTo(cit->finalPoint());
661 }
662 }
664 out(" },\n");
666 double cminx = cminmax.min()[X];
667 double cmaxx = cminmax.max()[X];
668 double cminy = cminmax.min()[Y];
669 double cmaxy = cminmax.max()[Y];
671 if (cminx < minx)
672 minx = cminx;
673 if (cmaxx > maxx)
674 maxx = cmaxx;
675 if (cminy < miny)
676 miny = cminy;
677 if (cmaxy > maxy)
678 maxy = cmaxy;
680 return true;
681 }
685 #endif /* #if o */
689 /**
690 * Output the tree data to buffer
691 */
692 bool JavaFXOutput::doTreeRecursive(SPDocument *doc, SPObject *obj)
693 {
694 /**
695 * Check the type of node and process
696 */
697 String id;
698 if (!obj->id)
699 {
700 char buf[16];
701 sprintf(buf, "id%d", idindex++);
702 id = buf;
703 }
704 else
705 {
706 id = obj->id;
707 }
708 if (SP_IS_ITEM(obj))
709 {
710 SPItem *item = SP_ITEM(obj);
711 if (!doCurve(item, id))
712 return false;
713 }
714 else if (SP_IS_GRADIENT(obj))
715 {
716 SPGradient *grad = SP_GRADIENT(obj);
717 if (!doGradient(grad, id))
718 return false;
719 }
721 /**
722 * Descend into children
723 */
724 for (SPObject *child = obj->firstChild() ; child ; child = child->next)
725 {
726 if (!doTreeRecursive(doc, child))
727 return false;
728 }
730 return true;
731 }
734 /**
735 * Output the curve data to buffer
736 */
737 bool JavaFXOutput::doTree(SPDocument *doc)
738 {
740 double bignum = 1000000.0;
741 minx = bignum;
742 maxx = -bignum;
743 miny = bignum;
744 maxy = -bignum;
746 if (!doTreeRecursive(doc, doc->root))
747 return false;
749 return true;
751 }
754 bool JavaFXOutput::doBody(SPDocument *doc, SPObject *obj)
755 {
756 /**
757 * Check the type of node and process
758 */
759 String id;
760 if (!obj->id)
761 {
762 char buf[16];
763 sprintf(buf, "id%d", idindex++);
764 id = buf;
765 }
766 else
767 {
768 id = obj->id;
769 }
771 if (SP_IS_ITEM(obj)) {
772 SPItem *item = SP_ITEM(obj);
773 //### Get the Shape
774 if (SP_IS_SHAPE(item)) {//Bulia's suggestion. Allow all shapes
775 SPShape *shape = SP_SHAPE(item);
776 SPCurve *curve = shape->curve;
777 if (!curve->is_empty())
778 out(" %s(),\n", id.c_str());
779 }
780 }
781 else if (SP_IS_GRADIENT(obj)) {
782 //TODO: what to do with Gradient within body?????
783 //SPGradient *grad = SP_GRADIENT(reprobj);
784 //if (!doGradient(grad, id))
785 // return false;
786 }
788 /**
789 * Descend into children
790 */
791 for (SPObject *child = obj->firstChild() ; child ; child = child->next)
792 {
793 if (!doBody(doc, child))
794 return false;
795 }
797 return true;
798 }
802 //########################################################################
803 //# M A I N O U T P U T
804 //########################################################################
808 /**
809 * Set values back to initial state
810 */
811 void JavaFXOutput::reset()
812 {
813 nrNodes = 0;
814 nrShapes = 0;
815 idindex = 0;
816 name.clear();
817 outbuf.clear();
818 foutbuf.clear();
819 }
823 /**
824 * Saves the <paths> of an Inkscape SVG file as JavaFX spline definitions
825 */
826 bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
827 {
828 reset();
831 name = Glib::path_get_basename(filename_utf8);
832 int pos = name.find('.');
833 if (pos > 0)
834 name = name.substr(0, pos);
837 //###### SAVE IN JAVAFX FORMAT TO BUFFER
838 //# Lets do the curves first, to get the stats
840 if (!doTree(doc))
841 return false;
842 String curveBuf = outbuf;
843 outbuf.clear();
845 if (!doHeader())
846 return false;
848 outbuf.append(curveBuf);
850 #ifdef JAVAFX_SDK_1_0
851 out(" override function create(): Node {\n");
852 #else
853 out(" public function create(): Node {\n");
854 #endif
855 out(" Group {\n");
856 out(" content: [\n");
857 idindex = 0;
859 doBody(doc, doc->root);
861 if (!doTail())
862 return false;
866 //###### WRITE TO FILE
867 FILE *f = Inkscape::IO::fopen_utf8name(filename_utf8, "w");
868 if (!f)
869 {
870 err("Could open JavaFX file '%s' for writing", filename_utf8);
871 return false;
872 }
874 for (String::iterator iter = outbuf.begin() ; iter!=outbuf.end(); iter++)
875 {
876 fputc(*iter, f);
877 }
879 fclose(f);
881 return true;
882 }
887 //########################################################################
888 //# EXTENSION API
889 //########################################################################
893 #include "clear-n_.h"
897 /**
898 * API call to save document
899 */
900 void
901 JavaFXOutput::save(Inkscape::Extension::Output */*mod*/,
902 SPDocument *doc, gchar const *filename_utf8)
903 {
904 /* N.B. The name `filename_utf8' represents the fact that we want it to be in utf8; whereas in
905 * fact we know that some callers of Extension::save pass something in the filesystem's
906 * encoding, while others do g_filename_to_utf8 before calling.
907 *
908 * In terms of safety, it's best to make all callers pass actual filenames, since in general
909 * one can't round-trip from filename to utf8 back to the same filename. Whereas the argument
910 * for passing utf8 filenames is one of convenience: we often want to pass to g_warning or
911 * store as a string (rather than a byte stream) in XML, or the like. */
912 if (!saveDocument(doc, filename_utf8))
913 {
914 g_warning("Could not save JavaFX file '%s'", filename_utf8);
915 }
916 }
920 /**
921 * Make sure that we are in the database
922 */
923 bool JavaFXOutput::check (Inkscape::Extension::Extension */*module*/)
924 {
925 /* We don't need a Key
926 if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_JFX))
927 return FALSE;
928 */
930 return true;
931 }
935 /**
936 * This is the definition of JavaFX output. This function just
937 * calls the extension system with the memory allocated XML that
938 * describes the data.
939 */
940 void
941 JavaFXOutput::init()
942 {
943 Inkscape::Extension::build_from_mem(
944 "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
945 "<name>" N_("JavaFX Output") "</name>\n"
946 "<id>org.inkscape.output.jfx</id>\n"
947 "<output>\n"
948 "<extension>.fx</extension>\n"
949 "<mimetype>text/x-javafx-script</mimetype>\n"
950 "<filetypename>" N_("JavaFX (*.fx)") "</filetypename>\n"
951 "<filetypetooltip>" N_("JavaFX Raytracer File") "</filetypetooltip>\n"
952 "</output>\n"
953 "</inkscape-extension>",
954 new JavaFXOutput());
955 }
961 } // namespace Internal
962 } // namespace Extension
963 } // namespace Inkscape
966 /*
967 Local Variables:
968 mode:c++
969 c-file-style:"stroustrup"
970 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
971 indent-tabs-mode:nil
972 fill-column:99
973 End:
974 */
975 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :