diff --git a/src/svg/svg-color.cpp b/src/svg/svg-color.cpp
index 89c273fe57e7dee37b06e4aa5f66ce6772129b52..2bae38641b170413e9642e17afe16e96716d966a 100644 (file)
--- a/src/svg/svg-color.cpp
+++ b/src/svg/svg-color.cpp
#endif
#include "svg-color.h"
+#include "svg-icc-color.h"
+#include <cassert>
#include <math.h>
+#include <glib/gmem.h>
#include <glib/gmessages.h>
#include <glib/gstrfuncs.h>
#include <glib/ghash.h>
#include <glib/gutils.h>
+#include <cstdio> // sprintf
+#include <errno.h>
#include "strneq.h"
+using std::sprintf;
struct SPSVGColor {
unsigned long rgb;
static GHashTable *sp_svg_create_color_hash();
guint32
-sp_svg_read_color(gchar const *str, guint32 def)
+sp_svg_read_color(gchar const *str, guint32 const dfl)
+{
+ return sp_svg_read_color(str, NULL, dfl);
+}
+
+static guint32
+internal_sp_svg_read_color(gchar const *str, gchar const **end_ptr, guint32 def)
{
static GHashTable *colors = NULL;
- gchar c[32];
guint32 val = 0;
if (str == NULL) return def;
/* must be either 3 or 6 digits. */
return def;
}
+ if (end_ptr) {
+ *end_ptr = str + i;
+ }
} else if (strneq(str, "rgb(", 4)) {
gboolean hasp, hasd;
gchar *s, *e;
} else {
hasd = TRUE;
}
+ while(*s && g_ascii_isspace(*s)) s += 1;
+ if (*s != ')') {
+ return def;
+ }
+ ++s;
if (hasp && hasd) return def;
if (hasp) {
val = (guint) floor(CLAMP(r, 0.0, 100.0) * 2.559999) << 24;
val |= ((guint) CLAMP(g, 0, 255) << 16);
val |= ((guint) CLAMP(b, 0, 255) << 8);
}
+ if (end_ptr) {
+ *end_ptr = s;
+ }
return val;
} else {
gint i;
if (!colors) {
colors = sp_svg_create_color_hash();
}
+ gchar c[32];
for (i = 0; i < 31; i++) {
- if (str[i] == ';') {
+ if (str[i] == ';' || g_ascii_isspace(str[i])) {
c[i] = '\0';
break;
}
} else {
return def;
}
+ if (end_ptr) {
+ *end_ptr = str + i;
+ }
}
return (val << 8);
}
+guint32
+sp_svg_read_color(gchar const *str, gchar const **end_ptr, guint32 dfl)
+{
+ /* I've been rather hurried in editing the above to add support for end_ptr, so I'm adding
+ * this check wrapper. */
+ gchar const *end = str;
+ guint32 const ret = internal_sp_svg_read_color(str, &end, dfl);
+ assert(ret == dfl && end == str
+ || (((ret & 0xff) == 0)
+ && str < end));
+ if (str < end) {
+ gchar *buf = (gchar *) g_malloc(end + 1 - str);
+ memcpy(buf, str, end - str);
+ buf[end - str] = '\0';
+ gchar const *buf_end = buf;
+ guint32 const check = internal_sp_svg_read_color(buf, &buf_end, 1);
+ assert(check == ret
+ && buf_end - buf == end - str);
+ g_free(buf);
+
+ if ( end_ptr ) {
+ *end_ptr = end;
+ }
+ }
+ return ret;
+}
+
+
/**
+ * Converts an RGB colour expressed in form 0x00rrggbb to a CSS/SVG representation of that colour.
+ * The result is valid even in SVG Tiny or non-SVG CSS.
+ */
+static void
+rgb24_to_css(char *const buf, unsigned const rgb24)
+{
+ assert(rgb24 < (1u << 24));
+
+ /* SVG 1.1 Full allows additional colour names not supported by SVG Tiny, but we don't bother
+ * with them: it's good for these colours to be copyable to non-SVG CSS stylesheets and for
+ * documents to be more viewable in SVG Tiny/Basic viewers; and some of the SVG Full names are
+ * less meaningful than hex equivalents anyway. And it's easier for a person to map from the
+ * restricted set because the only component values are {00,80,ff}, other than silver 0xc0c0c0.
+ */
+
+ char const *src = NULL;
+ switch (rgb24) {
+ /* Extracted mechanically from the table at
+ * http://www.w3.org/TR/REC-html40/types.html#h-6.5 .*/
+ case 0x000000: src = "black"; break;
+ case 0xc0c0c0: src = "silver"; break;
+ case 0x808080: src = "gray"; break;
+ case 0xffffff: src = "white"; break;
+ case 0x800000: src = "maroon"; break;
+ case 0xff0000: src = "red"; break;
+ case 0x800080: src = "purple"; break;
+ case 0xff00ff: src = "fuchsia"; break;
+ case 0x008000: src = "green"; break;
+ case 0x00ff00: src = "lime"; break;
+ case 0x808000: src = "olive"; break;
+ case 0xffff00: src = "yellow"; break;
+ case 0x000080: src = "navy"; break;
+ case 0x0000ff: src = "blue"; break;
+ case 0x008080: src = "teal"; break;
+ case 0x00ffff: src = "aqua"; break;
+
+ default: {
+ if ((rgb24 & 0xf0f0f) * 0x11 == rgb24) {
+ /* Can use the shorter three-digit form #rgb instead of #rrggbb. */
+ sprintf(buf, "#%x%x%x",
+ (rgb24 >> 16) & 0xf,
+ (rgb24 >> 8) & 0xf,
+ rgb24 & 0xf);
+ } else {
+ sprintf(buf, "#%06x", rgb24);
+ }
+ break;
+ }
+ }
+ if (src) {
+ strcpy(buf, src);
+ }
+
+ assert(sp_svg_read_color(buf, 0xff) == (rgb24 << 8));
+}
+
+/**
+ * Converts an RGBA32 colour to a CSS/SVG representation of the RGB portion of that colour. The
+ * result is valid even in SVG Tiny or non-SVG CSS.
+ *
+ * \param rgba32 Colour expressed in form 0xrrggbbaa.
* \pre buflen \>= 8.
*/
-gint
-sp_svg_write_color(gchar *buf, unsigned const buflen, guint32 const color)
+void
+sp_svg_write_color(gchar *buf, unsigned const buflen, guint32 const rgba32)
{
g_assert(8 <= buflen);
- return g_snprintf(buf, buflen, "#%06x", color >> 8);
+
+ unsigned const rgb24 = rgba32 >> 8;
+ rgb24_to_css(buf, rgb24);
}
static GHashTable *
return colors;
}
+
+bool sp_svg_read_icc_color( gchar const *str, gchar const **end_ptr, SVGICCColor* dest )
+{
+ bool good = true;
+
+ if ( end_ptr ) {
+ *end_ptr = str;
+ }
+ if ( dest ) {
+ dest->colorProfile.clear();
+ dest->colors.clear();
+ }
+
+ if ( !str ) {
+ // invalid input
+ good = false;
+ } else {
+ while ( g_ascii_isspace(*str) ) {
+ str++;
+ }
+
+ good = strneq( str, "icc-color(", 10 );
+
+ if ( good ) {
+ str += 10;
+ while ( g_ascii_isspace(*str) ) {
+ str++;
+ }
+
+ if ( !g_ascii_isalpha(*str)
+ && ( !(0x080 & *str) ) ) {
+ // Name must start with a certain type of character
+ good = false;
+ } else {
+ while ( g_ascii_isdigit(*str) || g_ascii_islower(*str) || (*str == '-') ) {
+ if ( dest ) {
+ dest->colorProfile += *str;
+ }
+ str++;
+ }
+ while ( g_ascii_isspace(*str) || *str == ',' ) {
+ str++;
+ }
+ }
+ }
+
+ if ( good ) {
+ while ( *str && *str != ')' ) {
+ if ( g_ascii_isdigit(*str) || *str == '.' || *str == '-' || *str == '+') {
+ gchar* endPtr = 0;
+ gdouble dbl = g_ascii_strtod( str, &endPtr );
+ if ( !errno ) {
+ if ( dest ) {
+ dest->colors.push_back( dbl );
+ }
+ str = endPtr;
+ } else {
+ good = false;
+ break;
+ }
+
+ while ( g_ascii_isspace(*str) || *str == ',' ) {
+ str++;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+ // We need to have ended on a closing parenthesis
+ if ( good ) {
+ while ( g_ascii_isspace(*str) ) {
+ str++;
+ }
+ good &= *str == ')';
+ }
+ }
+
+ if ( good ) {
+ if ( end_ptr ) {
+ *end_ptr = str;
+ }
+ } else {
+ if ( dest ) {
+ dest->colorProfile.clear();
+ dest->colors.clear();
+ }
+ }
+
+ return good;
+}
+
/*
Local Variables:
mode:c++