Code

un-remove applying item transform to each point
[inkscape.git] / src / extension / internal / javafx-out.cpp
1 /*\r
2  * A simple utility for exporting Inkscape svg Shapes as JavaFX paths.\r
3  *\r
4  *  For information on the JavaFX file format, see:\r
5  *      https://openjfx.dev.java.net/\r
6  *\r
7  * Authors:\r
8  *   Bob Jamison <ishmal@inkscape.org>\r
9  *\r
10  * Copyright (C) 2008 Authors\r
11  *\r
12  * Released under GNU GPL, read the file 'COPYING' for more information\r
13  */\r
14 \r
15 \r
16 #ifdef HAVE_CONFIG_H\r
17 # include <config.h>\r
18 #endif\r
19 #include "javafx-out.h"\r
20 #include <inkscape.h>\r
21 #include <inkscape_version.h>\r
22 #include <sp-path.h>\r
23 #include <style.h>\r
24 #include <display/curve.h>\r
25 #include <libnr/n-art-bpath.h>\r
26 #include <extension/system.h>\r
27 #include <2geom/pathvector.h>\r
28 #include <2geom/rect.h>\r
29 #include <2geom/bezier-curve.h>\r
30 #include <2geom/hvlinesegment.h>\r
31 #include "helper/geom.h"\r
32 #include <io/sys.h>\r
33 \r
34 #include <string>\r
35 #include <stdio.h>\r
36 #include <stdarg.h>\r
37 \r
38 \r
39 namespace Inkscape\r
40 {\r
41 namespace Extension\r
42 {\r
43 namespace Internal\r
44 {\r
45 \r
46 \r
47 \r
48 \r
49 //########################################################################\r
50 //# U T I L I T Y\r
51 //########################################################################\r
52 \r
53 \r
54 \r
55 /**\r
56  * This function searches the Repr tree recursively from the given node,\r
57  * and adds refs to all nodes with the given name, to the result vector\r
58  */\r
59 static void\r
60 findElementsByTagName(std::vector<Inkscape::XML::Node *> &results,\r
61                       Inkscape::XML::Node *node,\r
62                       char const *name)\r
63 {\r
64     if ( !name || strcmp(node->name(), name) == 0 )\r
65         results.push_back(node);\r
66 \r
67     for (Inkscape::XML::Node *child = node->firstChild() ; child ;\r
68               child = child->next())\r
69         findElementsByTagName( results, child, name );\r
70 \r
71 }\r
72 \r
73 \r
74 \r
75 //########################################################################\r
76 //# OUTPUT FORMATTING\r
77 //########################################################################\r
78 \r
79 \r
80 /**\r
81  * We want to control floating output format\r
82  */\r
83 static JavaFXOutput::String dstr(double d)\r
84 {\r
85     char dbuf[G_ASCII_DTOSTR_BUF_SIZE+1];\r
86     g_ascii_formatd(dbuf, G_ASCII_DTOSTR_BUF_SIZE,\r
87                   "%.8f", (gdouble)d);\r
88     JavaFXOutput::String s = dbuf;\r
89     return s;\r
90 }\r
91 \r
92 \r
93 \r
94 \r
95 /**\r
96  *  Output data to the buffer, printf()-style\r
97  */\r
98 void JavaFXOutput::out(const char *fmt, ...)\r
99 {\r
100     va_list args;\r
101     va_start(args, fmt);\r
102     gchar *output = g_strdup_vprintf(fmt, args);\r
103     va_end(args);\r
104     outbuf.append(output);\r
105     g_free(output);\r
106 }\r
107 \r
108 \r
109 /**\r
110  * Output the file header\r
111  */\r
112 bool JavaFXOutput::doHeader(const String &name)\r
113 {\r
114     time_t tim = time(NULL);\r
115     out("/*###################################################################\n");\r
116     out("### This JavaFX document was generated by Inkscape\n");\r
117     out("### http://www.inkscape.org\n");\r
118     out("### Created: %s", ctime(&tim));\r
119     out("### Version: %s\n", INKSCAPE_VERSION);\r
120     out("#####################################################################\n");\r
121     out("### NOTES:\n");\r
122     out("### ============\n");\r
123     out("### JavaFX information can be found at\n");\r
124     out("### hhttps://openjfx.dev.java.net\n");\r
125     out("###\n");\r
126     out("### If you have any problems with this output, please see the\n");\r
127     out("### Inkscape project at http://www.inkscape.org, or visit\n");\r
128     out("### the #inkscape channel on irc.freenode.net . \n");\r
129     out("###\n");\r
130     out("###################################################################*/\n");\r
131     out("\n\n");\r
132     out("/*###################################################################\n");\r
133     out("##   Exports in this file\n");\r
134     out("##==========================\n");\r
135     out("##    Shapes   : %d\n", nrShapes);\r
136     out("##    Segments : %d\n", nrSegments);\r
137     out("##    Nodes    : %d\n", nrNodes);\r
138     out("###################################################################*/\n");\r
139     out("\n\n");\r
140     out("import javafx.ui.UIElement;\n");\r
141     out("import javafx.ui.*;\n");\r
142     out("import javafx.ui.canvas.*;\n");\r
143     out("\n");\r
144     out("import java.lang.System;\n");\r
145     out("\n\n");\r
146     out("public class %s extends CompositeNode {\n", name.c_str());\r
147     out("}\n");\r
148     out("\n\n");\r
149     return true;\r
150 }\r
151 \r
152 \r
153 \r
154 /**\r
155  *  Output the file footer\r
156  */\r
157 bool JavaFXOutput::doTail(const String &name)\r
158 {\r
159     out("\n\n\n\n");\r
160     out("Frame {\n");\r
161     out("    title: \"Test\"\n");\r
162     out("    width: 500\n");\r
163     out("    height: 500\n");\r
164     out("    onClose: function()\n");\r
165     out("        {\n");\r
166     out("        return System.exit( 0 );\n");\r
167     out("        }\n");\r
168     out("    visible: true\n");\r
169     out("    content: Canvas {\n");\r
170     out("        content: tux{}\n");\r
171     out("    }\n");\r
172     out("}\n");\r
173     out("/*###################################################################\n");\r
174     out("### E N D   C L A S S    %s\n", name.c_str());\r
175     out("###################################################################*/\n");\r
176     out("\n\n");\r
177     return true;\r
178 }\r
179 \r
180 \r
181 \r
182 /**\r
183  *  Output the curve data to buffer\r
184  */\r
185 bool JavaFXOutput::doCurves(SPDocument *doc, const String &name)\r
186 {\r
187     using Geom::X;\r
188     using Geom::Y;\r
189     \r
190 \r
191     std::vector<Inkscape::XML::Node *>results;\r
192     findElementsByTagName(results, doc->rroot, NULL);\r
193     //findElementsByTagName(results, SP_ACTIVE_DOCUMENT->rroot, NULL);\r
194     if (results.size() == 0)\r
195         return true;\r
196 \r
197     out("function %s.composeNode() =\n", name.c_str());\r
198     out("Group\n");\r
199     out("    {\n");\r
200     out("    content:\n");\r
201     out("        [\n");\r
202 \r
203     for (unsigned int indx = 0; indx < results.size() ; indx++)\r
204         {\r
205         //### Fetch the object from the repr info\r
206         Inkscape::XML::Node *rpath = results[indx];\r
207         char *str  = (char *) rpath->attribute("id");\r
208         if (!str)\r
209             continue;\r
210 \r
211         String id = str;\r
212         SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(rpath);\r
213         if (!reprobj)\r
214             continue;\r
215 \r
216         //### Get the transform of the item\r
217         if (!SP_IS_ITEM(reprobj))\r
218             continue;\r
219 \r
220         SPItem *item = SP_ITEM(reprobj);\r
221         Geom::Matrix tf = sp_item_i2d_affine(item);\r
222 \r
223         //### Get the Shape\r
224         if (!SP_IS_SHAPE(reprobj))//Bulia's suggestion.  Allow all shapes\r
225             continue;\r
226 \r
227         SPShape *shape = SP_SHAPE(reprobj);\r
228         SPCurve *curve = shape->curve;\r
229         if (curve->is_empty())\r
230             continue;\r
231             \r
232         nrShapes++;\r
233 \r
234         out("        /*###################################################\n");\r
235         out("        ### PATH:  %s\n", id.c_str());\r
236         out("        ###################################################*/\n");\r
237         out("        Path \n");\r
238         out("        {\n");\r
239         out("        id: \"%s\"\n", id.c_str());\r
240 \r
241         /**\r
242          * Get the fill and stroke of the shape\r
243          */\r
244         SPStyle *style = SP_OBJECT_STYLE(shape);\r
245         if (style)\r
246             {\r
247             /**\r
248              * Fill\r
249              */\r
250             if (style->fill.isColor())\r
251                 {\r
252                 // see color.h for how to parse SPColor\r
253                 gint alpha = 0xffffffff;\r
254                 guint32 rgba = style->fill.value.color.toRGBA32(alpha);\r
255                 unsigned int r = SP_RGBA32_R_U(rgba);\r
256                 unsigned int g = SP_RGBA32_G_U(rgba);\r
257                 unsigned int b = SP_RGBA32_B_U(rgba);\r
258                 unsigned int a = SP_RGBA32_A_U(rgba);\r
259                 out("        fill: rgba(0x%02x, 0x%02x, 0x%02x, 0x%02x)\n",\r
260                                    r, g, b, a);\r
261                 }\r
262             /**\r
263              * Stroke\r
264              */\r
265             /**\r
266              * TODO:  stroke code here\r
267              */\r
268             }\r
269 \r
270 \r
271         // convert the path to only lineto's and cubic curveto's:\r
272         Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers( curve->get_pathvector() * tf );\r
273 \r
274         //Count the NR_CURVETOs/LINETOs (including closing line segment)\r
275         guint segmentCount = 0;\r
276         for(Geom::PathVector::const_iterator it = pathv.begin(); it != pathv.end(); ++it) {\r
277             segmentCount += (*it).size();\r
278             if (it->closed())\r
279                 segmentCount += 1;\r
280         }\r
281 \r
282         out("        d:\n");\r
283         out("            [\n");\r
284 \r
285         unsigned int segmentNr = 0;\r
286 \r
287         nrSegments += segmentCount;\r
288 \r
289         for (Geom::PathVector::const_iterator pit = pathv.begin(); pit != pathv.end(); ++pit)\r
290             {\r
291             Geom::Point p = pit->front().initialPoint() * tf;\r
292             out("            MoveTo {\n");\r
293             out("                x: %s\n", dstr(p[X]).c_str());\r
294             out("                y: %s\n", dstr(p[Y]).c_str());\r
295             out("                absolute: true\n");\r
296             out("                },\n");\r
297             for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit)\r
298                 {\r
299                 //### LINE\r
300                 if( dynamic_cast<Geom::LineSegment const *> (&*cit) ||\r
301                     dynamic_cast<Geom::HLineSegment const *>(&*cit) ||\r
302                     dynamic_cast<Geom::VLineSegment const *>(&*cit) )\r
303                     {\r
304                     Geom::Point p = cit->initialPoint() * tf;\r
305                     out("            LineTo {\n");\r
306                     out("                x: %s\n", dstr(p[X]).c_str());\r
307                     out("                y: %s\n", dstr(p[Y]).c_str());\r
308                     out("                absolute: true\n");\r
309                     out("                },\n");\r
310                     nrNodes++;\r
311                     }\r
312                 //### BEZIER\r
313                 else if(Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit))\r
314                     {\r
315                     std::vector<Geom::Point> points = cubic->points();\r
316                     Geom::Point p0 = points[0] * tf;\r
317                     Geom::Point p1 = points[1] * tf;\r
318                     Geom::Point p2 = points[2] * tf;\r
319                     out("            CurveTo {\n");\r
320                     out("                x1: %s\n", dstr(p0[X]).c_str());\r
321                     out("                y1: %s\n", dstr(p0[Y]).c_str());\r
322                     out("                x2: %s\n", dstr(p1[X]).c_str());\r
323                     out("                y2: %s\n", dstr(p1[Y]).c_str());\r
324                     out("                x3: %s\n", dstr(p2[X]).c_str());\r
325                     out("                y3: %s\n", dstr(p2[Y]).c_str());\r
326                     out("                smooth: false\n");\r
327                     out("                absolute: true\n");\r
328                     out("                },\n");\r
329                     nrNodes++;\r
330                     }\r
331                 else\r
332                     {\r
333                     g_error ("logical error, because pathv_to_linear_and_cubic_beziers was used");\r
334                     }\r
335                 }\r
336             if (pit->closed())\r
337                 {\r
338                 out("            ClosePath {},\n");\r
339                 }\r
340             }\r
341 \r
342         out("            ] // d\n");\r
343         out("        }, // Path\n");\r
344 \r
345                      \r
346         out("        /*###################################################\n");\r
347         out("        ### end path %s\n", id.c_str());\r
348         out("        ###################################################*/\n\n\n\n");\r
349 \r
350         }//for\r
351 \r
352     out("        ] // content\n");\r
353     out("    }; // Group\n");\r
354     out("// end function %s.composeNode()\n", name.c_str());\r
355 \r
356     return true;\r
357 \r
358 }\r
359 \r
360 \r
361 \r
362 \r
363 //########################################################################\r
364 //# M A I N    O U T P U T\r
365 //########################################################################\r
366 \r
367 \r
368 \r
369 /**\r
370  *  Set values back to initial state\r
371  */\r
372 void JavaFXOutput::reset()\r
373 {\r
374     nrNodes    = 0;\r
375     nrSegments = 0;\r
376     nrShapes   = 0;\r
377     outbuf.clear();\r
378 }\r
379 \r
380 \r
381 \r
382 /**\r
383  * Saves the <paths> of an Inkscape SVG file as JavaFX spline definitions\r
384  */\r
385 bool JavaFXOutput::saveDocument(SPDocument *doc, gchar const *uri)\r
386 {\r
387     reset();\r
388 \r
389 \r
390     String name = Glib::path_get_basename(uri);\r
391     int pos = name.find('.');\r
392     if (pos > 0)\r
393         name = name.substr(0, pos);\r
394 \r
395 \r
396     //###### SAVE IN POV FORMAT TO BUFFER\r
397     //# Lets do the curves first, to get the stats\r
398     \r
399     if (!doCurves(doc, name))\r
400         return false;\r
401     String curveBuf = outbuf;\r
402     outbuf.clear();\r
403 \r
404     if (!doHeader(name))\r
405         return false;\r
406     \r
407     outbuf.append(curveBuf);\r
408     \r
409     if (!doTail(name))\r
410         return false;\r
411 \r
412 \r
413 \r
414 \r
415     //###### WRITE TO FILE\r
416     FILE *f = Inkscape::IO::fopen_utf8name(uri, "w");\r
417     if (!f)\r
418         {\r
419         g_warning("Could open JavaFX file '%s' for writing", uri);\r
420         return false;\r
421         }\r
422 \r
423     for (String::iterator iter = outbuf.begin() ; iter!=outbuf.end(); iter++)\r
424         {\r
425         fputc(*iter, f);\r
426         }\r
427         \r
428     fclose(f);\r
429     \r
430     return true;\r
431 }\r
432 \r
433 \r
434 \r
435 \r
436 //########################################################################\r
437 //# EXTENSION API\r
438 //########################################################################\r
439 \r
440 \r
441 \r
442 #include "clear-n_.h"\r
443 \r
444 \r
445 \r
446 /**\r
447  * API call to save document\r
448 */\r
449 void\r
450 JavaFXOutput::save(Inkscape::Extension::Output */*mod*/,\r
451                         SPDocument *doc, gchar const *uri)\r
452 {\r
453     if (!saveDocument(doc, uri))\r
454         {\r
455         g_warning("Could not save JavaFX file '%s'", uri);\r
456         }\r
457 }\r
458 \r
459 \r
460 \r
461 /**\r
462  * Make sure that we are in the database\r
463  */\r
464 bool JavaFXOutput::check (Inkscape::Extension::Extension */*module*/)\r
465 {\r
466     /* We don't need a Key\r
467     if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_JFX))\r
468         return FALSE;\r
469     */\r
470 \r
471     return true;\r
472 }\r
473 \r
474 \r
475 \r
476 /**\r
477  * This is the definition of JavaFX output.  This function just\r
478  * calls the extension system with the memory allocated XML that\r
479  * describes the data.\r
480 */\r
481 void\r
482 JavaFXOutput::init()\r
483 {\r
484     Inkscape::Extension::build_from_mem(\r
485         "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"\r
486             "<name>" N_("JavaFX Output") "</name>\n"\r
487             "<id>org.inkscape.output.jfx</id>\n"\r
488             "<output>\n"\r
489                 "<extension>.fx</extension>\n"\r
490                 "<mimetype>text/x-javafx-script</mimetype>\n"\r
491                 "<filetypename>" N_("JavaFX (*.fx)") "</filetypename>\n"\r
492                 "<filetypetooltip>" N_("JavaFX Raytracer File") "</filetypetooltip>\n"\r
493             "</output>\n"\r
494         "</inkscape-extension>",\r
495         new JavaFXOutput());\r
496 }\r
497 \r
498 \r
499 \r
500 \r
501 \r
502 }  // namespace Internal\r
503 }  // namespace Extension\r
504 }  // namespace Inkscape\r
505 \r
506 \r
507 /*\r
508   Local Variables:\r
509   mode:c++\r
510   c-file-style:"stroustrup"\r
511   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))\r
512   indent-tabs-mode:nil\r
513   fill-column:99\r
514   End:\r
515 */\r
516 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :\r