[dia] svg: improve matrix parsing and handling



commit 6c7fd57ea22b41fd7e17321a85e7b4772f1fed56
Author: Hans Breuer <hans breuer org>
Date:   Sat Aug 10 23:14:32 2013 +0200

    svg: improve matrix parsing and handling
    
     - basic support for makes the W3C test suite work better
     - don't let a non-invertible matrix screw cairo context,
       but just don't render with it as SVG expects it to be
    
    animate-elem-34-t
      invalid matrix - by sequence of transforms: fixed.
    coords-trans-08-t
      skewX skewY wrong transformation: fixed
    coords-trans-09-t
      invalid matrix - by intent: matrix(0 0 0 0 0 0)
      stop cairo renderer complaints by not drawing

 lib/dia_svg.c                      |   70 +++++++++++++++++++++++++++--------
 lib/geometry.c                     |   13 +++++++
 lib/geometry.h                     |    1 +
 lib/libdia.def                     |    1 +
 plug-ins/cairo/diacairo-renderer.c |    3 ++
 5 files changed, 72 insertions(+), 16 deletions(-)
---
diff --git a/lib/dia_svg.c b/lib/dia_svg.c
index a60ffc3..6c84ae4 100644
--- a/lib/dia_svg.c
+++ b/lib/dia_svg.c
@@ -1365,18 +1365,20 @@ MORETOPARSE:
   return (points->len > 1);
 }
 
-DiaMatrix *
-dia_svg_parse_transform(const gchar *trans, real scale)
+static gboolean
+_parse_transform (const gchar *trans, DiaMatrix *m, real scale)
 {
-  DiaMatrix *m;
-  gchar *p = strchr (trans, '(');
   gchar **list;
+  gchar *p = strchr (trans, '(');
   int i = 0;
 
-  if (!p)
-    return NULL; /* silently ignore broken format */
+  while (   (*trans != '\0') 
+         && (*trans == ' ' || *trans == ',' || *trans == '\t' || *trans == '\n' || *trans == '\r'))
+    ++trans; /* skip whitespace */
+
+  if (!p || !*trans)
+    return FALSE; /* silently fail */
 
-  m = g_new0 (DiaMatrix, 1);
   list = g_regex_split_simple ("[\\s,]+", p+1, 0, 0);
   if (strncmp (trans, "matrix", 6) == 0) {
     if (list[i])
@@ -1405,6 +1407,7 @@ dia_svg_parse_transform(const gchar *trans, real scale)
     else
       m->yy = m->xx;
   } else if (strncmp (trans, "rotate", 6) == 0) {
+    DiaMatrix translate = {1, 0, 0, 1, 0, 0 };
     real angle;
     
     if (list[i])
@@ -1419,28 +1422,63 @@ dia_svg_parse_transform(const gchar *trans, real scale)
     m->yx =  sin(G_PI*angle/180);
     m->yy =  cos(G_PI*angle/180);
     /* FIXME: check with real world data, I'm uncertain */
-    if (list[i])
-      m->x0 = g_ascii_strtod (list[i], NULL), ++i;
-    if (list[i])
-      m->y0 = g_ascii_strtod (list[i], NULL), ++i;
+    if (list[i]) {
+      real cx, cy;
+      cx = g_ascii_strtod (list[i], NULL), ++i;
+      if (list[i])
+        cy = g_ascii_strtod (list[i], NULL), ++i;
+      /* rotate around the given offset */
+      translate.x0 = cx;
+      translate.y0 = cy;
+      dia_matrix_multiply (m, m, &translate);
+      translate.x0 = -cx;
+      translate.y0 = -cy;
+      dia_matrix_multiply (m, &translate, m);
+    }
   } else if (strncmp (trans, "skewX", 5) == 0) {
     m->xx = m->yy = 1.0;
     if (list[i])
-      m->yx = tan (G_PI*g_ascii_strtod (list[i], NULL)/180);
+      m->xy = tan (G_PI*g_ascii_strtod (list[i], NULL)/180);
   } else if (strncmp (trans, "skewY", 5) == 0) {
     m->xx = m->yy = 1.0;
     if (list[i])
-      m->xy = tan (G_PI*g_ascii_strtod (list[i], NULL)/180);
+      m->yx = tan (G_PI*g_ascii_strtod (list[i], NULL)/180);
   } else {
     g_warning ("SVG: %s?", trans);
-    g_free (m);
-    m = NULL;
+    return FALSE;
   }
+  g_strfreev(list);
+
   if (scale > 0 && m) {
     m->x0 /= scale;
     m->y0 /= scale;
   }
-  g_strfreev(list);
+  return TRUE;
+}
+
+DiaMatrix *
+dia_svg_parse_transform(const gchar *trans, real scale)
+{
+  DiaMatrix *m = NULL;
+  gchar **transforms = g_regex_split_simple ("\\)", trans, 0, 0);
+  int i = 0;
+
+  /* go through the list of ztansformations - not that one would be enough ;) */
+  while (transforms[i]) {
+    DiaMatrix mat = { 0, };
+
+    if (_parse_transform (transforms[i], &mat, scale)) {
+      if (!m) {
+       m = g_new (DiaMatrix, 1);
+       *m = mat;
+      } else {
+       dia_matrix_multiply (m, &mat, m);
+      }
+    }
+    ++i;
+  }
+  g_strfreev(transforms);
+
   return m;
 }
 
diff --git a/lib/geometry.c b/lib/geometry.c
index 662d562..dede3e9 100644
--- a/lib/geometry.c
+++ b/lib/geometry.c
@@ -792,3 +792,16 @@ dia_matrix_set_angle_and_scales (DiaMatrix *m,
   cairo_matrix_init_rotate ((cairo_matrix_t *)m, a);
   cairo_matrix_scale ((cairo_matrix_t *)m, sx, sy);
 }
+
+gboolean
+dia_matrix_is_invertible (const DiaMatrix *matrix)
+{
+  double a, b, c, d;
+  double det;
+
+  a = matrix->xx; b = matrix->yx;
+  c = matrix->xy; d = matrix->yy;
+  det = a*d - b*c;
+
+  return finite(det) && det != 0.0;
+}
\ No newline at end of file
diff --git a/lib/geometry.h b/lib/geometry.h
index 494b671..69a120b 100644
--- a/lib/geometry.h
+++ b/lib/geometry.h
@@ -136,6 +136,7 @@ void dia_matrix_set_angle_and_scales (DiaMatrix *m,
                                      real       sx,
                                      real       sy);
 void dia_matrix_multiply (DiaMatrix *result, const DiaMatrix *a, const DiaMatrix *b);
+gboolean dia_matrix_is_invertible (const DiaMatrix *matrix);
 
 #define ROUND(x) ((int) floor((x)+0.5))
 
diff --git a/lib/libdia.def b/lib/libdia.def
index 1d2f9fc..4242a39 100644
--- a/lib/libdia.def
+++ b/lib/libdia.def
@@ -371,6 +371,7 @@ EXPORTS
  dia_svg_parse_path
  dia_svg_parse_transform
  dia_svg_from_matrix
+ dia_matrix_is_invertible
 
  dia_svg_renderer_get_type
 
diff --git a/plug-ins/cairo/diacairo-renderer.c b/plug-ins/cairo/diacairo-renderer.c
index 08863f8..dd295f6 100644
--- a/plug-ins/cairo/diacairo-renderer.c
+++ b/plug-ins/cairo/diacairo-renderer.c
@@ -223,6 +223,9 @@ draw_object (DiaRenderer *self, DiaObject *object, DiaMatrix *matrix)
   cairo_matrix_t before;
   
   if (matrix) {
+    /* at least in SVG the intent of an invalid matrix is not rendering */
+    if (!dia_matrix_is_invertible(matrix))
+      return;
     cairo_get_matrix (renderer->cr, &before);
     g_assert (sizeof(cairo_matrix_t) == sizeof(DiaMatrix));
     cairo_transform (renderer->cr, (cairo_matrix_t *)matrix);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]