javafx-out.cpp revision 954579e135439cbf04fba913c9751fce95d4db61
/*
* 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>
*
* 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 <style.h>
#include <display/curve.h>
#include <libnr/n-art-bpath.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 <io/sys.h>
#include <string>
#include <stdio.h>
#include <stdarg.h>
namespace Inkscape
{
namespace Extension
{
namespace Internal
{
//########################################################################
//# U T I L I T Y
//########################################################################
/**
* This function searches the Repr tree recursively from the given node,
* and adds refs to all nodes with the given name, to the result vector
*/
static void
findElementsByTagName(std::vector<Inkscape::XML::Node *> &results,
Inkscape::XML::Node *node,
char const *name)
{
if ( !name || strcmp(node->name(), name) == 0 )
results.push_back(node);
for (Inkscape::XML::Node *child = node->firstChild() ; child ;
child = child->next())
findElementsByTagName( results, child, name );
}
//########################################################################
//# OUTPUT FORMATTING
//########################################################################
/**
* We want to control floating output format
*/
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;
}
/**
* 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(gchar const *uri)
{
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("## Segments : %d\n", nrSegments);
out("## Nodes : %d\n", nrNodes);
out("###################################################################*/\n");
out("\n\n");
out("import javafx.ui.UIElement;\n");
out("import javafx.ui.*;\n");
out("import javafx.ui.canvas.*;\n");
out("\n");
out("public class %s extends CompositeNode {\n", uri);
out("\n");
out("\n\n\n");
return true;
}
/**
* Output the file footer
*/
bool JavaFXOutput::doTail(gchar const *uri)
{
out("} // end class $s\n", uri);
out("\n\n");
out("/*###################################################################\n");
out("### E N D F I L E\n");
out("###################################################################*/\n");
out("\n\n");
return true;
}
/**
* Output the curve data to buffer
*/
bool JavaFXOutput::doCurves(SPDocument *doc, gchar const *uri)
{
using Geom::X;
using Geom::Y;
std::vector<Inkscape::XML::Node *>results;
findElementsByTagName(results, doc->rroot, NULL);
//findElementsByTagName(results, SP_ACTIVE_DOCUMENT->rroot, NULL);
if (results.size() == 0)
return true;
out("function composeNode()\n");
out("{\n");
out("Group\n");
out(" {\n");
out(" content:\n");
out(" [\n");
for (unsigned int indx = 0; indx < results.size() ; indx++)
{
//### Fetch the object from the repr info
Inkscape::XML::Node *rpath = results[indx];
char *str = (char *) rpath->attribute("id");
if (!str)
continue;
String id = str;
SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(rpath);
if (!reprobj)
continue;
//### Get the transform of the item
if (!SP_IS_ITEM(reprobj))
continue;
SPItem *item = SP_ITEM(reprobj);
Geom::Matrix tf = sp_item_i2d_affine(item);
//### Get the Shape
if (!SP_IS_SHAPE(reprobj))//Bulia's suggestion. Allow all shapes
continue;
SPShape *shape = SP_SHAPE(reprobj);
SPCurve *curve = shape->curve;
if (curve->is_empty())
continue;
nrShapes++;
out(" /*###################################################\n");
out(" ### PATH: %s\n", id.c_str());
out(" ###################################################*/\n");
out(" Path \n");
out(" {\n");
out(" name : \"%s\"\n", id.c_str());
//Try to get the fill color of the shape
SPStyle *style = SP_OBJECT_STYLE(shape);
/* fixme: Handle other fill types, even if this means translating gradients to a single
flat colour. */
if (style && (style->fill.isColor()))
{
// see color.h for how to parse SPColor
gint alpha = 0xffffffff;
guint32 rgba = style->fill.value.color.toRGBA32(alpha);
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);
out(" fill: rgba(0x%02x, 0x%02x, 0x%02x, 0x%02x)\n",
r, g, b, a);
}
// convert the path to only lineto's and cubic curveto's:
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(" d :\n");
out(" [\n");
int segmentNr = 0;
nrSegments += segmentCount;
for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit) {
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) )
{
out(" LineTo {\n");
out(" x: %s\n", dstr(cit->initialPoint()[X]).c_str());
out(" y: %s\n", dstr(cit->initialPoint()[Y]).c_str());
out(" absolute: true\n");
out(" }");
nrNodes++;
}
//### BEZIER
else if(Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
std::vector<Geom::Point> points = cubic->points();
out(" CurveTo {\n");
out(" x1: %s\n", dstr(points[0][X]).c_str());
out(" y1: %s\n", dstr(points[0][Y]).c_str());
out(" x2: %s\n", dstr(points[1][X]).c_str());
out(" y2: %s\n", dstr(points[1][Y]).c_str());
out(" x3: %s\n", dstr(points[2][X]).c_str());
out(" y3: %s\n", dstr(points[3][Y]).c_str());
out(" smooth: false\n");
out(" absolute: true\n");
out(" }");
nrNodes++;
}
else {
g_error ("logical error, because pathv_to_linear_and_cubic_beziers was used");
}
if (segmentNr <= segmentCount)
out(",\n");
else
out("\n");
}
}
out(" ] // d\n");
out(" } // Path\n");
out(" /*###################################################\n");
out(" ### end path %s\n", id.c_str());
out(" ###################################################*/\n\n\n\n");
}//for
out(" ] // content\n");
out(" } // Group\n");
out("} // function composeNode()\n");
return true;
}
//########################################################################
//# M A I N O U T P U T
//########################################################################
/**
* Set values back to initial state
*/
void JavaFXOutput::reset()
{
nrNodes = 0;
nrSegments = 0;
nrShapes = 0;
outbuf.clear();
}
/**
* Saves the <paths> of an Inkscape SVG file as JavaFX spline definitions
*/
bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *uri)
{
reset();
//###### SAVE IN POV FORMAT TO BUFFER
//# Lets do the curves first, to get the stats
if (!doCurves(doc, uri))
return false;
String curveBuf = outbuf;
outbuf.clear();
if (!doHeader(uri))
return false;
outbuf.append(curveBuf);
if (!doTail(uri))
return false;
//###### WRITE TO FILE
FILE *f = Inkscape::IO::fopen_utf8name(uri, "w");
if (!f)
{
g_warning("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 :