--- inkscape_svn/inkscape/src/extension/internal/javafx-out.cpp	2008-11-13 03:11:38.000000000 -0300
+++ inkscape/src/extension/internal/javafx-out.cpp	2008-11-13 03:19:45.000000000 -0300
@@ -1,966 +1,1928 @@
-/*
- * 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>
- *
- * Copyright (C) 2008 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/256.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 < 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);
-    out("#####################################################################\n");
-    out("### NOTES:\n");
-    out("### ============\n");
-    out("### JavaFX information can be found at\n");
-    out("### hhttps://openjfx.dev.java.net\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.application.*;\n");
-    out("import javafx.scene.*;\n");
-    out("import javafx.scene.geometry.*;\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("           transform: 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");
-
-    // Frame
-    out("Frame {\n");
-    out("    title: \"%s\"\n", name.c_str());
-    out("    width: %s\n",  ISTR(maxx-minx + border * 2.0));
-    out("    height: %s\n", ISTR(maxy-miny + border * 2.0));
-    out("    visible: true\n");
-
-    // Stage
-    out("    stage: Stage {\n");
-    out("        content: %s{}\n", name.c_str());
-    out("    } // Stage\n");
-
-    out("} // Frame\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("    private 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("    private 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 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 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("    private 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 = sp_item_i2d_affine(item) * 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("                CurveTo {\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 = sp_item_i2d_affine(item) * 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->id)
-        {
-        char buf[16];
-        sprintf(buf, "id%d", idindex++);
-        id = buf;
-        }
-    else
-        {
-        id = obj->id;
-        }
-    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->id)
-        {
-        char buf[16];
-        sprintf(buf, "id%d", idindex++);
-        id = buf;
-        }
-    else
-        {
-        id = obj->id;
-        }
-
-    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())
-                out("               %s(),\n", id.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 *uri)
-{
-    reset();
-
-
-    name = Glib::path_get_basename(uri);
-    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);
-
-#ifdef JAVAFX_SDK_1_0
-    out("   override function create(): Node {\n");
-#else
-    out("   public function create(): Node {\n");
-#endif
-    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(uri, "w");
-    if (!f)
-        {
-        err("Could open JavaFX file '%s' for writing", uri);
-        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 *uri)
-{
-    if (!saveDocument(doc, uri))
-        {
-        g_warning("Could not save JavaFX file '%s'", uri);
-        }
-}
-
-
-
-/**
- * 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:encoding=utf-8:textwidth=99 :
+/*
+ * 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>
+ *
+ * Copyright (C) 2008 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/256.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 (unsigned int pos = 0; pos < badstr.length(); ++pos )
+        if((badstr.at(pos)=='-')||(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);
+    out("#####################################################################\n");
+    out("### NOTES:\n");
+    out("### ============\n");
+    out("### JavaFX information can be found at\n");
+    out("### hhttps://openjfx.dev.java.net\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.application.*;\n");
+    out("import javafx.scene.*;\n");
+    out("import javafx.scene.geometry.*;\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("           transform: 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");
+
+    // Frame
+    out("Frame {\n");
+    out("    title: \"%s\"\n", name.c_str());
+    out("    width: %s\n",  ISTR(maxx-minx + border * 2.0));
+    out("    height: %s\n", ISTR(maxy-miny + border * 2.0));
+    out("    visible: true\n");
+
+    // Stage
+    out("    stage: Stage {\n");
+    out("        content: %s{}\n", name.c_str());
+    out("    } // Stage\n");
+
+    out("} // Frame\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("    public attribute %s: LinearGradient = LinearGradient{\n",  jfxid.c_str());
+        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("    } // 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("    public attribute %s:RadialGradient = RadialGradient {\n", jfxid.c_str());
+        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("    } // 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 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 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("    public attribute %s:Path = Path {\n",jfxid.c_str());
+    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 = sp_item_i2d_affine(item) * 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("                CurveTo {\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("    } // 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 = sp_item_i2d_affine(item) * 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->id)
+        {
+        char buf[16];
+        sprintf(buf, "id%d", idindex++);
+        id = buf;
+        }
+    else
+        {
+        id = obj->id;
+        }
+    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) {
+    /* If id was not defined, define one using idindex, otherwise, use it but
+     take care of sanatizing it first */
+    String id;
+    if (!obj->id){
+        char buf[16];
+        sprintf(buf, "id%d", idindex++);
+        id = buf;
+    }
+    else{
+        id = sanatize(obj->id);
+    }
+    
+    /**
+     * Check the type of node and process
+     */
+
+    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())
+                out("               %s,\n", id.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 *uri)
+{
+    reset();
+
+
+    name = Glib::path_get_basename(uri);
+    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);
+
+#ifdef JAVAFX_SDK_1_0
+    out("   override function create(): Node {\n");
+#else
+    out("   public function create(): Node {\n");
+#endif
+    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(uri, "w");
+    if (!f)
+        {
+        err("Could open JavaFX file '%s' for writing", uri);
+        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 *uri)
+{
+    if (!saveDocument(doc, uri))
+        {
+        g_warning("Could not save JavaFX file '%s'", uri);
+        }
+}
+
+
+
+/**
+ * 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:encoding=utf-8:textwidth=99 :
+=======
+/*
+ * 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>
+ *
+ * Copyright (C) 2008 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/256.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 < 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);
+    out("#####################################################################\n");
+    out("### NOTES:\n");
+    out("### ============\n");
+    out("### JavaFX information can be found at\n");
+    out("### hhttps://openjfx.dev.java.net\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.application.*;\n");
+    out("import javafx.scene.*;\n");
+    out("import javafx.scene.geometry.*;\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("           transform: 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");
+
+    // Frame
+    out("Frame {\n");
+    out("    title: \"%s\"\n", name.c_str());
+    out("    width: %s\n",  ISTR(maxx-minx + border * 2.0));
+    out("    height: %s\n", ISTR(maxy-miny + border * 2.0));
+    out("    visible: true\n");
+
+    // Stage
+    out("    stage: Stage {\n");
+    out("        content: %s{}\n", name.c_str());
+    out("    } // Stage\n");
+
+    out("} // Frame\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("    private 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("    private 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 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 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("    private 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 = sp_item_i2d_affine(item) * 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("                CurveTo {\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 = sp_item_i2d_affine(item) * 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->id)
+        {
+        char buf[16];
+        sprintf(buf, "id%d", idindex++);
+        id = buf;
+        }
+    else
+        {
+        id = obj->id;
+        }
+    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->id)
+        {
+        char buf[16];
+        sprintf(buf, "id%d", idindex++);
+        id = buf;
+        }
+    else
+        {
+        id = obj->id;
+        }
+
+    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())
+                out("               %s(),\n", id.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 *uri)
+{
+    reset();
+
+
+    name = Glib::path_get_basename(uri);
+    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);
+
+#ifdef JAVAFX_SDK_1_0
+    out("   override function create(): Node {\n");
+#else
+    out("   public function create(): Node {\n");
+#endif
+    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(uri, "w");
+    if (!f)
+        {
+        err("Could open JavaFX file '%s' for writing", uri);
+        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 *uri)
+{
+    if (!saveDocument(doc, uri))
+        {
+        g_warning("Could not save JavaFX file '%s'", uri);
+        }
+}
+
+
+
+/**
+ * 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:encoding=utf-8:textwidth=99 :
