X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fsvg%2Fsvg-color.cpp;h=7d43b68eef18d75c2e82c6f7d4e967ceb1f2ee95;hb=dedcb6c735b9199a15b5895b96e091a344dff187;hp=5a7602e4ffee8e5d6fc1ec1dff3c0ecaa32d69f4;hpb=4c0c5f0746040f82ed29fd56421a81f8cebf4f55;p=inkscape.git diff --git a/src/svg/svg-color.cpp b/src/svg/svg-color.cpp index 5a7602e4f..7d43b68ee 100644 --- a/src/svg/svg-color.cpp +++ b/src/svg/svg-color.cpp @@ -17,12 +17,36 @@ # include "config.h" #endif +#include +#include // sprintf +#include +#include +#include #include +#include +#include // g_assert +#include #include #include #include +#include -#include "svg.h" +#include "strneq.h" +#include "preferences.h" +#include "svg-color.h" +#include "svg-icc-color.h" +#include "svg-device-color.h" + +#if ENABLE_LCMS +#include +#include "color.h" +#include "color-profile.h" +#include "document.h" +#include "inkscape.h" +#include "profile-manager.h" +#endif // ENABLE_LCMS + +using std::sprintf; struct SPSVGColor { unsigned long rgb; @@ -182,22 +206,20 @@ static SPSVGColor const sp_svg_color_named[] = { { 0x9ACD32, "yellowgreen" } }; -#define SP_SVG_NUMCOLORS (sizeof(sp_svg_color_named) / sizeof(sp_svg_color_named[0])) - 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; - /* - * 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 - */ - if (str == NULL) return def; while ((*str <= ' ') && *str) str++; if (!*str) return def; @@ -226,23 +248,26 @@ sp_svg_read_color(gchar const *str, guint32 def) /* must be either 3 or 6 digits. */ return def; } - } else if (!strncmp(str, "rgb(", 4)) { - gboolean hasp, hasd; + if (end_ptr) { + *end_ptr = str + i; + } + } else if (strneq(str, "rgb(", 4)) { + bool hasp, hasd; gchar *s, *e; gdouble r, g, b; s = (gchar *) str + 4; - hasp = FALSE; - hasd = FALSE; + hasp = false; + hasd = false; r = g_ascii_strtod(s, &e); if (s == e) return def; s = e; if (*s == '%') { - hasp = TRUE; + hasp = true; s += 1; } else { - hasd = TRUE; + hasd = true; } while (*s && g_ascii_isspace(*s)) s += 1; if (*s != ',') return def; @@ -252,10 +277,10 @@ sp_svg_read_color(gchar const *str, guint32 def) if (s == e) return def; s = e; if (*s == '%') { - hasp = TRUE; + hasp = true; s += 1; } else { - hasd = TRUE; + hasd = true; } while (*s && g_ascii_isspace(*s)) s += 1; if (*s != ',') return def; @@ -265,10 +290,16 @@ sp_svg_read_color(gchar const *str, guint32 def) if (s == e) return def; s = e; if (*s == '%') { - hasp = TRUE; + hasp = true; + s += 1; } else { - hasd = TRUE; + 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; @@ -279,14 +310,18 @@ sp_svg_read_color(gchar const *str, guint32 def) 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; } @@ -301,15 +336,118 @@ sp_svg_read_color(gchar const *str, guint32 def) } else { return def; } + if (end_ptr) { + *end_ptr = str + i; + } } return (val << 8); } -gint -sp_svg_write_color(gchar *buf, gint buflen, guint32 color) +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. + */ +void +sp_svg_write_color(gchar *buf, unsigned const buflen, guint32 const rgba32) { - return g_snprintf(buf, buflen, "#%06x", color >> 8); + g_assert(8 <= buflen); + + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + unsigned const rgb24 = rgba32 >> 8; + if (prefs->getBool("/options/svgoutput/usenamedcolors")) { + rgb24_to_css(buf, rgb24); + } else { + g_snprintf(buf, buflen, "#%06x", rgb24); + } } static GHashTable * @@ -317,7 +455,7 @@ sp_svg_create_color_hash() { GHashTable *colors = g_hash_table_new(g_str_hash, g_str_equal); - for (unsigned i = 0 ; i < SP_SVG_NUMCOLORS ; i++) { + for (unsigned i = 0 ; i < G_N_ELEMENTS(sp_svg_color_named) ; i++) { g_hash_table_insert(colors, (gpointer)(sp_svg_color_named[i].name), (gpointer)(&sp_svg_color_named[i].rgb)); @@ -326,6 +464,253 @@ sp_svg_create_color_hash() return colors; } +#if ENABLE_LCMS +//helper function borrowed from src/widgets/sp-color-icc-selector.cpp: +void getThings( DWORD space, gchar const**& namers, gchar const**& tippies, guint const*& scalies ); + +void icc_color_to_sRGB(SVGICCColor* icc, guchar* r, guchar* g, guchar* b){ + guchar color_out[4]; + guchar color_in[4]; + if (icc){ +g_message("profile name: %s", icc->colorProfile.c_str()); + Inkscape::ColorProfile* prof = SP_ACTIVE_DOCUMENT->profileManager->find(icc->colorProfile.c_str()); + if ( prof ) { + cmsHTRANSFORM trans = prof->getTransfToSRGB8(); + if ( trans ) { + gchar const** names = 0; + gchar const** tips = 0; + guint const* scales = 0; + getThings( prof->getColorSpace(), names, tips, scales ); + + guint count = _cmsChannelsOf( prof->getColorSpace() ); + if (count>4) count=4; //do we need it? Should we allow an arbitrary number of color values? Or should we limit to a maximum? (max==4?) + for (guint i=0;icolors[i])*256.0) * (gdouble)scales[i]); +g_message("input[%d]: %d",i, color_in[i]); + } + + cmsDoTransform( trans, color_in, color_out, 1 ); +g_message("transform to sRGB done"); + } + *r = color_out[0]; + *g = color_out[1]; + *b = color_out[2]; + } + } +} +#endif //ENABLE_LCMS + +/* + * Some discussion at http://markmail.org/message/bhfvdfptt25kgtmj + * Allowed ASCII first characters: ':', 'A'-'Z', '_', 'a'-'z' + * Allowed ASCII remaining chars add: '-', '.', '0'-'9', + */ +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) ) + && (*str != '_') + && (*str != ':') ) { + // Name must start with a certain type of character + good = false; + } else { + while ( g_ascii_isdigit(*str) || g_ascii_isalpha(*str) + || (*str == '-') || (*str == ':') || (*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; +} + + +bool sp_svg_read_icc_color( gchar const *str, SVGICCColor* dest ) +{ + return sp_svg_read_icc_color(str, NULL, dest); +} + +bool sp_svg_read_device_color( gchar const *str, gchar const **end_ptr, SVGDeviceColor* dest) +{ + bool good = true; + unsigned int max_colors; + + if ( end_ptr ) { + *end_ptr = str; + } + if ( dest ) { + dest->colors.clear(); + } + + if ( !str ) { + // invalid input + good = false; + } else { + while ( g_ascii_isspace(*str) ) { + str++; + } + + dest->type = DEVICE_COLOR_INVALID; + if (strneq( str, "device-gray(", 12 )){ + dest->type = DEVICE_GRAY; + max_colors=1; + str += 12; + } + + if (strneq( str, "device-rgb(", 11 )){ + dest->type = DEVICE_RGB; + max_colors=3; + str += 11; + } + + if (strneq( str, "device-cmyk(", 12 )){ + dest->type = DEVICE_CMYK; + max_colors=4; + str += 12; + } + + if (strneq( str, "device-nchannel(", 16 )){ + dest->type = DEVICE_NCHANNEL; + max_colors=0; + str += 16; + } + + if ( dest->type != DEVICE_COLOR_INVALID ) { + while ( g_ascii_isspace(*str) ) { + str++; + } + + 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 ( dest->colors.size() == 0) good=false; + if ( dest->type != DEVICE_NCHANNEL && (dest->colors.size() != max_colors)) good=false; + + if ( good ) { + if ( end_ptr ) { + *end_ptr = str; + } + } else { + if ( dest ) { + dest->type = DEVICE_COLOR_INVALID; + dest->colors.clear(); + } + } + + return good; +} + + +bool sp_svg_read_device_color( gchar const *str, SVGDeviceColor* dest) +{ + return sp_svg_read_device_color(str, NULL, dest); +} + /* Local Variables: mode:c++