summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog-2000041418
-rw-r--r--Makefile.am1
-rw-r--r--components/help/Makefile.am1
-rw-r--r--components/history/Makefile.am1
-rw-r--r--components/html/Makefile.am1
-rw-r--r--components/music/Makefile.am1
-rw-r--r--components/notes/Makefile.am1
-rw-r--r--components/rpmview/Makefile.am1
-rw-r--r--components/sample/Makefile.am1
-rw-r--r--components/services/startup/Makefile.am1
-rw-r--r--components/websearch/Makefile.am1
-rw-r--r--configure.in1
-rw-r--r--icons/eazel/Makefile.am1
-rw-r--r--icons/eazel/i-directory-accept.svg13
-rw-r--r--libnautilus-extensions/nautilus-icon-factory.c53
-rw-r--r--libnautilus-private/nautilus-icon-factory.c53
-rw-r--r--libnautilus/nautilus-icon-factory.c53
-rw-r--r--librsvg/Makefile.am30
-rw-r--r--librsvg/art_rgba.c236
-rw-r--r--librsvg/art_rgba.h13
-rw-r--r--librsvg/art_rgba_svp.c257
-rw-r--r--librsvg/art_rgba_svp.h13
-rw-r--r--librsvg/rsvg-bpath-util.c172
-rw-r--r--librsvg/rsvg-bpath-util.h37
-rw-r--r--librsvg/rsvg-bpath.c168
-rw-r--r--librsvg/rsvg-path.c611
-rw-r--r--librsvg/rsvg-path.h2
-rw-r--r--librsvg/rsvg.c771
-rw-r--r--librsvg/rsvg.h2
-rw-r--r--src/Makefile.am1
30 files changed, 2497 insertions, 18 deletions
diff --git a/ChangeLog-20000414 b/ChangeLog-20000414
index 7aef154ab..4b2a84b31 100644
--- a/ChangeLog-20000414
+++ b/ChangeLog-20000414
@@ -1,3 +1,21 @@
+2000-04-13 Andy Hertzfeld <andy@eazel.com>
+
+ added Raph's scalable icon machinery. Right now the only way to see it
+ is to use the eazel theme and drag a file over a folder; the open folder
+ is a vector icon. Soon we'll add lots more.
+
+ * librsvg/*
+ Raph's new library
+ * libnautilus/nautilus-icon-factory.c:
+ integrate vector icons with the icon factory. Right now, it prefers
+ them to bitmapped ones, which is probably wrong.
+ * Makefile.am: added librsvg
+ * configure.in: added librsvg
+ * src/Makefile.am: link with librsvg
+ * components/*/Makefile.am: link with librsvg
+ * icons/eazel/Makefile.am: added i-directory-accept.svg
+ * icons/eazel/i-directory-accept.svg: our sole vector icon
+
2000-04-13 Darin Adler <darin@eazel.com>
* components/html/ntl-web-browser.c: (browser_vfs_read_callback):
diff --git a/Makefile.am b/Makefile.am
index 7df87195b..bbd7cfb73 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,6 +1,7 @@
NULL=
SUBDIRS =\
+ librsvg \
libnautilus \
nautilus-widgets \
src \
diff --git a/components/help/Makefile.am b/components/help/Makefile.am
index e9a89eff2..57794f9f1 100644
--- a/components/help/Makefile.am
+++ b/components/help/Makefile.am
@@ -12,6 +12,7 @@ INCLUDES=-I$(top_srcdir) -I$(top_builddir) \
LDADD =\
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
+ $(top_builddir)/librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS) \
$(XML_LIBS)
diff --git a/components/history/Makefile.am b/components/history/Makefile.am
index 3c6589138..f1959f325 100644
--- a/components/history/Makefile.am
+++ b/components/history/Makefile.am
@@ -5,6 +5,7 @@ INCLUDES=-I$(top_srcdir) -I$(top_builddir) $(GNOMEUI_CFLAGS) $(BONOBO_CFLAGS) $(
LDADD=\
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
+ $(top_builddir)/librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS)
diff --git a/components/html/Makefile.am b/components/html/Makefile.am
index 9aad4571c..26b491a55 100644
--- a/components/html/Makefile.am
+++ b/components/html/Makefile.am
@@ -31,6 +31,7 @@ ntl_web_browser_SOURCES = \
ntl_web_browser_LDFLAGS = \
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
+ $(top_builddir)/librsvg/librsvg.la \
$(GTKHTML_LIBS) \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS) \
diff --git a/components/music/Makefile.am b/components/music/Makefile.am
index fc5b0ed3c..f1308f18e 100644
--- a/components/music/Makefile.am
+++ b/components/music/Makefile.am
@@ -25,6 +25,7 @@ nautilus_music_view_SOURCES = \
nautilus_music_view_LDFLAGS = \
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
+ $(top_builddir)/librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS) \
$(VFS_LIBS) \
diff --git a/components/notes/Makefile.am b/components/notes/Makefile.am
index 90ce0f243..4d0e16009 100644
--- a/components/notes/Makefile.am
+++ b/components/notes/Makefile.am
@@ -5,6 +5,7 @@ INCLUDES=-I$(top_srcdir) -I$(top_builddir) $(GNOMEUI_CFLAGS) $(BONOBO_CFLAGS) $(
LDADD=\
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
+ $(top_builddir)/librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS)
diff --git a/components/rpmview/Makefile.am b/components/rpmview/Makefile.am
index 1e3cc7007..48becb179 100644
--- a/components/rpmview/Makefile.am
+++ b/components/rpmview/Makefile.am
@@ -26,6 +26,7 @@ nautilus_rpm_view_SOURCES = \
nautilus_rpm_view_LDFLAGS = \
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
+ $(top_builddir)/librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS) \
-lrpm \
diff --git a/components/sample/Makefile.am b/components/sample/Makefile.am
index 13eccd892..82efb0c27 100644
--- a/components/sample/Makefile.am
+++ b/components/sample/Makefile.am
@@ -24,6 +24,7 @@ nautilus_sample_content_view_SOURCES = \
nautilus_sample_content_view_LDFLAGS = \
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
+ $(top_builddir)/librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS) \
$(VFS_LIBS) \
diff --git a/components/services/startup/Makefile.am b/components/services/startup/Makefile.am
index 518ba52dc..c68f3fbef 100644
--- a/components/services/startup/Makefile.am
+++ b/components/services/startup/Makefile.am
@@ -28,6 +28,7 @@ nautilus_service_startup_view_SOURCES = \
nautilus_service_startup_view_LDFLAGS = \
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
+ $(top_builddir)/librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS) \
$(VFS_LIBS) \
diff --git a/components/websearch/Makefile.am b/components/websearch/Makefile.am
index 56d6046d1..448f1646d 100644
--- a/components/websearch/Makefile.am
+++ b/components/websearch/Makefile.am
@@ -5,6 +5,7 @@ INCLUDES=-I$(top_srcdir) -I$(top_builddir) $(GNOMEUI_CFLAGS) $(BONOBO_CFLAGS) $(
LDADD=\
$(top_builddir)/nautilus-widgets/libnautilus-widgets.la \
$(top_builddir)/libnautilus/libnautilus.la \
+ $(top_builddir)/librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNOMEUI_LIBS)
diff --git a/configure.in b/configure.in
index 087023daa..0a301dc86 100644
--- a/configure.in
+++ b/configure.in
@@ -152,6 +152,7 @@ data/Makefile
data/mime/Makefile
data/top/Makefile
idl/Makefile
+librsvg/Makefile
libnautilus/Makefile
nautilus-widgets/Makefile
src/Makefile
diff --git a/icons/eazel/Makefile.am b/icons/eazel/Makefile.am
index 2e4c782c9..a03147739 100644
--- a/icons/eazel/Makefile.am
+++ b/icons/eazel/Makefile.am
@@ -9,6 +9,7 @@ eazel_DATA = \
i-directory-accept.png \
i-directory-accept-36.png \
i-directory-accept-72.png \
+ i-directory-accept.svg \
i-regular.png \
i-regular.xml \
i-regular-36.png \
diff --git a/icons/eazel/i-directory-accept.svg b/icons/eazel/i-directory-accept.svg
new file mode 100644
index 000000000..4e9511668
--- /dev/null
+++ b/icons/eazel/i-directory-accept.svg
@@ -0,0 +1,13 @@
+<svg width="70" height="42">
+ <!-- Open folder 1.eps converted by ill2svg.pl -->
+ <g style="fill: #000000">
+ <path d="M9.54 41.4L60.9 41.4C60.9 41.4 69.156 12.216 69.348 11.544C69.54 10.872 69.924 9.816 68.916 9.048C68.366 8.628 66.852 8.568 65.316 8.664C63.78 8.76 52.26 8.664 52.26 8.664C52.26 8.664 52.452 6.552 51.684 5.88C50.916 5.208 50.244 4.728 46.884 4.824C43.524 4.92 22.884 4.824 22.884 4.824C22.884 4.824 22.404 5.4 21.54 4.056C20.676 2.712 19.908 0.792 18.948 0.696C17.988 0.6 6.468 0.696 6.468 0.696C6.468 0.696 5.06 0.696 4.772 2.04C4.484 3.384 4.459 4.253 4.164 4.632C3.716 5.208 3.14 4.984 2.468 5.08C1.796 5.176 0.196 4.92 0.132 6.552C0.076 7.994 2.148 14.616 2.244 15.288C2.34 15.96 9.54 41.4 9.54 41.4z"/>
+ </g>
+ <g style="fill: #ffffc0; stroke:#000000; stroke-width:0.6">
+ <path d="M10.5 40.056L17.86 13.88C17.86 13.88 18.228 12.504 19.588 12.6C20.61 12.672 37.38 12.664 37.892 12.664C38.404 12.664 38.9 12.081 40.068 10.936C41.652 9.384 42.308 9.848 43.396 9.784C44.355 9.728 67.012 9.848 67.012 9.848C67.012 9.848 68.292 10.104 68.036 11.064C67.78 12.024 59.716 40.184 59.716 40.184L10.5 40.056z"/>
+ </g>
+ <g style="fill: #ffc040; stroke:#000000; stroke-width:0.6">
+ <path d="M9.732 37.08C9.732 37.08 16.26 13.656 16.452 13.272C16.644 12.888 17.268 11.352 18.468 11.256C19.138 11.202 37.476 11.256 37.476 11.256C37.476 11.256 37.956 11.544 39.3 10.008C40.644 8.472 40.932 8.472 45.348 8.472C49.764 8.472 51.012 8.568 51.012 8.568C51.012 8.568 51.108 7.608 50.82 7.032C50.532 6.456 50.244 6.072 48.228 6.072C46.212 6.072 23.076 6.072 22.5 6.072C21.924 6.072 20.868 6.264 20.1 4.92C19.332 3.576 19.236 1.944 17.412 1.944C15.588 1.944 6.756 1.944 6.756 1.944C6.756 1.944 6.116 1.976 5.924 2.552C5.732 3.128 5.732 4.728 5.156 5.4C4.58 6.072 4.563 5.978 3.62 6.072C2.66 6.168 1.476 6.04 1.476 7.224C1.476 8.664 9.732 37.08 9.732 37.08z"/>
+ </g>
+</svg>
+
diff --git a/libnautilus-extensions/nautilus-icon-factory.c b/libnautilus-extensions/nautilus-icon-factory.c
index b446342f5..23fa14590 100644
--- a/libnautilus-extensions/nautilus-icon-factory.c
+++ b/libnautilus-extensions/nautilus-icon-factory.c
@@ -41,6 +41,8 @@
#include <parser.h>
#include <xmlmemory.h>
+#include "librsvg/rsvg.h"
+
#include "nautilus-string.h"
#include "nautilus-default-file-icon.h"
#include "nautilus-metadata.h"
@@ -54,6 +56,8 @@
static const char *icon_file_name_suffixes[] =
{
"",
+ ".svg",
+ ".SVG",
".png",
".PNG",
".gif",
@@ -507,6 +511,18 @@ make_full_icon_path (const char *path, const char *suffix)
return full_path;
}
+/* Return true if the given suffix is a scalable image. */
+static gboolean
+suffix_is_scalable (const char *path)
+{
+ const char *suffix;
+
+ suffix = (const char *)strrchr (path, '.');
+ if (suffix == NULL)
+ return FALSE;
+ return (!strcmp (suffix, ".svg") || !strcmp (suffix, ".SVG"));
+}
+
/* Pick a particular icon to use, trying all the various suffixes.
* Return the path of the icon or NULL if no icon is found.
*/
@@ -535,11 +551,15 @@ get_themed_icon_file_path (const char *theme_name,
/* Try each suffix. */
for (i = 0; i < NAUTILUS_N_ELEMENTS (icon_file_name_suffixes); i++) {
- /* Build a path for this icon. */
- partial_path = g_strdup_printf ("%s%s%.0u",
- themed_icon_name,
- include_size ? "-" : "",
- include_size ? icon_size : 0);
+ if (include_size &&
+ !suffix_is_scalable (icon_file_name_suffixes[i])) {
+ /* Build a path for this icon. */
+ partial_path = g_strdup_printf ("%s-%u",
+ themed_icon_name,
+ icon_size);
+ } else {
+ partial_path = g_strdup (themed_icon_name);
+ }
path = make_full_icon_path (partial_path,
icon_file_name_suffixes[i]);
@@ -1011,6 +1031,24 @@ get_next_icon_size_to_try (guint target_size, guint *current_size)
return FALSE;
}
+/* This loads an SVG image, scaling it to the appropriate size. */
+static GdkPixbuf *
+load_specific_image_svg (const char *path, guint size_in_pixels)
+{
+ FILE *f;
+ GdkPixbuf *result;
+
+ f = fopen (path, "r");
+ if (f == NULL) {
+ return NULL;
+ }
+ result = rsvg_render_file (f, size_in_pixels *
+ (1.0 / NAUTILUS_ICON_SIZE_STANDARD));
+ fclose (f);
+
+ return result;
+}
+
/* This load function returns NULL if the icon is not available at this size. */
static GdkPixbuf *
load_specific_image (NautilusScalableIcon *scalable_icon,
@@ -1046,7 +1084,10 @@ load_specific_image (NautilusScalableIcon *scalable_icon,
if (path == NULL) {
return NULL;
}
- image = gdk_pixbuf_new_from_file (path);
+ if (suffix_is_scalable (path))
+ image = load_specific_image_svg (path, size_in_pixels);
+ else
+ image = gdk_pixbuf_new_from_file (path);
g_free (path);
return image;
}
diff --git a/libnautilus-private/nautilus-icon-factory.c b/libnautilus-private/nautilus-icon-factory.c
index b446342f5..23fa14590 100644
--- a/libnautilus-private/nautilus-icon-factory.c
+++ b/libnautilus-private/nautilus-icon-factory.c
@@ -41,6 +41,8 @@
#include <parser.h>
#include <xmlmemory.h>
+#include "librsvg/rsvg.h"
+
#include "nautilus-string.h"
#include "nautilus-default-file-icon.h"
#include "nautilus-metadata.h"
@@ -54,6 +56,8 @@
static const char *icon_file_name_suffixes[] =
{
"",
+ ".svg",
+ ".SVG",
".png",
".PNG",
".gif",
@@ -507,6 +511,18 @@ make_full_icon_path (const char *path, const char *suffix)
return full_path;
}
+/* Return true if the given suffix is a scalable image. */
+static gboolean
+suffix_is_scalable (const char *path)
+{
+ const char *suffix;
+
+ suffix = (const char *)strrchr (path, '.');
+ if (suffix == NULL)
+ return FALSE;
+ return (!strcmp (suffix, ".svg") || !strcmp (suffix, ".SVG"));
+}
+
/* Pick a particular icon to use, trying all the various suffixes.
* Return the path of the icon or NULL if no icon is found.
*/
@@ -535,11 +551,15 @@ get_themed_icon_file_path (const char *theme_name,
/* Try each suffix. */
for (i = 0; i < NAUTILUS_N_ELEMENTS (icon_file_name_suffixes); i++) {
- /* Build a path for this icon. */
- partial_path = g_strdup_printf ("%s%s%.0u",
- themed_icon_name,
- include_size ? "-" : "",
- include_size ? icon_size : 0);
+ if (include_size &&
+ !suffix_is_scalable (icon_file_name_suffixes[i])) {
+ /* Build a path for this icon. */
+ partial_path = g_strdup_printf ("%s-%u",
+ themed_icon_name,
+ icon_size);
+ } else {
+ partial_path = g_strdup (themed_icon_name);
+ }
path = make_full_icon_path (partial_path,
icon_file_name_suffixes[i]);
@@ -1011,6 +1031,24 @@ get_next_icon_size_to_try (guint target_size, guint *current_size)
return FALSE;
}
+/* This loads an SVG image, scaling it to the appropriate size. */
+static GdkPixbuf *
+load_specific_image_svg (const char *path, guint size_in_pixels)
+{
+ FILE *f;
+ GdkPixbuf *result;
+
+ f = fopen (path, "r");
+ if (f == NULL) {
+ return NULL;
+ }
+ result = rsvg_render_file (f, size_in_pixels *
+ (1.0 / NAUTILUS_ICON_SIZE_STANDARD));
+ fclose (f);
+
+ return result;
+}
+
/* This load function returns NULL if the icon is not available at this size. */
static GdkPixbuf *
load_specific_image (NautilusScalableIcon *scalable_icon,
@@ -1046,7 +1084,10 @@ load_specific_image (NautilusScalableIcon *scalable_icon,
if (path == NULL) {
return NULL;
}
- image = gdk_pixbuf_new_from_file (path);
+ if (suffix_is_scalable (path))
+ image = load_specific_image_svg (path, size_in_pixels);
+ else
+ image = gdk_pixbuf_new_from_file (path);
g_free (path);
return image;
}
diff --git a/libnautilus/nautilus-icon-factory.c b/libnautilus/nautilus-icon-factory.c
index b446342f5..23fa14590 100644
--- a/libnautilus/nautilus-icon-factory.c
+++ b/libnautilus/nautilus-icon-factory.c
@@ -41,6 +41,8 @@
#include <parser.h>
#include <xmlmemory.h>
+#include "librsvg/rsvg.h"
+
#include "nautilus-string.h"
#include "nautilus-default-file-icon.h"
#include "nautilus-metadata.h"
@@ -54,6 +56,8 @@
static const char *icon_file_name_suffixes[] =
{
"",
+ ".svg",
+ ".SVG",
".png",
".PNG",
".gif",
@@ -507,6 +511,18 @@ make_full_icon_path (const char *path, const char *suffix)
return full_path;
}
+/* Return true if the given suffix is a scalable image. */
+static gboolean
+suffix_is_scalable (const char *path)
+{
+ const char *suffix;
+
+ suffix = (const char *)strrchr (path, '.');
+ if (suffix == NULL)
+ return FALSE;
+ return (!strcmp (suffix, ".svg") || !strcmp (suffix, ".SVG"));
+}
+
/* Pick a particular icon to use, trying all the various suffixes.
* Return the path of the icon or NULL if no icon is found.
*/
@@ -535,11 +551,15 @@ get_themed_icon_file_path (const char *theme_name,
/* Try each suffix. */
for (i = 0; i < NAUTILUS_N_ELEMENTS (icon_file_name_suffixes); i++) {
- /* Build a path for this icon. */
- partial_path = g_strdup_printf ("%s%s%.0u",
- themed_icon_name,
- include_size ? "-" : "",
- include_size ? icon_size : 0);
+ if (include_size &&
+ !suffix_is_scalable (icon_file_name_suffixes[i])) {
+ /* Build a path for this icon. */
+ partial_path = g_strdup_printf ("%s-%u",
+ themed_icon_name,
+ icon_size);
+ } else {
+ partial_path = g_strdup (themed_icon_name);
+ }
path = make_full_icon_path (partial_path,
icon_file_name_suffixes[i]);
@@ -1011,6 +1031,24 @@ get_next_icon_size_to_try (guint target_size, guint *current_size)
return FALSE;
}
+/* This loads an SVG image, scaling it to the appropriate size. */
+static GdkPixbuf *
+load_specific_image_svg (const char *path, guint size_in_pixels)
+{
+ FILE *f;
+ GdkPixbuf *result;
+
+ f = fopen (path, "r");
+ if (f == NULL) {
+ return NULL;
+ }
+ result = rsvg_render_file (f, size_in_pixels *
+ (1.0 / NAUTILUS_ICON_SIZE_STANDARD));
+ fclose (f);
+
+ return result;
+}
+
/* This load function returns NULL if the icon is not available at this size. */
static GdkPixbuf *
load_specific_image (NautilusScalableIcon *scalable_icon,
@@ -1046,7 +1084,10 @@ load_specific_image (NautilusScalableIcon *scalable_icon,
if (path == NULL) {
return NULL;
}
- image = gdk_pixbuf_new_from_file (path);
+ if (suffix_is_scalable (path))
+ image = load_specific_image_svg (path, size_in_pixels);
+ else
+ image = gdk_pixbuf_new_from_file (path);
g_free (path);
return image;
}
diff --git a/librsvg/Makefile.am b/librsvg/Makefile.am
new file mode 100644
index 000000000..670f0bf3c
--- /dev/null
+++ b/librsvg/Makefile.am
@@ -0,0 +1,30 @@
+NULL=
+
+lib_LTLIBRARIES=librsvg.la
+
+INCLUDES=-I$(top_srcdir) -I$(top_builddir) \
+ $(GNOME_CFLAGS) \
+ $(GNOMECANVASPIXBUF_INCLUDEDIR) \
+ $(XML_CFLAGS) \
+ $(WERROR) \
+ -D_REENTRANT
+
+librsvg_la_LDFLAGS=\
+ $(GNOME_LIBS) \
+ $(GNOMECANVASPIXBUF_LIBS) \
+ $(XML_LIBS) \
+ $(LIBPNG)
+
+librsvgincludedir=$(includedir)/librsvg
+librsvginclude_HEADERS= \
+ rsvg.h \
+ $(NULL)
+
+librsvg_la_SOURCES= \
+ rsvg.c \
+ rsvg-path.c \
+ rsvg-bpath-util.c \
+ art_rgba.c \
+ art_rgba_svp.c \
+ $(NULL)
+
diff --git a/librsvg/art_rgba.c b/librsvg/art_rgba.c
new file mode 100644
index 000000000..611746b3d
--- /dev/null
+++ b/librsvg/art_rgba.c
@@ -0,0 +1,236 @@
+#include <libart_lgpl/art_misc.h>
+#include "art_rgba.h"
+
+#define ART_OPTIMIZE_SPACE
+
+#ifndef ART_OPTIMIZE_SPACE
+#include "art_rgba_table.c"
+#endif
+
+/**
+ * art_rgba_rgba_composite: Composite RGBA image over RGBA buffer.
+ * @dst: Destination RGBA buffer.
+ * @src: Source RGBA buffer.
+ * @n: Number of RGBA pixels to composite.
+ *
+ * Composites the RGBA pixels in @dst over the @src buffer.
+ **/
+void
+art_rgba_rgba_composite (art_u8 *dst, const art_u8 *src, int n)
+{
+ int i;
+#ifdef WORDS_BIGENDIAN
+ art_u32 src_rgba, dst_rgba;
+#else
+ art_u32 src_abgr, dst_abgr;
+#endif
+ art_u8 src_alpha, dst_alpha;
+
+ for (i = 0; i < n; i++)
+ {
+#ifdef WORDS_BIGENDIAN
+ src_rgba = ((art_u32 *)src)[i];
+ src_alpha = src_rgba & 0xff;
+#else
+ src_abgr = ((art_u32 *)src)[i];
+ src_alpha = (src_abgr >> 24) & 0xff;
+#endif
+ if (src_alpha)
+ {
+ if (src_alpha == 0xff ||
+ (
+#ifdef WORDS_BIGENDIAN
+ dst_rgba = ((art_u32 *)dst)[i],
+ dst_alpha = dst_rgba & 0xff,
+#else
+ dst_abgr = ((art_u32 *)dst)[i],
+ dst_alpha = (dst_abgr >> 24),
+#endif
+ dst_alpha == 0))
+#ifdef WORDS_BIGENDIAN
+ ((art_u32 *)dst)[i] = src_rgba;
+#else
+ ((art_u32 *)dst)[i] = src_abgr;
+#endif
+ else
+ {
+ int r, g, b, a;
+ int src_r, src_g, src_b;
+ int dst_r, dst_g, dst_b;
+ int tmp;
+ int c;
+
+#ifdef ART_OPTIMIZE_SPACE
+ tmp = (255 - src_alpha) * (255 - dst_alpha) + 0x80;
+ a = 255 - ((tmp + (tmp >> 8)) >> 8);
+ c = ((src_alpha << 16) + (a >> 1)) / a;
+#else
+ tmp = art_rgba_composite_table[(src_alpha << 8) + dst_alpha];
+ c = tmp & 0x1ffff;
+ a = tmp >> 24;
+#endif
+#ifdef WORDS_BIGENDIAN
+ src_r = (src_rgba >> 24) & 0xff;
+ src_g = (src_rgba >> 16) & 0xff;
+ src_b = (src_rgba >> 8) & 0xff;
+ dst_r = (dst_rgba >> 24) & 0xff;
+ dst_g = (dst_rgba >> 16) & 0xff;
+ dst_b = (dst_rgba >> 8) & 0xff;
+#else
+ src_r = src_abgr & 0xff;
+ src_g = (src_abgr >> 8) & 0xff;
+ src_b = (src_abgr >> 16) & 0xff;
+ dst_r = dst_abgr & 0xff;
+ dst_g = (dst_abgr >> 8) & 0xff;
+ dst_b = (dst_abgr >> 16) & 0xff;
+#endif
+ r = dst_r + (((src_r - dst_r) * c + 0x8000) >> 16);
+ g = dst_g + (((src_g - dst_g) * c + 0x8000) >> 16);
+ b = dst_b + (((src_b - dst_b) * c + 0x8000) >> 16);
+#ifdef WORDS_BIGENDIAN
+ ((art_u32 *)dst)[i] = (r << 24) | (g << 16) | (b << 8) | a;
+#else
+ ((art_u32 *)dst)[i] = (a << 24) | (b << 16) | (g << 8) | r;
+#endif
+ }
+ }
+#if 0
+ /* it's not clear to me this optimization really wins */
+ else
+ {
+ /* skip over run of transparent pixels */
+ for (; i < n - 1; i++)
+ {
+#ifdef WORDS_BIGENDIAN
+ src_rgba = ((art_u32 *)src)[i + 1];
+ if (src_rgba & 0xff)
+ break;
+#else
+ src_abgr = ((art_u32 *)src)[i + 1];
+ if (src_abgr & 0xff000000)
+ break;
+#endif
+ }
+ }
+#endif
+ }
+}
+
+/**
+ * art_rgba_fill_run: fill an RGBA buffer a solid RGB color.
+ * @buf: Buffer to fill.
+ * @r: Red, range 0..255.
+ * @g: Green, range 0..255.
+ * @b: Blue, range 0..255.
+ * @n: Number of RGB triples to fill.
+ *
+ * Fills a buffer with @n copies of the (@r, @g, @b) triple, solid
+ * alpha. Thus, locations @buf (inclusive) through @buf + 4 * @n
+ * (exclusive) are written.
+ **/
+void
+art_rgba_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n)
+{
+ int i;
+#ifdef WORDS_BIGENDIAN
+ art_u32 src_rgba;
+#else
+ art_u32 src_abgr;
+#endif
+
+#ifdef WORDS_BIGENDIAN
+ src_rgba = (r << 24) | (g << 16) | (b << 8) | 255;
+#else
+ src_abgr = (255 << 24) | (b << 16) | (g << 8) | r;
+#endif
+ for (i = 0; i < n; i++)
+ {
+#ifdef WORDS_BIGENDIAN
+ ((art_u32 *)buf)[i] = src_rgba;
+#else
+ ((art_u32 *)buf)[i] = src_abgr;
+#endif
+ }
+}
+
+/**
+ * art_rgba_run_alpha: Render semitransparent color over RGBA buffer.
+ * @buf: Buffer for rendering.
+ * @r: Red, range 0..255.
+ * @g: Green, range 0..255.
+ * @b: Blue, range 0..255.
+ * @alpha: Alpha, range 0..255.
+ * @n: Number of RGB triples to render.
+ *
+ * Renders a sequential run of solid (@r, @g, @b) color over @buf with
+ * opacity @alpha. Note that the range of @alpha is 0..255, in contrast
+ * to art_rgb_run_alpha, which has a range of 0..256.
+ **/
+void
+art_rgba_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n)
+{
+ int i;
+#ifdef WORDS_BIGENDIAN
+ art_u32 src_rgba, dst_rgba;
+#else
+ art_u32 src_abgr, dst_abgr;
+#endif
+ art_u8 dst_alpha;
+ int a;
+ int dst_r, dst_g, dst_b;
+ int tmp;
+ int c;
+
+#ifdef WORDS_BIGENDIAN
+ src_rgba = (r << 24) | (g << 16) | (b << 8) | alpha;
+#else
+ src_abgr = (alpha << 24) | (b << 16) | (g << 8) | r;
+#endif
+ for (i = 0; i < n; i++)
+ {
+#ifdef WORDS_BIGENDIAN
+ dst_rgba = ((art_u32 *)buf)[i];
+ dst_alpha = dst_rgba & 0xff;
+#else
+ dst_abgr = ((art_u32 *)buf)[i];
+ dst_alpha = (dst_abgr >> 24) & 0xff;
+#endif
+ if (dst_alpha)
+ {
+#ifdef ART_OPTIMIZE_SPACE
+ tmp = (255 - alpha) * (255 - dst_alpha) + 0x80;
+ a = 255 - ((tmp + (tmp >> 8)) >> 8);
+ c = ((alpha << 16) + (a >> 1)) / a;
+#else
+ tmp = art_rgba_composite_table[(alpha << 8) + dst_alpha];
+ c = tmp & 0x1ffff;
+ a = tmp >> 24;
+#endif
+#ifdef WORDS_BIGENDIAN
+ dst_r = (dst_rgba >> 24) & 0xff;
+ dst_g = (dst_rgba >> 16) & 0xff;
+ dst_b = (dst_rgba >> 8) & 0xff;
+#else
+ dst_r = dst_abgr & 0xff;
+ dst_g = (dst_abgr >> 8) & 0xff;
+ dst_b = (dst_abgr >> 16) & 0xff;
+#endif
+ dst_r += (((r - dst_r) * c + 0x8000) >> 16);
+ dst_g += (((g - dst_g) * c + 0x8000) >> 16);
+ dst_b += (((b - dst_b) * c + 0x8000) >> 16);
+#ifdef WORDS_BIGENDIAN
+ ((art_u32 *)buf)[i] = (dst_r << 24) | (dst_g << 16) | (dst_b << 8) | a;
+#else
+ ((art_u32 *)buf)[i] = (a << 24) | (dst_b << 16) | (dst_g << 8) | dst_r;
+#endif
+ }
+ else
+ {
+#ifdef WORDS_BIGENDIAN
+ ((art_u32 *)buf)[i] = src_rgba;
+#else
+ ((art_u32 *)buf)[i] = src_abgr;
+#endif
+ }
+ }
+}
diff --git a/librsvg/art_rgba.h b/librsvg/art_rgba.h
new file mode 100644
index 000000000..79c525e0d
--- /dev/null
+++ b/librsvg/art_rgba.h
@@ -0,0 +1,13 @@
+#ifndef __ART_RGBA_H__
+#define __ART_RGBA_H__
+
+void
+art_rgba_rgba_composite (art_u8 *dst, const art_u8 *src, int n);
+
+void
+art_rgba_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n);
+
+void
+art_rgba_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n);
+
+#endif
diff --git a/librsvg/art_rgba_svp.c b/librsvg/art_rgba_svp.c
new file mode 100644
index 000000000..1db442802
--- /dev/null
+++ b/librsvg/art_rgba_svp.c
@@ -0,0 +1,257 @@
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_render_aa.h>
+#include "art_rgba.h"
+#include "art_rgba_svp.h"
+
+typedef struct _ArtRgbaSVPAlphaData ArtRgbaSVPAlphaData;
+
+struct _ArtRgbaSVPAlphaData {
+ int alphatab[256];
+ art_u8 r, g, b, alpha;
+ art_u8 *buf;
+ int rowstride;
+ int x0, x1;
+};
+
+static void
+art_rgba_svp_alpha_callback (void *callback_data, int y,
+ int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtRgbaSVPAlphaData *data = callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int x0, x1;
+ int k;
+ art_u8 r, g, b;
+ int *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0)
+ {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgba_run_alpha (linebuf,
+ r, g, b, alphatab[alpha],
+ run_x1 - x0);
+ }
+
+ /* render the steps into tmpbuf */
+ for (k = 0; k < n_steps - 1; k++)
+ {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0)
+ {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgba_run_alpha (linebuf + ((run_x0 - x0) << 2),
+ r, g, b, alphatab[alpha],
+ run_x1 - run_x0);
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1)
+ {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgba_run_alpha (linebuf + ((run_x1 - x0) << 2),
+ r, g, b, alphatab[alpha],
+ x1 - run_x1);
+ }
+ }
+ else
+ {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgba_run_alpha (linebuf,
+ r, g, b, alphatab[alpha],
+ x1 - x0);
+ }
+
+ data->buf += data->rowstride;
+}
+
+static void
+art_rgba_svp_alpha_opaque_callback (void *callback_data, int y,
+ int start,
+ ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtRgbaSVPAlphaData *data = callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int x0, x1;
+ int k;
+ art_u8 r, g, b;
+ int *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ x0 = data->x0;
+ x1 = data->x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0)
+ {
+ alpha = running_sum >> 16;
+ if (alpha)
+ {
+ if (alpha >= 255)
+ art_rgba_fill_run (linebuf,
+ r, g, b,
+ run_x1 - x0);
+ else
+ art_rgba_run_alpha (linebuf,
+ r, g, b, alphatab[alpha],
+ run_x1 - x0);
+ }
+ }
+
+ /* render the steps into tmpbuf */
+ for (k = 0; k < n_steps - 1; k++)
+ {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0)
+ {
+ alpha = running_sum >> 16;
+ if (alpha)
+ {
+ if (alpha >= 255)
+ art_rgba_fill_run (linebuf + ((run_x0 - x0) << 2),
+ r, g, b,
+ run_x1 - run_x0);
+ else
+ art_rgba_run_alpha (linebuf + ((run_x0 - x0) << 2),
+ r, g, b, alphatab[alpha],
+ run_x1 - run_x0);
+ }
+ }
+ }
+ running_sum += steps[k].delta;
+ if (x1 > run_x1)
+ {
+ alpha = running_sum >> 16;
+ if (alpha)
+ {
+ if (alpha >= 255)
+ art_rgba_fill_run (linebuf + ((run_x1 - x0) << 2),
+ r, g, b,
+ x1 - run_x1);
+ else
+ art_rgba_run_alpha (linebuf + ((run_x1 - x0) << 2),
+ r, g, b, alphatab[alpha],
+ x1 - run_x1);
+ }
+ }
+ }
+ else
+ {
+ alpha = running_sum >> 16;
+ if (alpha)
+ {
+ if (alpha >= 255)
+ art_rgba_fill_run (linebuf,
+ r, g, b,
+ x1 - x0);
+ else
+ art_rgba_run_alpha (linebuf,
+ r, g, b, alphatab[alpha],
+ x1 - x0);
+ }
+ }
+
+ data->buf += data->rowstride;
+}
+
+/**
+ * art_rgba_svp_alpha: Alpha-composite sorted vector path over RGBA buffer.
+ * @svp: The source sorted vector path.
+ * @x0: Left coordinate of destination rectangle.
+ * @y0: Top coordinate of destination rectangle.
+ * @x1: Right coordinate of destination rectangle.
+ * @y1: Bottom coordinate of destination rectangle.
+ * @rgba: Color in 0xRRGGBBAA format.
+ * @buf: Destination RGBA buffer.
+ * @rowstride: Rowstride of @buf buffer.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
+ *
+ * Renders the shape specified with @svp over the @buf RGBA buffer.
+ * @x1 - @x0 specifies the width, and @y1 - @y0 specifies the height,
+ * of the rectangle rendered. The new pixels are stored starting at
+ * the first byte of @buf. Thus, the @x0 and @y0 parameters specify
+ * an offset within @svp, and may be tweaked as a way of doing
+ * integer-pixel translations without fiddling with @svp itself.
+ *
+ * The @rgba argument specifies the color for the rendering. Pixels of
+ * entirely 0 winding number are left untouched. Pixels of entirely
+ * 1 winding number have the color @rgba composited over them (ie,
+ * are replaced by the red, green, blue components of @rgba if the alpha
+ * component is 0xff). Pixels of intermediate coverage are interpolated
+ * according to the rule in @alphagamma, or default to linear if
+ * @alphagamma is NULL.
+ **/
+void
+art_rgba_svp_alpha (const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ art_u32 rgba,
+ art_u8 *buf, int rowstride,
+ ArtAlphaGamma *alphagamma)
+{
+ ArtRgbaSVPAlphaData data;
+ int r, g, b, alpha;
+ int i;
+ int a, da;
+
+ r = rgba >> 24;
+ g = (rgba >> 16) & 0xff;
+ b = (rgba >> 8) & 0xff;
+ alpha = rgba & 0xff;
+
+ data.r = r;
+ data.g = g;
+ data.b = b;
+ data.alpha = alpha;
+
+ a = 0x8000;
+ da = (alpha * 65793 + 0x80) >> 8; /* 65793 equals 2 ^ 24 / 255 */
+
+ for (i = 0; i < 256; i++)
+ {
+ data.alphatab[i] = a >> 16;
+ a += da;
+ }
+
+ data.buf = buf;
+ data.rowstride = rowstride;
+ data.x0 = x0;
+ data.x1 = x1;
+ if (alpha == 255)
+ art_svp_render_aa (svp, x0, y0, x1, y1, art_rgba_svp_alpha_opaque_callback,
+ &data);
+ else
+ art_svp_render_aa (svp, x0, y0, x1, y1, art_rgba_svp_alpha_callback, &data);
+}
diff --git a/librsvg/art_rgba_svp.h b/librsvg/art_rgba_svp.h
new file mode 100644
index 000000000..cec17164d
--- /dev/null
+++ b/librsvg/art_rgba_svp.h
@@ -0,0 +1,13 @@
+#ifndef __ART_RGBA_SVP_H__
+#define __ART_RGBA_SVP_H__
+
+#include <libart_lgpl/art_alphagamma.h>
+
+void
+art_rgba_svp_alpha (const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ art_u32 rgba,
+ art_u8 *buf, int rowstride,
+ ArtAlphaGamma *alphagamma);
+
+#endif
diff --git a/librsvg/rsvg-bpath-util.c b/librsvg/rsvg-bpath-util.c
new file mode 100644
index 000000000..64f745636
--- /dev/null
+++ b/librsvg/rsvg-bpath-util.c
@@ -0,0 +1,172 @@
+#include <glib.h>
+#include <math.h>
+#include "rsvg-bpath-util.h"
+
+/* This is adapted from gnome-canvas-bpath-util in libgnomeprint
+ (originally developed as part of Gill). */
+
+RsvgBpathDef *
+rsvg_bpath_def_new (void)
+{
+ RsvgBpathDef *bpd;
+
+ bpd = g_new (RsvgBpathDef, 1);
+ bpd->n_bpath = 0;
+ bpd->n_bpath_max = 16;
+ bpd->moveto_idx = -1;
+ bpd->bpath = g_new (ArtBpath, bpd->n_bpath_max);
+ bpd->ref_count = 1;
+
+ return bpd;
+}
+
+RsvgBpathDef *
+rsvg_bpath_def_new_from (ArtBpath *path)
+{
+ RsvgBpathDef *bpd;
+ int i;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ for (i = 0; path[i].code != ART_END; i++)
+ ;
+ if (i <= 0)
+ return rsvg_bpath_def_new ();
+
+ bpd = g_new (RsvgBpathDef, 1);
+
+ bpd->n_bpath = i;
+ bpd->n_bpath_max = i;
+ bpd->moveto_idx = -1;
+ bpd->ref_count = 1;
+ bpd->bpath = g_new (ArtBpath, i);
+
+ memcpy (bpd->bpath, path, i * sizeof (ArtBpath));
+ return bpd;
+}
+
+RsvgBpathDef *
+rsvg_bpath_def_ref (RsvgBpathDef *bpd)
+{
+ g_return_val_if_fail (bpd != NULL, NULL);
+
+ bpd->ref_count += 1;
+ return bpd;
+}
+
+void
+rsvg_bpath_def_free (RsvgBpathDef *bpd)
+{
+ g_return_if_fail (bpd != NULL);
+
+ bpd->ref_count -= 1;
+ if (bpd->ref_count == 0)
+ {
+ g_free (bpd->bpath);
+ g_free (bpd);
+ }
+}
+
+void
+rsvg_bpath_def_moveto (RsvgBpathDef *bpd, double x, double y)
+{
+ ArtBpath *bpath;
+ int n_bpath;
+
+ g_return_if_fail (bpd != NULL);
+
+ n_bpath = bpd->n_bpath++;
+
+ if (n_bpath == bpd->n_bpath_max)
+ bpd->bpath = g_realloc (bpd->bpath,
+ (bpd->n_bpath_max <<= 1) * sizeof (ArtBpath));
+ bpath = bpd->bpath;
+ bpath[n_bpath].code = ART_MOVETO_OPEN;
+ bpath[n_bpath].x3 = x;
+ bpath[n_bpath].y3 = y;
+ bpd->moveto_idx = n_bpath;
+}
+
+void
+rsvg_bpath_def_lineto (RsvgBpathDef *bpd, double x, double y)
+{
+ ArtBpath *bpath;
+ int n_bpath;
+
+ g_return_if_fail (bpd != NULL);
+ g_return_if_fail (bpd->moveto_idx >= 0);
+
+ n_bpath = bpd->n_bpath++;
+
+ if (n_bpath == bpd->n_bpath_max)
+ bpd->bpath = g_realloc (bpd->bpath,
+ (bpd->n_bpath_max <<= 1) * sizeof (ArtBpath));
+ bpath = bpd->bpath;
+ bpath[n_bpath].code = ART_LINETO;
+ bpath[n_bpath].x3 = x;
+ bpath[n_bpath].y3 = y;
+}
+
+void
+rsvg_bpath_def_curveto (RsvgBpathDef *bpd, double x1, double y1, double x2, double y2, double x3, double y3)
+{
+ ArtBpath *bpath;
+ int n_bpath;
+
+ g_return_if_fail (bpd != NULL);
+ g_return_if_fail (bpd->moveto_idx >= 0);
+
+ n_bpath = bpd->n_bpath++;
+
+ if (n_bpath == bpd->n_bpath_max)
+ bpd->bpath = g_realloc (bpd->bpath,
+ (bpd->n_bpath_max <<= 1) * sizeof (ArtBpath));
+ bpath = bpd->bpath;
+ bpath[n_bpath].code = ART_CURVETO;
+ bpath[n_bpath].x1 = x1;
+ bpath[n_bpath].y1 = y1;
+ bpath[n_bpath].x2 = x2;
+ bpath[n_bpath].y2 = y2;
+ bpath[n_bpath].x3 = x3;
+ bpath[n_bpath].y3 = y3;
+}
+
+void
+rsvg_bpath_def_closepath (RsvgBpathDef *bpd)
+{
+ ArtBpath *bpath;
+ int n_bpath;
+
+ g_return_if_fail (bpd != NULL);
+ g_return_if_fail (bpd->moveto_idx >= 0);
+ g_return_if_fail (bpd->n_bpath > 0);
+
+ bpath = bpd->bpath;
+ n_bpath = bpd->n_bpath;
+
+ /* Add closing vector if we need it. */
+ if (bpath[n_bpath - 1].x3 != bpath[bpd->moveto_idx].x3 ||
+ bpath[n_bpath - 1].y3 != bpath[bpd->moveto_idx].y3)
+ {
+ rsvg_bpath_def_lineto (bpd, bpath[bpd->moveto_idx].x3,
+ bpath[bpd->moveto_idx].y3);
+ bpath = bpd->bpath;
+ }
+ bpath[bpd->moveto_idx].code = ART_MOVETO;
+ bpd->moveto_idx = -1;
+}
+
+void
+rsvg_bpath_def_art_finish (RsvgBpathDef *bpd)
+{
+ int n_bpath;
+
+ g_return_if_fail (bpd != NULL);
+
+ n_bpath = bpd->n_bpath++;
+
+ if (n_bpath == bpd->n_bpath_max)
+ bpd->bpath = g_realloc (bpd->bpath,
+ (bpd->n_bpath_max <<= 1) * sizeof (ArtBpath));
+ bpd->bpath[n_bpath].code = ART_END;
+}
diff --git a/librsvg/rsvg-bpath-util.h b/librsvg/rsvg-bpath-util.h
new file mode 100644
index 000000000..edebb2b00
--- /dev/null
+++ b/librsvg/rsvg-bpath-util.h
@@ -0,0 +1,37 @@
+#ifndef RSVG_BPATH_UTIL_H
+#define RSVG_BPATH_UTIL_H
+
+#include <libart_lgpl/art_bpath.h>
+
+typedef struct _RsvgBpathDef RsvgBpathDef;
+
+struct _RsvgBpathDef {
+ int ref_count;
+ ArtBpath *bpath;
+ int n_bpath;
+ int n_bpath_max;
+ int moveto_idx;
+};
+
+
+RsvgBpathDef *rsvg_bpath_def_new (void);
+RsvgBpathDef *rsvg_bpath_def_new_from (ArtBpath *bpath);
+RsvgBpathDef *rsvg_bpath_def_ref (RsvgBpathDef *bpd);
+
+#define rsvg_bpath_def_unref rsvg_bpath_def_free
+void rsvg_bpath_def_free (RsvgBpathDef *bpd);
+
+void rsvg_bpath_def_moveto (RsvgBpathDef *bpd,
+ double x, double y);
+void rsvg_bpath_def_lineto (RsvgBpathDef *bpd,
+ double x, double y);
+void rsvg_bpath_def_curveto (RsvgBpathDef *bpd,
+ double x1, double y1,
+ double x2, double y2,
+ double x3, double y3);
+void rsvg_bpath_def_closepath (RsvgBpathDef *bpd);
+
+void rsvg_bpath_def_art_finish (RsvgBpathDef *bpd);
+
+#endif
+
diff --git a/librsvg/rsvg-bpath.c b/librsvg/rsvg-bpath.c
new file mode 100644
index 000000000..dbfdd3902
--- /dev/null
+++ b/librsvg/rsvg-bpath.c
@@ -0,0 +1,168 @@
+#include <glib.h>
+#include <math.h>
+#include "rsvg-bpath-util.h"
+
+GnomeCanvasBpathDef *
+gnome_canvas_bpath_def_new (void)
+{
+ GnomeCanvasBpathDef *bpd;
+
+ bpd = g_new (GnomeCanvasBpathDef, 1);
+ bpd->n_bpath = 0;
+ bpd->n_bpath_max = 16;
+ bpd->moveto_idx = -1;
+ bpd->bpath = g_new (ArtBpath, bpd->n_bpath_max);
+ bpd->ref_count = 1;
+
+ return bpd;
+}
+
+GnomeCanvasBpathDef *
+gnome_canvas_bpath_def_new_from (ArtBpath *path)
+{
+ GnomeCanvasBpathDef *bpd;
+ int n, i;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ for (i = 0; path [i].code != ART_END; i++)
+ ;
+ if (i <= 0)
+ return gnome_canvas_bpath_def_new ();
+
+ bpd = g_new (GnomeCanvasBpathDef, 1);
+
+ bpd->n_bpath = i;
+ bpd->n_bpath_max = i;
+ bpd->moveto_idx = -1;
+ bpd->ref_count = 1;
+ bpd->bpath = g_new (ArtBpath, i);
+
+ memcpy (bpd->bpath, path, i * sizeof (ArtBpath));
+ return bpd;
+}
+
+GnomeCanvasBpathDef *
+gnome_canvas_bpath_def_ref (GnomeCanvasBpathDef *bpd)
+{
+ g_return_val_if_fail (bpd != NULL, NULL);
+
+ bpd->ref_count += 1;
+ return bpd;
+}
+
+void
+gnome_canvas_bpath_def_free (GnomeCanvasBpathDef *bpd)
+{
+ g_return_if_fail (bpd != NULL);
+
+ bpd->ref_count -= 1;
+ if (bpd->ref_count == 0) {
+ g_free (bpd->bpath);
+ g_free (bpd);
+ }
+}
+
+void
+gnome_canvas_bpath_def_moveto (GnomeCanvasBpathDef *bpd, double x, double y)
+{
+ ArtBpath *bpath;
+ int n_bpath;
+
+ g_return_if_fail (bpd != NULL);
+
+ n_bpath = bpd->n_bpath++;
+
+ if (n_bpath == bpd->n_bpath_max)
+ bpd->bpath = g_realloc (bpd->bpath,
+ (bpd->n_bpath_max <<= 1) * sizeof (ArtBpath));
+ bpath = bpd->bpath;
+ bpath[n_bpath].code = ART_MOVETO_OPEN;
+ bpath[n_bpath].x3 = x;
+ bpath[n_bpath].y3 = y;
+ bpd->moveto_idx = n_bpath;
+}
+
+void
+gnome_canvas_bpath_def_lineto (GnomeCanvasBpathDef *bpd, double x, double y)
+{
+ ArtBpath *bpath;
+ int n_bpath;
+
+ g_return_if_fail (bpd != NULL);
+ g_return_if_fail (bpd->moveto_idx >= 0);
+
+ n_bpath = bpd->n_bpath++;
+
+ if (n_bpath == bpd->n_bpath_max)
+ bpd->bpath = g_realloc (bpd->bpath,
+ (bpd->n_bpath_max <<= 1) * sizeof (ArtBpath));
+ bpath = bpd->bpath;
+ bpath[n_bpath].code = ART_LINETO;
+ bpath[n_bpath].x3 = x;
+ bpath[n_bpath].y3 = y;
+}
+
+void
+gnome_canvas_bpath_def_curveto (GnomeCanvasBpathDef *bpd, double x1, double y1, double x2, double y2, double x3, double y3)
+{
+ ArtBpath *bpath;
+ int n_bpath;
+
+ g_return_if_fail (bpd != NULL);
+ g_return_if_fail (bpd->moveto_idx >= 0);
+
+ n_bpath = bpd->n_bpath++;
+
+ if (n_bpath == bpd->n_bpath_max)
+ bpd->bpath = g_realloc (bpd->bpath,
+ (bpd->n_bpath_max <<= 1) * sizeof (ArtBpath));
+ bpath = bpd->bpath;
+ bpath[n_bpath].code = ART_CURVETO;
+ bpath[n_bpath].x1 = x1;
+ bpath[n_bpath].y1 = y1;
+ bpath[n_bpath].x2 = x2;
+ bpath[n_bpath].y2 = y2;
+ bpath[n_bpath].x3 = x3;
+ bpath[n_bpath].y3 = y3;
+}
+
+void
+gnome_canvas_bpath_def_closepath (GnomeCanvasBpathDef *bpd)
+{
+ ArtBpath *bpath;
+ int n_bpath;
+
+ g_return_if_fail (bpd != NULL);
+ g_return_if_fail (bpd->moveto_idx >= 0);
+ g_return_if_fail (bpd->n_bpath > 0);
+
+ bpath = bpd->bpath;
+ n_bpath = bpd->n_bpath;
+
+ /* Add closing vector if we need it. */
+ if (bpath[n_bpath - 1].x3 != bpath[bpd->moveto_idx].x3 ||
+ bpath[n_bpath - 1].y3 != bpath[bpd->moveto_idx].y3) {
+ gnome_canvas_bpath_def_lineto (bpd, bpath[bpd->moveto_idx].x3,
+ bpath[bpd->moveto_idx].y3);
+ bpath = bpd->bpath;
+ }
+ bpath[bpd->moveto_idx].code = ART_MOVETO;
+ bpd->moveto_idx = -1;
+}
+
+void
+gnome_canvas_bpath_def_art_finish (GnomeCanvasBpathDef *bpd)
+{
+ int n_bpath;
+
+ g_return_if_fail (bpd != NULL);
+
+ n_bpath = bpd->n_bpath++;
+
+ if (n_bpath == bpd->n_bpath_max)
+ bpd->bpath = g_realloc (bpd->bpath,
+ (bpd->n_bpath_max <<= 1) * sizeof (ArtBpath));
+ bpd->bpath [n_bpath].code = ART_END;
+}
+
diff --git a/librsvg/rsvg-path.c b/librsvg/rsvg-path.c
new file mode 100644
index 000000000..3254ae2be
--- /dev/null
+++ b/librsvg/rsvg-path.c
@@ -0,0 +1,611 @@
+/* rsvg-path.c
+
+ Copyright (C) 1999,2000 Raph Levien <raph@acm.org>
+
+ Gill 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.
+
+ Gill 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 Gill; see the file COPYING. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Raph Levien <raph@acm.org>
+*/
+
+/* This is adapted from svg-path in Gill. */
+
+
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <glib.h>
+
+#include "rsvg-bpath-util.h"
+#include "rsvg-path.h"
+
+/* This module parses an SVG path element into an RsvgBpathDef.
+
+ At present, there is no support for <marker> or any other contextual
+ information from the SVG file. The API will need to change rather
+ significantly to support these.
+
+ Reference: SVG working draft 3 March 2000, section 8.
+*/
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif /* M_PI */
+
+typedef struct _RSVGParsePathCtx RSVGParsePathCtx;
+
+struct _RSVGParsePathCtx {
+ RsvgBpathDef *bpath;
+ double cpx, cpy; /* current point */
+ double rpx, rpy; /* reflection point (for 's' and 't' commands) */
+ char cmd; /* current command (lowercase) */
+ int param; /* parameter number */
+ gboolean rel; /* true if relative coords */
+ double params[7]; /* parameters that have been parsed */
+};
+
+static void
+rsvg_path_arc_segment (RSVGParsePathCtx *ctx,
+ double xc, double yc,
+ double th0, double th1,
+ double rx, double ry, double x_axis_rotation)
+{
+ double sin_th, cos_th;
+ double a00, a01, a10, a11;
+ double x1, y1, x2, y2, x3, y3;
+ double t;
+ double th_half;
+
+ sin_th = sin (x_axis_rotation * (M_PI / 180.0));
+ cos_th = cos (x_axis_rotation * (M_PI / 180.0));
+ /* inverse transform compared with rsvg_path_arc */
+ a00 = cos_th * rx;
+ a01 = -sin_th * ry;
+ a10 = sin_th * rx;
+ a11 = cos_th * ry;
+
+ th_half = 0.5 * (th1 - th0);
+ t = (8.0 / 3.0) * sin (th_half * 0.5) * sin (th_half * 0.5) / sin (th_half);
+ x1 = xc + cos (th0) - t * sin (th0);
+ y1 = yc + sin (th0) + t * cos (th0);
+ x3 = xc + cos (th1);
+ y3 = yc + sin (th1);
+ x2 = x3 + t * sin (th1);
+ y2 = y3 - t * cos (th1);
+ rsvg_bpath_def_curveto (ctx->bpath,
+ a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
+ a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
+ a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
+}
+
+/**
+ * rsvg_path_arc: Add an RSVG arc to the path context.
+ * @ctx: Path context.
+ * @rx: Radius in x direction (before rotation).
+ * @ry: Radius in y direction (before rotation).
+ * @x_axis_rotation: Rotation angle for axes.
+ * @large_arc_flag: 0 for arc length <= 180, 1 for arc >= 180.
+ * @sweep: 0 for "negative angle", 1 for "positive angle".
+ * @x: New x coordinate.
+ * @y: New y coordinate.
+ *
+ **/
+static void
+rsvg_path_arc (RSVGParsePathCtx *ctx,
+ double rx, double ry, double x_axis_rotation,
+ int large_arc_flag, int sweep_flag,
+ double x, double y)
+{
+ double sin_th, cos_th;
+ double a00, a01, a10, a11;
+ double x0, y0, x1, y1, xc, yc;
+ double d, sfactor, sfactor_sq;
+ double th0, th1, th_arc;
+ int i, n_segs;
+
+ sin_th = sin (x_axis_rotation * (M_PI / 180.0));
+ cos_th = cos (x_axis_rotation * (M_PI / 180.0));
+ a00 = cos_th / rx;
+ a01 = sin_th / rx;
+ a10 = -sin_th / ry;
+ a11 = cos_th / ry;
+ x0 = a00 * ctx->cpx + a01 * ctx->cpy;
+ y0 = a10 * ctx->cpx + a11 * ctx->cpy;
+ x1 = a00 * x + a01 * y;
+ y1 = a10 * x + a11 * y;
+ /* (x0, y0) is current point in transformed coordinate space.
+ (x1, y1) is new point in transformed coordinate space.
+
+ The arc fits a unit-radius circle in this space.
+ */
+ d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
+ sfactor_sq = 1.0 / d - 0.25;
+ if (sfactor_sq < 0) sfactor_sq = 0;
+ sfactor = sqrt (sfactor_sq);
+ if (sweep_flag == large_arc_flag) sfactor = -sfactor;
+ xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
+ yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
+ /* (xc, yc) is center of the circle. */
+
+ th0 = atan2 (y0 - yc, x0 - xc);
+ th1 = atan2 (y1 - yc, x1 - xc);
+
+ th_arc = th1 - th0;
+ if (th_arc < 0 && sweep_flag)
+ th_arc += 2 * M_PI;
+ else if (th_arc > 0 && !sweep_flag)
+ th_arc -= 2 * M_PI;
+
+ n_segs = ceil (fabs (th_arc / (M_PI * 0.5 + 0.001)));
+
+ for (i = 0; i < n_segs; i++)
+ rsvg_path_arc_segment (ctx, xc, yc,
+ th0 + i * th_arc / n_segs,
+ th0 + (i + 1) * th_arc / n_segs,
+ rx, ry, x_axis_rotation);
+
+ ctx->cpx = x;
+ ctx->cpy = y;
+}
+
+
+/* supply defaults for missing parameters, assuming relative coordinates
+ are to be interpreted as x,y */
+static void
+rsvg_parse_path_default_xy (RSVGParsePathCtx *ctx, int n_params)
+{
+ int i;
+
+ if (ctx->rel)
+ {
+ for (i = ctx->param; i < n_params; i++)
+ {
+ if (i > 2)
+ ctx->params[i] = ctx->params[i - 2];
+ else if (i == 1)
+ ctx->params[i] = ctx->cpy;
+ else if (i == 0)
+ /* we shouldn't get here (usually ctx->param > 0 as
+ precondition) */
+ ctx->params[i] = ctx->cpx;
+ }
+ }
+ else
+ {
+ for (i = ctx->param; i < n_params; i++)
+ ctx->params[i] = 0.0;
+ }
+}
+
+static void
+rsvg_parse_path_do_cmd (RSVGParsePathCtx *ctx, gboolean final)
+{
+ double x1, y1, x2, y2, x3, y3;
+
+#ifdef VERBOSE
+ int i;
+
+ g_print ("parse_path %c:", ctx->cmd);
+ for (i = 0; i < ctx->param; i++)
+ g_print (" %f", ctx->params[i]);
+ g_print (final ? ".\n" : "\n");
+#endif
+
+ switch (ctx->cmd)
+ {
+ case 'm':
+ /* moveto */
+ if (ctx->param == 2 || final)
+ {
+ rsvg_parse_path_default_xy (ctx, 2);
+#ifdef VERBOSE
+ g_print ("'m' moveto %g,%g\n",
+ ctx->params[0], ctx->params[1]);
+#endif
+ rsvg_bpath_def_moveto (ctx->bpath,
+ ctx->params[0], ctx->params[1]);
+ ctx->cpx = ctx->rpx = ctx->params[0];
+ ctx->cpy = ctx->rpy = ctx->params[1];
+ ctx->param = 0;
+ }
+ break;
+ case 'l':
+ /* lineto */
+ if (ctx->param == 2 || final)
+ {
+ rsvg_parse_path_default_xy (ctx, 2);
+#ifdef VERBOSE
+ g_print ("'l' lineto %g,%g\n",
+ ctx->params[0], ctx->params[1]);
+#endif
+ rsvg_bpath_def_lineto (ctx->bpath,
+ ctx->params[0], ctx->params[1]);
+ ctx->cpx = ctx->rpx = ctx->params[0];
+ ctx->cpy = ctx->rpy = ctx->params[1];
+ ctx->param = 0;
+ }
+ break;
+ case 'c':
+ /* curveto */
+ if (ctx->param == 6 || final)
+ {
+ rsvg_parse_path_default_xy (ctx, 6);
+ x1 = ctx->params[0];
+ y1 = ctx->params[1];
+ x2 = ctx->params[2];
+ y2 = ctx->params[3];
+ x3 = ctx->params[4];
+ y3 = ctx->params[5];
+#ifdef VERBOSE
+ g_print ("'c' curveto %g,%g %g,%g, %g,%g\n",
+ x1, y1, x2, y2, x3, y3);
+#endif
+ rsvg_bpath_def_curveto (ctx->bpath,
+ x1, y1, x2, y2, x3, y3);
+ ctx->rpx = x2;
+ ctx->rpy = y2;
+ ctx->cpx = x3;
+ ctx->cpy = y3;
+ ctx->param = 0;
+ }
+ break;
+ case 's':
+ /* smooth curveto */
+ if (ctx->param == 4 || final)
+ {
+ rsvg_parse_path_default_xy (ctx, 4);
+ x1 = 2 * ctx->cpx - ctx->rpx;
+ y1 = 2 * ctx->cpy - ctx->rpy;
+ x2 = ctx->params[0];
+ y2 = ctx->params[1];
+ x3 = ctx->params[2];
+ y3 = ctx->params[3];
+#ifdef VERBOSE
+ g_print ("'s' curveto %g,%g %g,%g, %g,%g\n",
+ x1, y1, x2, y2, x3, y3);
+#endif
+ rsvg_bpath_def_curveto (ctx->bpath,
+ x1, y1, x2, y2, x3, y3);
+ ctx->rpx = x2;
+ ctx->rpy = y2;
+ ctx->cpx = x3;
+ ctx->cpy = y3;
+ ctx->param = 0;
+ }
+ break;
+ case 'h':
+ /* horizontal lineto */
+ if (ctx->param == 1)
+ {
+#ifdef VERBOSE
+ g_print ("'h' lineto %g,%g\n",
+ ctx->params[0], ctx->cpy);
+#endif
+ rsvg_bpath_def_lineto (ctx->bpath,
+ ctx->params[0], ctx->cpy);
+ ctx->cpx = ctx->rpx = ctx->params[0];
+ ctx->param = 0;
+ }
+ break;
+ case 'v':
+ /* vertical lineto */
+ if (ctx->param == 1)
+ {
+#ifdef VERBOSE
+ g_print ("'v' lineto %g,%g\n",
+ ctx->cpx, ctx->params[0]);
+#endif
+ rsvg_bpath_def_lineto (ctx->bpath,
+ ctx->cpx, ctx->params[0]);
+ ctx->cpy = ctx->rpy = ctx->params[0];
+ ctx->param = 0;
+ }
+ break;
+ case 'q':
+ /* quadratic bezier curveto */
+
+ /* non-normative reference:
+ http://www.icce.rug.nl/erikjan/bluefuzz/beziers/beziers/beziers.html
+ */
+ if (ctx->param == 4 || final)
+ {
+ rsvg_parse_path_default_xy (ctx, 4);
+ /* raise quadratic bezier to cubic */
+ x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
+ y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
+ x3 = ctx->params[2];
+ y3 = ctx->params[3];
+ x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
+ y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
+#ifdef VERBOSE
+ g_print ("'q' curveto %g,%g %g,%g, %g,%g\n",
+ x1, y1, x2, y2, x3, y3);
+#endif
+ rsvg_bpath_def_curveto (ctx->bpath,
+ x1, y1, x2, y2, x3, y3);
+ ctx->rpx = x2;
+ ctx->rpy = y2;
+ ctx->cpx = x3;
+ ctx->cpy = y3;
+ ctx->param = 0;
+ }
+ break;
+ case 't':
+ /* Truetype quadratic bezier curveto */
+ if (ctx->param == 2 || final)
+ {
+ double xc, yc; /* quadratic control point */
+
+ xc = 2 * ctx->cpx - ctx->rpx;
+ yc = 2 * ctx->cpy - ctx->rpy;
+ /* generate a quadratic bezier with control point = xc, yc */
+ x1 = (ctx->cpx + 2 * xc) * (1.0 / 3.0);
+ y1 = (ctx->cpy + 2 * yc) * (1.0 / 3.0);
+ x3 = ctx->params[0];
+ y3 = ctx->params[1];
+ x2 = (x3 + 2 * xc) * (1.0 / 3.0);
+ y2 = (y3 + 2 * yc) * (1.0 / 3.0);
+#ifdef VERBOSE
+ g_print ("'t' curveto %g,%g %g,%g, %g,%g\n",
+ x1, y1, x2, y2, x3, y3);
+#endif
+ rsvg_bpath_def_curveto (ctx->bpath,
+ x1, y1, x2, y2, x3, y3);
+ ctx->rpx = xc;
+ ctx->rpy = yc;
+ ctx->cpx = x3;
+ ctx->cpy = y3;
+ ctx->param = 0;
+ }
+ else if (final)
+ {
+ if (ctx->param > 2)
+ {
+ rsvg_parse_path_default_xy (ctx, 4);
+ /* raise quadratic bezier to cubic */
+ x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
+ y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
+ x3 = ctx->params[2];
+ y3 = ctx->params[3];
+ x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
+ y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
+#ifdef VERBOSE
+ g_print ("'t' curveto %g,%g %g,%g, %g,%g\n",
+ x1, y1, x2, y2, x3, y3);
+#endif
+ rsvg_bpath_def_curveto (ctx->bpath,
+ x1, y1, x2, y2, x3, y3);
+ ctx->rpx = x2;
+ ctx->rpy = y2;
+ ctx->cpx = x3;
+ ctx->cpy = y3;
+ }
+ else
+ {
+ rsvg_parse_path_default_xy (ctx, 2);
+#ifdef VERBOSE
+ g_print ("'t' lineto %g,%g\n",
+ ctx->params[0], ctx->params[1]);
+#endif
+ rsvg_bpath_def_lineto (ctx->bpath,
+ ctx->params[0], ctx->params[1]);
+ ctx->cpx = ctx->rpx = ctx->params[0];
+ ctx->cpy = ctx->rpy = ctx->params[1];
+ }
+ ctx->param = 0;
+ }
+ break;
+ case 'a':
+ if (ctx->param == 7 || final)
+ {
+ rsvg_path_arc (ctx,
+ ctx->params[0], ctx->params[1], ctx->params[2],
+ ctx->params[3], ctx->params[4],
+ ctx->params[5], ctx->params[6]);
+ ctx->param = 0;
+ }
+ break;
+ default:
+ ctx->param = 0;
+ }
+}
+
+static void
+rsvg_parse_path_data (RSVGParsePathCtx *ctx, const char *data)
+{
+ int i = 0;
+ double val = 0;
+ char c = 0;
+ gboolean in_num = FALSE;
+ gboolean in_frac = FALSE;
+ gboolean in_exp = FALSE;
+ gboolean exp_wait_sign = FALSE;
+ int sign = 0;
+ int exp = 0;
+ int exp_sign = 0;
+ double frac = 0.0;
+
+ in_num = FALSE;
+ for (i = 0; ; i++)
+ {
+ c = data[i];
+ if (c >= '0' && c <= '9')
+ {
+ /* digit */
+ if (in_num)
+ {
+ if (in_exp)
+ {
+ exp = (exp * 10) + c - '0';
+ exp_wait_sign = FALSE;
+ }
+ else if (in_frac)
+ val += (frac *= 0.1) * (c - '0');
+ else
+ val = (val * 10) + c - '0';
+ }
+ else
+ {
+ in_num = TRUE;
+ in_frac = FALSE;
+ in_exp = FALSE;
+ exp = 0;
+ exp_sign = 1;
+ exp_wait_sign = FALSE;
+ val = c - '0';
+ sign = 1;
+ }
+ }
+ else if (c == '.')
+ {
+ if (!in_num)
+ {
+ in_num = TRUE;
+ val = 0;
+ }
+ in_frac = TRUE;
+ frac = 1;
+ }
+ else if ((c == 'E' || c == 'e') && in_num)
+ {
+ in_exp = TRUE;
+ exp_wait_sign = TRUE;
+ exp = 0;
+ exp_sign = 1;
+ }
+ else if ((c == '+' || c == '-') && in_exp)
+ {
+ exp_sign = c == '+' ? 1 : -1;
+ }
+ else if (in_num)
+ {
+ /* end of number */
+
+ val *= sign * pow (10, exp_sign * exp);
+ if (ctx->rel)
+ {
+ /* Handle relative coordinates. This switch statement attempts
+ to determine _what_ the coords are relative to. This is
+ underspecified in the 12 Apr working draft. */
+ switch (ctx->cmd)
+ {
+ case 'l':
+ case 'm':
+ case 'c':
+ case 's':
+ case 'q':
+ case 't':
+#ifndef RSVGV_RELATIVE
+ /* rule: even-numbered params are x-relative, odd-numbered
+ are y-relative */
+ if (ctx->param == 0)
+ val += ctx->cpx;
+ else if (ctx->param == 1)
+ val += ctx->cpy;
+ else
+ val += ctx->params[ctx->param - 2];
+ break;
+#else
+ /* rule: even-numbered params are x-relative, odd-numbered
+ are y-relative */
+ if (ctx->param == 0 || (ctx->param % 2 ==0))
+ val += ctx->cpx;
+ else
+ val += ctx->cpy;
+ break;
+#endif
+ case 'a':
+ /* rule: sixth and seventh are x and y, rest are not
+ relative */
+ if (ctx->param == 5)
+ val += ctx->cpx;
+ else if (ctx->param == 6)
+ val += ctx->cpy;
+ break;
+ case 'h':
+ /* rule: x-relative */
+ val += ctx->cpx;
+ break;
+ case 'v':
+ /* rule: y-relative */
+ val += ctx->cpy;
+ break;
+ }
+ }
+ ctx->params[ctx->param++] = val;
+ rsvg_parse_path_do_cmd (ctx, FALSE);
+ in_num = FALSE;
+ }
+
+ if (c == '\0')
+ break;
+ else if ((c == '+' || c == '-') && !exp_wait_sign)
+ {
+ sign = c == '+' ? 1 : -1;;
+ val = 0;
+ in_num = TRUE;
+ in_frac = FALSE;
+ in_exp = FALSE;
+ exp = 0;
+ exp_sign = 1;
+ exp_wait_sign = FALSE;
+ }
+ else if (c == 'z' || c == 'Z')
+ {
+ if (ctx->param)
+ rsvg_parse_path_do_cmd (ctx, TRUE);
+#ifdef VERBOSE
+ g_print ("'z' closepath\n");
+#endif
+ rsvg_bpath_def_closepath (ctx->bpath);
+ }
+ else if (c >= 'A' && c <= 'Z' && c != 'E')
+ {
+ if (ctx->param)
+ rsvg_parse_path_do_cmd (ctx, TRUE);
+ ctx->cmd = c + 'a' - 'A';
+ ctx->rel = FALSE;
+ }
+ else if (c >= 'a' && c <= 'z' && c != 'e')
+ {
+ if (ctx->param)
+ rsvg_parse_path_do_cmd (ctx, TRUE);
+ ctx->cmd = c;
+ ctx->rel = TRUE;
+ }
+ /* else c _should_ be whitespace or , */
+ }
+}
+
+RsvgBpathDef *
+rsvg_parse_path (const char *path_str)
+{
+ RSVGParsePathCtx ctx;
+
+ ctx.bpath = rsvg_bpath_def_new ();
+ ctx.cpx = 0.0;
+ ctx.cpy = 0.0;
+ ctx.cmd = 0;
+ ctx.param = 0;
+
+ rsvg_parse_path_data (&ctx, path_str);
+
+ if (ctx.param)
+ rsvg_parse_path_do_cmd (&ctx, TRUE);
+
+ return ctx.bpath;
+}
+
diff --git a/librsvg/rsvg-path.h b/librsvg/rsvg-path.h
new file mode 100644
index 000000000..3d7c669c0
--- /dev/null
+++ b/librsvg/rsvg-path.h
@@ -0,0 +1,2 @@
+RsvgBpathDef *
+rsvg_parse_path (const char *path_str);
diff --git a/librsvg/rsvg.c b/librsvg/rsvg.c
new file mode 100644
index 000000000..688f83a3d
--- /dev/null
+++ b/librsvg/rsvg.c
@@ -0,0 +1,771 @@
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#include <glib.h>
+
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_affine.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_bpath.h>
+#include <libart_lgpl/art_vpath.h>
+#include <libart_lgpl/art_vpath_bpath.h>
+#include <libart_lgpl/art_rgb_svp.h>
+#include <libart_lgpl/art_svp_vpath_stroke.h>
+#include <libart_lgpl/art_svp_vpath.h>
+#include <libart_lgpl/art_svp_wind.h>
+
+#include "art_rgba_svp.h"
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "SAX.h"
+
+#include "rsvg-bpath-util.h"
+#include "rsvg-path.h"
+#include "rsvg.h"
+
+#define noVERBOSE
+
+typedef struct _RsvgCtx RsvgCtx;
+typedef struct _RsvgState RsvgState;
+
+struct _RsvgCtx {
+ GdkPixbuf *pixbuf;
+
+ double zoom;
+
+ /* stack; there is a state for each element */
+ RsvgState *state;
+ int n_state;
+ int n_state_max;
+};
+
+struct _RsvgState {
+ double affine[6];
+
+ gboolean fill;
+ guint32 fill_color; /* rgb */
+ gint fill_opacity; /* 0...255 */
+
+ gboolean stroke;
+ guint32 stroke_color; /* rgb */
+ gint stroke_opacity; /* 0..255 */
+ double stroke_width;
+
+ ArtPathStrokeCapType cap;
+ ArtPathStrokeJoinType join;
+
+ double font_size;
+};
+
+static RsvgCtx *
+rsvg_ctx_new (void)
+{
+ RsvgCtx *result;
+
+ result = g_new (RsvgCtx, 1);
+ result->pixbuf = NULL;
+ result->zoom = 1.0;
+ result->n_state = 0;
+ result->n_state_max = 16;
+ result->state = g_new (RsvgState, result->n_state_max);
+ return result;
+}
+
+/* does not destroy the pixbuf */
+static void
+rsvg_ctx_free (RsvgCtx *ctx)
+{
+ free (ctx->state);
+ free (ctx);
+}
+
+static void
+rsvg_state_init (RsvgState *state)
+{
+ art_affine_identity (state->affine);
+
+ state->fill = 0;
+ state->fill_color = 0;
+ state->fill_opacity = 0xff;
+ state->stroke = 0;
+ state->stroke_color = 0;
+ state->stroke_opacity = 0xff;
+ state->stroke_width = 1;
+ state->cap = ART_PATH_STROKE_CAP_BUTT;
+ state->join = ART_PATH_STROKE_JOIN_MITER;
+}
+
+/**
+ * rsvg_css_parse_length: Parse CSS2 length to a pixel value.
+ * @str: Original string.
+ * @fixed: Where to store boolean value of whether length is fixed.
+ *
+ * Parses a CSS2 length into a pixel value.
+ *
+ * Returns: returns the length.
+ **/
+static double
+rsvg_css_parse_length (const char *str, gint *fixed)
+{
+ char *p;
+
+ /*
+ * The supported CSS length unit specifiers are:
+ * em, ex, px, pt, pc, cm, mm, in, and percentages.
+ */
+
+ *fixed = FALSE;
+
+ p = strstr (str, "px");
+ if (p != NULL)
+ {
+ *fixed = TRUE;
+ return atof (str);
+ }
+ p = strstr (str, "in");
+ if (p != NULL)
+ {
+ *fixed = TRUE;
+ /* return svg->pixels_per_inch * atof (str); */
+ }
+ return atof (str);
+}
+
+static void
+rsvg_pixmap_destroy (guchar *pixels, gpointer data)
+{
+ g_free (pixels);
+}
+
+static void
+rsvg_start_svg (RsvgCtx *ctx, const xmlChar **atts)
+{
+ int i;
+ int width = -1, height = -1;
+ int rowstride;
+ art_u8 *pixels;
+ gint fixed;
+ RsvgState *state;
+ gboolean has_alpha = 1;
+
+ if (atts != NULL)
+ {
+ for (i = 0; atts[i] != NULL; i += 2)
+ {
+ if (!strcmp ((char *)atts[i], "width"))
+ width = rsvg_css_parse_length ((char *)atts[i + 1], &fixed);
+ else if (!strcmp ((char *)atts[i], "height"))
+ height = rsvg_css_parse_length ((char *)atts[i + 1], &fixed);
+ }
+#ifdef VERBOSE
+ fprintf (stdout, "rsvg_start_svg: width = %d, height = %d\n",
+ width, height);
+#endif
+
+ /* Scale size of target pixbuf */
+ width = ceil (width * ctx->zoom);
+ height = ceil (height * ctx->zoom);
+
+ state = &ctx->state[ctx->n_state - 1];
+ art_affine_scale (state->affine, ctx->zoom, ctx->zoom);
+
+ rowstride = (width * (has_alpha ? 4 : 3) + 3) & -4;
+ pixels = g_new (art_u8, rowstride * height);
+ memset (pixels, has_alpha ? 0 : 255, rowstride * height);
+ ctx->pixbuf = gdk_pixbuf_new_from_data (pixels,
+ GDK_COLORSPACE_RGB,
+ has_alpha, 8,
+ width, height,
+ rowstride,
+ rsvg_pixmap_destroy,
+ NULL);
+ }
+}
+
+static gboolean
+rsvg_css_param_match (const char *str, const char *param_name)
+{
+ int i;
+
+ for (i = 0; str[i] != '\0' && str[i] != ':'; i++)
+ if (param_name[i] != str[i])
+ return FALSE;
+ return str[i] == ':' && param_name[i] == '\0';
+}
+
+static int
+rsvg_css_param_arg_offset (const char *str)
+{
+ int i;
+
+ for (i = 0; str[i] != '\0' && str[i] != ':'; i++);
+ if (str[i] != '\0') i++;
+ for (; str[i] == ' '; i++);
+ return i;
+}
+
+/* Parse a CSS2 color, returning rgb */
+static guint32
+rsvg_css_parse_color (const char *str)
+{
+ gint val = 0;
+ static GHashTable *colors = NULL;
+
+ /*
+ * todo: handle the rgb (r, g, b) and rgb ( r%, g%, b%), syntax
+ * defined in http://www.w3.org/TR/REC-CSS2/syndata.html#color-units
+ */
+#ifdef VERBOSE
+ g_print ("color = %s\n", str);
+#endif
+ if (str[0] == '#')
+ {
+ int i;
+ for (i = 1; str[i]; i++)
+ {
+ int hexval;
+ if (str[i] >= '0' && str[i] <= '9')
+ hexval = str[i] - '0';
+ else if (str[i] >= 'A' && str[i] <= 'F')
+ hexval = str[i] - 'A' + 10;
+ else if (str[i] >= 'a' && str[i] <= 'f')
+ hexval = str[i] - 'a' + 10;
+ else
+ break;
+ val = (val << 4) + hexval;
+ }
+ /* handle #rgb case */
+ if (i == 4)
+ {
+ val = ((val & 0xf00) << 8) |
+ ((val & 0x0f0) << 4) |
+ (val & 0x00f);
+ val |= val << 4;
+ }
+#ifdef VERBOSE
+ printf ("val = %x\n", val);
+#endif
+ }
+ else
+ {
+ GString * string;
+ if (!colors)
+ {
+ colors = g_hash_table_new (g_str_hash, g_str_equal);
+
+ g_hash_table_insert (colors, "black", GINT_TO_POINTER (0x000000));
+ g_hash_table_insert (colors, "silver", GINT_TO_POINTER (0xc0c0c0));
+ g_hash_table_insert (colors, "gray", GINT_TO_POINTER (0x808080));
+ g_hash_table_insert (colors, "white", GINT_TO_POINTER (0xFFFFFF));
+ g_hash_table_insert (colors, "maroon", GINT_TO_POINTER (0x800000));
+ g_hash_table_insert (colors, "red", GINT_TO_POINTER (0xFF0000));
+ g_hash_table_insert (colors, "purple", GINT_TO_POINTER (0x800080));
+ g_hash_table_insert (colors, "fuchsia", GINT_TO_POINTER (0xFF00FF));
+ g_hash_table_insert (colors, "green", GINT_TO_POINTER (0x008000));
+ g_hash_table_insert (colors, "lime", GINT_TO_POINTER (0x00FF00));
+ g_hash_table_insert (colors, "olive", GINT_TO_POINTER (0x808000));
+ g_hash_table_insert (colors, "yellow", GINT_TO_POINTER (0xFFFF00));
+ g_hash_table_insert (colors, "navy", GINT_TO_POINTER (0x000080));
+ g_hash_table_insert (colors, "blue", GINT_TO_POINTER (0x0000FF));
+ g_hash_table_insert (colors, "teal", GINT_TO_POINTER (0x008080));
+ g_hash_table_insert (colors, "aqua", GINT_TO_POINTER (0x00FFFF));
+ }
+
+ string = g_string_down (g_string_new (str));
+
+
+ /* this will default to black on a failed lookup */
+ val = GPOINTER_TO_INT (g_hash_table_lookup (colors, string->str));
+ }
+
+ return val;
+}
+
+static guint
+rsvg_css_parse_opacity (const char *str)
+{
+ char *end_ptr;
+ double opacity;
+
+ opacity = strtod (str, &end_ptr);
+
+ if (end_ptr[0] == '%')
+ opacity *= 0.01;
+
+ return floor (opacity * 255 + 0.5);
+}
+
+static double
+rsvg_css_parse_fontsize (RsvgState *state, const char *str)
+{
+ char *end_ptr;
+ double size;
+
+ /* todo: handle absolute-size and relative-size tags and proper units */
+ size = strtod (str, &end_ptr);
+
+ if (end_ptr[0] == '%')
+ size = (state->font_size * size * 0.01);
+
+ return size;
+}
+
+/* Parse a CSS2 style argument, setting the SVG context attributes. */
+static void
+rsvg_parse_style_arg (RsvgState *state, const char *str)
+{
+ int arg_off;
+
+ if (rsvg_css_param_match (str, "opacity"))
+ {
+ arg_off = rsvg_css_param_arg_offset (str);
+ /* state->opacity = rsvg_css_parse_opacity (str + arg_off); */
+ }
+ else if (rsvg_css_param_match (str, "fill"))
+ {
+ arg_off = rsvg_css_param_arg_offset (str);
+ if (!strcmp (str + arg_off, "none"))
+ state->fill = 0;
+ else
+ {
+ state->fill = 1;
+ state->fill_color = rsvg_css_parse_color (str + arg_off);
+ }
+ }
+ else if (rsvg_css_param_match (str, "fill-opacity"))
+ {
+ arg_off = rsvg_css_param_arg_offset (str);
+ state->fill_opacity = rsvg_css_parse_opacity (str + arg_off);
+ }
+ else if (rsvg_css_param_match (str, "stroke"))
+ {
+ arg_off = rsvg_css_param_arg_offset (str);
+ if (!strcmp (str + arg_off, "none"))
+ state->stroke = 0;
+ else
+ {
+ state->stroke = 1;
+ state->stroke_color = rsvg_css_parse_color (str + arg_off);
+ }
+ }
+ else if (rsvg_css_param_match (str, "stroke-width"))
+ {
+ int fixed;
+ arg_off = rsvg_css_param_arg_offset (str);
+ state->stroke_width = rsvg_css_parse_length (str + arg_off, &fixed);
+ }
+ else if (rsvg_css_param_match (str, "stroke-linecap"))
+ {
+ arg_off = rsvg_css_param_arg_offset (str);
+ if (!strcmp (str + arg_off, "butt"))
+ state->cap = ART_PATH_STROKE_CAP_BUTT;
+ else if (!strcmp (str + arg_off, "round"))
+ state->cap = ART_PATH_STROKE_CAP_ROUND;
+ else if (!strcmp (str + arg_off, "square"))
+ state->cap = ART_PATH_STROKE_CAP_SQUARE;
+ else
+ g_warning ("unknown line cap style %s", str + arg_off);
+ }
+ else if (rsvg_css_param_match (str, "stroke-opacity"))
+ {
+ arg_off = rsvg_css_param_arg_offset (str);
+ state->stroke_opacity = rsvg_css_parse_opacity (str + arg_off);
+ }
+ else if (rsvg_css_param_match (str, "stroke-linejoin"))
+ {
+ arg_off = rsvg_css_param_arg_offset (str);
+ if (!strcmp (str + arg_off, "miter"))
+ state->join = ART_PATH_STROKE_JOIN_MITER;
+ else if (!strcmp (str + arg_off, "round"))
+ state->join = ART_PATH_STROKE_JOIN_ROUND;
+ else if (!strcmp (str + arg_off, "bevel"))
+ state->join = ART_PATH_STROKE_JOIN_BEVEL;
+ else
+ g_warning ("unknown line join style %s", str + arg_off);
+ }
+ else if (rsvg_css_param_match (str, "font-size"))
+ {
+ arg_off = rsvg_css_param_arg_offset (str);
+ state->font_size = rsvg_css_parse_fontsize (state, str + arg_off);
+ }
+ else if (rsvg_css_param_match (str, "font-family"))
+ {
+ arg_off = rsvg_css_param_arg_offset (str);
+ /* state->font_family = g_strdup (str + arg_off); */
+ }
+
+}
+
+/* Split a CSS2 style into individual style arguments, setting attributes
+ in the SVG context.
+
+ It's known that this is _way_ out of spec. A more complete CSS2
+ implementation will happen later.
+*/
+static void
+rsvg_parse_style (RsvgState *state, const char *str)
+{
+ int start, end;
+ char *arg;
+
+ start = 0;
+ while (str[start] != '\0')
+ {
+ for (end = start; str[end] != '\0' && str[end] != ';'; end++);
+ arg = g_new (char, 1 + end - start);
+ memcpy (arg, str + start, end - start);
+ arg[end - start] = '\0';
+ rsvg_parse_style_arg (state, arg);
+ g_free (arg);
+ start = end;
+ if (str[start] == ';') start++;
+ while (str[start] == ' ') start++;
+ }
+}
+
+/**
+ * rsvg_parse_style_attrs: Parse style attribute.
+ * @ctx: Rsvg context.
+ * @atts: Attributes in SAX style.
+ *
+ * Parses style attribute and modifies state at top of stack.
+ *
+ * Note: this routine will also be responsible for parsing transforms.
+ **/
+static void
+rsvg_parse_style_attrs (RsvgCtx *ctx, const xmlChar **atts)
+{
+ int i;
+
+ if (atts != NULL)
+ {
+ for (i = 0; atts[i] != NULL; i += 2)
+ {
+ if (!strcmp ((char *)atts[i], "style"))
+ rsvg_parse_style (&ctx->state[ctx->n_state - 1],
+ (char *)atts[i + 1]);
+ }
+ }
+}
+
+static void
+rsvg_start_g (RsvgCtx *ctx, const xmlChar **atts)
+{
+ rsvg_parse_style_attrs (ctx, atts);
+}
+
+/**
+ * rsvg_close_vpath: Close a vector path.
+ * @src: Source vector path.
+ *
+ * Closes any open subpaths in the vector path.
+ *
+ * Return value: Closed vector path, allocated with g_new.
+ **/
+static ArtVpath *
+rsvg_close_vpath (const ArtVpath *src)
+{
+ ArtVpath *result;
+ int n_result, n_result_max;
+ int src_ix;
+ double beg_x, beg_y;
+ gboolean open;
+
+ n_result = 0;
+ n_result_max = 16;
+ result = g_new (ArtVpath, n_result_max);
+
+ beg_x = 0;
+ beg_y = 0;
+ open = FALSE;
+
+ for (src_ix = 0; src[src_ix].code != ART_END; src_ix++)
+ {
+ if (n_result == n_result_max)
+ result = g_renew (ArtVpath, result, n_result_max <<= 1);
+ result[n_result].code = src[src_ix].code == ART_MOVETO_OPEN ?
+ ART_MOVETO : src[src_ix].code;
+ result[n_result].x = src[src_ix].x;
+ result[n_result].y = src[src_ix].y;
+ n_result++;
+ if (src[src_ix].code == ART_MOVETO_OPEN)
+ {
+ beg_x = src[src_ix].x;
+ beg_y = src[src_ix].y;
+ open = TRUE;
+ }
+ else if (src[src_ix + 1].code != ART_LINETO)
+ {
+ if (open && (beg_x != src[src_ix].x || beg_y != src[src_ix].y))
+ {
+ if (n_result == n_result_max)
+ result = g_renew (ArtVpath, result, n_result_max <<= 1);
+ result[n_result].code = ART_LINETO;
+ result[n_result].x = beg_x;
+ result[n_result].y = beg_y;
+ n_result++;
+ }
+ open = FALSE;
+ }
+ }
+ if (n_result == n_result_max)
+ result = g_renew (ArtVpath, result, n_result_max <<= 1);
+ result[n_result].code = ART_END;
+ result[n_result].x = 0.0;
+ result[n_result].y = 0.0;
+ return result;
+}
+
+/**
+ * rsvg_render_svp: Render an SVP.
+ * @ctx: Context in which to render.
+ * @svp: SVP to render.
+ * @rgba: Color.
+ *
+ * Renders the SVP over the pixbuf in @ctx.
+ **/
+static void
+rsvg_render_svp (RsvgCtx *ctx, const ArtSVP *svp, guint32 rgba)
+{
+ GdkPixbuf *pixbuf;
+ pixbuf = ctx->pixbuf;
+
+ if (gdk_pixbuf_get_has_alpha (pixbuf))
+ art_rgba_svp_alpha (svp, 0, 0, gdk_pixbuf_get_width (pixbuf),
+ gdk_pixbuf_get_height (pixbuf),
+ rgba,
+ gdk_pixbuf_get_pixels (pixbuf),
+ gdk_pixbuf_get_rowstride (pixbuf),
+ NULL);
+ else
+ art_rgb_svp_alpha (svp, 0, 0, gdk_pixbuf_get_width (pixbuf),
+ gdk_pixbuf_get_height (pixbuf),
+ rgba,
+ gdk_pixbuf_get_pixels (pixbuf),
+ gdk_pixbuf_get_rowstride (pixbuf),
+ NULL);
+}
+
+static void
+rsvg_render_bpath (RsvgCtx *ctx, const ArtBpath *bpath)
+{
+ RsvgState *state;
+ ArtBpath *affine_bpath;
+ ArtVpath *vpath;
+ ArtSVP *svp;
+ GdkPixbuf *pixbuf;
+
+ state = &ctx->state[ctx->n_state - 1];
+ pixbuf = ctx->pixbuf;
+ affine_bpath = art_bpath_affine_transform (bpath,
+ state->affine);
+
+ vpath = art_bez_path_to_vec (affine_bpath, 0.25);
+ art_free (affine_bpath);
+
+ if (state->fill)
+ {
+ ArtVpath *closed_vpath;
+ ArtVpath *perturbed_vpath;
+ ArtSVP *tmp_svp;
+ ArtWindRule art_wind;
+
+ closed_vpath = rsvg_close_vpath (vpath);
+ perturbed_vpath = art_vpath_perturb (closed_vpath);
+ g_free (closed_vpath);
+ svp = art_svp_from_vpath (perturbed_vpath);
+ art_free (perturbed_vpath);
+ tmp_svp = art_svp_uncross (svp);
+ art_svp_free (svp);
+ art_wind = ART_WIND_RULE_NONZERO; /* todo - get from state */
+ svp = art_svp_rewind_uncrossed (tmp_svp, art_wind);
+ art_svp_free (tmp_svp);
+
+ rsvg_render_svp (ctx, svp,
+ (state->fill_color << 8) | state->fill_opacity);
+ art_svp_free (svp);
+ }
+
+ if (state->stroke)
+ {
+ svp = art_svp_vpath_stroke (vpath, state->join, state->cap,
+ state->stroke_width, 4, 0.25);
+ rsvg_render_svp (ctx, svp,
+ (state->stroke_color << 8) | state->stroke_opacity);
+ art_svp_free (svp);
+ }
+ art_free (vpath);
+}
+
+static void
+rsvg_start_path (RsvgCtx *ctx, const xmlChar **atts)
+{
+ int i;
+ char *d = NULL;
+
+ if (atts != NULL)
+ {
+ for (i = 0; atts[i] != NULL; i += 2)
+ {
+ if (!strcmp ((char *)atts[i], "d"))
+ d = (char *)atts[i + 1];
+ }
+ }
+ if (d != NULL)
+ {
+ RsvgBpathDef *bpath_def;
+
+ bpath_def = rsvg_parse_path (d);
+ rsvg_bpath_def_art_finish (bpath_def);
+
+ rsvg_render_bpath (ctx, bpath_def->bpath);
+
+ rsvg_bpath_def_free (bpath_def);
+ }
+}
+
+static void
+rsvg_start_element (void *data, const xmlChar *name, const xmlChar **atts)
+{
+ RsvgCtx *ctx = (RsvgCtx *)data;
+#ifdef VERBOSE
+ int i;
+#endif
+
+#ifdef VERBOSE
+ fprintf (stdout, "SAX.startElement(%s", (char *) name);
+ if (atts != NULL) {
+ for (i = 0;(atts[i] != NULL);i++) {
+ fprintf (stdout, ", %s='", atts[i++]);
+ fprintf (stdout, "%s'", atts[i]);
+ }
+ }
+ fprintf (stdout, ")\n");
+#endif
+
+ /* push the state stack */
+ if (ctx->n_state == ctx->n_state_max)
+ ctx->state = g_renew (RsvgState, ctx->state, ctx->n_state_max <<= 1);
+ if (ctx->n_state)
+ ctx->state[ctx->n_state] = ctx->state[ctx->n_state - 1];
+ else
+ rsvg_state_init (ctx->state);
+ ctx->n_state++;
+
+ if (!strcmp ((char *)name, "svg"))
+ rsvg_start_svg (ctx, atts);
+ else if (!strcmp ((char *)name, "g"))
+ rsvg_start_g (ctx, atts);
+ else if (!strcmp ((char *)name, "path"))
+ rsvg_start_path (ctx, atts);
+}
+
+static void
+rsvg_end_element (void *data, const xmlChar *name)
+{
+ RsvgCtx *ctx = (RsvgCtx *)data;
+
+ /* pop the state stack */
+ ctx->n_state--;
+
+#ifdef VERBOSE
+ fprintf (stdout, "SAX.endElement(%s)\n", (char *) name);
+#endif
+}
+
+xmlSAXHandler emptySAXHandlerStruct = {
+ NULL, /* internalSubset */
+ NULL, /* isStandalone */
+ NULL, /* hasInternalSubset */
+ NULL, /* hasExternalSubset */
+ NULL, /* resolveEntity */
+ NULL, /* getEntity */
+ NULL, /* entityDecl */
+ NULL, /* notationDecl */
+ NULL, /* attributeDecl */
+ NULL, /* elementDecl */
+ NULL, /* unparsedEntityDecl */
+ NULL, /* setDocumentLocator */
+ NULL, /* startDocument */
+ NULL, /* endDocument */
+ rsvg_start_element, /* startElement */
+ rsvg_end_element, /* endElement */
+ NULL, /* reference */
+ NULL, /* characters */
+ NULL, /* ignorableWhitespace */
+ NULL, /* processingInstruction */
+ NULL, /* comment */
+ NULL, /* xmlParserWarning */
+ NULL, /* xmlParserError */
+ NULL, /* xmlParserError */
+ NULL, /* getParameterEntity */
+};
+
+xmlSAXHandlerPtr emptySAXHandler = &emptySAXHandlerStruct;
+
+GdkPixbuf *
+rsvg_render_file (FILE *f, double zoom)
+{
+ int res;
+ char chars[10];
+ xmlParserCtxtPtr ctxt;
+ RsvgCtx *ctx;
+ GdkPixbuf *result;
+
+ ctx = rsvg_ctx_new ();
+ ctx->zoom = zoom;
+ res = fread(chars, 1, 4, f);
+ if (res > 0) {
+ ctxt = xmlCreatePushParserCtxt(emptySAXHandler, ctx,
+ chars, res, "filename XXX");
+ while ((res = fread(chars, 1, 3, f)) > 0) {
+ xmlParseChunk(ctxt, chars, res, 0);
+ }
+ xmlParseChunk(ctxt, chars, 0, 1);
+ xmlFreeParserCtxt(ctxt);
+ }
+ result = ctx->pixbuf;
+ rsvg_ctx_free (ctx);
+ return result;
+}
+
+#ifdef RSVG_MAIN
+static void
+write_pixbuf (ArtPixBuf *pixbuf)
+{
+ int y;
+ printf ("P6\n%d %d\n255\n", pixbuf->width, pixbuf->height);
+ for (y = 0; y < pixbuf->height; y++)
+ fwrite (pixbuf->pixels + y * pixbuf->rowstride, 1, pixbuf->width * 3,
+ stdout);
+}
+
+int
+main (int argc, char **argv)
+{
+ FILE *f;
+ ArtPixBuf *pixbuf;
+
+ if (argc == 1)
+ f = stdin;
+ else
+ {
+ f = fopen (argv[1], "r");
+ if (f == NULL)
+ {
+ fprintf (stderr, "Error opening source file %s\n", argv[1]);
+ }
+ }
+
+ pixbuf = rsvg_render_file (f);
+
+ if (f != stdin)
+ fclose (f);
+
+ write_pixbuf (pixbuf);
+
+ return 0;
+}
+#endif
diff --git a/librsvg/rsvg.h b/librsvg/rsvg.h
new file mode 100644
index 000000000..4ec50da4b
--- /dev/null
+++ b/librsvg/rsvg.h
@@ -0,0 +1,2 @@
+GdkPixbuf *
+rsvg_render_file (FILE *f, double zoom);
diff --git a/src/Makefile.am b/src/Makefile.am
index 48d7d3b32..0e362ce86 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,6 +18,7 @@ LDADD =\
file-manager/libntl-file-manager.la \
../nautilus-widgets/libnautilus-widgets.la \
../libnautilus/libnautilus.la \
+ ../librsvg/librsvg.la \
$(BONOBO_LIBS) \
$(GNORBA_LIBS) \
$(GNOMEUI_LIBS) \