rewrote large parts of the SVG parser. It now handles nested groups and
2003-09-13 Sven Neumann <sven@gimp.org> * app/vectors/gimpvectors-import.c: rewrote large parts of the SVG parser. It now handles nested groups and transformations. Still not perfect but close.
This commit is contained in:

committed by
Sven Neumann

parent
e76b755640
commit
804315b8cf
@ -1,3 +1,9 @@
|
|||||||
|
2003-09-13 Sven Neumann <sven@gimp.org>
|
||||||
|
|
||||||
|
* app/vectors/gimpvectors-import.c: rewrote large parts of the SVG
|
||||||
|
parser. It now handles nested groups and transformations. Still not
|
||||||
|
perfect but close.
|
||||||
|
|
||||||
2003-09-12 Helvetix Victorinox <helvetix@gimp.org>
|
2003-09-12 Helvetix Victorinox <helvetix@gimp.org>
|
||||||
|
|
||||||
* app/composite/gimp-composite-generic.[ch]: Added a proper
|
* app/composite/gimp-composite-generic.[ch]: Added a proper
|
||||||
|
@ -44,62 +44,81 @@
|
|||||||
#include "gimp-intl.h"
|
#include "gimp-intl.h"
|
||||||
|
|
||||||
|
|
||||||
typedef enum
|
typedef struct _SvgParser SvgParser;
|
||||||
|
struct _SvgParser
|
||||||
{
|
{
|
||||||
PARSER_START,
|
|
||||||
PARSER_IN_SVG,
|
|
||||||
PARSER_IN_PATH,
|
|
||||||
PARSER_IN_UNKNOWN
|
|
||||||
} ParserState;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
ParserState state;
|
|
||||||
ParserState last_known_state;
|
|
||||||
gint unknown_depth;
|
|
||||||
GimpImage *image;
|
|
||||||
gboolean merge;
|
gboolean merge;
|
||||||
GimpVectors *vectors;
|
GQueue *stack;
|
||||||
GimpMatrix3 matrix;
|
};
|
||||||
} VectorsParser;
|
|
||||||
|
typedef struct _SvgHandler SvgHandler;
|
||||||
|
struct _SvgHandler
|
||||||
|
{
|
||||||
|
const gchar *name;
|
||||||
|
gdouble width;
|
||||||
|
gdouble height;
|
||||||
|
GList *strokes;
|
||||||
|
GimpMatrix3 *transform;
|
||||||
|
|
||||||
|
void (* start) (SvgHandler *handler,
|
||||||
|
const gchar **names,
|
||||||
|
const gchar **values,
|
||||||
|
SvgParser *parser);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
static void parser_start_element (GMarkupParseContext *context,
|
static void svg_parser_start_element (GMarkupParseContext *context,
|
||||||
const gchar *element_name,
|
const gchar *element_name,
|
||||||
const gchar **attribute_names,
|
const gchar **attribute_names,
|
||||||
const gchar **attribute_values,
|
const gchar **attribute_values,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
GError **error);
|
GError **error);
|
||||||
static void parser_end_element (GMarkupParseContext *context,
|
static void svg_parser_end_element (GMarkupParseContext *context,
|
||||||
const gchar *element_name,
|
const gchar *element_name,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
static void parser_start_unknown (VectorsParser *parser);
|
|
||||||
static void parser_end_unknown (VectorsParser *parser);
|
|
||||||
|
|
||||||
static gboolean parse_svg_viewbox (const gchar *value,
|
|
||||||
gint width,
|
|
||||||
gint height,
|
|
||||||
GimpMatrix3 *matrix);
|
|
||||||
static gboolean parse_svg_transform (const gchar *value,
|
|
||||||
GimpMatrix3 *matrix);
|
|
||||||
static void parse_path_data (GimpVectors *vectors,
|
|
||||||
const gchar *data);
|
|
||||||
static void parser_add_vectors (VectorsParser *parser,
|
|
||||||
GimpVectors *vectors);
|
|
||||||
|
|
||||||
|
|
||||||
static const GMarkupParser markup_parser =
|
static const GMarkupParser markup_parser =
|
||||||
{
|
{
|
||||||
parser_start_element,
|
svg_parser_start_element,
|
||||||
parser_end_element,
|
svg_parser_end_element,
|
||||||
NULL, /* characters */
|
NULL, /* characters */
|
||||||
NULL, /* passthrough */
|
NULL, /* passthrough */
|
||||||
NULL /* error */
|
NULL /* error */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static void svg_handler_svg (SvgHandler *handler,
|
||||||
|
const gchar **names,
|
||||||
|
const gchar **values,
|
||||||
|
SvgParser *parser);
|
||||||
|
static void svg_handler_group (SvgHandler *handler,
|
||||||
|
const gchar **names,
|
||||||
|
const gchar **values,
|
||||||
|
SvgParser *parser);
|
||||||
|
static void svg_handler_path (SvgHandler *handler,
|
||||||
|
const gchar **names,
|
||||||
|
const gchar **values,
|
||||||
|
SvgParser *parser);
|
||||||
|
|
||||||
|
static SvgHandler svg_handlers[] =
|
||||||
|
{
|
||||||
|
{ "svg", 0, 0, NULL, NULL, svg_handler_svg },
|
||||||
|
{ "g", 0, 0, NULL, NULL, svg_handler_group },
|
||||||
|
{ "path", 0, 0, NULL, NULL, svg_handler_path },
|
||||||
|
{ NULL, 0, 0, NULL, NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static gboolean parse_svg_viewbox (const gchar *value,
|
||||||
|
gdouble width,
|
||||||
|
gdouble height,
|
||||||
|
GimpMatrix3 *matrix);
|
||||||
|
static gboolean parse_svg_transform (const gchar *value,
|
||||||
|
GimpMatrix3 *matrix);
|
||||||
|
static GList * parse_path_data (const gchar *data);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* gimp_vectors_import:
|
* gimp_vectors_import:
|
||||||
* @image: the #GimpImage to add the paths to
|
* @image: the #GimpImage to add the paths to
|
||||||
@ -120,7 +139,8 @@ gimp_vectors_import (GimpImage *image,
|
|||||||
{
|
{
|
||||||
GMarkupParseContext *context;
|
GMarkupParseContext *context;
|
||||||
FILE *file;
|
FILE *file;
|
||||||
VectorsParser parser;
|
SvgParser parser;
|
||||||
|
SvgHandler base;
|
||||||
gboolean success = TRUE;
|
gboolean success = TRUE;
|
||||||
gsize bytes;
|
gsize bytes;
|
||||||
gchar buf[4096];
|
gchar buf[4096];
|
||||||
@ -138,11 +158,17 @@ gimp_vectors_import (GimpImage *image,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset (&parser, 0, sizeof (VectorsParser));
|
|
||||||
|
|
||||||
parser.state = PARSER_START;
|
|
||||||
parser.image = image;
|
|
||||||
parser.merge = merge;
|
parser.merge = merge;
|
||||||
|
parser.stack = g_queue_new ();
|
||||||
|
|
||||||
|
/* the base of the stack, defines the size of the view-port */
|
||||||
|
base.name = "image";
|
||||||
|
base.width = image->width;
|
||||||
|
base.height = image->height;
|
||||||
|
base.strokes = NULL;
|
||||||
|
base.transform = NULL;
|
||||||
|
|
||||||
|
g_queue_push_head (parser.stack, &base);
|
||||||
|
|
||||||
context = g_markup_parse_context_new (&markup_parser, 0, &parser, NULL);
|
context = g_markup_parse_context_new (&markup_parser, 0, &parser, NULL);
|
||||||
|
|
||||||
@ -156,170 +182,171 @@ gimp_vectors_import (GimpImage *image,
|
|||||||
fclose (file);
|
fclose (file);
|
||||||
g_markup_parse_context_free (context);
|
g_markup_parse_context_free (context);
|
||||||
|
|
||||||
if (merge && parser.vectors)
|
g_queue_free (parser.stack);
|
||||||
parser_add_vectors (&parser, parser.vectors);
|
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
if (base.strokes)
|
||||||
|
{
|
||||||
|
GimpVectors *vectors;
|
||||||
|
GList *list;
|
||||||
|
|
||||||
|
vectors = gimp_vectors_new (image, _("Imported Path"));
|
||||||
|
|
||||||
|
for (list = base.strokes; list; list = list->next)
|
||||||
|
gimp_vectors_stroke_add (vectors, GIMP_STROKE (list->data));
|
||||||
|
|
||||||
|
gimp_image_add_vectors (image, vectors, -1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_set_error (error, 0, 0, _("No paths found in '%s'"), filename);
|
||||||
|
success = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_list_free (base.strokes);
|
||||||
|
g_free (base.transform);
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
parser_start_element (GMarkupParseContext *context,
|
svg_parser_start_element (GMarkupParseContext *context,
|
||||||
const gchar *element_name,
|
const gchar *element_name,
|
||||||
const gchar **attribute_names,
|
const gchar **attribute_names,
|
||||||
const gchar **attribute_values,
|
const gchar **attribute_values,
|
||||||
gpointer user_data,
|
gpointer user_data,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
VectorsParser *parser = (VectorsParser *) user_data;
|
SvgParser *parser = (SvgParser *) user_data;
|
||||||
|
SvgHandler *base;
|
||||||
|
SvgHandler *handler = NULL;
|
||||||
|
gint i;
|
||||||
|
|
||||||
switch (parser->state)
|
base = g_queue_peek_head (parser->stack);
|
||||||
|
|
||||||
|
for (i = 0; !handler && i < G_N_ELEMENTS (svg_handlers); i++)
|
||||||
{
|
{
|
||||||
case PARSER_START:
|
if (svg_handlers[i].name == NULL)
|
||||||
if (strcmp (element_name, "svg") == 0)
|
handler = svg_handlers + i;
|
||||||
parser->state = PARSER_IN_SVG;
|
else if (strcmp (svg_handlers[i].name, element_name) == 0)
|
||||||
else
|
handler = svg_handlers + i;
|
||||||
parser_start_unknown (parser);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PARSER_IN_SVG:
|
|
||||||
if (strcmp (element_name, "path") == 0)
|
|
||||||
parser->state = PARSER_IN_PATH;
|
|
||||||
else
|
|
||||||
parser_start_unknown (parser);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PARSER_IN_PATH:
|
|
||||||
case PARSER_IN_UNKNOWN:
|
|
||||||
parser_start_unknown (parser);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (parser->state)
|
handler = g_memdup (handler, sizeof (SvgHandler));
|
||||||
|
handler->width = base->width;
|
||||||
|
handler->height = base->height;
|
||||||
|
|
||||||
|
g_queue_push_head (parser->stack, handler);
|
||||||
|
|
||||||
|
if (handler->start)
|
||||||
|
handler->start (handler, attribute_names, attribute_values, parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
svg_parser_end_element (GMarkupParseContext *context,
|
||||||
|
const gchar *element_name,
|
||||||
|
gpointer user_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
SvgParser *parser = (SvgParser *) user_data;
|
||||||
|
SvgHandler *handler;
|
||||||
|
SvgHandler *base;
|
||||||
|
GList *list;
|
||||||
|
|
||||||
|
handler = g_queue_pop_head (parser->stack);
|
||||||
|
|
||||||
|
if (handler->strokes)
|
||||||
{
|
{
|
||||||
case PARSER_IN_SVG:
|
if (handler->transform)
|
||||||
while (*attribute_names)
|
|
||||||
{
|
{
|
||||||
if (strcmp (*attribute_names, "viewBox") == 0)
|
for (list = handler->strokes; list; list = list->next)
|
||||||
{
|
gimp_stroke_transform (GIMP_STROKE (list->data),
|
||||||
GimpMatrix3 matrix;
|
handler->transform);
|
||||||
|
g_free (handler->transform);
|
||||||
if (parse_svg_viewbox (*attribute_values,
|
|
||||||
parser->image->width,
|
|
||||||
parser->image->height,
|
|
||||||
&matrix))
|
|
||||||
parser->matrix = matrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
attribute_names++;
|
|
||||||
attribute_values++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PARSER_IN_PATH:
|
|
||||||
while (*attribute_names)
|
|
||||||
{
|
|
||||||
if (strcmp (*attribute_names, "d") == 0)
|
|
||||||
{
|
|
||||||
GimpVectors *vectors = NULL;
|
|
||||||
|
|
||||||
if (parser->merge)
|
|
||||||
vectors = parser->vectors;
|
|
||||||
|
|
||||||
if (! vectors)
|
|
||||||
vectors = gimp_vectors_new (parser->image,
|
|
||||||
_("Imported Path"));
|
|
||||||
|
|
||||||
parse_path_data (vectors, *attribute_values);
|
|
||||||
|
|
||||||
if (! parser->merge)
|
|
||||||
parser_add_vectors (parser, vectors);
|
|
||||||
|
|
||||||
parser->vectors = vectors;
|
|
||||||
}
|
|
||||||
|
|
||||||
attribute_names++;
|
|
||||||
attribute_values++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
base = g_queue_peek_head (parser->stack);
|
||||||
|
base->strokes = g_list_concat (base->strokes, handler->strokes);
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_free (handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
parser_end_element (GMarkupParseContext *context,
|
svg_handler_svg (SvgHandler *handler,
|
||||||
const gchar *element_name,
|
const gchar **names,
|
||||||
gpointer user_data,
|
const gchar **values,
|
||||||
GError **error)
|
SvgParser *parser)
|
||||||
{
|
{
|
||||||
VectorsParser *parser = (VectorsParser *) user_data;
|
while (*names)
|
||||||
|
|
||||||
switch (parser->state)
|
|
||||||
{
|
{
|
||||||
case PARSER_START:
|
if (strcmp (*names, "viewBox") == 0 && !handler->transform)
|
||||||
g_return_if_reached ();
|
{
|
||||||
break;
|
GimpMatrix3 matrix;
|
||||||
|
|
||||||
case PARSER_IN_SVG:
|
if (parse_svg_viewbox (*values,
|
||||||
parser->state = PARSER_START;
|
handler->width, handler->height, &matrix))
|
||||||
break;
|
handler->transform = g_memdup (&matrix, sizeof (GimpMatrix3));
|
||||||
|
}
|
||||||
case PARSER_IN_PATH:
|
names++;
|
||||||
parser->state = PARSER_IN_SVG;
|
values++;
|
||||||
break;
|
|
||||||
|
|
||||||
case PARSER_IN_UNKNOWN:
|
|
||||||
parser_end_unknown (parser);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
parser_start_unknown (VectorsParser *parser)
|
svg_handler_group (SvgHandler *handler,
|
||||||
|
const gchar **names,
|
||||||
|
const gchar **values,
|
||||||
|
SvgParser *parser)
|
||||||
{
|
{
|
||||||
if (parser->unknown_depth == 0)
|
while (*names)
|
||||||
parser->last_known_state = parser->state;
|
|
||||||
|
|
||||||
parser->state = PARSER_IN_UNKNOWN;
|
|
||||||
parser->unknown_depth++;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
parser_end_unknown (VectorsParser *parser)
|
|
||||||
{
|
|
||||||
g_return_if_fail (parser->unknown_depth > 0 &&
|
|
||||||
parser->state == PARSER_IN_UNKNOWN);
|
|
||||||
|
|
||||||
parser->unknown_depth--;
|
|
||||||
|
|
||||||
if (parser->unknown_depth == 0)
|
|
||||||
parser->state = parser->last_known_state;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
parser_add_vectors (VectorsParser *parser,
|
|
||||||
GimpVectors *vectors)
|
|
||||||
{
|
|
||||||
GList *list;
|
|
||||||
|
|
||||||
for (list = vectors->strokes; list; list = list->next)
|
|
||||||
{
|
{
|
||||||
GimpStroke *stroke = list->data;
|
if (strcmp (*names, "transform") == 0 && !handler->transform)
|
||||||
|
{
|
||||||
|
GimpMatrix3 matrix;
|
||||||
|
|
||||||
gimp_stroke_transform (stroke, &parser->matrix);
|
if (parse_svg_transform (*values, &matrix))
|
||||||
|
handler->transform = g_memdup (&matrix, sizeof (GimpMatrix3));
|
||||||
|
}
|
||||||
|
|
||||||
|
names++;
|
||||||
|
values++;
|
||||||
}
|
}
|
||||||
|
|
||||||
gimp_image_add_vectors (parser->image, vectors, -1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
svg_handler_path (SvgHandler *handler,
|
||||||
|
const gchar **names,
|
||||||
|
const gchar **values,
|
||||||
|
SvgParser *parser)
|
||||||
|
{
|
||||||
|
while (*names)
|
||||||
|
{
|
||||||
|
if (strcmp (*names, "d") == 0)
|
||||||
|
{
|
||||||
|
handler->strokes = g_list_concat (handler->strokes,
|
||||||
|
parse_path_data (*values));
|
||||||
|
}
|
||||||
|
else if (strcmp (*names, "transform") == 0 && !handler->transform)
|
||||||
|
{
|
||||||
|
GimpMatrix3 matrix;
|
||||||
|
|
||||||
|
if (parse_svg_transform (*values, &matrix))
|
||||||
|
handler->transform = g_memdup (&matrix, sizeof (GimpMatrix3));
|
||||||
|
}
|
||||||
|
|
||||||
|
names++;
|
||||||
|
values++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
parse_svg_viewbox (const gchar *value,
|
parse_svg_viewbox (const gchar *value,
|
||||||
gint width,
|
gdouble width,
|
||||||
gint height,
|
gdouble height,
|
||||||
GimpMatrix3 *matrix)
|
GimpMatrix3 *matrix)
|
||||||
{
|
{
|
||||||
gdouble x, y, w, h;
|
gdouble x, y, w, h;
|
||||||
gchar *tok;
|
gchar *tok;
|
||||||
@ -361,7 +388,7 @@ parse_svg_viewbox (const gchar *value,
|
|||||||
gimp_matrix3_translate (matrix, x, y);
|
gimp_matrix3_translate (matrix, x, y);
|
||||||
|
|
||||||
if (w && h)
|
if (w && h)
|
||||||
gimp_matrix3_scale (matrix, (gdouble) width / w, (gdouble) height / h);
|
gimp_matrix3_scale (matrix, width / w, height / h);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -507,7 +534,7 @@ parse_svg_transform (const gchar *value,
|
|||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
GimpVectors *vectors;
|
GList *strokes;
|
||||||
GimpStroke *stroke;
|
GimpStroke *stroke;
|
||||||
gdouble cpx, cpy; /* current point */
|
gdouble cpx, cpy; /* current point */
|
||||||
gdouble rpx, rpy; /* reflection point (for 's' and 't' commands) */
|
gdouble rpx, rpy; /* reflection point (for 's' and 't' commands) */
|
||||||
@ -524,12 +551,10 @@ static void parse_path_do_cmd (ParsePathContext *ctx,
|
|||||||
gboolean final);
|
gboolean final);
|
||||||
|
|
||||||
|
|
||||||
static void
|
static GList *
|
||||||
parse_path_data (GimpVectors *vectors,
|
parse_path_data (const gchar *data)
|
||||||
const gchar *data)
|
|
||||||
{
|
{
|
||||||
ParsePathContext ctx;
|
ParsePathContext ctx;
|
||||||
|
|
||||||
gboolean in_num = FALSE;
|
gboolean in_num = FALSE;
|
||||||
gboolean in_frac = FALSE;
|
gboolean in_frac = FALSE;
|
||||||
gboolean in_exp = FALSE;
|
gboolean in_exp = FALSE;
|
||||||
@ -542,13 +567,7 @@ parse_path_data (GimpVectors *vectors,
|
|||||||
gdouble frac = 0.0;
|
gdouble frac = 0.0;
|
||||||
gint i;
|
gint i;
|
||||||
|
|
||||||
ctx.vectors = vectors;
|
memset (&ctx, 0, sizeof (ParsePathContext));
|
||||||
ctx.stroke = NULL;
|
|
||||||
ctx.cpx = 0.0;
|
|
||||||
ctx.cpy = 0.0;
|
|
||||||
ctx.cmd = 0;
|
|
||||||
ctx.param = 0;
|
|
||||||
ctx.rel = FALSE;
|
|
||||||
|
|
||||||
for (i = 0; ; i++)
|
for (i = 0; ; i++)
|
||||||
{
|
{
|
||||||
@ -687,6 +706,8 @@ parse_path_data (GimpVectors *vectors,
|
|||||||
}
|
}
|
||||||
/* else c _should_ be whitespace or , */
|
/* else c _should_ be whitespace or , */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ctx.strokes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* supply defaults for missing parameters, assuming relative coordinates
|
/* supply defaults for missing parameters, assuming relative coordinates
|
||||||
@ -735,9 +756,7 @@ parse_path_do_cmd (ParsePathContext *ctx,
|
|||||||
coords.y = ctx->cpy = ctx->rpy = ctx->params[1];
|
coords.y = ctx->cpy = ctx->rpy = ctx->params[1];
|
||||||
|
|
||||||
ctx->stroke = gimp_bezier_stroke_new_moveto (&coords);
|
ctx->stroke = gimp_bezier_stroke_new_moveto (&coords);
|
||||||
|
ctx->strokes = g_list_append (ctx->strokes, ctx->stroke);
|
||||||
gimp_vectors_stroke_add (ctx->vectors, ctx->stroke);
|
|
||||||
g_object_unref (ctx->stroke);
|
|
||||||
|
|
||||||
ctx->param = 0;
|
ctx->param = 0;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user