[cairo] Nasty bug in _cairo_strtod: no negative numbers!

Tobias Fleischer (reduxFX) tobias.fleischer at reduxfx.com
Wed Jul 15 12:43:29 UTC 2020


After spending several days of trying to track down weird behaviour when
using Cairo, I finally found the culprit:
In cairo-misc.c there is a function called _cairo_strtod() that is intended
as a replacement to the standard strtod(), meaning it should convert a
string to a double. The difference according to the comment is that it
"ignores locale and only accepts decimal points".

However, in difference to the original strtod() function, it can't deal
with negative numbers in strings and will always return "0" for them. Not
good!
This is particularly relevant when dealing with variation fonts, as the
axis parameters need to be given as a string and many of them have a
negative range, which leads to faulty display of these fonts.

I made a hotfix/verification in my own Cairo sources, and it seems to work
fine, but I can't make a pull request at the moment, so I post it below,
though there might be an easier way to fix this for sure:

double
_cairo_strtod (const char *nptr, char **endptr)
{
    const char *decimal_point;
    int decimal_point_len;
    const char *p;
    char buf[100];
    char *bufptr;
    char *bufend = buf + sizeof(buf) - 1;
    double value;
    char *end;
    int delta;
    cairo_bool_t have_dp;
    int inv; //[TF]

    decimal_point = _cairo_get_locale_decimal_point ();
    decimal_point_len = strlen (decimal_point);
    assert (decimal_point_len != 0);

    p = nptr;
    bufptr = buf;
    delta = 0;
    have_dp = FALSE;
    while (*p && _cairo_isspace (*p)) {
	p++;
	delta++;
    }

    inv = 1; //[TF]
    while (*p && (bufptr + decimal_point_len < bufend)) {
	if (_cairo_isdigit (*p)) {
	    *bufptr++ = *p;
	} else if (*p == '-') { //[TF]
	    inv = -1; //[TF]
	    delta++; //[TF]
	} else if (*p == '.') {
	    if (have_dp)
		break;
	    strncpy (bufptr, decimal_point, decimal_point_len);
	    bufptr += decimal_point_len;
	    delta -= decimal_point_len - 1;
	    have_dp = TRUE;
	} else {
	    break;
	}
	p++;
    }
    *bufptr = 0;

    value = strtod (buf, &end) * inv; //[TF]
    if (endptr) {
	if (end == buf)
	    *endptr = (char*)(nptr);
	else
	    *endptr = (char*)(nptr + (end - buf) + delta);
    }

    return value;
}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.cairographics.org/archives/cairo/attachments/20200715/622cc3be/attachment.htm>


More information about the cairo mailing list