index 69ee296e58a0a73c80cdb8833d236b6bf42d5cc9..7098027c7847dd4dfc3316c1cff9b1ba0b50902d 100644 (file)
-/*\r
- * A simple utility for exporting Inkscape svg Shapes as JavaFX paths.\r
- *\r
- * For information on the JavaFX file format, see:\r
- * https://openjfx.dev.java.net/\r
- *\r
- * Authors:\r
- * Bob Jamison <ishmal@inkscape.org>\r
- * Silveira Neto <silveiraneto@gmail.com>\r
- * Jim Clarke <Jim.Clarke@sun.com>\r
- *\r
- * Copyright (C) 2008 Authors\r
- *\r
- * Released under GNU GPL, read the file 'COPYING' for more information\r
- */\r
-\r
-\r
-#ifdef HAVE_CONFIG_H\r
-# include <config.h>\r
-#endif\r
-#include "javafx-out.h"\r
-#include <inkscape.h>\r
-#include <inkscape_version.h>\r
-#include <sp-path.h>\r
-#include <sp-linear-gradient.h>\r
-#include <sp-radial-gradient.h>\r
-#include <style.h>\r
-#include <display/curve.h>\r
-#include <display/canvas-bpath.h>\r
-#include <svg/svg.h>\r
-#include <extension/system.h>\r
-#include <2geom/pathvector.h>\r
-#include <2geom/rect.h>\r
-#include <2geom/bezier-curve.h>\r
-#include <2geom/hvlinesegment.h>\r
-#include "helper/geom.h"\r
-#include "helper/geom-curves.h"\r
-#include <io/sys.h>\r
-\r
-\r
-#include <string>\r
-#include <stdio.h>\r
-#include <stdarg.h>\r
-\r
-\r
-namespace Inkscape\r
-{\r
-namespace Extension\r
-{\r
-namespace Internal\r
-{\r
-\r
-\r
-\r
-\r
-//########################################################################\r
-//# M E S S A G E S\r
-//########################################################################\r
-\r
-static void err(const char *fmt, ...)\r
-{\r
- va_list args;\r
- g_log(NULL, G_LOG_LEVEL_WARNING, "javafx-out err: ");\r
- va_start(args, fmt);\r
- g_logv(NULL, G_LOG_LEVEL_WARNING, fmt, args);\r
- va_end(args);\r
- g_log(NULL, G_LOG_LEVEL_WARNING, "\n");\r
-}\r
-\r
-\r
-//########################################################################\r
-//# U T I L I T Y\r
-//########################################################################\r
-\r
-/**\r
- * Got this method from Bulia, and modified it a bit. It basically\r
- * starts with this style, gets its SPObject parent, walks up the object\r
- * tree and finds all of the opacities and multiplies them.\r
- *\r
- * We use this for our "flat" object output. If the code is modified\r
- * to reflect a tree of <groups>, then this will be unneccessary.\r
- */\r
-static double effective_opacity(const SPStyle *style)\r
-{\r
- double val = 1.0;\r
- for (SPObject const *obj = style->object; obj ; obj = obj->parent)\r
- {\r
- style = SP_OBJECT_STYLE(obj);\r
- if (style)\r
- val *= SP_SCALE24_TO_FLOAT(style->opacity.value);\r
- }\r
- return val;\r
-}\r
-\r
-//########################################################################\r
-//# OUTPUT FORMATTING\r
-//########################################################################\r
-\r
-\r
-/**\r
- * We want to control floating output format.\r
- * Especially to avoid localization. (decimal ',' for example)\r
- */\r
-static JavaFXOutput::String dstr(double d)\r
-{\r
- char dbuf[G_ASCII_DTOSTR_BUF_SIZE+1];\r
- g_ascii_formatd(dbuf, G_ASCII_DTOSTR_BUF_SIZE,\r
- "%.8f", (gdouble)d);\r
- JavaFXOutput::String s = dbuf;\r
- return s;\r
-}\r
-\r
-#define DSTR(d) (dstr(d).c_str())\r
-\r
-\r
-/**\r
- * Format a double as an integer\r
- */\r
-static JavaFXOutput::String istr(double d)\r
-{\r
- char dbuf[G_ASCII_DTOSTR_BUF_SIZE+1];\r
- g_ascii_formatd(dbuf, G_ASCII_DTOSTR_BUF_SIZE,\r
- "%.0f", (gdouble)d);\r
- JavaFXOutput::String s = dbuf;\r
- return s;\r
-}\r
-\r
-#define ISTR(d) (istr(d).c_str())\r
-\r
-\r
-/**\r
- * Format an rgba() string\r
- */\r
-static JavaFXOutput::String rgba(guint32 rgba)\r
-{\r
- unsigned int r = SP_RGBA32_R_U(rgba);\r
- unsigned int g = SP_RGBA32_G_U(rgba);\r
- unsigned int b = SP_RGBA32_B_U(rgba);\r
- unsigned int a = SP_RGBA32_A_U(rgba);\r
- char buf[80];\r
- snprintf(buf, 79, "Color.rgb(0x%02x, 0x%02x, 0x%02x, %s)",\r
- r, g, b, DSTR((double)a/256.0));\r
- JavaFXOutput::String s = buf;\r
- return s;\r
-}\r
-\r
-\r
-/**\r
- * Format an rgba() string for a color and a 0.0-1.0 alpha\r
- */\r
-static JavaFXOutput::String rgba(SPColor color, gdouble alpha)\r
-{\r
- return rgba(color.toRGBA32(alpha));\r
-}\r
-\r
-/**\r
- * Map Inkscape linecap styles to JavaFX\r
- */\r
-static JavaFXOutput::String getStrokeLineCap(unsigned value) {\r
- switch(value) {\r
- case SP_STROKE_LINECAP_BUTT:\r
- return "StrokeLineCap.BUTT";\r
- case SP_STROKE_LINECAP_ROUND:\r
- return "StrokeLineCap.ROUND";\r
- case SP_STROKE_LINECAP_SQUARE:\r
- return "StrokeLineCap.SQUARE";\r
- default:\r
- return "INVALID LINE CAP";\r
- }\r
-}\r
-\r
-\r
-/**\r
- * Map Inkscape linejoin styles to JavaFX\r
- */\r
-static JavaFXOutput::String getStrokeLineJoin(unsigned value) {\r
- switch(value) {\r
- case SP_STROKE_LINEJOIN_MITER:\r
- return "StrokeLineJoin.MITER";\r
- case SP_STROKE_LINEJOIN_ROUND:\r
- return "StrokeLineJoin.ROUND";\r
- case SP_STROKE_LINEJOIN_BEVEL:\r
- return "StrokeLineJoin.BEVEL";\r
- default:\r
- return "INVALID LINE JOIN";\r
- }\r
-}\r
-\r
-\r
-/**\r
- * Replace illegal characters for JavaFX for a underscore.\r
- */\r
-static JavaFXOutput::String sanatize(const JavaFXOutput::String &badstr){\r
- JavaFXOutput::String good(badstr);\r
- for (int pos = 0; pos < badstr.length(); ++pos )\r
- if((badstr.at(pos)=='-')||(badstr.at(pos)==' '))\r
- good.replace(pos, 1, "_");\r
- return good;\r
-}\r
-\r
-/**\r
- * Output data to the buffer, printf()-style\r
- */\r
-void JavaFXOutput::out(const char *fmt, ...)\r
-{\r
- va_list args;\r
- va_start(args, fmt);\r
- gchar *output = g_strdup_vprintf(fmt, args);\r
- va_end(args);\r
- outbuf.append(output);\r
- g_free(output);\r
-}\r
-\r
-\r
-\r
-/**\r
- * Output the file header\r
- */\r
-bool JavaFXOutput::doHeader()\r
-{\r
- time_t tim = time(NULL);\r
- out("/*###################################################################\n");\r
- out("### This JavaFX document was generated by Inkscape\n");\r
- out("### http://www.inkscape.org\n");\r
- out("### Created: %s", ctime(&tim));\r
- out("### Version: %s\n", INKSCAPE_VERSION);\r
- out("#####################################################################\n");\r
- out("### NOTES:\n");\r
- out("### ============\n");\r
- out("### JavaFX information can be found at\n");\r
- out("### hhttps://openjfx.dev.java.net\n");\r
- out("###\n");\r
- out("### If you have any problems with this output, please see the\n");\r
- out("### Inkscape project at http://www.inkscape.org, or visit\n");\r
- out("### the #inkscape channel on irc.freenode.net . \n");\r
- out("###\n");\r
- out("###################################################################*/\n");\r
- out("\n\n");\r
- out("/*###################################################################\n");\r
- out("## Exports in this file\n");\r
- out("##==========================\n");\r
- out("## Shapes : %d\n", nrShapes);\r
- out("## Nodes : %d\n", nrNodes);\r
- out("###################################################################*/\n");\r
- out("\n\n");\r
-\r
- // import javafx libraries we can need\r
- out("import javafx.application.*;\n");\r
- out("import javafx.scene.*;\n");\r
- out("import javafx.scene.geometry.*;\n");\r
- out("import javafx.scene.transform.*;\n");\r
- out("import javafx.scene.paint.*;\n");\r
- out("\n");\r
-\r
- out("\n\n");\r
-\r
- // Creates a class extended from CustomNode\r
- out("public class %s extends CustomNode {\n", name.c_str());\r
-\r
- return true;\r
-}\r
-\r
-\r
-\r
-/**\r
- * Output the file footer\r
- */\r
-bool JavaFXOutput::doTail()\r
-{\r
- float border = 25.0;\r
-\r
- // Write the tail of CustomNode\r
- out(" ] // content\n");\r
- out(" transform: Translate { x : %s, y : %s }\n",\r
- DSTR((-minx) + border), DSTR((-miny) + border) );\r
- out(" } // Group\n");\r
- out(" } // function create()\n");\r
- out("} // class %s\n", name.c_str());\r
- out("\n");\r
-\r
- // Frame\r
- out("Frame {\n");\r
- out(" title: \"%s\"\n", name.c_str());\r
- out(" width: %s\n", ISTR(maxx-minx + border * 2.0));\r
- out(" height: %s\n", ISTR(maxy-miny + border * 2.0));\r
- out(" visible: true\n");\r
-\r
- // Stage\r
- out(" stage: Stage {\n");\r
- out(" content: %s{}\n", name.c_str());\r
- out(" } // Stage\n");\r
-\r
- out("} // Frame\n");\r
-\r
- out("\n");\r
-\r
- out("/*###################################################################\n");\r
- out("### E N D C L A S S %s\n", name.c_str());\r
- out("###################################################################*/\n");\r
- out("\n\n");\r
- return true;\r
-}\r
-\r
-\r
-\r
-/**\r
- * Output gradient information to the buffer\r
- */\r
-bool JavaFXOutput::doGradient(SPGradient *grad, const String &id)\r
-{\r
- String jfxid = sanatize(id);\r
-\r
- if (SP_IS_LINEARGRADIENT(grad))\r
- {\r
- SPLinearGradient *g = SP_LINEARGRADIENT(grad);\r
- out(" /* create LinearGradient for %s */\n", jfxid.c_str());\r
- out(" private function %s(): LinearGradient {\n", jfxid.c_str());\r
- out(" LinearGradient {\n");\r
- std::vector<SPGradientStop> stops = g->vector.stops;\r
- if (stops.size() > 0)\r
- {\r
- out(" stops:\n");\r
- out(" [\n");\r
- for (unsigned int i = 0 ; i<stops.size() ; i++)\r
- {\r
- SPGradientStop stop = stops[i];\r
- out(" Stop {\n");\r
- out(" offset: %s\n", DSTR(stop.offset));\r
- out(" color: %s\n", rgba(stop.color, stop.opacity).c_str());\r
- out(" },\n");\r
- }\r
- out(" ]\n");\r
- }\r
- out(" };\n");\r
- out(" } // end LinearGradient: %s\n", jfxid.c_str());\r
- out("\n\n");\r
- }\r
- else if (SP_IS_RADIALGRADIENT(grad))\r
- {\r
- SPRadialGradient *g = SP_RADIALGRADIENT(grad);\r
- out(" /* create RadialGradient for %s */\n", jfxid.c_str());\r
- out(" private function %s() {\n", jfxid.c_str());\r
- out(" RadialGradient {\n");\r
- out(" centerX: %s\n", DSTR(g->cx.value));\r
- out(" centerY: %s\n", DSTR(g->cy.value));\r
- out(" focusX: %s\n", DSTR(g->fx.value));\r
- out(" focusY: %s\n", DSTR(g->fy.value));\r
- out(" radius: %s\n", DSTR(g->r.value ));\r
- std::vector<SPGradientStop> stops = g->vector.stops;\r
- if (stops.size() > 0)\r
- {\r
- out(" stops:\n");\r
- out(" [\n");\r
- for (unsigned int i = 0 ; i<stops.size() ; i++)\r
- {\r
- SPGradientStop stop = stops[i];\r
- out(" Stop {\n");\r
- out(" offset: %s\n", DSTR(stop.offset));\r
- out(" color: %s\n", rgba(stop.color, stop.opacity).c_str());\r
- out(" },\n");\r
- }\r
- out(" ]\n");\r
- }\r
- out(" };\n");\r
- out(" } // end RadialGradient: %s\n", jfxid.c_str());\r
- out("\n\n");\r
- }\r
- else\r
- {\r
- err("Unknown gradient type for '%s'\n", jfxid.c_str());\r
- return false;\r
- }\r
-\r
-\r
- return true;\r
-}\r
-\r
-\r
-\r
-\r
-/**\r
- * Output an element's style attribute\r
- */\r
-bool JavaFXOutput::doStyle(SPStyle *style)\r
-{\r
- if (!style)\r
- return true;\r
-\r
- out(" opacity: %s\n", DSTR(effective_opacity(style)));\r
-\r
- /**\r
- * Fill\r
- */\r
- SPIPaint fill = style->fill;\r
- if (fill.isColor())\r
- {\r
- // see color.h for how to parse SPColor\r
- out(" fill: %s\n",\r
- rgba(fill.value.color, SP_SCALE24_TO_FLOAT(style->fill_opacity.value)).c_str());\r
- }\r
- else if (fill.isPaintserver()){\r
- if (fill.value.href && fill.value.href->getURI() ){\r
- String uri = fill.value.href->getURI()->toString();\r
- /* trim the anchor '#' from the front */\r
- if (uri.size() > 0 && uri[0]=='#')\r
- uri = uri.substr(1);\r
- out(" fill: %s()\n", sanatize(uri).c_str());\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Stroke\r
- */\r
- /**\r
- *NOTE: Things in style we can use:\r
- * SPIPaint stroke;\r
- * SPILength stroke_width;\r
- * SPIEnum stroke_linecap;\r
- * SPIEnum stroke_linejoin;\r
- * SPIFloat stroke_miterlimit;\r
- * NRVpathDash stroke_dash;\r
- * unsigned stroke_dasharray_set : 1;\r
- * unsigned stroke_dasharray_inherit : 1;\r
- * unsigned stroke_dashoffset_set : 1;\r
- * SPIScale24 stroke_opacity;\r
- */\r
- if (style->stroke_opacity.value > 0)\r
- {\r
- SPIPaint stroke = style->stroke;\r
- out(" stroke: %s\n",\r
- rgba(stroke.value.color, SP_SCALE24_TO_FLOAT(style->stroke_opacity.value)).c_str());\r
- double strokewidth = style->stroke_width.value;\r
- unsigned linecap = style->stroke_linecap.value;\r
- unsigned linejoin = style->stroke_linejoin.value;\r
- out(" strokeWidth: %s\n", DSTR(strokewidth));\r
- out(" strokeLineCap: %s\n", getStrokeLineCap(linecap).c_str());\r
- out(" strokeLineJoin: %s\n", getStrokeLineJoin(linejoin).c_str());\r
- out(" strokeMiterLimit: %s\n", DSTR(style->stroke_miterlimit.value));\r
- if(style->stroke_dasharray_set) {\r
- if(style->stroke_dashoffset_set) {\r
- out(" strokeDashOffset: %s\n", DSTR(style->stroke_dash.offset));\r
- }\r
- out(" strokeDashArray: [ ");\r
- for(int i = 0; i < style->stroke_dash.n_dash; i++ ) {\r
- if(i > 0) {\r
- out(", %.2lf", style->stroke_dash.dash[i]);\r
- }else {\r
- out(" %.2lf", style->stroke_dash.dash[i]);\r
- }\r
- }\r
- out(" ]\n");\r
- }\r
-\r
- }\r
-\r
- return true;\r
-}\r
-\r
-\r
-#if 1\r
-\r
-/**\r
- * Output the curve data to buffer\r
- */\r
-bool JavaFXOutput::doCurve(SPItem *item, const String &id)\r
-{\r
- using Geom::X;\r
- using Geom::Y;\r
-\r
- String jfxid = sanatize(id);\r
-\r
- //### Get the Shape\r
- if (!SP_IS_SHAPE(item))//Bulia's suggestion. Allow all shapes\r
- return true;\r
-\r
- SPShape *shape = SP_SHAPE(item);\r
- SPCurve *curve = shape->curve;\r
- if (curve->is_empty())\r
- return true;\r
-\r
- nrShapes++;\r
-\r
- out(" /** path %s */\n", jfxid.c_str());\r
- out(" private function %s() : Path {\n",jfxid.c_str());\r
- out(" Path {\n");\r
- out(" id: \"%s\"\n", jfxid.c_str());\r
-\r
- /**\r
- * Output the style information\r
- */\r
- if (!doStyle(SP_OBJECT_STYLE(shape)))\r
- return false;\r
-\r
- // convert the path to only lineto's and cubic curveto's:\r
- Geom::Scale yflip(1.0, -1.0);\r
- Geom::Matrix tf = sp_item_i2d_affine(item) * yflip;\r
- Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() * tf );\r
-\r
- //Count the NR_CURVETOs/LINETOs (including closing line segment)\r
- guint segmentCount = 0;\r
- for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {\r
- segmentCount += (*it).size();\r
- if (it->closed())\r
- segmentCount += 1;\r
- }\r
-\r
- out(" elements: [\n");\r
-\r
- unsigned int segmentNr = 0;\r
-\r
- nrNodes += segmentCount;\r
-\r
- Geom::Rect cminmax( pathv.front().initialPoint(), pathv.front().initialPoint() );\r
-\r
- /**\r
- * For all Subpaths in the <path>\r
- */\r
- for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit)\r
- {\r
- Geom::Point p = pit->front().initialPoint();\r
- cminmax.expandTo(p);\r
- out(" MoveTo {\n");\r
- out(" x: %s\n", DSTR(p[X]));\r
- out(" y: %s\n", DSTR(p[Y]));\r
- out(" },\n");\r
-\r
- /**\r
- * For all segments in the subpath\r
- */\r
- for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit)\r
- {\r
- //### LINE\r
- if( dynamic_cast<Geom::LineSegment const *> (&*cit) ||\r
- dynamic_cast<Geom::HLineSegment const *> (&*cit) ||\r
- dynamic_cast<Geom::VLineSegment const *> (&*cit) )\r
- {\r
- Geom::Point p = cit->finalPoint();\r
- out(" LineTo {\n");\r
- out(" x: %s\n", DSTR(p[X]));\r
- out(" y: %s\n", DSTR(p[Y]));\r
- out(" },\n");\r
- nrNodes++;\r
- }\r
- //### BEZIER\r
- else if(Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit))\r
- {\r
- std::vector<Geom::Point> points = cubic->points();\r
- Geom::Point p1 = points[1];\r
- Geom::Point p2 = points[2];\r
- Geom::Point p3 = points[3];\r
- out(" CurveTo {\n");\r
- out(" controlX1: %s\n", DSTR(p1[X]));\r
- out(" controlY1: %s\n", DSTR(p1[Y]));\r
- out(" controlX2: %s\n", DSTR(p2[X]));\r
- out(" controlY2: %s\n", DSTR(p2[Y]));\r
- out(" x: %s\n", DSTR(p3[X]));\r
- out(" y: %s\n", DSTR(p3[Y]));\r
- out(" },\n");\r
- nrNodes++;\r
- }\r
- else\r
- {\r
- g_error ("logical error, because pathv_to_linear_and_cubic_beziers was used");\r
- }\r
- segmentNr++;\r
- cminmax.expandTo(cit->finalPoint());\r
- }\r
- if (pit->closed())\r
- {\r
- out(" ClosePath {},\n");\r
- }\r
- }\r
-\r
- out(" ] // elements\n");\r
- out(" }; // Path\n");\r
- out(" } // end path %s\n\n", jfxid.c_str());\r
-\r
- double cminx = cminmax.min()[X];\r
- double cmaxx = cminmax.max()[X];\r
- double cminy = cminmax.min()[Y];\r
- double cmaxy = cminmax.max()[Y];\r
-\r
- if (cminx < minx)\r
- minx = cminx;\r
- if (cmaxx > maxx)\r
- maxx = cmaxx;\r
- if (cminy < miny)\r
- miny = cminy;\r
- if (cmaxy > maxy)\r
- maxy = cmaxy;\r
-\r
- return true;\r
-}\r
-\r
-\r
-\r
-#else\r
-\r
-/**\r
- * Output the curve data to buffer\r
- */\r
-bool JavaFXOutput::doCurve(SPItem *item, const String &id)\r
-{\r
- using Geom::X;\r
- using Geom::Y;\r
-\r
- //### Get the Shape\r
- if (!SP_IS_SHAPE(item))//Bulia's suggestion. Allow all shapes\r
- return true;\r
-\r
- SPShape *shape = SP_SHAPE(item);\r
- SPCurve *curve = shape->curve;\r
- if (curve->is_empty())\r
- return true;\r
-\r
- nrShapes++;\r
-\r
- out(" SVGPath \n");\r
- out(" {\n");\r
- out(" id: \"%s\"\n", id.c_str());\r
-\r
- /**\r
- * Output the style information\r
- */\r
- if (!doStyle(SP_OBJECT_STYLE(shape)))\r
- return false;\r
-\r
- // convert the path to only lineto's and cubic curveto's:\r
- Geom::Scale yflip(1.0, -1.0);\r
- Geom::Matrix tf = sp_item_i2d_affine(item) * yflip;\r
- Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() * tf );\r
- \r
- //Count the NR_CURVETOs/LINETOs (including closing line segment)\r
- nrNodes = 0;\r
- for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {\r
- nrNodes += (*it).size();\r
- if (it->closed())\r
- nrNodes += 1;\r
- }\r
-\r
- char *dataStr = sp_svg_write_path(pathv);\r
- out(" content: \"%s\"\n", dataStr);\r
- free(dataStr);\r
-\r
- Geom::Rect cminmax( pathv.front().initialPoint(), pathv.front().initialPoint() ); \r
-\r
- /**\r
- * Get the Min and Max X and Y extends for the Path. \r
- * ....For all Subpaths in the <path>\r
- */ \r
- for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit)\r
- {\r
- cminmax.expandTo(pit->front().initialPoint());\r
- /**\r
- * For all segments in the subpath\r
- */ \r
- for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit)\r
- {\r
- cminmax.expandTo(cit->finalPoint());\r
- }\r
- }\r
-\r
- out(" },\n");\r
-\r
- double cminx = cminmax.min()[X];\r
- double cmaxx = cminmax.max()[X];\r
- double cminy = cminmax.min()[Y];\r
- double cmaxy = cminmax.max()[Y];\r
-\r
- if (cminx < minx)\r
- minx = cminx;\r
- if (cmaxx > maxx)\r
- maxx = cmaxx;\r
- if (cminy < miny)\r
- miny = cminy;\r
- if (cmaxy > maxy)\r
- maxy = cmaxy;\r
-\r
- return true;\r
-}\r
-\r
-\r
-\r
-#endif /* #if o */\r
-\r
-\r
-\r
-/**\r
- * Output the tree data to buffer\r
- */\r
-bool JavaFXOutput::doTreeRecursive(SPDocument *doc, SPObject *obj)\r
-{\r
- /**\r
- * Check the type of node and process\r
- */\r
- String id;\r
- if (!obj->id)\r
- {\r
- char buf[16];\r
- sprintf(buf, "id%d", idindex++);\r
- id = buf;\r
- }\r
- else\r
- {\r
- id = obj->id;\r
- }\r
- if (SP_IS_ITEM(obj))\r
- {\r
- SPItem *item = SP_ITEM(obj);\r
- if (!doCurve(item, id))\r
- return false;\r
- }\r
- else if (SP_IS_GRADIENT(obj))\r
- {\r
- SPGradient *grad = SP_GRADIENT(obj);\r
- if (!doGradient(grad, id))\r
- return false;\r
- }\r
-\r
- /**\r
- * Descend into children\r
- */ \r
- for (SPObject *child = obj->firstChild() ; child ; child = child->next)\r
- {\r
- if (!doTreeRecursive(doc, child))\r
- return false;\r
- }\r
-\r
- return true;\r
-}\r
-\r
-\r
-/**\r
- * Output the curve data to buffer\r
- */\r
-bool JavaFXOutput::doTree(SPDocument *doc)\r
-{\r
-\r
- double bignum = 1000000.0;\r
- minx = bignum;\r
- maxx = -bignum;\r
- miny = bignum;\r
- maxy = -bignum;\r
-\r
- if (!doTreeRecursive(doc, doc->root))\r
- return false;\r
-\r
- return true;\r
-\r
-}\r
-\r
-\r
-bool JavaFXOutput::doBody(SPDocument *doc, SPObject *obj)\r
-{\r
- /**\r
- * Check the type of node and process\r
- */\r
- String id;\r
- if (!obj->id)\r
- {\r
- char buf[16];\r
- sprintf(buf, "id%d", idindex++);\r
- id = buf;\r
- }\r
- else\r
- {\r
- id = obj->id;\r
- }\r
-\r
- if (SP_IS_ITEM(obj)) {\r
- SPItem *item = SP_ITEM(obj);\r
- //### Get the Shape\r
- if (SP_IS_SHAPE(item)) {//Bulia's suggestion. Allow all shapes\r
- SPShape *shape = SP_SHAPE(item);\r
- SPCurve *curve = shape->curve;\r
- if (!curve->is_empty())\r
- out(" %s(),\n", id.c_str());\r
- }\r
- }\r
- else if (SP_IS_GRADIENT(obj)) {\r
- //TODO: what to do with Gradient within body?????\r
- //SPGradient *grad = SP_GRADIENT(reprobj);\r
- //if (!doGradient(grad, id))\r
- // return false;\r
- }\r
-\r
- /**\r
- * Descend into children\r
- */\r
- for (SPObject *child = obj->firstChild() ; child ; child = child->next)\r
- {\r
- if (!doBody(doc, child))\r
- return false;\r
- }\r
-\r
- return true;\r
-}\r
-\r
-\r
-\r
-//########################################################################\r
-//# M A I N O U T P U T\r
-//########################################################################\r
-\r
-\r
-\r
-/**\r
- * Set values back to initial state\r
- */\r
-void JavaFXOutput::reset()\r
-{\r
- nrNodes = 0;\r
- nrShapes = 0;\r
- idindex = 0;\r
- name.clear();\r
- outbuf.clear();\r
- foutbuf.clear();\r
-}\r
-\r
-\r
-\r
-/**\r
- * Saves the <paths> of an Inkscape SVG file as JavaFX spline definitions\r
- */\r
-bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *uri)\r
-{\r
- reset();\r
-\r
-\r
- name = Glib::path_get_basename(uri);\r
- int pos = name.find('.');\r
- if (pos > 0)\r
- name = name.substr(0, pos);\r
-\r
-\r
- //###### SAVE IN JAVAFX FORMAT TO BUFFER\r
- //# Lets do the curves first, to get the stats\r
-\r
- if (!doTree(doc))\r
- return false;\r
- String curveBuf = outbuf;\r
- outbuf.clear();\r
-\r
- if (!doHeader())\r
- return false;\r
-\r
- outbuf.append(curveBuf);\r
-\r
-#ifdef JAVAFX_SDK_1_0\r
- out(" override function create(): Node {\n");\r
-#else\r
- out(" public function create(): Node {\n");\r
-#endif\r
- out(" Group {\n");\r
- out(" content: [\n");\r
- idindex = 0;\r
-\r
- doBody(doc, doc->root);\r
-\r
- if (!doTail())\r
- return false;\r
-\r
-\r
-\r
- //###### WRITE TO FILE\r
- FILE *f = Inkscape::IO::fopen_utf8name(uri, "w");\r
- if (!f)\r
- {\r
- err("Could open JavaFX file '%s' for writing", uri);\r
- return false;\r
- }\r
-\r
- for (String::iterator iter = outbuf.begin() ; iter!=outbuf.end(); iter++)\r
- {\r
- fputc(*iter, f);\r
- }\r
- \r
- fclose(f);\r
- \r
- return true;\r
-}\r
-\r
-\r
-\r
-\r
-//########################################################################\r
-//# EXTENSION API\r
-//########################################################################\r
-\r
-\r
-\r
-#include "clear-n_.h"\r
-\r
-\r
-\r
-/**\r
- * API call to save document\r
-*/\r
-void\r
-JavaFXOutput::save(Inkscape::Extension::Output */*mod*/,\r
- SPDocument *doc, gchar const *uri)\r
-{\r
- if (!saveDocument(doc, uri))\r
- {\r
- g_warning("Could not save JavaFX file '%s'", uri);\r
- }\r
-}\r
-\r
-\r
-\r
-/**\r
- * Make sure that we are in the database\r
- */\r
-bool JavaFXOutput::check (Inkscape::Extension::Extension */*module*/)\r
-{\r
- /* We don't need a Key\r
- if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_JFX))\r
- return FALSE;\r
- */\r
-\r
- return true;\r
-}\r
-\r
-\r
-\r
-/**\r
- * This is the definition of JavaFX output. This function just\r
- * calls the extension system with the memory allocated XML that\r
- * describes the data.\r
-*/\r
-void\r
-JavaFXOutput::init()\r
-{\r
- Inkscape::Extension::build_from_mem(\r
- "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"\r
- "<name>" N_("JavaFX Output") "</name>\n"\r
- "<id>org.inkscape.output.jfx</id>\n"\r
- "<output>\n"\r
- "<extension>.fx</extension>\n"\r
- "<mimetype>text/x-javafx-script</mimetype>\n"\r
- "<filetypename>" N_("JavaFX (*.fx)") "</filetypename>\n"\r
- "<filetypetooltip>" N_("JavaFX Raytracer File") "</filetypetooltip>\n"\r
- "</output>\n"\r
- "</inkscape-extension>",\r
- new JavaFXOutput());\r
-}\r
-\r
-\r
-\r
-\r
-\r
-} // namespace Internal\r
-} // namespace Extension\r
-} // namespace Inkscape\r
-\r
-\r
-/*\r
- Local Variables:\r
- mode:c++\r
- c-file-style:"stroustrup"\r
- c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
- indent-tabs-mode:nil\r
- fill-column:99\r
- End:\r
-*/\r
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r
+/*
+ * A simple utility for exporting Inkscape svg Shapes as JavaFX paths.
+ *
+ * For information on the JavaFX file format, see:
+ * https://openjfx.dev.java.net/
+ *
+ * Authors:
+ * Bob Jamison <ishmal@inkscape.org>
+ * Silveira Neto <silveiraneto@gmail.com>
+ * Jim Clarke <Jim.Clarke@sun.com>
+ * Jon A. Cruz <jon@joncruz.org>
+ * Abhishek Sharma
+ *
+ * Copyright (C) 2008,2009 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "javafx-out.h"
+#include <inkscape.h>
+#include <inkscape-version.h>
+#include <sp-path.h>
+#include <sp-linear-gradient.h>
+#include <sp-radial-gradient.h>
+#include <style.h>
+#include <display/curve.h>
+#include <display/canvas-bpath.h>
+#include <svg/svg.h>
+#include <extension/system.h>
+#include <2geom/pathvector.h>
+#include <2geom/rect.h>
+#include <2geom/bezier-curve.h>
+#include <2geom/hvlinesegment.h>
+#include "helper/geom.h"
+#include "helper/geom-curves.h"
+#include <io/sys.h>
+
+
+#include <string>
+#include <stdio.h>
+#include <stdarg.h>
+
+
+namespace Inkscape
+{
+namespace Extension
+{
+namespace Internal
+{
+
+
+
+
+//########################################################################
+//# M E S S A G E S
+//########################################################################
+
+static void err(const char *fmt, ...)
+{
+ va_list args;
+ g_log(NULL, G_LOG_LEVEL_WARNING, "javafx-out err: ");
+ va_start(args, fmt);
+ g_logv(NULL, G_LOG_LEVEL_WARNING, fmt, args);
+ va_end(args);
+ g_log(NULL, G_LOG_LEVEL_WARNING, "\n");
+}
+
+
+//########################################################################
+//# U T I L I T Y
+//########################################################################
+
+/**
+ * Got this method from Bulia, and modified it a bit. It basically
+ * starts with this style, gets its SPObject parent, walks up the object
+ * tree and finds all of the opacities and multiplies them.
+ *
+ * We use this for our "flat" object output. If the code is modified
+ * to reflect a tree of <groups>, then this will be unneccessary.
+ */
+static double effective_opacity(const SPStyle *style)
+{
+ double val = 1.0;
+ for (SPObject const *obj = style->object; obj ; obj = obj->parent)
+ {
+ style = SP_OBJECT_STYLE(obj);
+ if (style) {
+ val *= SP_SCALE24_TO_FLOAT(style->opacity.value);
+ }
+ }
+ return val;
+}
+
+//########################################################################
+//# OUTPUT FORMATTING
+//########################################################################
+
+
+/**
+ * We want to control floating output format.
+ * Especially to avoid localization. (decimal ',' for example)
+ */
+static JavaFXOutput::String dstr(double d)
+{
+ char dbuf[G_ASCII_DTOSTR_BUF_SIZE+1];
+ g_ascii_formatd(dbuf, G_ASCII_DTOSTR_BUF_SIZE,
+ "%.8f", (gdouble)d);
+ JavaFXOutput::String s = dbuf;
+ return s;
+}
+
+#define DSTR(d) (dstr(d).c_str())
+
+
+/**
+ * Format a double as an integer
+ */
+static JavaFXOutput::String istr(double d)
+{
+ char dbuf[G_ASCII_DTOSTR_BUF_SIZE+1];
+ g_ascii_formatd(dbuf, G_ASCII_DTOSTR_BUF_SIZE,
+ "%.0f", (gdouble)d);
+ JavaFXOutput::String s = dbuf;
+ return s;
+}
+
+#define ISTR(d) (istr(d).c_str())
+
+
+/**
+ * Format an rgba() string
+ */
+static JavaFXOutput::String rgba(guint32 rgba)
+{
+ unsigned int r = SP_RGBA32_R_U(rgba);
+ unsigned int g = SP_RGBA32_G_U(rgba);
+ unsigned int b = SP_RGBA32_B_U(rgba);
+ unsigned int a = SP_RGBA32_A_U(rgba);
+ char buf[80];
+ snprintf(buf, 79, "Color.rgb(0x%02x, 0x%02x, 0x%02x, %s)",
+ r, g, b, DSTR((double)a/255.0));
+ JavaFXOutput::String s = buf;
+ return s;
+}
+
+
+/**
+ * Format an rgba() string for a color and a 0.0-1.0 alpha
+ */
+static JavaFXOutput::String rgba(SPColor color, gdouble alpha)
+{
+ return rgba(color.toRGBA32(alpha));
+}
+
+/**
+ * Map Inkscape linecap styles to JavaFX
+ */
+static JavaFXOutput::String getStrokeLineCap(unsigned value) {
+ switch(value) {
+ case SP_STROKE_LINECAP_BUTT:
+ return "StrokeLineCap.BUTT";
+ case SP_STROKE_LINECAP_ROUND:
+ return "StrokeLineCap.ROUND";
+ case SP_STROKE_LINECAP_SQUARE:
+ return "StrokeLineCap.SQUARE";
+ default:
+ return "INVALID LINE CAP";
+ }
+}
+
+
+/**
+ * Map Inkscape linejoin styles to JavaFX
+ */
+static JavaFXOutput::String getStrokeLineJoin(unsigned value) {
+ switch(value) {
+ case SP_STROKE_LINEJOIN_MITER:
+ return "StrokeLineJoin.MITER";
+ case SP_STROKE_LINEJOIN_ROUND:
+ return "StrokeLineJoin.ROUND";
+ case SP_STROKE_LINEJOIN_BEVEL:
+ return "StrokeLineJoin.BEVEL";
+ default:
+ return "INVALID LINE JOIN";
+ }
+}
+
+
+/**
+ * Replace illegal characters for JavaFX for a underscore.
+ */
+static JavaFXOutput::String sanatize(const JavaFXOutput::String &badstr){
+ JavaFXOutput::String good(badstr);
+ for (int pos = 0; pos < static_cast<int>(badstr.length()); ++pos )
+ if ((badstr.at(pos)=='-')||(badstr.at(pos)==' ')) {
+ good.replace(pos, 1, "_");
+ }
+ return good;
+}
+
+/**
+ * Output data to the buffer, printf()-style
+ */
+void JavaFXOutput::out(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ gchar *output = g_strdup_vprintf(fmt, args);
+ va_end(args);
+ outbuf.append(output);
+ g_free(output);
+}
+
+
+
+/**
+ * Output the file header
+ */
+bool JavaFXOutput::doHeader()
+{
+ time_t tim = time(NULL);
+ out("/*###################################################################\n");
+ out("### This JavaFX document was generated by Inkscape\n");
+ out("### http://www.inkscape.org\n");
+ out("### Created: %s", ctime(&tim));
+ out("### Version: %s\n", Inkscape::version_string);
+ out("#####################################################################\n");
+ out("### NOTES:\n");
+ out("### ============\n");
+ out("### JavaFX information can be found at\n");
+ out("### http://www.javafx.com/\n");
+ out("###\n");
+ out("### If you have any problems with this output, please see the\n");
+ out("### Inkscape project at http://www.inkscape.org, or visit\n");
+ out("### the #inkscape channel on irc.freenode.net . \n");
+ out("###\n");
+ out("###################################################################*/\n");
+ out("\n\n");
+ out("/*###################################################################\n");
+ out("## Exports in this file\n");
+ out("##==========================\n");
+ out("## Shapes : %d\n", nrShapes);
+ out("## Nodes : %d\n", nrNodes);
+ out("###################################################################*/\n");
+ out("\n\n");
+
+ // import javafx libraries we can need
+ out("import javafx.scene.*;\n");
+ out("import javafx.scene.shape.*;\n");
+ out("import javafx.scene.transform.*;\n");
+ out("import javafx.scene.paint.*;\n");
+ out("\n");
+
+ out("\n\n");
+
+ // Creates a class extended from CustomNode
+ out("public class %s extends CustomNode {\n", name.c_str());
+
+ return true;
+}
+
+
+
+/**
+ * Output the file footer
+ */
+bool JavaFXOutput::doTail()
+{
+ float border = 25.0;
+
+ // Write the tail of CustomNode
+ out(" ] // content\n");
+ out(" transforms: Translate { x : %s, y : %s }\n",
+ DSTR((-minx) + border), DSTR((-miny) + border) );
+ out(" } // Group\n");
+ out(" } // function create()\n");
+ out("} // class %s\n", name.c_str());
+ out("\n");
+
+ // Stage
+// out(" stage: Stage {\n");
+// out(" content: %s{}\n", name.c_str());
+// out(" } // Stage\n");
+
+
+ out("\n");
+
+ out("/*###################################################################\n");
+ out("### E N D C L A S S %s\n", name.c_str());
+ out("###################################################################*/\n");
+ out("\n\n");
+ return true;
+}
+
+
+
+/**
+ * Output gradient information to the buffer
+ */
+bool JavaFXOutput::doGradient(SPGradient *grad, const String &id)
+{
+ String jfxid = sanatize(id);
+
+ if (SP_IS_LINEARGRADIENT(grad))
+ {
+ SPLinearGradient *g = SP_LINEARGRADIENT(grad);
+ out(" /* create LinearGradient for %s */\n", jfxid.c_str());
+ out(" function %s(): LinearGradient {\n", jfxid.c_str());
+ out(" LinearGradient {\n");
+ std::vector<SPGradientStop> stops = g->vector.stops;
+ if (stops.size() > 0)
+ {
+ out(" stops:\n");
+ out(" [\n");
+ for (unsigned int i = 0 ; i<stops.size() ; i++)
+ {
+ SPGradientStop stop = stops[i];
+ out(" Stop {\n");
+ out(" offset: %s\n", DSTR(stop.offset));
+ out(" color: %s\n", rgba(stop.color, stop.opacity).c_str());
+ out(" },\n");
+ }
+ out(" ]\n");
+ }
+ out(" };\n");
+ out(" } // end LinearGradient: %s\n", jfxid.c_str());
+ out("\n\n");
+ }
+ else if (SP_IS_RADIALGRADIENT(grad))
+ {
+ SPRadialGradient *g = SP_RADIALGRADIENT(grad);
+ out(" /* create RadialGradient for %s */\n", jfxid.c_str());
+ out(" function %s() {\n", jfxid.c_str());
+ out(" RadialGradient {\n");
+ out(" centerX: %s\n", DSTR(g->cx.value));
+ out(" centerY: %s\n", DSTR(g->cy.value));
+ out(" focusX: %s\n", DSTR(g->fx.value));
+ out(" focusY: %s\n", DSTR(g->fy.value));
+ out(" radius: %s\n", DSTR(g->r.value ));
+ std::vector<SPGradientStop> stops = g->vector.stops;
+ if (stops.size() > 0)
+ {
+ out(" stops:\n");
+ out(" [\n");
+ for (unsigned int i = 0 ; i<stops.size() ; i++)
+ {
+ SPGradientStop stop = stops[i];
+ out(" Stop {\n");
+ out(" offset: %s\n", DSTR(stop.offset));
+ out(" color: %s\n", rgba(stop.color, stop.opacity).c_str());
+ out(" },\n");
+ }
+ out(" ]\n");
+ }
+ out(" };\n");
+ out(" } // end RadialGradient: %s\n", jfxid.c_str());
+ out("\n\n");
+ }
+ else
+ {
+ err("Unknown gradient type for '%s'\n", jfxid.c_str());
+ return false;
+ }
+
+
+ return true;
+}
+
+
+
+
+/**
+ * Output an element's style attribute
+ */
+bool JavaFXOutput::doStyle(SPStyle *style)
+{
+ if (!style) {
+ return true;
+ }
+
+ out(" opacity: %s\n", DSTR(effective_opacity(style)));
+
+ /**
+ * Fill
+ */
+ SPIPaint const &fill = style->fill;
+ if (fill.isColor())
+ {
+ // see color.h for how to parse SPColor
+ out(" fill: %s\n",
+ rgba(fill.value.color, SP_SCALE24_TO_FLOAT(style->fill_opacity.value)).c_str());
+ }
+ else if (fill.isPaintserver()){
+ if (fill.value.href && fill.value.href->getURI() ){
+ String uri = fill.value.href->getURI()->toString();
+ /* trim the anchor '#' from the front */
+ if (uri.size() > 0 && uri[0]=='#') {
+ uri = uri.substr(1);
+ }
+ out(" fill: %s()\n", sanatize(uri).c_str());
+ }
+ }
+
+
+ /**
+ * Stroke
+ */
+ /**
+ *NOTE: Things in style we can use:
+ * SPIPaint stroke;
+ * SPILength stroke_width;
+ * SPIEnum stroke_linecap;
+ * SPIEnum stroke_linejoin;
+ * SPIFloat stroke_miterlimit;
+ * NRVpathDash stroke_dash;
+ * unsigned stroke_dasharray_set : 1;
+ * unsigned stroke_dasharray_inherit : 1;
+ * unsigned stroke_dashoffset_set : 1;
+ * SPIScale24 stroke_opacity;
+ */
+ if (style->stroke_opacity.value > 0)
+ {
+ SPIPaint const &stroke = style->stroke;
+ out(" stroke: %s\n",
+ rgba(stroke.value.color, SP_SCALE24_TO_FLOAT(style->stroke_opacity.value)).c_str());
+ double strokewidth = style->stroke_width.value;
+ unsigned linecap = style->stroke_linecap.value;
+ unsigned linejoin = style->stroke_linejoin.value;
+ out(" strokeWidth: %s\n", DSTR(strokewidth));
+ out(" strokeLineCap: %s\n", getStrokeLineCap(linecap).c_str());
+ out(" strokeLineJoin: %s\n", getStrokeLineJoin(linejoin).c_str());
+ out(" strokeMiterLimit: %s\n", DSTR(style->stroke_miterlimit.value));
+ if (style->stroke_dasharray_set) {
+ if (style->stroke_dashoffset_set) {
+ out(" strokeDashOffset: %s\n", DSTR(style->stroke_dash.offset));
+ }
+ out(" strokeDashArray: [ ");
+ for(int i = 0; i < style->stroke_dash.n_dash; i++ ) {
+ if (i > 0) {
+ out(", %.2lf", style->stroke_dash.dash[i]);
+ }else {
+ out(" %.2lf", style->stroke_dash.dash[i]);
+ }
+ }
+ out(" ]\n");
+ }
+
+ }
+
+ return true;
+}
+
+
+#if 1
+
+/**
+ * Output the curve data to buffer
+ */
+bool JavaFXOutput::doCurve(SPItem *item, const String &id)
+{
+ using Geom::X;
+ using Geom::Y;
+
+ String jfxid = sanatize(id);
+
+ //### Get the Shape
+ if (!SP_IS_SHAPE(item)) { //Bulia's suggestion. Allow all shapes
+ return true;
+ }
+
+ SPShape *shape = SP_SHAPE(item);
+ SPCurve *curve = shape->curve;
+ if (curve->is_empty()) {
+ return true;
+ }
+
+ nrShapes++;
+
+ out(" /** path %s */\n", jfxid.c_str());
+ out(" function %s() : Path {\n",jfxid.c_str());
+ out(" Path {\n");
+ out(" id: \"%s\"\n", jfxid.c_str());
+
+ /**
+ * Output the style information
+ */
+ if (!doStyle(SP_OBJECT_STYLE(shape))) {
+ return false;
+ }
+
+ // convert the path to only lineto's and cubic curveto's:
+ Geom::Scale yflip(1.0, -1.0);
+ Geom::Matrix tf = item->i2d_affine() * yflip;
+ Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() * tf );
+
+ //Count the NR_CURVETOs/LINETOs (including closing line segment)
+ guint segmentCount = 0;
+ for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {
+ segmentCount += (*it).size();
+ if (it->closed()) {
+ segmentCount += 1;
+ }
+ }
+
+ out(" elements: [\n");
+
+ unsigned int segmentNr = 0;
+
+ nrNodes += segmentCount;
+
+ Geom::Rect cminmax( pathv.front().initialPoint(), pathv.front().initialPoint() );
+
+ /**
+ * For all Subpaths in the <path>
+ */
+ for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit)
+ {
+ Geom::Point p = pit->front().initialPoint();
+ cminmax.expandTo(p);
+ out(" MoveTo {\n");
+ out(" x: %s\n", DSTR(p[X]));
+ out(" y: %s\n", DSTR(p[Y]));
+ out(" },\n");
+
+ /**
+ * For all segments in the subpath
+ */
+ for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit)
+ {
+ //### LINE
+ if ( dynamic_cast<Geom::LineSegment const *> (&*cit) ||
+ dynamic_cast<Geom::HLineSegment const *> (&*cit) ||
+ dynamic_cast<Geom::VLineSegment const *> (&*cit) )
+ {
+ Geom::Point p = cit->finalPoint();
+ out(" LineTo {\n");
+ out(" x: %s\n", DSTR(p[X]));
+ out(" y: %s\n", DSTR(p[Y]));
+ out(" },\n");
+ nrNodes++;
+ }
+ //### BEZIER
+ else if (Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit))
+ {
+ std::vector<Geom::Point> points = cubic->points();
+ Geom::Point p1 = points[1];
+ Geom::Point p2 = points[2];
+ Geom::Point p3 = points[3];
+ out(" CubicCurveTo {\n");
+ out(" controlX1: %s\n", DSTR(p1[X]));
+ out(" controlY1: %s\n", DSTR(p1[Y]));
+ out(" controlX2: %s\n", DSTR(p2[X]));
+ out(" controlY2: %s\n", DSTR(p2[Y]));
+ out(" x: %s\n", DSTR(p3[X]));
+ out(" y: %s\n", DSTR(p3[Y]));
+ out(" },\n");
+ nrNodes++;
+ }
+ else
+ {
+ g_error ("logical error, because pathv_to_linear_and_cubic_beziers was used");
+ }
+ segmentNr++;
+ cminmax.expandTo(cit->finalPoint());
+ }
+ if (pit->closed())
+ {
+ out(" ClosePath {},\n");
+ }
+ }
+
+ out(" ] // elements\n");
+ out(" }; // Path\n");
+ out(" } // end path %s\n\n", jfxid.c_str());
+
+ double cminx = cminmax.min()[X];
+ double cmaxx = cminmax.max()[X];
+ double cminy = cminmax.min()[Y];
+ double cmaxy = cminmax.max()[Y];
+
+ if (cminx < minx) {
+ minx = cminx;
+ }
+ if (cmaxx > maxx) {
+ maxx = cmaxx;
+ }
+ if (cminy < miny) {
+ miny = cminy;
+ }
+ if (cmaxy > maxy) {
+ maxy = cmaxy;
+ }
+
+ return true;
+}
+
+
+
+#else
+
+/**
+ * Output the curve data to buffer
+ */
+bool JavaFXOutput::doCurve(SPItem *item, const String &id)
+{
+ using Geom::X;
+ using Geom::Y;
+
+ //### Get the Shape
+ if (!SP_IS_SHAPE(item)) { //Bulia's suggestion. Allow all shapes
+ return true;
+ }
+
+ SPShape *shape = SP_SHAPE(item);
+ SPCurve *curve = shape->curve;
+ if (curve->is_empty()) {
+ return true;
+ }
+
+ nrShapes++;
+
+ out(" SVGPath \n");
+ out(" {\n");
+ out(" id: \"%s\"\n", id.c_str());
+
+ /**
+ * Output the style information
+ */
+ if (!doStyle(SP_OBJECT_STYLE(shape))) {
+ return false;
+ }
+
+ // convert the path to only lineto's and cubic curveto's:
+ Geom::Scale yflip(1.0, -1.0);
+ Geom::Matrix tf = item->i2d_affine() * yflip;
+ Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() * tf );
+
+ //Count the NR_CURVETOs/LINETOs (including closing line segment)
+ nrNodes = 0;
+ for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {
+ nrNodes += (*it).size();
+ if (it->closed()) {
+ nrNodes += 1;
+ }
+ }
+
+ char *dataStr = sp_svg_write_path(pathv);
+ out(" content: \"%s\"\n", dataStr);
+ free(dataStr);
+
+ Geom::Rect cminmax( pathv.front().initialPoint(), pathv.front().initialPoint() );
+
+ /**
+ * Get the Min and Max X and Y extends for the Path.
+ * ....For all Subpaths in the <path>
+ */
+ for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit)
+ {
+ cminmax.expandTo(pit->front().initialPoint());
+ /**
+ * For all segments in the subpath
+ */
+ for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit)
+ {
+ cminmax.expandTo(cit->finalPoint());
+ }
+ }
+
+ out(" },\n");
+
+ double cminx = cminmax.min()[X];
+ double cmaxx = cminmax.max()[X];
+ double cminy = cminmax.min()[Y];
+ double cmaxy = cminmax.max()[Y];
+
+ if (cminx < minx) {
+ minx = cminx;
+ }
+ if (cmaxx > maxx) {
+ maxx = cmaxx;
+ }
+ if (cminy < miny) {
+ miny = cminy;
+ }
+ if (cmaxy > maxy) {
+ maxy = cmaxy;
+ }
+
+ return true;
+}
+
+
+
+#endif /* #if o */
+
+
+
+/**
+ * Output the tree data to buffer
+ */
+bool JavaFXOutput::doTreeRecursive(SPDocument *doc, SPObject *obj)
+{
+ /**
+ * Check the type of node and process
+ */
+ String id;
+ if (!obj->getId())
+ {
+ char buf[16];
+ sprintf(buf, "id%d", idindex++);
+ id = buf;
+ }
+ else
+ {
+ id = obj->getId();
+ }
+ if (SP_IS_ITEM(obj))
+ {
+ SPItem *item = SP_ITEM(obj);
+ if (!doCurve(item, id)) {
+ return false;
+ }
+ }
+ else if (SP_IS_GRADIENT(obj))
+ {
+ SPGradient *grad = SP_GRADIENT(obj);
+ if (!doGradient(grad, id)) {
+ return false;
+ }
+ }
+
+ /**
+ * Descend into children
+ */
+ for (SPObject *child = obj->firstChild() ; child ; child = child->next)
+ {
+ if (!doTreeRecursive(doc, child)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+/**
+ * Output the curve data to buffer
+ */
+bool JavaFXOutput::doTree(SPDocument *doc)
+{
+
+ double bignum = 1000000.0;
+ minx = bignum;
+ maxx = -bignum;
+ miny = bignum;
+ maxy = -bignum;
+
+ if (!doTreeRecursive(doc, doc->root)) {
+ return false;
+ }
+
+ return true;
+
+}
+
+
+bool JavaFXOutput::doBody(SPDocument *doc, SPObject *obj)
+{
+ /**
+ * Check the type of node and process
+ */
+ String id;
+ if (!obj->getId())
+ {
+ char buf[16];
+ sprintf(buf, "id%d", idindex++);
+ id = buf;
+ }
+ else
+ {
+ id = obj->getId();
+ }
+
+ if (SP_IS_ITEM(obj)) {
+ SPItem *item = SP_ITEM(obj);
+ //### Get the Shape
+ if (SP_IS_SHAPE(item)) {//Bulia's suggestion. Allow all shapes
+ SPShape *shape = SP_SHAPE(item);
+ SPCurve *curve = shape->curve;
+ if (!curve->is_empty()) {
+ String jfxid = sanatize(id);
+ out(" %s(),\n", jfxid.c_str());
+ }
+ }
+ }
+ else if (SP_IS_GRADIENT(obj)) {
+ //TODO: what to do with Gradient within body?????
+ //SPGradient *grad = SP_GRADIENT(reprobj);
+ //if (!doGradient(grad, id)) {
+ // return false;
+ //}
+ }
+
+ /**
+ * Descend into children
+ */
+ for (SPObject *child = obj->firstChild() ; child ; child = child->next)
+ {
+ if (!doBody(doc, child)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+//########################################################################
+//# M A I N O U T P U T
+//########################################################################
+
+
+
+/**
+ * Set values back to initial state
+ */
+void JavaFXOutput::reset()
+{
+ nrNodes = 0;
+ nrShapes = 0;
+ idindex = 0;
+ name.clear();
+ outbuf.clear();
+ foutbuf.clear();
+}
+
+
+
+/**
+ * Saves the <paths> of an Inkscape SVG file as JavaFX spline definitions
+ */
+bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *filename_utf8)
+{
+ reset();
+
+
+ name = Glib::path_get_basename(filename_utf8);
+ int pos = name.find('.');
+ if (pos > 0) {
+ name = name.substr(0, pos);
+ }
+
+
+ //###### SAVE IN JAVAFX FORMAT TO BUFFER
+ //# Lets do the curves first, to get the stats
+
+ if (!doTree(doc)) {
+ return false;
+ }
+ String curveBuf = outbuf;
+ outbuf.clear();
+
+ if (!doHeader()) {
+ return false;
+ }
+
+ outbuf.append(curveBuf);
+
+ out(" override function create(): Node {\n");
+ out(" Group {\n");
+ out(" content: [\n");
+ idindex = 0;
+
+ doBody(doc, doc->root);
+
+ if (!doTail()) {
+ return false;
+ }
+
+
+
+ //###### WRITE TO FILE
+ FILE *f = Inkscape::IO::fopen_utf8name(filename_utf8, "w");
+ if (!f)
+ {
+ err("Could open JavaFX file '%s' for writing", filename_utf8);
+ return false;
+ }
+
+ for (String::iterator iter = outbuf.begin() ; iter!=outbuf.end(); iter++)
+ {
+ fputc(*iter, f);
+ }
+
+ fclose(f);
+
+ return true;
+}
+
+
+
+
+//########################################################################
+//# EXTENSION API
+//########################################################################
+
+
+
+#include "clear-n_.h"
+
+
+
+/**
+ * API call to save document
+*/
+void
+JavaFXOutput::save(Inkscape::Extension::Output */*mod*/,
+ SPDocument *doc, gchar const *filename_utf8)
+{
+ /* N.B. The name `filename_utf8' represents the fact that we want it to be in utf8; whereas in
+ * fact we know that some callers of Extension::save pass something in the filesystem's
+ * encoding, while others do g_filename_to_utf8 before calling.
+ *
+ * In terms of safety, it's best to make all callers pass actual filenames, since in general
+ * one can't round-trip from filename to utf8 back to the same filename. Whereas the argument
+ * for passing utf8 filenames is one of convenience: we often want to pass to g_warning or
+ * store as a string (rather than a byte stream) in XML, or the like. */
+ if (!saveDocument(doc, filename_utf8))
+ {
+ g_warning("Could not save JavaFX file '%s'", filename_utf8);
+ }
+}
+
+
+
+/**
+ * Make sure that we are in the database
+ */
+bool JavaFXOutput::check (Inkscape::Extension::Extension */*module*/)
+{
+ /* We don't need a Key
+ if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_JFX)) {
+ return FALSE;
+ }
+ */
+
+ return true;
+}
+
+
+
+/**
+ * This is the definition of JavaFX output. This function just
+ * calls the extension system with the memory allocated XML that
+ * describes the data.
+*/
+void
+JavaFXOutput::init()
+{
+ Inkscape::Extension::build_from_mem(
+ "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
+ "<name>" N_("JavaFX Output") "</name>\n"
+ "<id>org.inkscape.output.jfx</id>\n"
+ "<output>\n"
+ "<extension>.fx</extension>\n"
+ "<mimetype>text/x-javafx-script</mimetype>\n"
+ "<filetypename>" N_("JavaFX (*.fx)") "</filetypename>\n"
+ "<filetypetooltip>" N_("JavaFX Raytracer File") "</filetypetooltip>\n"
+ "</output>\n"
+ "</inkscape-extension>",
+ new JavaFXOutput());
+}
+
+
+
+
+
+} // namespace Internal
+} // namespace Extension
+} // namespace Inkscape
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :