summaryrefslogtreecommitdiff
path: root/app/bin/svgoutput.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/bin/svgoutput.c')
-rw-r--r--app/bin/svgoutput.c490
1 files changed, 490 insertions, 0 deletions
diff --git a/app/bin/svgoutput.c b/app/bin/svgoutput.c
new file mode 100644
index 0000000..a8224be
--- /dev/null
+++ b/app/bin/svgoutput.c
@@ -0,0 +1,490 @@
+/** \file svgoutput.c
+ * Exporting SVG files
+*/
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2020 Martin Fischer
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#ifdef WINDOWS
+ #include <io.h>
+ #define UTFCONVERT
+#else
+ #include <errno.h>
+#endif
+
+#include <xtrkcad-config.h>
+#include <locale.h>
+#include <assert.h>
+#include <mxml.h>
+#include <dynstring.h>
+
+#include "cselect.h"
+#include "custom.h"
+#include "draw.h"
+#include "include/svgformat.h"
+#include "fileio.h"
+#include "i18n.h"
+#include "layout.h"
+#include "messages.h"
+#include "paths.h"
+#include "track.h"
+#include "include/utf8convert.h"
+#include "utility.h"
+#include "wlib.h"
+
+static struct wFilSel_t * exportSVGFile_fs;
+static coOrd roomSize;
+
+/**
+ * get line style for element
+ *
+ * \param d drawCmd_p to process.
+ *
+ * \returns the line style
+ */
+
+static unsigned
+SvgDrawGetLineStyle(drawCmd_p d)
+{
+ unsigned long notSolid = DC_NOTSOLIDLINE;
+ unsigned long opt = d->options & notSolid;
+ unsigned lineOpt;
+
+ switch (opt) {
+ case DC_DASH:
+ lineOpt = wDrawLineDash;
+ break;
+ case DC_DOT:
+ lineOpt = wDrawLineDot;
+ break;
+ case DC_DASHDOT:
+ lineOpt = wDrawLineDashDot;
+ break;
+ case DC_DASHDOTDOT:
+ lineOpt = wDrawLineDashDotDot;
+ break;
+ case DC_CENTER:
+ lineOpt = wDrawLineCenter;
+ break;
+ case DC_PHANTOM:
+ lineOpt = wDrawLinePhantom;
+ break;
+ default:
+ lineOpt = wDrawLineSolid;
+ break;
+ }
+
+ return (lineOpt);
+}
+
+/**
+ * Svg draw line
+ *
+ * \param d A drawCmd_p to process.
+ * \param p0 The p 0.
+ * \param p1 The first coOrd.
+ * \param width The width.
+ * \param color The color.
+ */
+
+static void SvgDrawLine(
+ drawCmd_p d,
+ coOrd p0,
+ coOrd p1,
+ wDrawWidth width,
+ wDrawColor color)
+{
+ unsigned lineOpt = SvgDrawGetLineStyle(d);
+
+ width = (wDrawWidth)(width > MININMUMLINEWIDTH ? width : MININMUMLINEWIDTH);
+
+ SvgLineCommand((SVGParent *)(d->d),
+ p0.x, roomSize.y - p0.y,
+ p1.x, roomSize.y - p1.y,
+ (double)width,
+ wDrawGetRGB(color),
+ lineOpt);
+}
+
+/**
+ * Svg draw arc
+ *
+ * \param d A drawCmd_p to process.
+ * \param p A coOrd to process.
+ * \param r A DIST_T to process.
+ * \param angle0 The angle 0.
+ * \param angle1 The first angle.
+ * \param drawCenter The draw center.
+ * \param width The width.
+ * \param color The color.
+ */
+
+static void SvgDrawArc(
+ drawCmd_p d,
+ coOrd p,
+ DIST_T r,
+ ANGLE_T angle0,
+ ANGLE_T angle1,
+ BOOL_T drawCenter,
+ wDrawWidth width,
+ wDrawColor color)
+{
+ unsigned lineOpt = SvgDrawGetLineStyle(d);
+
+ if (angle1 >= 360.0) {
+ SvgCircleCommand((SVGParent *)(d->d),
+ p.x,
+ roomSize.y - p.y,
+ r,
+ (width > MININMUMLINEWIDTH ? width : MININMUMLINEWIDTH),
+ wDrawGetRGB(color),
+ false,
+ lineOpt);
+ } else {
+ SvgArcCommand((SVGParent *)(d->d),
+ p.x,
+ roomSize.y-p.y,
+ r,
+ angle0,
+ angle1,
+ drawCenter,
+ (width > MININMUMLINEWIDTH ? width: MININMUMLINEWIDTH),
+ wDrawGetRGB(color),
+ lineOpt);
+ }
+
+}
+
+/**
+ * Svg draw string. Perform conversion to UTF-8 if required.
+ *
+ * \param d A drawCmd_p to process.
+ * \param p position of text
+ * \param a text angle
+ * \param [in,out] s the string
+ * \param fp font definition (ignored)
+ * \param fontSize Size of the font.
+ * \param color color.
+ */
+
+static void SvgDrawString(
+ drawCmd_p d,
+ coOrd p,
+ ANGLE_T a,
+ char * s,
+ wFont_p fp,
+ FONTSIZE_T fontSize,
+ wDrawColor color)
+{
+ char *text = MyStrdup(s);
+
+#ifdef UTFCONVERT
+ text = Convert2UTF8(text);
+#endif // UTFCONVERT
+
+ SvgTextCommand((SVGParent *)(d->d),
+ p.x,
+ roomSize.y - p.y,
+ fontSize,
+ wDrawGetRGB(color),
+ text);
+
+ MyFree(text);
+}
+
+/**
+ * Svg draw bitmap
+ *
+ * \param d A drawCmd_p to process.
+ * \param p A coOrd to process.
+ * \param bm The bm.
+ * \param color The color.
+ */
+
+static void SvgDrawBitmap(
+ drawCmd_p d,
+ coOrd p,
+ wDrawBitMap_p bm,
+ wDrawColor color)
+{
+}
+
+/**
+ * Svg draw fill polygon
+ *
+ * \param d A drawCmd_p to process.
+ * \param cnt Number of points in polyline.
+ * \param [in,out] pts the coordinates
+ * \param [in,out] pointer If non-null, the pointer.
+ * \param color color.
+ * \param width line width.
+ * \param fillStyle fill style.
+ */
+
+static void SvgDrawFillPoly(
+ drawCmd_p d,
+ int cnt,
+ coOrd * pts,
+ int * pointer,
+ wDrawColor color, wDrawWidth width, drawFill_e fillStyle)
+{
+ int i;
+ double *points = malloc((cnt + 1) * 2 * sizeof(double));
+
+ unsigned lineOpt = SvgDrawGetLineStyle(d);
+
+ if (!points) {
+ puts("memory for poly line coordinates could not be allocated!");
+ abort();
+ }
+ for (i = 0; i < cnt; i++) {
+ points[i * 2] = pts[i].x;
+ points[i * 2 + 1] = roomSize.y - pts[i].y;
+ }
+
+ if (fillStyle == DRAW_CLOSED || fillStyle == DRAW_FILL) {
+ points[i * 2] = points[0];
+ points[i * 2 + 1] = points[1];
+ cnt++;
+ }
+
+ width = (wDrawWidth)(width > MININMUMLINEWIDTH ? width : MININMUMLINEWIDTH);
+ SvgPolyLineCommand((SVGParent *)(d->d), cnt, points, wDrawGetRGB(color),
+ (double)width, fillStyle == DRAW_FILL, lineOpt);
+
+ free(points);
+}
+
+/**
+ * Svg draw filled circle
+ *
+ * \param d A drawCmd_p to process.
+ * \param center The center.
+ * \param radius The radius.
+ * \param color The fill color.
+ */
+
+static void SvgDrawFillCircle(drawCmd_p d, coOrd center, DIST_T radius,
+ wDrawColor color)
+{
+ SvgCircleCommand((SVGParent *)(d->d),
+ center.x,
+ roomSize.y - center.y,
+ radius,
+ 0,
+ wDrawGetRGB(color),
+ true,
+ 0);
+}
+
+/**
+ * Svg draw rectangle
+ *
+ * \param d A drawCmd_p to process.
+ * \param corner1 The first corner.
+ * \param corner2 The second corner.
+ * \param color The color.
+ * \param pattern Specifies the pattern.
+ */
+
+static void
+SvgDrawRectangle(drawCmd_p d, coOrd corner1, coOrd corner2, wDrawColor color,
+ drawFill_e fillOpt)
+{
+ SvgRectCommand((SVGParent *)(d->d),
+ corner1.x, roomSize.y - corner1.y,
+ corner2.x, roomSize.y - corner2.y,
+ wDrawGetRGB(color),
+ fillOpt);
+}
+
+static drawFuncs_t svgDrawFuncs = {
+ SvgDrawLine,
+ SvgDrawArc,
+ SvgDrawString,
+ SvgDrawBitmap,
+ SvgDrawFillPoly,
+ SvgDrawFillCircle,
+ SvgDrawRectangle
+};
+
+static drawCmd_t svgD = {
+ NULL, &svgDrawFuncs, 0, 1.0, 0.0, {0.0,0.0}, {0.0,0.0}, Pix2CoOrd, CoOrd2Pix, 100.0
+};
+
+/**
+ * Creates valid identifier from a string. Whitespaces are removed
+ * and characters are prepended to make sure the i starts with
+ * valid chars.
+ * https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/id
+ *
+ * \param [in,out] base the base for the id.
+ *
+ * \returns Null if it fails, else the new valid identifier.
+ */
+
+static char *
+CreateValidId(char *base)
+{
+ const char *idHead = "id";
+ char *out = MyMalloc(strlen(idHead) + strlen(base) + 1);
+ char *tmp;
+ int j;
+
+ strcpy(out, idHead);
+ j = strlen(out);
+
+ for (unsigned int i = 0; i < strlen(base); i++) {
+ if (isblank(base[i])) {
+ i++;
+ } else {
+ out[j++] = base[i];
+ }
+ }
+
+ out[j] = '\0';
+
+ // strip off the extension
+ tmp = strchr(out, '.');
+ if (tmp) {
+ *tmp = '\0';
+ }
+ return (out);
+}
+
+/**
+ * get a valid identifier for SVG export
+ *
+ * \returns Null if it fails, else a pointer to a char.
+ */
+
+static char * SvgGetId(void)
+{
+ char *fileName = GetLayoutFilename();
+ char *id = NULL;
+
+ if (fileName) {
+ id = CreateValidId(fileName);
+#ifdef UTFCONVERT
+ id = Convert2UTF8(id);
+#endif
+ }
+
+ return (id);
+}
+
+/**
+ * Set title for SVG file
+ * The first title line of the design is used and stored in the SVG file
+ *
+ * \param d A drawCmd_p to process.
+ */
+
+static void SvgSetTitle(drawCmd_p d)
+{
+ char *tmp = GetLayoutTitle();
+ char *title;
+
+ if (tmp) {
+ title = MyStrdup(tmp);
+#ifdef UTFCONVERT
+ title = Convert2UTF8(title);
+#endif
+ SvgAddTitle((SVGParent *)(d->d), title);
+ MyFree(title);
+ }
+
+}
+
+/**
+ * Executes the export tracks to SVG operation
+ *
+ * \param cnt Number of filenames, has to be 1
+ * \param [in] fileName filename of the export file.
+ * \param [in] data If non-null, the data.
+ *
+ * \returns TRUE on success, FALSE on failure
+ */
+
+static int DoExportSVGTracks(
+ int cnt,
+ char ** fileName,
+ void * data)
+{
+ DynString command = NaS;
+ SVGDocument *svg;
+ SVGParent *svgData;
+ char *id;
+
+ assert(fileName != NULL);
+ assert(cnt == 1);
+
+ SetCLocale();
+ GetLayoutRoomSize(&roomSize);
+
+ SetCurrentPath(SVGPATHKEY, fileName[ 0 ]);
+
+ svg = SvgCreateDocument();
+ id = SvgGetId();
+ svgData = SvgPrologue(svg, id, 0, 0.0, 0.0, roomSize.x, roomSize.y);
+ MyFree(id);
+
+ wSetCursor(mainD.d, wCursorWait);
+// time(&clock);
+
+ svgD.d = (wDraw_p)svgData;
+
+ DrawSelectedTracks(&svgD);
+ SvgAddCSSStyle((SVGParent *)svgD.d);
+ SvgSetTitle(&svgD); // make sure this is the last element
+
+ if (!SvgSaveFile(svg, fileName[0])) {
+ NoticeMessage(MSG_OPEN_FAIL, _("Cancel"), NULL, "SVG", fileName[0],
+ strerror(errno));
+
+ SvgDestroyDocument(svg);
+ wSetCursor(mainD.d, wCursorNormal);
+ SetUserLocale();
+ return FALSE;
+ }
+ SvgDestroyDocument(svg);
+ Reset(); /**<TODO: was tut das? */
+ wSetCursor(mainD.d, wCursorNormal);
+ SetUserLocale();
+ return TRUE;
+}
+
+/**
+ * Create and show the dialog for selecting the DXF export filename
+ */
+
+void DoExportSVG(void)
+{
+ assert(selectedTrackCount > 0);
+
+ if (exportSVGFile_fs == NULL)
+ exportSVGFile_fs = wFilSelCreate(mainW, FS_SAVE, 0, _("Export to SVG"),
+ sSVGFilePattern, DoExportSVGTracks, NULL);
+
+ wFilSelect(exportSVGFile_fs, GetCurrentPath(SVGPATHKEY));
+}
+
+