[cairo] Q: gtk_main() and svg_cairo_parse()

Michael N. Filippov michael at idisys.iae.nsk.su
Mon Oct 3 22:26:45 PDT 2005


Michael N. Filippov wrote:
> Hello!
> 
> I tried to compile a very simple code (based on libsvg.cairo from 
> cairo-demo/cairo_snippets, see below) with libcairo2-dev_1.0.0-3, 
> libsvg-cairo1-dev_0.1.6-5 and libgtk2.0-dev_2.6.10-1 (also tried 2.8.3) 
> and noticed that if gtk_main() is called before svg_cairo_parse() then 
> the PNG picture looks bad (no shadows, yellow border is below the window 
> etc). But if gtk_init() is called after svg_cairo_parse(), everything is 
> fine. Did I miss something about API, environment variables or something 
> else? Any help would be greatly appreciated.
> 
> Thanks in advance,
> 
> Michael
> 
> 
> #include <gtk/gtk.h>
> #include <svg-cairo.h>
> 
> int main(int argc, char* argv[])
> {
>     double  IMAGE_WIDTH     = 256;
>     double  IMAGE_HEIGHT = 256;
>     cairo_surface_t*    surface = cairo_image_surface_create(
>         CAIRO_FORMAT_ARGB32, IMAGE_WIDTH, IMAGE_HEIGHT);
>     cairo_t*    cr = cairo_create(surface);
>     cairo_scale(cr, IMAGE_WIDTH, IMAGE_HEIGHT);
>     svg_cairo_t*    svgc;
>     svg_cairo_create(&svgc);
>     gtk_init(&argc, &argv);    // 1) doesn't work
>     svg_cairo_parse(svgc, "data/home.svg");
> //    gtk_init(&argc, &argv);    // 2) works
>     unsigned    w, h;
>     svg_cairo_get_size(svgc, &w, &h);
>     cairo_scale(cr, 1.0 / w, 1.0 / h);
>     svg_cairo_render(svgc, cr);
>     svg_cairo_destroy(svgc);
>     cairo_surface_write_to_png(surface, "out.png");
>     cairo_destroy(cr);
>     cairo_surface_destroy(surface);
>     return 0;
> }
> _______________________________________________
> cairo mailing list
> cairo at cairographics.org
> http://cairographics.org/cgi-bin/mailman/listinfo/cairo

Hello again!

Seems I found the problem (solution attached): it's a bug in 
_svg_ascii_strtod() (libsvg-0.1.4/src/svg_ascii.c).

Example:
 >~/work/svg/test$ LC_ALL=C ./main
decimal_point: '.'
Result: _svg_ascii_strtod("0,123.456", NULL) = 0
 >~/work/svg/test$ LC_ALL=ru_RU.KOI8-R ./main
decimal_point: ','
Result: _svg_ascii_strtod("0,123.456", NULL) = 0,123

Details:
if input parameter nptr == 0,123.456 and current locale decimal_point == 
",",
then after  /* Skip leading space */ decimal_point_pos == NULL, stdlib
strtod("0,123.456") is called and result is 0.123 (should be 0).

Solution:
Own strtod function for SVG numbers (I think it is much better than hack 
to use locale dependent std::strtod).
According to http://www.w3.org/TR/2003/REC-SVG11-20030114/paths.html:
number:
     sign? integer-constant
     | sign? floating-point-constant
integer-constant:
     digit-sequence
floating-point-constant:
     fractional-constant exponent?
     | digit-sequence exponent
fractional-constant:
     digit-sequence? "." digit-sequence
     | digit-sequence "."
exponent:
     ( "e" | "E" ) sign? digit-sequence
sign:
     "+" | "-"
digit-sequence:
     digit
     | digit digit-sequence
digit:
     "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
So the new procedure is very simple (see below). I had to add couple of 
if's to make it fully compatible with std::strtod(). Attached main.c 
contains this procedure and code to compare results with std::strtod().

double
_svg_ascii_strtod (const char  *nptr,
		   const char **endptr)
{
   const char	*p = nptr;
   const char	*end = p;
   double	value = 0;
   int		sign = 1;
   double	factor;
   int		exp;

   while (_svg_ascii_isspace (*p))
     p++;

   switch (*p)
     {
       case '-':
	sign = -1;
	/* FALLTHRU */
       case '+':
	++p;
     }

   while (_svg_ascii_isdigit (*p))
     {
	value = value * 10 + (*p - '0');
	end = ++p;
     }

   if (*p == '.')
     {
       ++p;
       if (end != nptr) /* digits. */
         end = p;
       else
         {
	  if (!_svg_ascii_isdigit (*p))
	    goto exit;
	}
       factor = 1;
       while (_svg_ascii_isdigit (*p))
         {
             factor *= 0.1;
             value += (*p - '0') * factor;
	    end = ++p;
         }
     }

   if (end != nptr && (*p == 'e' || *p == 'E'))
     {
       ++p;
       factor = 10;
       switch (*p)
	{
	    case '-':
	      factor = 0.1;
	      /* FALLTHRU */
	    case '+':
	      ++p;
	}

	exp = 0;
	while (_svg_ascii_isdigit (*p))
	  {
	    exp = exp * 10 + (*p - '0');
	    end = ++p;
	  }

	if (value != 0)	/* 0e1000 */
	  {
	    while (1)
	      {
	        if (exp & 1)
	          value *= factor;
                 if ((exp >>= 1) == 0)
                   break;
                 factor *= factor;
               }
	  }
     }

exit:

   if (endptr)
     *endptr = end;

   if (end == nptr)  /* "-a" gives 0 */
     return 0;

   return value * sign;
}

Best regards,

Michael
-------------- next part --------------
A non-text attachment was scrubbed...
Name: bad.png
Type: image/png
Size: 12491 bytes
Desc: not available
Url : http://lists.freedesktop.org/archives/cairo/attachments/20051004/8c15b364/bad-0001.png
-------------- next part --------------
A non-text attachment was scrubbed...
Name: good.png
Type: image/png
Size: 13059 bytes
Desc: not available
Url : http://lists.freedesktop.org/archives/cairo/attachments/20051004/8c15b364/good-0001.png
-------------- next part --------------
A non-text attachment was scrubbed...
Name: main.c.gz
Type: application/x-gzip
Size: 1597 bytes
Desc: not available
Url : http://lists.freedesktop.org/archives/cairo/attachments/20051004/8c15b364/main.c-0001.bin


More information about the cairo mailing list