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:
Sven Neumann
2003-09-12 22:18:09 +00:00
committed by Sven Neumann
parent e76b755640
commit 804315b8cf
2 changed files with 220 additions and 195 deletions

View File

@ -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

View File

@ -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;
} }