[librsvg] Fix path data number parsing
- From: Behdad Esfahbod <behdad src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg] Fix path data number parsing
- Date: Mon, 10 Nov 2014 01:25:55 +0000 (UTC)
commit 5ba4343bccc7e1765f38f87490b3d6a3a500fde1
Author: Behdad Esfahbod <behdad behdad org>
Date: Sun Nov 9 17:24:09 2014 -0800
Fix path data number parsing
Previously highly-optimized path data was being garbled during loading.
A patch has been available for over four years. Fix it. This is pretty
much the same patch as one submitted by Louis Simard, which I verified
was correct except for two cases where it was using "&=" when "|=" was
intended.
https://bugzilla.gnome.org/show_bug.cgi?id=620923
rsvg-path.c | 189 +++++++++++++++++++++++++++++++++++------------------------
1 files changed, 112 insertions(+), 77 deletions(-)
---
diff --git a/rsvg-path.c b/rsvg-path.c
index 69c7d47..ce9be42 100644
--- a/rsvg-path.c
+++ b/rsvg-path.c
@@ -568,100 +568,135 @@ rsvg_path_end_of_number (RSVGParsePathCtx * ctx, double val, int sign, int exp_s
rsvg_parse_path_do_cmd (ctx, FALSE);
}
+#define RSVGN_IN_PREINTEGER 0
+#define RSVGN_IN_INTEGER 1
+#define RSVGN_IN_FRACTION 2
+#define RSVGN_IN_PREEXPONENT 3
+#define RSVGN_IN_EXPONENT 4
+
+#define RSVGN_GOT_SIGN 0x1
+#define RSVGN_GOT_EXPONENT_SIGN 0x2
+
+/* Returns the length of the number parsed, so it can be skipped
+ * in rsvg_parse_path_data. Calls rsvg_path_end_number to have the number
+ * processed in its command.
+ */
+static int
+rsvg_parse_number (RSVGParsePathCtx * ctx, const char *data)
+{
+ int length = 0;
+ int in = RSVGN_IN_PREINTEGER; /* Current location within the number */
+ int got = 0x0; /* [bitfield] Having 2 of each of these is an error */
+ gboolean end = FALSE; /* Set to true if the number should end after a char */
+ gboolean error = FALSE; /* Set to true if the number ended due to an error */
+
+ double value = 0.0;
+ double fraction = 1.0;
+ int sign = +1; /* Presume the INTEGER is positive if it has no sign */
+ int exponent = 0;
+ int exponent_sign = +1; /* Presume the EXPONENT is positive if it has no sign */
+
+ while (data[length] != '\0' && !end && !error) {
+ char c = data[length];
+ switch (in) {
+ case RSVGN_IN_PREINTEGER: /* No numbers yet, we're just starting out */
+ /* LEGAL: + - .->FRACTION DIGIT->INTEGER */
+ if (c == '+' || c == '-') {
+ if (got & RSVGN_GOT_SIGN) {
+ error = TRUE; /* Two signs: not allowed */
+ } else {
+ sign = c == '+' ? +1 : -1;
+ got |= RSVGN_GOT_SIGN;
+ }
+ } else if (c == '.') {
+ in = RSVGN_IN_FRACTION;
+ } else if (c >= '0' && c <= '9') {
+ value = c - '0';
+ in = RSVGN_IN_INTEGER;
+ }
+ break;
+ case RSVGN_IN_INTEGER: /* Previous character(s) was/were digit(s) */
+ /* LEGAL: DIGIT .->FRACTION E->PREEXPONENT */
+ if (c >= '0' && c <= '9') {
+ value = value * 10 + (c - '0');
+ }
+ else if (c == '.') {
+ in = RSVGN_IN_FRACTION;
+ }
+ else if (c == 'e' || c == 'E') {
+ in = RSVGN_IN_PREEXPONENT;
+ }
+ else {
+ end = TRUE;
+ }
+ break;
+ case RSVGN_IN_FRACTION: /* Previously, digit(s) in the fractional part */
+ /* LEGAL: DIGIT E->PREEXPONENT */
+ if (c >= '0' && c <= '9') {
+ fraction *= 0.1;
+ value += fraction * (c - '0');
+ }
+ else if (c == 'e' || c == 'E') {
+ in = RSVGN_IN_PREEXPONENT;
+ }
+ else {
+ end = TRUE;
+ }
+ break;
+ case RSVGN_IN_PREEXPONENT: /* Right after E */
+ /* LEGAL: + - DIGIT->EXPONENT */
+ if (c == '+' || c == '-') {
+ if (got & RSVGN_GOT_EXPONENT_SIGN) {
+ error = TRUE; /* Two signs: not allowed */
+ } else {
+ exponent_sign = c == '+' ? +1 : -1;
+ got |= RSVGN_GOT_EXPONENT_SIGN;
+ }
+ } else if (c >= '0' && c <= '9') {
+ exponent = c - '0';
+ in = RSVGN_IN_EXPONENT;
+ }
+ break;
+ case RSVGN_IN_EXPONENT: /* After E and the sign, if applicable */
+ /* LEGAL: DIGIT */
+ if (c >= '0' && c <= '9') {
+ exponent = exponent * 10 + (c - '0');
+ } else {
+ end = TRUE;
+ }
+ break;
+ }
+ length++;
+ }
+
+ /* TODO? if (error) report_the_error_somehow(); */
+ rsvg_path_end_of_number(ctx, value, sign, exponent_sign, exponent);
+ return end /* && !error */ ? length - 1 : length;
+}
+
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++) {
+
+ for (i = 0; data[i] != '\0'; i++) {
c = data[i];
- if (c >= '0' && c <= '9') {
+ if (c >= '0' && c <= '9' || c == '+' || c == '-' || c == '.') {
/* 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_frac = TRUE;
- val = 0;
- }
- else if (in_frac) {
- rsvg_path_end_of_number(ctx, val, sign, exp_sign, exp);
- in_frac = FALSE;
- in_exp = FALSE;
- exp = 0;
- exp_sign = 1;
- exp_wait_sign = FALSE;
- val = 0;
- sign = 1;
- }
- else {
- in_frac = TRUE;
- }
- in_num = 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 */
- rsvg_path_end_of_number(ctx, val, sign, exp_sign, exp);
- 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;
+ i += rsvg_parse_number(ctx, data + i) - 1;
} else if (c == 'z' || c == 'Z') {
if (ctx->param)
rsvg_parse_path_do_cmd (ctx, TRUE);
rsvg_path_builder_close_path (&ctx->builder);
ctx->cp = ctx->rp = g_array_index (ctx->builder.path_data, cairo_path_data_t,
ctx->builder.path_data->len - 1);
- } else if (c >= 'A' && c <= 'Z' && c != 'E') {
+ } 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') {
+ } else if (c >= 'a' && c < 'z' && c != 'e') {
if (ctx->param)
rsvg_parse_path_do_cmd (ctx, TRUE);
ctx->cmd = c;
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]