mirror of https://github.com/GNOME/gimp.git
save the vectors (or rather image) dimensions in the exported SVG.
2003-09-18 Sven Neumann <sven@gimp.org> * app/vectors/gimpvectors-export.c: save the vectors (or rather image) dimensions in the exported SVG. * app/vectors/gimpvectors-import.c: added SVG units parser and finished viewport handling. The parser now respects the size specified in the SVG and the image resolution. Should also handle nested SVGs correctly, but this is untested.
This commit is contained in:
parent
ed0acf91cd
commit
1fc2adfc80
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
|||
2003-09-18 Sven Neumann <sven@gimp.org>
|
||||
|
||||
* app/vectors/gimpvectors-export.c: save the vectors (or rather
|
||||
image) dimensions in the exported SVG.
|
||||
|
||||
* app/vectors/gimpvectors-import.c: added SVG units parser and
|
||||
finished viewport handling. The parser now respects the size
|
||||
specified in the SVG and the image resolution. Should also handle
|
||||
nested SVGs correctly, but this is untested.
|
||||
|
||||
2003-09-18 Michael Natterer <mitch@gimp.org>
|
||||
|
||||
* app/Makefile.am (gimp_1_3_LDFLAGS): specifying one symbol per
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "libgimpbase/gimpbase.h"
|
||||
|
||||
#include "vectors-types.h"
|
||||
|
||||
#include "core/gimpimage.h"
|
||||
|
@ -42,6 +44,7 @@
|
|||
static void gimp_vectors_export_path (const GimpVectors *vectors,
|
||||
FILE *file);
|
||||
static gchar * gimp_vectors_path_data (const GimpVectors *vectors);
|
||||
static gchar * gimp_vectors_image_size (const GimpImage *image);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -62,6 +65,7 @@ gimp_vectors_export (const GimpImage *image,
|
|||
GError **error)
|
||||
{
|
||||
FILE *file;
|
||||
gchar *size;
|
||||
|
||||
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
||||
g_return_val_if_fail (vectors == NULL || GIMP_IS_VECTORS (vectors), FALSE);
|
||||
|
@ -80,10 +84,17 @@ gimp_vectors_export (const GimpImage *image,
|
|||
fprintf (file,
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
|
||||
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20010904//EN\"\n"
|
||||
" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n");
|
||||
fprintf (file,
|
||||
"<svg xmlns=\"http://www.w3.org/2000/svg\"\n"
|
||||
" viewBox=\"0 0 %d %d\">\n\n",
|
||||
" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"
|
||||
"<svg xmlns=\"http://www.w3.org/2000/svg\"\n");
|
||||
|
||||
size = gimp_vectors_image_size (image);
|
||||
if (size)
|
||||
{
|
||||
fprintf (file, " %s\n", size);
|
||||
g_free (size);
|
||||
}
|
||||
|
||||
fprintf (file, " viewBox=\"0 0 %d %d\">\n",
|
||||
image->width, image->height);
|
||||
|
||||
if (vectors)
|
||||
|
@ -111,6 +122,40 @@ gimp_vectors_export (const GimpImage *image,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static gchar *
|
||||
gimp_vectors_image_size (const GimpImage *image)
|
||||
{
|
||||
const gchar *abbrev = NULL;
|
||||
|
||||
switch (image->unit)
|
||||
{
|
||||
case GIMP_UNIT_INCH: abbrev = "in"; break;
|
||||
case GIMP_UNIT_MM: abbrev = "mm"; break;
|
||||
case GIMP_UNIT_POINT: abbrev = "pt"; break;
|
||||
case GIMP_UNIT_PICA: abbrev = "pc"; break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (abbrev)
|
||||
{
|
||||
gchar w[G_ASCII_DTOSTR_BUF_SIZE];
|
||||
gchar h[G_ASCII_DTOSTR_BUF_SIZE];
|
||||
|
||||
g_ascii_formatd (w, sizeof (w), "%g",
|
||||
(image->width * gimp_unit_get_factor (image->unit) /
|
||||
image->xresolution));
|
||||
g_ascii_formatd (h, sizeof (h), "%g",
|
||||
(image->height * gimp_unit_get_factor (image->unit) /
|
||||
image->yresolution));
|
||||
|
||||
return g_strdup_printf ("width=\"%s%s\" height=\"%s%s\"",
|
||||
w, abbrev, h, abbrev);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_vectors_export_path (const GimpVectors *vectors,
|
||||
FILE *file)
|
||||
|
|
|
@ -7,6 +7,12 @@
|
|||
* Some code here is based on code from librsvg that was originally
|
||||
* written by Raph Levien <raph@artofcode.com> for Gill.
|
||||
*
|
||||
* This SVG path importer implements a subset of SVG that is sufficient
|
||||
* to parse path elements and to apply all defined transformations as
|
||||
* described by the SVG specification: http://www.w3.org/TR/SVG/.
|
||||
* It must handle the SVG files exported by GIMP but it is also supposed
|
||||
* to be able to extract paths from foreign SVG documents.
|
||||
*
|
||||
* 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
|
||||
|
@ -29,6 +35,7 @@
|
|||
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "libgimpbase/gimpbase.h"
|
||||
#include "libgimpmath/gimpmath.h"
|
||||
|
||||
#include "vectors-types.h"
|
||||
|
@ -46,19 +53,27 @@
|
|||
#include "gimp-intl.h"
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GQueue *stack;
|
||||
GimpImage *image;
|
||||
} SvgParser;
|
||||
|
||||
typedef struct _SvgHandler SvgHandler;
|
||||
struct _SvgHandler
|
||||
{
|
||||
const gchar *name;
|
||||
|
||||
void (* start) (SvgHandler *handler,
|
||||
const gchar **names,
|
||||
const gchar **values,
|
||||
SvgParser *parser);
|
||||
|
||||
gdouble width;
|
||||
gdouble height;
|
||||
gchar *id;
|
||||
GList *paths;
|
||||
GimpMatrix3 *transform;
|
||||
|
||||
void (* start) (SvgHandler *handler,
|
||||
const gchar **names,
|
||||
const gchar **values);
|
||||
};
|
||||
|
||||
typedef struct
|
||||
|
@ -91,23 +106,30 @@ static const GMarkupParser markup_parser =
|
|||
|
||||
static void svg_handler_svg (SvgHandler *handler,
|
||||
const gchar **names,
|
||||
const gchar **values);
|
||||
const gchar **values,
|
||||
SvgParser *parser);
|
||||
static void svg_handler_group (SvgHandler *handler,
|
||||
const gchar **names,
|
||||
const gchar **values);
|
||||
const gchar **values,
|
||||
SvgParser *parser);
|
||||
static void svg_handler_path (SvgHandler *handler,
|
||||
const gchar **names,
|
||||
const gchar **values);
|
||||
const gchar **values,
|
||||
SvgParser *parser);
|
||||
|
||||
static SvgHandler svg_handlers[] =
|
||||
static const SvgHandler svg_handlers[] =
|
||||
{
|
||||
{ "svg", 0, 0, NULL, NULL, NULL, svg_handler_svg },
|
||||
{ "g", 0, 0, NULL, NULL, NULL, svg_handler_group },
|
||||
{ "path", 0, 0, NULL, NULL, NULL, svg_handler_path },
|
||||
{ NULL, 0, 0, NULL, NULL, NULL, NULL }
|
||||
{ "svg", svg_handler_svg },
|
||||
{ "g", svg_handler_group },
|
||||
{ "path",svg_handler_path }
|
||||
};
|
||||
|
||||
|
||||
static gboolean parse_svg_length (const gchar *value,
|
||||
gdouble reference,
|
||||
gdouble resolution,
|
||||
gdouble *scale,
|
||||
gdouble *length);
|
||||
static gboolean parse_svg_viewbox (const gchar *value,
|
||||
gdouble width,
|
||||
gdouble height,
|
||||
|
@ -135,8 +157,8 @@ gimp_vectors_import (GimpImage *image,
|
|||
gboolean merge,
|
||||
GError **error)
|
||||
{
|
||||
GimpXmlParser *parser;
|
||||
GQueue *stack;
|
||||
GimpXmlParser *xml_parser;
|
||||
SvgParser parser;
|
||||
GList *paths;
|
||||
SvgHandler *base;
|
||||
gboolean success = TRUE;
|
||||
|
@ -145,7 +167,8 @@ gimp_vectors_import (GimpImage *image,
|
|||
g_return_val_if_fail (filename != NULL, FALSE);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
|
||||
stack = g_queue_new ();
|
||||
parser.stack = g_queue_new ();
|
||||
parser.image = image;
|
||||
|
||||
/* the base of the stack, defines the size of the view-port */
|
||||
base = g_new0 (SvgHandler, 1);
|
||||
|
@ -153,13 +176,13 @@ gimp_vectors_import (GimpImage *image,
|
|||
base->width = image->width;
|
||||
base->height = image->height;
|
||||
|
||||
g_queue_push_head (stack, base);
|
||||
g_queue_push_head (parser.stack, base);
|
||||
|
||||
parser = gimp_xml_parser_new (&markup_parser, stack);
|
||||
xml_parser = gimp_xml_parser_new (&markup_parser, &parser);
|
||||
|
||||
success = gimp_xml_parser_parse_file (parser, filename, error);
|
||||
success = gimp_xml_parser_parse_file (xml_parser, filename, error);
|
||||
|
||||
gimp_xml_parser_free (parser);
|
||||
gimp_xml_parser_free (xml_parser);
|
||||
|
||||
if (success)
|
||||
{
|
||||
|
@ -210,7 +233,7 @@ gimp_vectors_import (GimpImage *image,
|
|||
}
|
||||
}
|
||||
|
||||
while ((base = g_queue_pop_head (stack)) != NULL)
|
||||
while ((base = g_queue_pop_head (parser.stack)) != NULL)
|
||||
{
|
||||
for (paths = base->paths; paths; paths = paths->next)
|
||||
{
|
||||
|
@ -229,7 +252,7 @@ gimp_vectors_import (GimpImage *image,
|
|||
g_free (base);
|
||||
}
|
||||
|
||||
g_queue_free (stack);
|
||||
g_queue_free (parser.stack);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
@ -242,29 +265,32 @@ svg_parser_start_element (GMarkupParseContext *context,
|
|||
gpointer user_data,
|
||||
GError **error)
|
||||
{
|
||||
GQueue *stack = user_data;
|
||||
SvgParser *parser = user_data;
|
||||
SvgHandler *handler;
|
||||
SvgHandler *base;
|
||||
SvgHandler *handler = NULL;
|
||||
gint i;
|
||||
gint i = 0;
|
||||
|
||||
base = g_queue_peek_head (stack);
|
||||
handler = g_new0 (SvgHandler, 1);
|
||||
base = g_queue_peek_head (parser->stack);
|
||||
|
||||
for (i = 0; !handler && i < G_N_ELEMENTS (svg_handlers); i++)
|
||||
{
|
||||
if (svg_handlers[i].name == NULL)
|
||||
handler = svg_handlers + i;
|
||||
else if (strcmp (svg_handlers[i].name, element_name) == 0)
|
||||
handler = svg_handlers + i;
|
||||
}
|
||||
if (!base->width || !base->height)
|
||||
i = G_N_ELEMENTS (svg_handlers);
|
||||
|
||||
for (; i < G_N_ELEMENTS (svg_handlers); i++)
|
||||
if (strcmp (svg_handlers[i].name, element_name) == 0)
|
||||
{
|
||||
handler->name = svg_handlers[i].name;
|
||||
handler->start = svg_handlers[i].start;
|
||||
break;
|
||||
}
|
||||
|
||||
handler = g_memdup (handler, sizeof (SvgHandler));
|
||||
handler->width = base->width;
|
||||
handler->height = base->height;
|
||||
|
||||
g_queue_push_head (stack, handler);
|
||||
g_queue_push_head (parser->stack, handler);
|
||||
|
||||
if (handler->start)
|
||||
handler->start (handler, attribute_names, attribute_values);
|
||||
handler->start (handler, attribute_names, attribute_values, parser);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -273,12 +299,16 @@ svg_parser_end_element (GMarkupParseContext *context,
|
|||
gpointer user_data,
|
||||
GError **error)
|
||||
{
|
||||
GQueue *stack = user_data;
|
||||
SvgParser *parser = user_data;
|
||||
SvgHandler *handler;
|
||||
SvgHandler *base;
|
||||
GList *paths;
|
||||
|
||||
handler = g_queue_pop_head (stack);
|
||||
handler = g_queue_pop_head (parser->stack);
|
||||
|
||||
g_return_if_fail (handler != NULL &&
|
||||
(handler->name == NULL ||
|
||||
strcmp (handler->name, element_name) == 0));
|
||||
|
||||
if (handler->paths)
|
||||
{
|
||||
|
@ -297,7 +327,7 @@ svg_parser_end_element (GMarkupParseContext *context,
|
|||
g_free (handler->transform);
|
||||
}
|
||||
|
||||
base = g_queue_peek_head (stack);
|
||||
base = g_queue_peek_head (parser->stack);
|
||||
base->paths = g_list_concat (base->paths, handler->paths);
|
||||
}
|
||||
|
||||
|
@ -307,27 +337,82 @@ svg_parser_end_element (GMarkupParseContext *context,
|
|||
static void
|
||||
svg_handler_svg (SvgHandler *handler,
|
||||
const gchar **names,
|
||||
const gchar **values)
|
||||
const gchar **values,
|
||||
SvgParser *parser)
|
||||
{
|
||||
GimpMatrix3 *matrix;
|
||||
GimpMatrix3 box;
|
||||
const gchar *viewbox = NULL;
|
||||
gdouble x = 0;
|
||||
gdouble y = 0;
|
||||
gdouble w = handler->width;
|
||||
gdouble h = handler->height;
|
||||
gdouble xscale = 1.0;
|
||||
gdouble yscale = 1.0;
|
||||
|
||||
matrix = g_new (GimpMatrix3, 1);
|
||||
gimp_matrix3_identity (matrix);
|
||||
|
||||
while (*names)
|
||||
{
|
||||
if (strcmp (*names, "viewBox") == 0 && !handler->transform)
|
||||
if (strcmp (*names, "x") == 0)
|
||||
{
|
||||
GimpMatrix3 matrix;
|
||||
|
||||
if (parse_svg_viewbox (*values,
|
||||
handler->width, handler->height, &matrix))
|
||||
handler->transform = g_memdup (&matrix, sizeof (GimpMatrix3));
|
||||
parse_svg_length (*values,
|
||||
handler->width, parser->image->xresolution,
|
||||
NULL, &x);
|
||||
}
|
||||
else if (strcmp (*names, "y") == 0)
|
||||
{
|
||||
parse_svg_length (*values,
|
||||
handler->height, parser->image->yresolution,
|
||||
NULL, &y);
|
||||
}
|
||||
else if (strcmp (*names, "width") == 0)
|
||||
{
|
||||
parse_svg_length (*values,
|
||||
handler->width, parser->image->xresolution,
|
||||
&xscale, &w);
|
||||
}
|
||||
else if (strcmp (*names, "height") == 0)
|
||||
{
|
||||
parse_svg_length (*values,
|
||||
handler->height, parser->image->yresolution,
|
||||
&yscale, &h);
|
||||
}
|
||||
else if (strcmp (*names, "viewBox") == 0)
|
||||
{
|
||||
viewbox = *values;
|
||||
}
|
||||
|
||||
names++;
|
||||
values++;
|
||||
}
|
||||
|
||||
handler->width = w;
|
||||
handler->height = h;
|
||||
|
||||
gimp_matrix3_scale (matrix, xscale, yscale);
|
||||
|
||||
if (x || y)
|
||||
{
|
||||
SvgHandler *base = g_queue_peek_head (parser->stack);
|
||||
|
||||
/* according to the spec offsets are meaningless on the outermost svg */
|
||||
if (strcmp (base->name, "image"))
|
||||
gimp_matrix3_translate (matrix, x, y);
|
||||
}
|
||||
|
||||
if (viewbox && parse_svg_viewbox (viewbox, w, h, &box))
|
||||
gimp_matrix3_mult (&box, matrix);
|
||||
|
||||
handler->transform = matrix;
|
||||
}
|
||||
|
||||
static void
|
||||
svg_handler_group (SvgHandler *handler,
|
||||
const gchar **names,
|
||||
const gchar **values)
|
||||
const gchar **values,
|
||||
SvgParser *parser)
|
||||
{
|
||||
while (*names)
|
||||
{
|
||||
|
@ -347,7 +432,8 @@ svg_handler_group (SvgHandler *handler,
|
|||
static void
|
||||
svg_handler_path (SvgHandler *handler,
|
||||
const gchar **names,
|
||||
const gchar **values)
|
||||
const gchar **values,
|
||||
SvgParser *parser)
|
||||
{
|
||||
SvgPath *path = g_new0 (SvgPath, 1);
|
||||
|
||||
|
@ -376,53 +462,126 @@ svg_handler_path (SvgHandler *handler,
|
|||
handler->paths = g_list_prepend (handler->paths, path);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_svg_length (const gchar *value,
|
||||
gdouble reference,
|
||||
gdouble resolution,
|
||||
gdouble *scale,
|
||||
gdouble *length)
|
||||
{
|
||||
GimpUnit unit = GIMP_UNIT_PIXEL;
|
||||
gdouble len;
|
||||
gchar *ptr;
|
||||
|
||||
len = g_ascii_strtod (value, &ptr);
|
||||
|
||||
while (g_ascii_isspace (*ptr))
|
||||
ptr++;
|
||||
|
||||
switch (ptr[0])
|
||||
{
|
||||
case '\0':
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
switch (ptr[1])
|
||||
{
|
||||
case 'x': break;
|
||||
case 't': unit = GIMP_UNIT_POINT; break;
|
||||
case 'c': unit = GIMP_UNIT_PICA; break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
if (ptr[1] == 'm')
|
||||
len *= 10.0, unit = GIMP_UNIT_MM;
|
||||
else
|
||||
return FALSE;
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
if (ptr[1] == 'm')
|
||||
unit = GIMP_UNIT_MM;
|
||||
else
|
||||
return FALSE;
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
if (ptr[1] == 'n')
|
||||
unit = GIMP_UNIT_INCH;
|
||||
else
|
||||
return FALSE;
|
||||
ptr += 2;
|
||||
break;
|
||||
|
||||
case '%':
|
||||
unit = GIMP_UNIT_PERCENT;
|
||||
ptr += 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
while (g_ascii_isspace (*ptr))
|
||||
ptr++;
|
||||
|
||||
if (*ptr)
|
||||
return FALSE;
|
||||
|
||||
switch (unit)
|
||||
{
|
||||
case GIMP_UNIT_PERCENT:
|
||||
*scale = len / 100.0;
|
||||
*length = len * reference / 100.0;
|
||||
break;
|
||||
|
||||
default:
|
||||
len = len / gimp_unit_get_factor (unit) * resolution;
|
||||
case GIMP_UNIT_PIXEL:
|
||||
*scale = len / reference;
|
||||
*length = len;
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
parse_svg_viewbox (const gchar *value,
|
||||
gdouble width,
|
||||
gdouble height,
|
||||
GimpMatrix3 *matrix)
|
||||
{
|
||||
gdouble x, y, w, h;
|
||||
gchar *tok;
|
||||
gchar *str = g_strdup (value);
|
||||
gboolean success = FALSE;
|
||||
gdouble args[4];
|
||||
gchar *ptr;
|
||||
gint i;
|
||||
|
||||
x = y = w = h = 0;
|
||||
for (i = 0; *value && i < 4; i++)
|
||||
{
|
||||
args[i] = g_ascii_strtod (value, &ptr);
|
||||
|
||||
while (*ptr == ',' || g_ascii_isspace (*ptr))
|
||||
ptr++;
|
||||
|
||||
value = ptr;
|
||||
}
|
||||
|
||||
if (i < 4)
|
||||
return FALSE;
|
||||
|
||||
gimp_matrix3_identity (matrix);
|
||||
|
||||
tok = strtok (str, ", \t");
|
||||
if (tok)
|
||||
{
|
||||
x = g_ascii_strtod (tok, NULL);
|
||||
tok = strtok (NULL, ", \t");
|
||||
if (tok)
|
||||
{
|
||||
y = g_ascii_strtod (tok, NULL);
|
||||
tok = strtok (NULL, ", \t");
|
||||
if (tok != NULL)
|
||||
{
|
||||
w = g_ascii_strtod (tok, NULL);
|
||||
tok = strtok (NULL, ", \t");
|
||||
if (tok)
|
||||
{
|
||||
h = g_ascii_strtod (tok, NULL);
|
||||
success = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (args[0] || args[1])
|
||||
gimp_matrix3_translate (matrix, args[0], args[1]);
|
||||
|
||||
g_free (str);
|
||||
|
||||
if (!success)
|
||||
return FALSE;
|
||||
|
||||
if (x || y)
|
||||
gimp_matrix3_translate (matrix, x, y);
|
||||
|
||||
if (w && h)
|
||||
gimp_matrix3_scale (matrix, width / w, height / h);
|
||||
if (args[2] && args[3])
|
||||
gimp_matrix3_scale (matrix, width / args[2], height / args[3]);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue