mirror of https://github.com/GNOME/gimp.git
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:
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>
|
||||
|
||||
* app/composite/gimp-composite-generic.[ch]: Added a proper
|
||||
|
|
|
@ -44,62 +44,81 @@
|
|||
#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;
|
||||
GimpVectors *vectors;
|
||||
GimpMatrix3 matrix;
|
||||
} VectorsParser;
|
||||
GQueue *stack;
|
||||
};
|
||||
|
||||
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,
|
||||
const gchar *element_name,
|
||||
const gchar **attribute_names,
|
||||
const gchar **attribute_values,
|
||||
gpointer user_data,
|
||||
GError **error);
|
||||
static void parser_end_element (GMarkupParseContext *context,
|
||||
const gchar *element_name,
|
||||
gpointer user_data,
|
||||
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 void svg_parser_start_element (GMarkupParseContext *context,
|
||||
const gchar *element_name,
|
||||
const gchar **attribute_names,
|
||||
const gchar **attribute_values,
|
||||
gpointer user_data,
|
||||
GError **error);
|
||||
static void svg_parser_end_element (GMarkupParseContext *context,
|
||||
const gchar *element_name,
|
||||
gpointer user_data,
|
||||
GError **error);
|
||||
|
||||
static const GMarkupParser markup_parser =
|
||||
{
|
||||
parser_start_element,
|
||||
parser_end_element,
|
||||
svg_parser_start_element,
|
||||
svg_parser_end_element,
|
||||
NULL, /* characters */
|
||||
NULL, /* passthrough */
|
||||
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:
|
||||
* @image: the #GimpImage to add the paths to
|
||||
|
@ -120,7 +139,8 @@ gimp_vectors_import (GimpImage *image,
|
|||
{
|
||||
GMarkupParseContext *context;
|
||||
FILE *file;
|
||||
VectorsParser parser;
|
||||
SvgParser parser;
|
||||
SvgHandler base;
|
||||
gboolean success = TRUE;
|
||||
gsize bytes;
|
||||
gchar buf[4096];
|
||||
|
@ -138,11 +158,17 @@ gimp_vectors_import (GimpImage *image,
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
memset (&parser, 0, sizeof (VectorsParser));
|
||||
|
||||
parser.state = PARSER_START;
|
||||
parser.image = image;
|
||||
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);
|
||||
|
||||
|
@ -156,170 +182,171 @@ gimp_vectors_import (GimpImage *image,
|
|||
fclose (file);
|
||||
g_markup_parse_context_free (context);
|
||||
|
||||
if (merge && parser.vectors)
|
||||
parser_add_vectors (&parser, parser.vectors);
|
||||
g_queue_free (parser.stack);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void
|
||||
parser_start_element (GMarkupParseContext *context,
|
||||
const gchar *element_name,
|
||||
const gchar **attribute_names,
|
||||
const gchar **attribute_values,
|
||||
gpointer user_data,
|
||||
GError **error)
|
||||
svg_parser_start_element (GMarkupParseContext *context,
|
||||
const gchar *element_name,
|
||||
const gchar **attribute_names,
|
||||
const gchar **attribute_values,
|
||||
gpointer user_data,
|
||||
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 (strcmp (element_name, "svg") == 0)
|
||||
parser->state = PARSER_IN_SVG;
|
||||
else
|
||||
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;
|
||||
if (svg_handlers[i].name == NULL)
|
||||
handler = svg_handlers + i;
|
||||
else if (strcmp (svg_handlers[i].name, element_name) == 0)
|
||||
handler = svg_handlers + i;
|
||||
}
|
||||
|
||||
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:
|
||||
while (*attribute_names)
|
||||
if (handler->transform)
|
||||
{
|
||||
if (strcmp (*attribute_names, "viewBox") == 0)
|
||||
{
|
||||
GimpMatrix3 matrix;
|
||||
|
||||
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++;
|
||||
for (list = handler->strokes; list; list = list->next)
|
||||
gimp_stroke_transform (GIMP_STROKE (list->data),
|
||||
handler->transform);
|
||||
g_free (handler->transform);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
base = g_queue_peek_head (parser->stack);
|
||||
base->strokes = g_list_concat (base->strokes, handler->strokes);
|
||||
}
|
||||
|
||||
g_free (handler);
|
||||
}
|
||||
|
||||
static void
|
||||
parser_end_element (GMarkupParseContext *context,
|
||||
const gchar *element_name,
|
||||
gpointer user_data,
|
||||
GError **error)
|
||||
svg_handler_svg (SvgHandler *handler,
|
||||
const gchar **names,
|
||||
const gchar **values,
|
||||
SvgParser *parser)
|
||||
{
|
||||
VectorsParser *parser = (VectorsParser *) user_data;
|
||||
|
||||
switch (parser->state)
|
||||
while (*names)
|
||||
{
|
||||
case PARSER_START:
|
||||
g_return_if_reached ();
|
||||
break;
|
||||
if (strcmp (*names, "viewBox") == 0 && !handler->transform)
|
||||
{
|
||||
GimpMatrix3 matrix;
|
||||
|
||||
case PARSER_IN_SVG:
|
||||
parser->state = PARSER_START;
|
||||
break;
|
||||
|
||||
case PARSER_IN_PATH:
|
||||
parser->state = PARSER_IN_SVG;
|
||||
break;
|
||||
|
||||
case PARSER_IN_UNKNOWN:
|
||||
parser_end_unknown (parser);
|
||||
break;
|
||||
if (parse_svg_viewbox (*values,
|
||||
handler->width, handler->height, &matrix))
|
||||
handler->transform = g_memdup (&matrix, sizeof (GimpMatrix3));
|
||||
}
|
||||
names++;
|
||||
values++;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
while (*names)
|
||||
{
|
||||
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
|
||||
parse_svg_viewbox (const gchar *value,
|
||||
gint width,
|
||||
gint height,
|
||||
GimpMatrix3 *matrix)
|
||||
parse_svg_viewbox (const gchar *value,
|
||||
gdouble width,
|
||||
gdouble height,
|
||||
GimpMatrix3 *matrix)
|
||||
{
|
||||
gdouble x, y, w, h;
|
||||
gchar *tok;
|
||||
|
@ -361,7 +388,7 @@ parse_svg_viewbox (const gchar *value,
|
|||
gimp_matrix3_translate (matrix, x, y);
|
||||
|
||||
if (w && h)
|
||||
gimp_matrix3_scale (matrix, (gdouble) width / w, (gdouble) height / h);
|
||||
gimp_matrix3_scale (matrix, width / w, height / h);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -507,7 +534,7 @@ parse_svg_transform (const gchar *value,
|
|||
|
||||
typedef struct
|
||||
{
|
||||
GimpVectors *vectors;
|
||||
GList *strokes;
|
||||
GimpStroke *stroke;
|
||||
gdouble cpx, cpy; /* current point */
|
||||
gdouble rpx, rpy; /* reflection point (for 's' and 't' commands) */
|
||||
|
@ -524,12 +551,10 @@ static void parse_path_do_cmd (ParsePathContext *ctx,
|
|||
gboolean final);
|
||||
|
||||
|
||||
static void
|
||||
parse_path_data (GimpVectors *vectors,
|
||||
const gchar *data)
|
||||
static GList *
|
||||
parse_path_data (const gchar *data)
|
||||
{
|
||||
ParsePathContext ctx;
|
||||
|
||||
gboolean in_num = FALSE;
|
||||
gboolean in_frac = FALSE;
|
||||
gboolean in_exp = FALSE;
|
||||
|
@ -542,13 +567,7 @@ parse_path_data (GimpVectors *vectors,
|
|||
gdouble frac = 0.0;
|
||||
gint i;
|
||||
|
||||
ctx.vectors = vectors;
|
||||
ctx.stroke = NULL;
|
||||
ctx.cpx = 0.0;
|
||||
ctx.cpy = 0.0;
|
||||
ctx.cmd = 0;
|
||||
ctx.param = 0;
|
||||
ctx.rel = FALSE;
|
||||
memset (&ctx, 0, sizeof (ParsePathContext));
|
||||
|
||||
for (i = 0; ; i++)
|
||||
{
|
||||
|
@ -687,6 +706,8 @@ parse_path_data (GimpVectors *vectors,
|
|||
}
|
||||
/* else c _should_ be whitespace or , */
|
||||
}
|
||||
|
||||
return ctx.strokes;
|
||||
}
|
||||
|
||||
/* 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];
|
||||
|
||||
ctx->stroke = gimp_bezier_stroke_new_moveto (&coords);
|
||||
|
||||
gimp_vectors_stroke_add (ctx->vectors, ctx->stroke);
|
||||
g_object_unref (ctx->stroke);
|
||||
ctx->strokes = g_list_append (ctx->strokes, ctx->stroke);
|
||||
|
||||
ctx->param = 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue