summaryrefslogtreecommitdiff
path: root/deps/libmscgen/mscgen_svg_out.c
diff options
context:
space:
mode:
Diffstat (limited to 'deps/libmscgen/mscgen_svg_out.c')
-rw-r--r--deps/libmscgen/mscgen_svg_out.c585
1 files changed, 585 insertions, 0 deletions
diff --git a/deps/libmscgen/mscgen_svg_out.c b/deps/libmscgen/mscgen_svg_out.c
new file mode 100644
index 0000000..73378be
--- /dev/null
+++ b/deps/libmscgen/mscgen_svg_out.c
@@ -0,0 +1,585 @@
+/***************************************************************************
+ *
+ * $Id: svg_out.c 186 2011-03-01 21:18:19Z Michael.McTernan $
+ *
+ * This file is part of mscgen, a message sequence chart renderer.
+ * Copyright (C) 2005 Michael C McTernan, Michael.McTernan.2001@cs.bris.ac.uk
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ **************************************************************************/
+
+#include "mscgen_config.h"
+#include <math.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include "mscgen_adraw_int.h"
+#include "mscgen_safe.h"
+#include "mscgen_utf8.h"
+
+/***************************************************************************
+ * Local types
+ ***************************************************************************/
+
+typedef struct SvgContextTag
+{
+ /** Output file. */
+ FILE *of;
+
+ /** Current pen colour name. */
+ const char *penColName;
+
+ /** Current background pen colour name. */
+ const char *penBgColName;
+
+ int fontPoints;
+}
+SvgContext;
+
+typedef struct
+{
+ int capheight, xheight, ascender, descender;
+ int widths[256];
+}
+SvgCharMetric;
+
+/** Helvetica character widths.
+ * This gives the width of each character is 1/1000ths of a point.
+ * The values are taken from the Adobe Font Metric file for Hevletica.
+ */
+static const SvgCharMetric SvgHelvetica =
+{
+ 718, 523, 718, -207,
+ {
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ 278, 278, 355, 556, 556, 889, 667, 222,
+ 333, 333, 389, 584, 278, 333, 278, 278,
+ 556, 556, 556, 556, 556, 556, 556, 556,
+ 556, 556, 278, 278, 584, 584, 584, 556,
+ 1015, 667, 667, 722, 722, 667, 611, 778,
+ 722, 278, 500, 667, 556, 833, 722, 778,
+ 667, 778, 722, 667, 611, 722, 667, 944,
+ 667, 667, 611, 278, 278, 278, 469, 556,
+ 222, 556, 556, 500, 556, 556, 278, 556,
+ 556, 222, 222, 500, 222, 833, 556, 556,
+ 556, 556, 333, 500, 278, 556, 500, 722,
+ 500, 500, 500, 334, 260, 334, 584, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 333, 556, 556, 167, 556, 556, 556,
+ 556, 191, 333, 556, 333, 333, 500, 500,
+ -1, 556, 556, 556, 278, -1, 537, 350,
+ 222, 333, 333, 556, 1000, 1000, -1, 611,
+ -1, 333, 333, 333, 333, 333, 333, 333,
+ 333, -1, 333, 333, -1, 333, 333, 333,
+ 1000, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 1000, -1, 370, -1, -1, -1, -1,
+ 556, 778, 1000, 365, -1, -1, -1, -1,
+ -1, 889, -1, -1, -1, 278, -1, -1,
+ 222, 611, 944, 611, -1, -1, -1, -1
+ }
+};
+
+/***************************************************************************
+ * Helper functions
+ ***************************************************************************/
+
+
+/** Get the context pointer from an ADraw structure.
+ */
+static SvgContext *getSvgCtx(struct ADrawTag *ctx)
+{
+ return (SvgContext *)ctx->internal;
+}
+
+/** Get the context pointer from an ADraw structure.
+ */
+static FILE *getSvgFile(struct ADrawTag *ctx)
+{
+ return getSvgCtx(ctx)->of;
+}
+
+static const char *getSvgPen(struct ADrawTag *ctx)
+{
+ return getSvgCtx(ctx)->penColName;
+}
+
+static const char *getSvgBgPen(struct ADrawTag *ctx)
+{
+ return getSvgCtx(ctx)->penBgColName;
+}
+
+/** Given a font metric measurement, return device dependent units.
+ * Font metric data is stored as 1/1000th of a point, and therefore
+ * needs to be multiplied by the font point size and divided by
+ * 1000 to give a value in device dependent units.
+ */
+static int getSpace(struct ADrawTag *ctx, long thousanths)
+{
+ return ((thousanths * getSvgCtx(ctx)->fontPoints) + 500) / 1000;
+}
+
+
+/** Compute a point on an ellipse.
+ * This computes the point on an ellipse.
+ *
+ * \param[in] cx,cy Center of the ellipse.
+ * \param[in] w,h Ellipse width and height.
+ * \param[in] a Angle in degrees.
+ * \param[in,out] x,y Pointer to be populated with result co-ordinates.
+ */
+static void arcPoint(float cx,
+ float cy,
+ float w,
+ float h,
+ float a,
+ unsigned int *x,
+ unsigned int *y)
+{
+ float rad = (float)((a * M_PI) / 180.0f);
+
+ /* Compute point, noting this is for SVG co-ordinate system */
+ *x = (unsigned int)round(cx + ((w / 2.0f) * cos(rad)));
+ *y = (unsigned int)round(cy + ((h / 2.0f) * sin(rad)));
+}
+
+
+/** Write out a line of text, escaping special characters.
+ */
+static void writeEscaped(struct ADrawTag *ctx, const char *string)
+{
+ FILE *f = getSvgFile(ctx);
+
+ while(*string != '\0')
+ {
+ unsigned int code, bytes;
+
+ switch(*string)
+ {
+ case '<': fprintf(f, "&lt;"); break;
+ case '>': fprintf(f, "&gt;"); break;
+ case '"': fprintf(f, "&quot;"); break;
+ case '&': fprintf(f, "&amp;"); break;
+ default:
+ if(Utf8Decode(string, &code, &bytes))
+ {
+ fprintf(f, "&#x%x;", code);
+ string += bytes - 1;
+ }
+ else
+ {
+ fputc(*string, f);
+ }
+ break;
+
+ }
+
+ string++;
+ }
+}
+
+
+
+static void svgRect(struct ADrawTag *ctx,
+ const char *colour,
+ unsigned int x1,
+ unsigned int y1,
+ unsigned int x2,
+ unsigned int y2)
+{
+ fprintf(getSvgFile(ctx),
+ "<polygon fill=\"%s\" points=\"%u,%u %u,%u %u,%u %u,%u\"/>\n",
+ colour,
+ x1, y1,
+ x2, y1,
+ x2, y2,
+ x1, y2);
+}
+
+static const char *svgColour(ADrawColour col)
+{
+ switch(col)
+ {
+ case ADRAW_COL_WHITE:
+ return "white";
+
+ case ADRAW_COL_BLACK:
+ return "black";
+
+ case ADRAW_COL_BLUE:
+ return "blue";
+
+ case ADRAW_COL_RED:
+ return "red";
+
+ case ADRAW_COL_GREEN:
+ return "green";
+
+ default:
+ return NULL;
+ }
+}
+
+
+/***************************************************************************
+ * API Functions
+ ***************************************************************************/
+
+unsigned int SvgTextWidth(struct ADrawTag *ctx,
+ const char *string)
+{
+ unsigned long width = 0;
+
+ while(*string != '\0')
+ {
+ int i = *string & 0xff;
+ unsigned long w = SvgHelvetica.widths[i];
+
+ /* Ignore undefined characters */
+ width += w > 0 ? w : 0;
+
+ string++;
+ }
+
+ return getSpace(ctx, width);
+}
+
+
+int SvgTextHeight(struct ADrawTag *ctx)
+{
+ return getSpace(ctx, SvgHelvetica.ascender - SvgHelvetica.descender);
+}
+
+
+void SvgLine(struct ADrawTag *ctx,
+ unsigned int x1,
+ unsigned int y1,
+ unsigned int x2,
+ unsigned int y2)
+{
+ fprintf(getSvgFile(ctx),
+ "<line x1=\"%u\" y1=\"%u\" x2=\"%u\" y2=\"%u\" stroke=\"%s\"/>\n",
+ x1, y1, x2, y2, getSvgPen(ctx));
+
+}
+
+
+void SvgDottedLine(struct ADrawTag *ctx,
+ unsigned int x1,
+ unsigned int y1,
+ unsigned int x2,
+ unsigned int y2)
+{
+ fprintf(getSvgFile(ctx),
+ "<line x1=\"%u\" y1=\"%u\" x2=\"%u\" y2=\"%u\" stroke=\"%s\" stroke-dasharray=\"2,2\"/>\n",
+ x1, y1, x2, y2, getSvgPen(ctx));
+}
+
+
+void SvgTextR(struct ADrawTag *ctx,
+ unsigned int x,
+ unsigned int y,
+ const char *string)
+{
+ SvgContext *context = getSvgCtx(ctx);
+
+ svgRect(ctx, getSvgBgPen(ctx), x - 2, y - SvgTextHeight(ctx) + 1, x + SvgTextWidth(ctx, string), y - 1);
+
+ y += getSpace(ctx, SvgHelvetica.descender);
+
+ fprintf(getSvgFile(ctx),
+ "<text x=\"%u\" y=\"%u\" textLength=\"%u\" font-family=\"Helvetica\" font-size=\"%u\" fill=\"%s\">\n",
+ x - 1, y, SvgTextWidth(ctx, string), context->fontPoints, context->penColName);
+ writeEscaped(ctx, string);
+ fprintf(getSvgFile(ctx), "\n</text>\n");
+}
+
+
+void SvgTextL(struct ADrawTag *ctx,
+ unsigned int x,
+ unsigned int y,
+ const char *string)
+{
+ SvgContext *context = getSvgCtx(ctx);
+
+ svgRect(ctx, getSvgBgPen(ctx), x - (SvgTextWidth(ctx, string) + 2), y - SvgTextHeight(ctx) + 1, x, y - 1);
+
+ y += getSpace(ctx, SvgHelvetica.descender);
+
+ fprintf(getSvgFile(ctx),
+ "<text x=\"%u\" y=\"%u\" textLength=\"%u\" font-family=\"Helvetica\" font-size=\"%u\" fill=\"%s\" text-anchor=\"end\">\n",
+ x, y, SvgTextWidth(ctx, string), context->fontPoints, context->penColName);
+ writeEscaped(ctx, string);
+ fprintf(getSvgFile(ctx), "\n</text>\n");
+
+
+}
+
+
+void SvgTextC(struct ADrawTag *ctx,
+ unsigned int x,
+ unsigned int y,
+ const char *string)
+{
+ SvgContext *context = getSvgCtx(ctx);
+ unsigned int hw = SvgTextWidth(ctx, string) / 2;
+
+ svgRect(ctx, getSvgBgPen(ctx), x - (hw + 2), y - SvgTextHeight(ctx) + 1, x + hw, y - 1);
+
+ y += getSpace(ctx, SvgHelvetica.descender);
+
+ fprintf(getSvgFile(ctx),
+ "<text x=\"%u\" y=\"%u\" textLength=\"%u\" font-family=\"Helvetica\" font-size=\"%u\" fill=\"%s\" text-anchor=\"middle\">\n\n",
+ x, y, SvgTextWidth(ctx, string), context->fontPoints, context->penColName);
+ writeEscaped(ctx, string);
+ fprintf(getSvgFile(ctx), "\n</text>\n");
+}
+
+
+void SvgFilledTriangle(struct ADrawTag *ctx,
+ unsigned int x1,
+ unsigned int y1,
+ unsigned int x2,
+ unsigned int y2,
+ unsigned int x3,
+ unsigned int y3)
+{
+
+ fprintf(getSvgFile(ctx),
+ "<polygon fill=\"%s\" points=\"%u,%u %u,%u %u,%u\"/>\n",
+ getSvgPen(ctx),
+ x1, y1,
+ x2, y2,
+ x3, y3);
+}
+
+
+void SvgFilledCircle(struct ADrawTag *ctx,
+ unsigned int x,
+ unsigned int y,
+ unsigned int r)
+{
+ fprintf(getSvgFile(ctx),
+ "<circle fill=\"%s\" cx=\"%u\" cy=\"%u\" r=\"%u\"/>\n",
+ getSvgPen(ctx),
+ x, y, r);
+}
+
+
+void SvgFilledRectangle(struct ADrawTag *ctx,
+ unsigned int x1,
+ unsigned int y1,
+ unsigned int x2,
+ unsigned int y2)
+{
+ svgRect(ctx, getSvgPen(ctx), x1, y1, x2, y2);
+}
+
+
+void SvgArc(struct ADrawTag *ctx,
+ unsigned int cx,
+ unsigned int cy,
+ unsigned int w,
+ unsigned int h,
+ unsigned int s,
+ unsigned int e)
+{
+ unsigned int sx, sy, ex, ey;
+
+ /* Get start and end x,y */
+ arcPoint((float)cx, (float)cy, (float)w, (float)h, (float)s, &sx, &sy);
+ arcPoint((float)cx, (float)cy, (float)w, (float)h, (float)e, &ex, &ey);
+
+ fprintf(getSvgFile(ctx),
+ "<path d=\"M %u %u A%u,%u 0 0,1 %u,%u\" stroke=\"%s\" fill=\"none\"/>",
+ sx, sy, w / 2, h / 2, ex, ey, getSvgPen(ctx));
+}
+
+
+void SvgDottedArc(struct ADrawTag *ctx,
+ unsigned int cx,
+ unsigned int cy,
+ unsigned int w,
+ unsigned int h,
+ unsigned int s,
+ unsigned int e)
+{
+ unsigned int sx, sy, ex, ey;
+
+ /* Get start and end x,y */
+ arcPoint((float)cx, (float)cy, (float)w, (float)h, (float)s, &sx, &sy);
+ arcPoint((float)cx, (float)cy, (float)w, (float)h, (float)e, &ex, &ey);
+
+
+ fprintf(getSvgFile(ctx),
+ "<path d=\"M %u %u A%u,%u 0 0,1 %u,%u\" stroke=\"%s\" fill=\"none\" stroke-dasharray=\"2,2\"/>",
+ sx, sy, w / 2, h / 2, ex, ey, getSvgPen(ctx));
+}
+
+
+void SvgSetPen(struct ADrawTag *ctx,
+ ADrawColour col)
+{
+ static char colCmd[10];
+
+ getSvgCtx(ctx)->penColName = svgColour(col);
+ if(getSvgCtx(ctx)->penColName == NULL)
+ {
+ /* Print the RGB value into the local storage */
+ sprintf(colCmd, "#%06X", col);
+
+ /* Now set the colour name to the local store */
+ getSvgCtx(ctx)->penColName = colCmd;
+ }
+}
+
+
+void SvgSetBgPen(struct ADrawTag *ctx,
+ ADrawColour col)
+{
+ static char colCmd[10];
+
+ getSvgCtx(ctx)->penBgColName = svgColour(col);
+ if(getSvgCtx(ctx)->penBgColName == NULL)
+ {
+ /* Print the RGB value into the local storage */
+ sprintf(colCmd, "#%06X", col);
+
+ /* Now set the colour name to the local store */
+ getSvgCtx(ctx)->penBgColName = colCmd;
+ }
+}
+
+
+void SvgSetFontSize(struct ADrawTag *ctx,
+ ADrawFontSize size)
+{
+ SvgContext *context = getSvgCtx(ctx);
+
+ switch(size)
+ {
+ case ADRAW_FONT_TINY:
+ context->fontPoints = 8;
+ break;
+
+ case ADRAW_FONT_SMALL:
+ context->fontPoints = 12;
+ break;
+
+ default:
+ assert(0);
+ }
+
+}
+
+
+Boolean SvgClose(struct ADrawTag *ctx)
+{
+ SvgContext *context = getSvgCtx(ctx);
+
+ /* Close the SVG */
+ fprintf(context->of, "</svg>\n");
+
+ /* Close the output file */
+ if(context->of != stdout)
+ {
+ fclose(context->of);
+ }
+
+ /* Free and destroy context */
+ free(context);
+ ctx->internal = NULL;
+
+ return TRUE;
+}
+
+
+
+Boolean SvgInit(unsigned int w,
+ unsigned int h,
+ const char *file,
+ struct ADrawTag *outContext)
+{
+ SvgContext *context;
+
+ /* Create context */
+ context = outContext->internal = malloc_s(sizeof(SvgContext));
+ if(context == NULL)
+ {
+ return FALSE;
+ }
+
+ /* Open the output file */
+ if(strcmp(file, "-") == 0)
+ {
+ context->of = stdout;
+ }
+ else
+ {
+ context->of = fopen(file, "wb");
+ if(!context->of)
+ {
+ fprintf(stderr, "SvgInit: Failed to open output file '%s': %s\n", file, strerror(errno));
+ return FALSE;
+ }
+ }
+
+ /* Set the initial pen state */
+ SvgSetPen(outContext, ADRAW_COL_BLACK);
+ SvgSetBgPen(outContext, ADRAW_COL_WHITE);
+
+ /* Default to small font */
+ SvgSetFontSize(outContext, ADRAW_FONT_SMALL);
+
+ fprintf(context->of, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n"
+ " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n");
+
+ fprintf(context->of, "<svg version=\"1.1\"\n"
+ " width=\"%upx\" height=\"%upx\"\n"
+ " viewBox=\"0 0 %u %u\"\n"
+ " xmlns=\"http://www.w3.org/2000/svg\" shape-rendering=\"crispEdges\"\n"
+ " stroke-width=\"1\" text-rendering=\"geometricPrecision\">\n",
+ w, h, w, h);
+
+ /* Now fill in the function pointers */
+ outContext->line = SvgLine;
+ outContext->dottedLine = SvgDottedLine;
+ outContext->textL = SvgTextL;
+ outContext->textC = SvgTextC;
+ outContext->textR = SvgTextR;
+ outContext->textWidth = SvgTextWidth;
+ outContext->textHeight = SvgTextHeight;
+ outContext->filledRectangle = SvgFilledRectangle;
+ outContext->filledTriangle = SvgFilledTriangle;
+ outContext->filledCircle = SvgFilledCircle;
+ outContext->arc = SvgArc;
+ outContext->dottedArc = SvgDottedArc;
+ outContext->setPen = SvgSetPen;
+ outContext->setBgPen = SvgSetBgPen;
+ outContext->setFontSize = SvgSetFontSize;
+ outContext->close = SvgClose;
+
+ return TRUE;
+}
+
+/* END OF FILE */