Code

eliminate direct accesses to SPCurve::bpath
[inkscape.git] / src / extension / internal / odf.cpp
1 /**
2  * OpenDocument <drawing> input and output
3  *
4  * This is an an entry in the extensions mechanism to begin to enable
5  * the inputting and outputting of OpenDocument Format (ODF) files from
6  * within Inkscape.  Although the initial implementations will be very lossy
7  * do to the differences in the models of SVG and ODF, they will hopefully
8  * improve greatly with time.  People should consider this to be a framework
9  * that can be continously upgraded for ever improving fidelity.  Potential
10  * developers should especially look in preprocess() and writeTree() to see how
11  * the SVG tree is scanned, read, translated, and then written to ODF.
12  *
13  * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html
14  *
15  * Authors:
16  *   Bob Jamison
17  *
18  * Copyright (C) 2006 Bob Jamison
19  *
20  *  This library is free software; you can redistribute it and/or
21  *  modify it under the terms of the GNU Lesser General Public
22  *  License as published by the Free Software Foundation; either
23  *  version 2.1 of the License, or (at your option) any later version.
24  *
25  *  This library is distributed in the hope that it will be useful,
26  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
27  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
28  *  Lesser General Public License for more details.
29  *
30  *  You should have received a copy of the GNU Lesser General Public
31  *  License along with this library; if not, write to the Free Software
32  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
33  */
37 #ifdef HAVE_CONFIG_H
38 # include <config.h>
39 #endif
41 #include "odf.h"
43 //# System includes
44 #include <stdio.h>
45 #include <time.h>
46 #include <vector>
49 //# Inkscape includes
50 #include "clear-n_.h"
51 #include "inkscape.h"
52 #include <style.h>
53 #include "display/curve.h"
54 #include "libnr/n-art-bpath.h"
55 #include "extension/system.h"
57 #include "xml/repr.h"
58 #include "xml/attribute-record.h"
59 #include "sp-image.h"
60 #include "sp-gradient.h"
61 #include "sp-linear-gradient.h"
62 #include "sp-radial-gradient.h"
63 #include "sp-path.h"
64 #include "sp-text.h"
65 #include "sp-flowtext.h"
66 #include "svg/svg.h"
67 #include "text-editing.h"
70 //# DOM-specific includes
71 #include "dom/dom.h"
72 #include "dom/util/ziptool.h"
73 #include "dom/io/domstream.h"
74 #include "dom/io/bufferstream.h"
81 namespace Inkscape
82 {
83 namespace Extension
84 {
85 namespace Internal
86 {
90 //# Shorthand notation
91 typedef org::w3c::dom::DOMString DOMString;
92 typedef org::w3c::dom::io::OutputStreamWriter OutputStreamWriter;
93 typedef org::w3c::dom::io::BufferOutputStream BufferOutputStream;
95 //########################################################################
96 //# C L A S S    SingularValueDecomposition
97 //########################################################################
98 #include <math.h>
100 class SVDMatrix
102 public:
104     SVDMatrix()
105         {
106         d = (double *)0;
107         rows = cols = size = 0;
108         }
110     SVDMatrix(unsigned int rowSize, unsigned int colSize)
111         {
112         rows = rowSize;
113         cols = colSize;
114         size = rows * cols;
115         d = new double[size];
116         for (unsigned int i=0 ; i<size ; i++)
117             d[i] = 0.0;
118         }
120     SVDMatrix(double *vals, unsigned int rowSize, unsigned int colSize)
121         {
122         rows = rowSize;
123         cols = colSize;
124         size = rows * cols;
125         d = new double[size];
126         for (unsigned int i=0 ; i<size ; i++)
127             d[i] = vals[i];
128         }
130     virtual ~SVDMatrix()
131         {
132         delete d;
133         }
135     SVDMatrix(const SVDMatrix &other)
136         {
137         assign(other);
138         }
140     SVDMatrix &operator=(const SVDMatrix &other)
141         {
142         assign(other);
143         return *this;
144         }
146      double& operator() (unsigned int row, unsigned int col)
147          {
148          if (row >= rows || col >= cols)
149              return badval;
150          return d[cols*row + col];
151          }
153      double operator() (unsigned int row, unsigned int col) const
154          {
155          if (row >= rows || col >= cols)
156              return badval;
157          return d[cols*row + col];
158          }
160      unsigned int getRows()
161          {
162          return rows;
163          }
165      unsigned int getCols()
166          {
167          return cols;
168          }
170      SVDMatrix multiply(const SVDMatrix &other)
171          {
172          if (cols != other.rows)
173              {
174              SVDMatrix dummy;
175              return dummy;
176              }
177          SVDMatrix result(rows, other.cols);
178          for (unsigned int i=0 ; i<rows ; i++)
179              {
180              for (unsigned int j=0 ; j<other.cols ; j++)
181                  {
182                  double sum = 0.0;
183                  for (unsigned int k=0 ; k<cols ; k++)
184                      {
185                      //sum += a[i][k] * b[k][j];
186                      sum += d[i*cols +k] * other(k, j);
187                      }
188                  result(i, j) = sum;
189                  }
191              }
192          return result;
193          }
195      SVDMatrix transpose()
196          {
197          SVDMatrix result(cols, rows);
198          for (unsigned int i=0 ; i<rows ; i++)
199              for (unsigned int j=0 ; j<cols ; j++)
200                  result(j, i) = d[i*cols + j];
201          return result;
202          }
204 private:
207      void assign(const SVDMatrix &other)
208         {
209         if (d)
210             delete d;
211         rows = other.rows;
212         cols = other.cols;
213         size = other.size;
214         d = new double[size];
215         for (unsigned int i=0 ; i<size ; i++)
216             d[i] = other.d[i];
217         }
219     double badval;
221     double *d;
222     unsigned int rows;
223     unsigned int cols;
224     unsigned int size;
225 };
227 /**
228  *
229  * ====================================================
230  *
231  * NOTE:
232  * This class is ported almost verbatim from the public domain
233  * JAMA Matrix package.  It is modified to handle only 3x3 matrices
234  * and our NR::Matrix affine transform class.  We give full
235  * attribution to them, along with many thanks.  JAMA can be found at:
236  *     http://math.nist.gov/javanumerics/jama
237  *
238  * ====================================================
239  *
240  * Singular Value Decomposition.
241  * <P>
242  * For an m-by-n matrix A with m >= n, the singular value decomposition is
243  * an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and
244  * an n-by-n orthogonal matrix V so that A = U*S*V'.
245  * <P>
246  * The singular values, sigma[k] = S[k][k], are ordered so that
247  * sigma[0] >= sigma[1] >= ... >= sigma[n-1].
248  * <P>
249  * The singular value decompostion always exists, so the constructor will
250  * never fail.  The matrix condition number and the effective numerical
251  * rank can be computed from this decomposition.
252  */
253 class SingularValueDecomposition
255 public:
257    /** Construct the singular value decomposition
258    @param A    Rectangular matrix
259    @return     Structure to access U, S and V.
260    */
262     SingularValueDecomposition (const SVDMatrix &mat)
263         {
264         A = mat;
265         calculate();
266         }
268     virtual ~SingularValueDecomposition()
269         {
270         delete s;
271         }
273     /**
274      * Return the left singular vectors
275      * @return     U
276      */
277     SVDMatrix &getU();
279     /**
280      * Return the right singular vectors
281      * @return     V
282      */
283     SVDMatrix &getV();
285     /**
286      *  Return the s[index] value
287      */
288     double getS(unsigned int index);
290     /**
291      * Two norm
292      * @return max(S)
293      */
294     double norm2();
296     /**
297      * Two norm condition number
298      *  @return max(S)/min(S)
299      */
300     double cond();
302     /**
303      *  Effective numerical matrix rank
304      *  @return     Number of nonnegligible singular values.
305      */
306     int rank();
308 private:
310       void calculate();
312       SVDMatrix A;
313       SVDMatrix U;
314       double *s;
315       unsigned int s_size;
316       SVDMatrix V;
318 };
321 static double svd_hypot(double a, double b)
323     double r;
325     if (fabs(a) > fabs(b))
326         {
327         r = b/a;
328         r = fabs(a) * sqrt(1+r*r);
329         }
330     else if (b != 0)
331         {
332         r = a/b;
333         r = fabs(b) * sqrt(1+r*r);
334         }
335     else
336         {
337         r = 0.0;
338         }
339     return r;
344 void SingularValueDecomposition::calculate()
346       // Initialize.
347       int m = A.getRows();
348       int n = A.getCols();
350       int nu = (m > n) ? m : n;
351       s_size = (m+1 < n) ? m+1 : n;
352       s = new double[s_size];
353       U = SVDMatrix(m, nu);
354       V = SVDMatrix(n, n);
355       double *e = new double[n];
356       double *work = new double[m];
357       bool wantu = true;
358       bool wantv = true;
360       // Reduce A to bidiagonal form, storing the diagonal elements
361       // in s and the super-diagonal elements in e.
363       int nct = (m-1<n) ? m-1 : n;
364       int nrtx = (n-2<m) ? n-2 : m;
365       int nrt = (nrtx>0) ? nrtx : 0;
366       for (int k = 0; k < 2; k++) {
367          if (k < nct) {
369             // Compute the transformation for the k-th column and
370             // place the k-th diagonal in s[k].
371             // Compute 2-norm of k-th column without under/overflow.
372             s[k] = 0;
373             for (int i = k; i < m; i++) {
374                s[k] = svd_hypot(s[k],A(i, k));
375             }
376             if (s[k] != 0.0) {
377                if (A(k, k) < 0.0) {
378                   s[k] = -s[k];
379                }
380                for (int i = k; i < m; i++) {
381                   A(i, k) /= s[k];
382                }
383                A(k, k) += 1.0;
384             }
385             s[k] = -s[k];
386          }
387          for (int j = k+1; j < n; j++) {
388             if ((k < nct) & (s[k] != 0.0))  {
390             // Apply the transformation.
392                double t = 0;
393                for (int i = k; i < m; i++) {
394                   t += A(i, k) * A(i, j);
395                }
396                t = -t/A(k, k);
397                for (int i = k; i < m; i++) {
398                   A(i, j) += t*A(i, k);
399                }
400             }
402             // Place the k-th row of A into e for the
403             // subsequent calculation of the row transformation.
405             e[j] = A(k, j);
406          }
407          if (wantu & (k < nct)) {
409             // Place the transformation in U for subsequent back
410             // multiplication.
412             for (int i = k; i < m; i++) {
413                U(i, k) = A(i, k);
414             }
415          }
416          if (k < nrt) {
418             // Compute the k-th row transformation and place the
419             // k-th super-diagonal in e[k].
420             // Compute 2-norm without under/overflow.
421             e[k] = 0;
422             for (int i = k+1; i < n; i++) {
423                e[k] = svd_hypot(e[k],e[i]);
424             }
425             if (e[k] != 0.0) {
426                if (e[k+1] < 0.0) {
427                   e[k] = -e[k];
428                }
429                for (int i = k+1; i < n; i++) {
430                   e[i] /= e[k];
431                }
432                e[k+1] += 1.0;
433             }
434             e[k] = -e[k];
435             if ((k+1 < m) & (e[k] != 0.0)) {
437             // Apply the transformation.
439                for (int i = k+1; i < m; i++) {
440                   work[i] = 0.0;
441                }
442                for (int j = k+1; j < n; j++) {
443                   for (int i = k+1; i < m; i++) {
444                      work[i] += e[j]*A(i, j);
445                   }
446                }
447                for (int j = k+1; j < n; j++) {
448                   double t = -e[j]/e[k+1];
449                   for (int i = k+1; i < m; i++) {
450                      A(i, j) += t*work[i];
451                   }
452                }
453             }
454             if (wantv) {
456             // Place the transformation in V for subsequent
457             // back multiplication.
459                for (int i = k+1; i < n; i++) {
460                   V(i, k) = e[i];
461                }
462             }
463          }
464       }
466       // Set up the final bidiagonal matrix or order p.
468       int p = (n < m+1) ? n : m+1;
469       if (nct < n) {
470          s[nct] = A(nct, nct);
471       }
472       if (m < p) {
473          s[p-1] = 0.0;
474       }
475       if (nrt+1 < p) {
476          e[nrt] = A(nrt, p-1);
477       }
478       e[p-1] = 0.0;
480       // If required, generate U.
482       if (wantu) {
483          for (int j = nct; j < nu; j++) {
484             for (int i = 0; i < m; i++) {
485                U(i, j) = 0.0;
486             }
487             U(j, j) = 1.0;
488          }
489          for (int k = nct-1; k >= 0; k--) {
490             if (s[k] != 0.0) {
491                for (int j = k+1; j < nu; j++) {
492                   double t = 0;
493                   for (int i = k; i < m; i++) {
494                      t += U(i, k)*U(i, j);
495                   }
496                   t = -t/U(k, k);
497                   for (int i = k; i < m; i++) {
498                      U(i, j) += t*U(i, k);
499                   }
500                }
501                for (int i = k; i < m; i++ ) {
502                   U(i, k) = -U(i, k);
503                }
504                U(k, k) = 1.0 + U(k, k);
505                for (int i = 0; i < k-1; i++) {
506                   U(i, k) = 0.0;
507                }
508             } else {
509                for (int i = 0; i < m; i++) {
510                   U(i, k) = 0.0;
511                }
512                U(k, k) = 1.0;
513             }
514          }
515       }
517       // If required, generate V.
519       if (wantv) {
520          for (int k = n-1; k >= 0; k--) {
521             if ((k < nrt) & (e[k] != 0.0)) {
522                for (int j = k+1; j < nu; j++) {
523                   double t = 0;
524                   for (int i = k+1; i < n; i++) {
525                      t += V(i, k)*V(i, j);
526                   }
527                   t = -t/V(k+1, k);
528                   for (int i = k+1; i < n; i++) {
529                      V(i, j) += t*V(i, k);
530                   }
531                }
532             }
533             for (int i = 0; i < n; i++) {
534                V(i, k) = 0.0;
535             }
536             V(k, k) = 1.0;
537          }
538       }
540       // Main iteration loop for the singular values.
542       int pp = p-1;
543       int iter = 0;
544       //double eps = pow(2.0,-52.0);
545       //double tiny = pow(2.0,-966.0);
546       //let's just calculate these now
547       //a double can be e Â± 308.25, so this is safe
548       double eps = 2.22e-16;
549       double tiny = 1.6e-291;
550       while (p > 0) {
551          int k,kase;
553          // Here is where a test for too many iterations would go.
555          // This section of the program inspects for
556          // negligible elements in the s and e arrays.  On
557          // completion the variables kase and k are set as follows.
559          // kase = 1     if s(p) and e[k-1] are negligible and k<p
560          // kase = 2     if s(k) is negligible and k<p
561          // kase = 3     if e[k-1] is negligible, k<p, and
562          //              s(k), ..., s(p) are not negligible (qr step).
563          // kase = 4     if e(p-1) is negligible (convergence).
565          for (k = p-2; k >= -1; k--) {
566             if (k == -1) {
567                break;
568             }
569             if (fabs(e[k]) <=
570                   tiny + eps*(fabs(s[k]) + fabs(s[k+1]))) {
571                e[k] = 0.0;
572                break;
573             }
574          }
575          if (k == p-2) {
576             kase = 4;
577          } else {
578             int ks;
579             for (ks = p-1; ks >= k; ks--) {
580                if (ks == k) {
581                   break;
582                }
583                double t = (ks != p ? fabs(e[ks]) : 0.) +
584                           (ks != k+1 ? fabs(e[ks-1]) : 0.);
585                if (fabs(s[ks]) <= tiny + eps*t)  {
586                   s[ks] = 0.0;
587                   break;
588                }
589             }
590             if (ks == k) {
591                kase = 3;
592             } else if (ks == p-1) {
593                kase = 1;
594             } else {
595                kase = 2;
596                k = ks;
597             }
598          }
599          k++;
601          // Perform the task indicated by kase.
603          switch (kase) {
605             // Deflate negligible s(p).
607             case 1: {
608                double f = e[p-2];
609                e[p-2] = 0.0;
610                for (int j = p-2; j >= k; j--) {
611                   double t = svd_hypot(s[j],f);
612                   double cs = s[j]/t;
613                   double sn = f/t;
614                   s[j] = t;
615                   if (j != k) {
616                      f = -sn*e[j-1];
617                      e[j-1] = cs*e[j-1];
618                   }
619                   if (wantv) {
620                      for (int i = 0; i < n; i++) {
621                         t = cs*V(i, j) + sn*V(i, p-1);
622                         V(i, p-1) = -sn*V(i, j) + cs*V(i, p-1);
623                         V(i, j) = t;
624                      }
625                   }
626                }
627             }
628             break;
630             // Split at negligible s(k).
632             case 2: {
633                double f = e[k-1];
634                e[k-1] = 0.0;
635                for (int j = k; j < p; j++) {
636                   double t = svd_hypot(s[j],f);
637                   double cs = s[j]/t;
638                   double sn = f/t;
639                   s[j] = t;
640                   f = -sn*e[j];
641                   e[j] = cs*e[j];
642                   if (wantu) {
643                      for (int i = 0; i < m; i++) {
644                         t = cs*U(i, j) + sn*U(i, k-1);
645                         U(i, k-1) = -sn*U(i, j) + cs*U(i, k-1);
646                         U(i, j) = t;
647                      }
648                   }
649                }
650             }
651             break;
653             // Perform one qr step.
655             case 3: {
657                // Calculate the shift.
659                double scale = fabs(s[p-1]);
660                double d = fabs(s[p-2]);
661                if (d>scale) scale=d;
662                d = fabs(e[p-2]);
663                if (d>scale) scale=d;
664                d = fabs(s[k]);
665                if (d>scale) scale=d;
666                d = fabs(e[k]);
667                if (d>scale) scale=d;
668                double sp = s[p-1]/scale;
669                double spm1 = s[p-2]/scale;
670                double epm1 = e[p-2]/scale;
671                double sk = s[k]/scale;
672                double ek = e[k]/scale;
673                double b = ((spm1 + sp)*(spm1 - sp) + epm1*epm1)/2.0;
674                double c = (sp*epm1)*(sp*epm1);
675                double shift = 0.0;
676                if ((b != 0.0) | (c != 0.0)) {
677                   shift = sqrt(b*b + c);
678                   if (b < 0.0) {
679                      shift = -shift;
680                   }
681                   shift = c/(b + shift);
682                }
683                double f = (sk + sp)*(sk - sp) + shift;
684                double g = sk*ek;
686                // Chase zeros.
688                for (int j = k; j < p-1; j++) {
689                   double t = svd_hypot(f,g);
690                   double cs = f/t;
691                   double sn = g/t;
692                   if (j != k) {
693                      e[j-1] = t;
694                   }
695                   f = cs*s[j] + sn*e[j];
696                   e[j] = cs*e[j] - sn*s[j];
697                   g = sn*s[j+1];
698                   s[j+1] = cs*s[j+1];
699                   if (wantv) {
700                      for (int i = 0; i < n; i++) {
701                         t = cs*V(i, j) + sn*V(i, j+1);
702                         V(i, j+1) = -sn*V(i, j) + cs*V(i, j+1);
703                         V(i, j) = t;
704                      }
705                   }
706                   t = svd_hypot(f,g);
707                   cs = f/t;
708                   sn = g/t;
709                   s[j] = t;
710                   f = cs*e[j] + sn*s[j+1];
711                   s[j+1] = -sn*e[j] + cs*s[j+1];
712                   g = sn*e[j+1];
713                   e[j+1] = cs*e[j+1];
714                   if (wantu && (j < m-1)) {
715                      for (int i = 0; i < m; i++) {
716                         t = cs*U(i, j) + sn*U(i, j+1);
717                         U(i, j+1) = -sn*U(i, j) + cs*U(i, j+1);
718                         U(i, j) = t;
719                      }
720                   }
721                }
722                e[p-2] = f;
723                iter = iter + 1;
724             }
725             break;
727             // Convergence.
729             case 4: {
731                // Make the singular values positive.
733                if (s[k] <= 0.0) {
734                   s[k] = (s[k] < 0.0 ? -s[k] : 0.0);
735                   if (wantv) {
736                      for (int i = 0; i <= pp; i++) {
737                         V(i, k) = -V(i, k);
738                      }
739                   }
740                }
742                // Order the singular values.
744                while (k < pp) {
745                   if (s[k] >= s[k+1]) {
746                      break;
747                   }
748                   double t = s[k];
749                   s[k] = s[k+1];
750                   s[k+1] = t;
751                   if (wantv && (k < n-1)) {
752                      for (int i = 0; i < n; i++) {
753                         t = V(i, k+1); V(i, k+1) = V(i, k); V(i, k) = t;
754                      }
755                   }
756                   if (wantu && (k < m-1)) {
757                      for (int i = 0; i < m; i++) {
758                         t = U(i, k+1); U(i, k+1) = U(i, k); U(i, k) = t;
759                      }
760                   }
761                   k++;
762                }
763                iter = 0;
764                p--;
765             }
766             break;
767          }
768       }
770     delete e;
771     delete work;
777 /**
778  * Return the left singular vectors
779  * @return     U
780  */
781 SVDMatrix &SingularValueDecomposition::getU()
783     return U;
786 /**
787  * Return the right singular vectors
788  * @return     V
789  */
791 SVDMatrix &SingularValueDecomposition::getV()
793     return V;
796 /**
797  *  Return the s[0] value
798  */
799 double SingularValueDecomposition::getS(unsigned int index)
801     if (index >= s_size)
802         return 0.0;
803     return s[index];
806 /**
807  * Two norm
808  * @return     max(S)
809  */
810 double SingularValueDecomposition::norm2()
812     return s[0];
815 /**
816  * Two norm condition number
817  *  @return     max(S)/min(S)
818  */
820 double SingularValueDecomposition::cond()
822     return s[0]/s[2];
825 /**
826  *  Effective numerical matrix rank
827  *  @return     Number of nonnegligible singular values.
828  */
829 int SingularValueDecomposition::rank()
831     double eps = pow(2.0,-52.0);
832     double tol = 3.0*s[0]*eps;
833     int r = 0;
834     for (int i = 0; i < 3; i++)
835         {
836         if (s[i] > tol)
837             r++;
838         }
839     return r;
842 //########################################################################
843 //# E N D    C L A S S    SingularValueDecomposition
844 //########################################################################
850 #define pi 3.14159
851 //#define pxToCm  0.0275
852 #define pxToCm  0.04
853 #define piToRad 0.0174532925
854 #define docHeightCm 22.86
857 //########################################################################
858 //# O U T P U T
859 //########################################################################
861 /**
862  * Get the value of a node/attribute pair
863  */
864 static std::string getAttribute( Inkscape::XML::Node *node, char *attrName)
866     std::string val;
867     char *valstr = (char *)node->attribute(attrName);
868     if (valstr)
869         val = (const char *)valstr;
870     return val;
875 /**
876  * Get the extension suffix from the end of a file name
877  */
878 static std::string getExtension(const std::string &fname)
880     std::string ext;
882     unsigned int pos = fname.rfind('.');
883     if (pos == fname.npos)
884         {
885         ext = "";
886         }
887     else
888         {
889         ext = fname.substr(pos);
890         }
891     return ext;
895 static std::string formatTransform(NR::Matrix &tf)
897     std::string str;
898     if (!tf.test_identity())
899         {
900         char buf[128];
901         snprintf(buf, 127, "matrix(%.3f %.3f %.3f %.3f %.3f %.3f)",
902                 tf[0], tf[1], tf[2], tf[3], tf[4], tf[5]);
903         str = buf;
904         }
905     return str;
912 /**
913  * Get the general transform from SVG pixels to
914  * ODF cm
915  */
916 static NR::Matrix getODFTransform(const SPItem *item)
918     //### Get SVG-to-ODF transform
919     NR::Matrix tf;
920     tf                   = sp_item_i2d_affine(item);
921     //Flip Y into document coordinates
922     double doc_height    = sp_document_height(SP_ACTIVE_DOCUMENT);
923     NR::Matrix doc2dt_tf = NR::Matrix(NR::scale(1.0, -1.0));
924     doc2dt_tf            = doc2dt_tf * NR::Matrix(NR::translate(0, doc_height));
925     tf                   = tf * doc2dt_tf;
926     tf                   = tf * NR::Matrix(NR::scale(pxToCm));
927     return tf;
933 /**
934  * Get the bounding box of an item, as mapped onto
935  * an ODF document, in cm.
936  */
937 static NR::Rect getODFBoundingBox(const SPItem *item)
939     NR::Rect bbox        = sp_item_bbox_desktop((SPItem *)item);
940     double doc_height    = sp_document_height(SP_ACTIVE_DOCUMENT);
941     NR::Matrix doc2dt_tf = NR::Matrix(NR::scale(1.0, -1.0));
942     doc2dt_tf            = doc2dt_tf * NR::Matrix(NR::translate(0, doc_height));
943     bbox                 = bbox * doc2dt_tf;
944     bbox                 = bbox * NR::Matrix(NR::scale(pxToCm));
945     return bbox;
950 /**
951  * Get the transform for an item, correcting for
952  * handedness reversal
953  */
954 static NR::Matrix getODFItemTransform(const SPItem *item)
956     NR::Matrix itemTransform = NR::Matrix(NR::scale(1, -1));
957     itemTransform = itemTransform * item->transform;
958     itemTransform = itemTransform * NR::Matrix(NR::scale(1, -1));
959     return itemTransform;
964 /**
965  * Get some fun facts from the transform
966  */
967 static void analyzeTransform(NR::Matrix &tf,
968            double &rotate, double &xskew, double &yskew,
969            double &xscale, double &yscale)
971     SVDMatrix mat(2, 2);
972     mat(0, 0) = tf[0];
973     mat(0, 1) = tf[1];
974     mat(1, 0) = tf[2];
975     mat(1, 1) = tf[3];
977     SingularValueDecomposition svd(mat);
979     SVDMatrix U = svd.getU();
980     SVDMatrix V = svd.getV();
981     SVDMatrix Vt = V.transpose();
982     SVDMatrix UVt = U.multiply(Vt);
983     double s0 = svd.getS(0);
984     double s1 = svd.getS(1);
985     xscale = s0;
986     yscale = s1;
987     //g_message("## s0:%.3f s1:%.3f", s0, s1);
988     //g_message("## u:%.3f %.3f %.3f %.3f", U(0,0), U(0,1), U(1,0), U(1,1));
989     //g_message("## v:%.3f %.3f %.3f %.3f", V(0,0), V(0,1), V(1,0), V(1,1));
990     //g_message("## vt:%.3f %.3f %.3f %.3f", Vt(0,0), Vt(0,1), Vt(1,0), Vt(1,1));
991     //g_message("## uvt:%.3f %.3f %.3f %.3f", UVt(0,0), UVt(0,1), UVt(1,0), UVt(1,1));
992     rotate = UVt(0,0);
998 /**
999  * FIRST PASS.
1000  * Method descends into the repr tree, converting image, style, and gradient info
1001  * into forms compatible in ODF.
1002  */
1003 void
1004 OdfOutput::preprocess(ZipFile &zf, Inkscape::XML::Node *node)
1007     std::string nodeName = node->name();
1008     std::string id       = getAttribute(node, "id");
1010     SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(node);
1011     if (!reprobj)
1012         return;
1013     if (!SP_IS_ITEM(reprobj))
1014         {
1015         return;
1016         }
1017     SPItem *item  = SP_ITEM(reprobj);
1018     //### Get SVG-to-ODF transform
1019     NR::Matrix tf = getODFTransform(item);
1022     if (nodeName == "image" || nodeName == "svg:image")
1023         {
1024         //g_message("image");
1025         std::string href = getAttribute(node, "xlink:href");
1026         if (href.size() > 0)
1027             {
1028             std::string oldName = href;
1029             std::string ext = getExtension(oldName);
1030             if (ext == ".jpeg")
1031                 ext = ".jpg";
1032             if (imageTable.find(oldName) == imageTable.end())
1033                 {
1034                 char buf[64];
1035                 snprintf(buf, 63, "Pictures/image%d%s",
1036                     (int)imageTable.size(), ext.c_str());
1037                 std::string newName = buf;
1038                 imageTable[oldName] = newName;
1039                 std::string comment = "old name was: ";
1040                 comment.append(oldName);
1041                 URI oldUri(oldName);
1042                 //g_message("oldpath:%s", oldUri.getNativePath().c_str());
1043                 //# if relative to the documentURI, get proper path
1044                 URI resUri = documentUri.resolve(oldUri);
1045                 DOMString pathName = resUri.getNativePath();
1046                 //g_message("native path:%s", pathName.c_str());
1047                 ZipEntry *ze = zf.addFile(pathName, comment);
1048                 if (ze)
1049                     {
1050                     ze->setFileName(newName);
1051                     }
1052                 else
1053                     {
1054                     g_warning("Could not load image file '%s'", pathName.c_str());
1055                     }
1056                 }
1057             }
1058         }
1062     //###### Get style
1063     SPStyle *style = SP_OBJECT_STYLE(item);
1064     if (style && id.size()>0)
1065         {
1066         bool isGradient = false;
1068         StyleInfo si;
1069         //## Style.  Look in writeStyle() below to see what info
1070         //   we need to read into StyleInfo.  Note that we need to
1071         //   determine whether information goes into a style element
1072         //   or a gradient element.
1073         //## FILL
1074         if (style->fill.type == SP_PAINT_TYPE_COLOR)
1075             {
1076             guint32 fillCol =
1077                 sp_color_get_rgba32_ualpha(&style->fill.value.color, 0);
1078             char buf[16];
1079             int r = (fillCol >> 24) & 0xff;
1080             int g = (fillCol >> 16) & 0xff;
1081             int b = (fillCol >>  8) & 0xff;
1082             //g_message("## %s %lx", id.c_str(), (unsigned int)fillCol);
1083             snprintf(buf, 15, "#%02x%02x%02x", r, g, b);
1084             si.fillColor = buf;
1085             si.fill      = "solid";
1086             double opacityPercent = 100.0 *
1087                  (SP_SCALE24_TO_FLOAT(style->fill_opacity.value));
1088             snprintf(buf, 15, "%.3f%%", opacityPercent);
1089             si.fillOpacity = buf;
1090             }
1091         else if (style->fill.type == SP_PAINT_TYPE_PAINTSERVER)
1092             {
1093             //## Gradient.  Look in writeStyle() below to see what info
1094             //   we need to read into GradientInfo.
1095             if (!SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)))
1096                 return;
1097             isGradient = true;
1098             GradientInfo gi;
1099             SPGradient *gradient = SP_GRADIENT(SP_STYLE_FILL_SERVER(style));
1100             if (SP_IS_LINEARGRADIENT(gradient))
1101                 {
1102                 gi.style = "linear";
1103                 SPLinearGradient *linGrad = SP_LINEARGRADIENT(gradient);
1104                 gi.x1 = linGrad->x1.value;
1105                 gi.y1 = linGrad->y1.value;
1106                 gi.x2 = linGrad->x2.value;
1107                 gi.y2 = linGrad->y2.value;
1108                 }
1109             else if (SP_IS_RADIALGRADIENT(gradient))
1110                 {
1111                 gi.style = "radial";
1112                 SPRadialGradient *radGrad = SP_RADIALGRADIENT(gradient);
1113                 gi.cx = radGrad->cx.computed * 100.0;//ODG cx is percentages
1114                 gi.cy = radGrad->cy.computed * 100.0;
1115                 }
1116             else
1117                 {
1118                 g_warning("not a supported gradient type");
1119                 }
1121             //Look for existing identical style;
1122             bool gradientMatch = false;
1123             std::vector<GradientInfo>::iterator iter;
1124             for (iter=gradientTable.begin() ; iter!=gradientTable.end() ; iter++)
1125                 {
1126                 if (gi.equals(*iter))
1127                     {
1128                     //map to existing gradientTable entry
1129                     std::string gradientName = iter->name;
1130                     //g_message("found duplicate style:%s", gradientName.c_str());
1131                     gradientLookupTable[id] = gradientName;
1132                     gradientMatch = true;
1133                     break;
1134                     }
1135                 }
1136             //None found, make a new pair or entries
1137             if (!gradientMatch)
1138                 {
1139                 char buf[16];
1140                 snprintf(buf, 15, "gradient%d", (int)gradientTable.size());
1141                 std::string gradientName = buf;
1142                 gi.name = gradientName;
1143                 gradientTable.push_back(gi);
1144                 gradientLookupTable[id] = gradientName;
1145                 }
1146             }
1148         //## STROKE
1149         if (style->stroke.type == SP_PAINT_TYPE_COLOR)
1150             {
1151             guint32 strokeCol =
1152                 sp_color_get_rgba32_ualpha(&style->stroke.value.color, 0);
1153             char buf[16];
1154             int r = (strokeCol >> 24) & 0xff;
1155             int g = (strokeCol >> 16) & 0xff;
1156             int b = (strokeCol >>  8) & 0xff;
1157             snprintf(buf, 15, "#%02x%02x%02x", r, g, b);
1158             si.strokeColor = buf;
1159             snprintf(buf, 15, "%.3fpt", style->stroke_width.value);
1160             si.strokeWidth = buf;
1161             si.stroke      = "solid";
1162             double opacityPercent = 100.0 *
1163                  (SP_SCALE24_TO_FLOAT(style->stroke_opacity.value));
1164             snprintf(buf, 15, "%.3f%%", opacityPercent);
1165             si.strokeOpacity = buf;
1166             }
1168         if (!isGradient)
1169             {
1170             //Look for existing identical style;
1171             bool styleMatch = false;
1172             std::vector<StyleInfo>::iterator iter;
1173             for (iter=styleTable.begin() ; iter!=styleTable.end() ; iter++)
1174                 {
1175                 if (si.equals(*iter))
1176                     {
1177                     //map to existing styleTable entry
1178                     std::string styleName = iter->name;
1179                     //g_message("found duplicate style:%s", styleName.c_str());
1180                     styleLookupTable[id] = styleName;
1181                     styleMatch = true;
1182                     break;
1183                     }
1184                 }
1185             //None found, make a new pair or entries
1186             if (!styleMatch)
1187                 {
1188                 char buf[16];
1189                 snprintf(buf, 15, "style%d", (int)styleTable.size());
1190                 std::string styleName = buf;
1191                 si.name = styleName;
1192                 styleTable.push_back(si);
1193                 styleLookupTable[id] = styleName;
1194                 }
1195             }
1196         }
1198     for (Inkscape::XML::Node *child = node->firstChild() ;
1199             child ; child = child->next())
1200         preprocess(zf, child);
1205 /**
1206  * Writes the manifest.  Currently it only changes according to the
1207  * file names of images packed into the zip file.
1208  */
1209 bool OdfOutput::writeManifest(ZipFile &zf)
1211     BufferOutputStream bouts;
1212     OutputStreamWriter outs(bouts);
1214     time_t tim;
1215     time(&tim);
1217     outs.printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1218     outs.printf("<!DOCTYPE manifest:manifest PUBLIC \"-//OpenOffice.org//DTD Manifest 1.0//EN\" \"Manifest.dtd\">\n");
1219     outs.printf("\n");
1220     outs.printf("\n");
1221     outs.printf("<!--\n");
1222     outs.printf("*************************************************************************\n");
1223     outs.printf("  file:  manifest.xml\n");
1224     outs.printf("  Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
1225     outs.printf("  http://www.inkscape.org\n");
1226     outs.printf("*************************************************************************\n");
1227     outs.printf("-->\n");
1228     outs.printf("\n");
1229     outs.printf("\n");
1230     outs.printf("<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\">\n");
1231     outs.printf("    <manifest:file-entry manifest:media-type=\"application/vnd.oasis.opendocument.graphics\" manifest:full-path=\"/\"/>\n");
1232     outs.printf("    <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/>\n");
1233     outs.printf("    <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"meta.xml\"/>\n");
1234     outs.printf("    <!--List our images here-->\n");
1235     std::map<std::string, std::string>::iterator iter;
1236     for (iter = imageTable.begin() ; iter!=imageTable.end() ; iter++)
1237         {
1238         std::string oldName = iter->first;
1239         std::string newName = iter->second;
1241         std::string ext = getExtension(oldName);
1242         if (ext == ".jpeg")
1243             ext = ".jpg";
1244         outs.printf("    <manifest:file-entry manifest:media-type=\"");
1245         if (ext == ".gif")
1246             outs.printf("image/gif");
1247         else if (ext == ".png")
1248             outs.printf("image/png");
1249         else if (ext == ".jpg")
1250             outs.printf("image/jpeg");
1251         outs.printf("\" manifest:full-path=\"");
1252         outs.printf((char *)newName.c_str());
1253         outs.printf("\"/>\n");
1254         }
1255     outs.printf("</manifest:manifest>\n");
1257     outs.close();
1259     //Make our entry
1260     ZipEntry *ze = zf.newEntry("META-INF/manifest.xml", "ODF file manifest");
1261     ze->setUncompressedData(bouts.getBuffer());
1262     ze->finish();
1264     return true;
1268 /**
1269  * This writes the document meta information to meta.xml
1270  */
1271 bool OdfOutput::writeMeta(ZipFile &zf)
1273     BufferOutputStream bouts;
1274     OutputStreamWriter outs(bouts);
1276     time_t tim;
1277     time(&tim);
1279     outs.printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1280     outs.printf("\n");
1281     outs.printf("\n");
1282     outs.printf("<!--\n");
1283     outs.printf("*************************************************************************\n");
1284     outs.printf("  file:  meta.xml\n");
1285     outs.printf("  Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
1286     outs.printf("  http://www.inkscape.org\n");
1287     outs.printf("*************************************************************************\n");
1288     outs.printf("-->\n");
1289     outs.printf("\n");
1290     outs.printf("\n");
1291     outs.printf("<office:document-meta\n");
1292     outs.printf("xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
1293     outs.printf("xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
1294     outs.printf("xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
1295     outs.printf("xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
1296     outs.printf("xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
1297     outs.printf("xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
1298     outs.printf("xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
1299     outs.printf("xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
1300     outs.printf("office:version=\"1.0\">\n");
1301     outs.printf("<office:meta>\n");
1302     outs.printf("    <meta:generator>Inkscape.org - 0.44</meta:generator>\n");
1303     outs.printf("    <meta:initial-creator>clark kent</meta:initial-creator>\n");
1304     outs.printf("    <meta:creation-date>2006-04-13T17:12:29</meta:creation-date>\n");
1305     outs.printf("    <dc:creator>clark kent</dc:creator>\n");
1306     outs.printf("    <dc:date>2006-04-13T17:13:20</dc:date>\n");
1307     outs.printf("    <dc:language>en-US</dc:language>\n");
1308     outs.printf("    <meta:editing-cycles>2</meta:editing-cycles>\n");
1309     outs.printf("    <meta:editing-duration>PT56S</meta:editing-duration>\n");
1310     outs.printf("    <meta:user-defined meta:name=\"Info 1\"/>\n");
1311     outs.printf("    <meta:user-defined meta:name=\"Info 2\"/>\n");
1312     outs.printf("    <meta:user-defined meta:name=\"Info 3\"/>\n");
1313     outs.printf("    <meta:user-defined meta:name=\"Info 4\"/>\n");
1314     outs.printf("    <meta:document-statistic meta:object-count=\"2\"/>\n");
1315     outs.printf("</office:meta>\n");
1316     outs.printf("</office:document-meta>\n");
1317     outs.printf("\n");
1318     outs.printf("\n");
1321     outs.close();
1323     //Make our entry
1324     ZipEntry *ze = zf.newEntry("meta.xml", "ODF info file");
1325     ze->setUncompressedData(bouts.getBuffer());
1326     ze->finish();
1328     return true;
1334 /**
1335  * This is called just before writeTree(), since it will write style and
1336  * gradient information above the <draw> tag in the content.xml file
1337  */
1338 bool OdfOutput::writeStyle(Writer &outs)
1340     outs.printf("<office:automatic-styles>\n");
1341     outs.printf("<!-- ####### 'Standard' styles ####### -->\n");
1342     outs.printf("<style:style style:name=\"dp1\" style:family=\"drawing-page\"/>\n");
1343     outs.printf("<style:style style:name=\"gr1\" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");
1344     outs.printf("  <style:graphic-properties draw:stroke=\"none\" draw:fill=\"none\"\n");
1345     outs.printf("       draw:textarea-horizontal-align=\"center\"\n");
1346     outs.printf("       draw:textarea-vertical-align=\"middle\" draw:color-mode=\"standard\"\n");
1347     outs.printf("       draw:luminance=\"0%\" draw:contrast=\"0%\" draw:gamma=\"100%\" draw:red=\"0%\"\n");
1348     outs.printf("       draw:green=\"0%\" draw:blue=\"0%\" fo:clip=\"rect(0cm 0cm 0cm 0cm)\"\n");
1349     outs.printf("       draw:image-opacity=\"100%\" style:mirror=\"none\"/>\n");
1350     outs.printf("</style:style>\n");
1351     outs.printf("<style:style style:name=\"P1\" style:family=\"paragraph\">\n");
1352     outs.printf("  <style:paragraph-properties fo:text-align=\"center\"/>\n");
1353     outs.printf("</style:style>\n");
1355     /*
1356     ==========================================================
1357     Dump our style table.  Styles should have a general layout
1358     something like the following.  Look in:
1359     http://books.evc-cit.info/odbook/ch06.html#draw-style-file-section
1360     for style and gradient information.
1361     <style:style style:name="gr13"
1362       style:family="graphic" style:parent-style-name="standard">
1363         <style:graphic-properties draw:stroke="solid"
1364             svg:stroke-width="0.1cm"
1365             svg:stroke-color="#ff0000"
1366             draw:fill="solid" draw:fill-color="#e6e6ff"/>
1367     </style:style>
1368     ==========================================================
1369     */
1370     outs.printf("<!-- ####### Styles from Inkscape document ####### -->\n");
1371     std::vector<StyleInfo>::iterator iter;
1372     for (iter = styleTable.begin() ; iter != styleTable.end() ; iter++)
1373         {
1374         outs.printf("<style:style style:name=\"%s\"", iter->name.c_str());
1375         StyleInfo s(*iter);
1376         outs.printf(" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");
1377         outs.printf("  <style:graphic-properties");
1378         outs.printf(" draw:fill=\"%s\" ", s.fill.c_str());
1379         if (s.fill != "none")
1380             {
1381             outs.printf(" draw:fill-color=\"%s\" ", s.fillColor.c_str());
1382             outs.printf(" draw:fill-opacity=\"%s\" ", s.fillOpacity.c_str());
1383             }
1384         outs.printf(" draw:stroke=\"%s\" ", s.stroke.c_str());
1385         if (s.stroke != "none")
1386             {
1387             outs.printf(" svg:stroke-width=\"%s\" ", s.strokeWidth.c_str());
1388             outs.printf(" svg:stroke-color=\"%s\" ", s.strokeColor.c_str());
1389             outs.printf(" svg:stroke-opacity=\"%s\" ", s.strokeOpacity.c_str());
1390             }
1391         outs.printf("/>\n");
1392         outs.printf("</style:style>\n");
1393         }
1395     //##  Dump our gradient table
1396     outs.printf("\n");
1397     outs.printf("<!-- ####### Gradients from Inkscape document ####### -->\n");
1398     std::vector<GradientInfo>::iterator giter;
1399     for (giter = gradientTable.begin() ; giter != gradientTable.end() ; giter++)
1400         {
1401         GradientInfo gi(*giter);
1402         outs.printf("<draw:gradient draw:name=\"%s\" ", gi.name.c_str());
1403         outs.printf("draw:style=\"%s\" ", gi.style.c_str());
1404         if (gi.style == "linear")
1405             {
1406             /*
1407             ===================================================================
1408             LINEAR gradient.  We need something that looks like this:
1409             <draw:gradient draw:name="Gradient_20_7"
1410                 draw:display-name="Gradient 7"
1411                 draw:style="linear"
1412                 draw:start-color="#008080" draw:end-color="#993366"
1413                 draw:start-intensity="100%" draw:end-intensity="100%"
1414                 draw:angle="150" draw:border="0%"/>
1415             ===================================================================
1416             */
1417             outs.printf("draw:display-name=\"linear borderless\" ");
1418             }
1419         else if (gi.style == "radial")
1420             {
1421             /*
1422             ===================================================================
1423             RADIAL gradient.  We need something that looks like this:
1424             <!-- radial gradient, light gray to white, centered, 0% border -->
1425             <draw:gradient draw:name="radial_20_borderless"
1426                 draw:display-name="radial borderless"
1427                 draw:style="radial"
1428                 draw:cx="50%" draw:cy="50%"
1429                 draw:start-color="#999999" draw:end-color="#ffffff"
1430                 draw:border="0%"/>
1431             ===================================================================
1432             */
1433             outs.printf("draw:display-name=\"radial borderless\" ");
1434             outs.printf("draw:cx=\".2f%%\" draw:cy=\".2f%%\" ", gi.cx, gi.cy);
1435             }
1436         else
1437             {
1438             g_warning("unsupported gradient style '%s'", gi.style.c_str());
1439             }
1440         outs.printf("/>\n");
1441         }
1443     outs.printf("\n");
1444     outs.printf("</office:automatic-styles>\n");
1445     outs.printf("\n");
1447     return true;
1452 /**
1453  * Writes an SVG path as an ODF <draw:path>
1454  */
1455 static void
1456 writePath(Writer &outs, NArtBpath const *bpath,
1457           NR::Matrix &tf, double xoff, double yoff)
1459     bool closed = false;
1460     NArtBpath *bp = (NArtBpath *)bpath;
1461     for (  ; bp->code != NR_END; bp++)
1462         {
1463         NR::Point const p1(bp->c(1) * tf);
1464         NR::Point const p2(bp->c(2) * tf);
1465         NR::Point const p3(bp->c(3) * tf);
1466         double x1 = (p1[NR::X] - xoff) * 1000.0;
1467         double y1 = (p1[NR::Y] - yoff) * 1000.0;
1468         double x2 = (p2[NR::X] - xoff) * 1000.0;
1469         double y2 = (p2[NR::Y] - yoff) * 1000.0;
1470         double x3 = (p3[NR::X] - xoff) * 1000.0;
1471         double y3 = (p3[NR::Y] - yoff) * 1000.0;
1473         switch (bp->code)
1474             {
1475             case NR_LINETO:
1476                 outs.printf("L %.3f,%.3f ",  x3 , y3);
1477                 break;
1479             case NR_CURVETO:
1480                 outs.printf("C %.3f,%.3f %.3f,%.3f %.3f,%.3f ",
1481                               x1, y1, x2, y2, x3, y3);
1482                 break;
1484             case NR_MOVETO_OPEN:
1485             case NR_MOVETO:
1486                 if (closed)
1487                     outs.printf("z ");
1488                 closed = ( bp->code == NR_MOVETO );
1489                 outs.printf("M %.3f,%.3f ",  x3 , y3);
1490                 break;
1492             default:
1493                 break;
1495             }
1497         }
1499     if (closed)
1500         outs.printf("z");;
1506 /**
1507  * SECOND PASS.
1508  * This is the main SPObject tree output to ODF.  preprocess()
1509  * must be called prior to this, as elements will often reference
1510  * data parsed and tabled in preprocess().
1511  */
1512 bool OdfOutput::writeTree(Writer &outs, Inkscape::XML::Node *node)
1514     //# Get the SPItem, if applicable
1515     SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(node);
1516     if (!reprobj)
1517         return true;
1518     if (!SP_IS_ITEM(reprobj))
1519         {
1520         return true;
1521         }
1522     SPItem *item = SP_ITEM(reprobj);
1525     std::string nodeName = node->name();
1526     std::string id       = getAttribute(node, "id");
1528     //### Get SVG-to-ODF transform
1529     NR::Matrix tf        = getODFTransform(item);
1531     //### Get ODF bounding box params for item
1532     NR::Rect bbox        = getODFBoundingBox(item);
1533     double bbox_x        = bbox.min()[NR::X];
1534     double bbox_y        = bbox.min()[NR::Y];
1535     double bbox_width    = bbox.max()[NR::X] - bbox.min()[NR::X];
1536     double bbox_height   = bbox.max()[NR::Y] - bbox.min()[NR::Y];
1538     double rotate;
1539     double xskew;
1540     double yskew;
1541     double xscale;
1542     double yscale;
1543     analyzeTransform(tf, rotate, xskew, yskew, xscale, yscale);
1545     //# Do our stuff
1546     SPCurve *curve = NULL;
1548     //g_message("##### %s #####", nodeName.c_str());
1550     if (nodeName == "svg" || nodeName == "svg:svg")
1551         {
1552         //# Iterate through the children
1553         for (Inkscape::XML::Node *child = node->firstChild() ; child ; child = child->next())
1554             {
1555             if (!writeTree(outs, child))
1556                 return false;
1557             }
1558         return true;
1559         }
1560     else if (nodeName == "g" || nodeName == "svg:g")
1561         {
1562         if (id.size() > 0)
1563             outs.printf("<draw:g id=\"%s\">\n", id.c_str());
1564         else
1565             outs.printf("<draw:g>\n");
1566         //# Iterate through the children
1567         for (Inkscape::XML::Node *child = node->firstChild() ; child ; child = child->next())
1568             {
1569             if (!writeTree(outs, child))
1570                 return false;
1571             }
1572         if (id.size() > 0)
1573             outs.printf("</draw:g> <!-- id=\"%s\" -->\n", id.c_str());
1574         else
1575             outs.printf("</draw:g>\n");
1576         return true;
1577         }
1578     else if (nodeName == "image" || nodeName == "svg:image")
1579         {
1580         if (!SP_IS_IMAGE(item))
1581             {
1582             g_warning("<image> is not an SPImage.  Why?  ;-)");
1583             return false;
1584             }
1586         SPImage *img   = SP_IMAGE(item);
1587         double ix      = img->x.value;
1588         double iy      = img->y.value;
1589         double iwidth  = img->width.value;
1590         double iheight = img->height.value;
1592         NR::Rect ibbox(NR::Point(ix, iy), NR::Point(ix+iwidth, iy+iheight));
1593         ibbox = ibbox * tf;
1594         ix      = ibbox.min()[NR::X];
1595         iy      = ibbox.min()[NR::Y];
1596         //iwidth  = ibbox.max()[NR::X] - ibbox.min()[NR::X];
1597         //iheight = ibbox.max()[NR::Y] - ibbox.min()[NR::Y];
1598         iwidth  = xscale * iwidth;
1599         iheight = yscale * iheight;
1601         NR::Matrix itemTransform = getODFItemTransform(item);
1603         std::string itemTransformString = formatTransform(itemTransform);
1605         std::string href = getAttribute(node, "xlink:href");
1606         std::map<std::string, std::string>::iterator iter = imageTable.find(href);
1607         if (iter == imageTable.end())
1608             {
1609             g_warning("image '%s' not in table", href.c_str());
1610             return false;
1611             }
1612         std::string newName = iter->second;
1614         outs.printf("<draw:frame ");
1615         if (id.size() > 0)
1616             outs.printf("id=\"%s\" ", id.c_str());
1617         outs.printf("draw:style-name=\"gr1\" draw:text-style-name=\"P1\" draw:layer=\"layout\" ");
1618         //no x or y.  make them the translate transform, last one
1619         outs.printf("svg:width=\"%.3fcm\" svg:height=\"%.3fcm\" ",
1620                                   iwidth, iheight);
1621         if (itemTransformString.size() > 0)
1622             outs.printf("draw:transform=\"%s translate(%.3fcm, %.3fcm)\" ",
1623                 itemTransformString.c_str(), ix, iy);
1625         outs.printf(">\n");
1626         outs.printf("    <draw:image xlink:href=\"%s\" xlink:type=\"simple\"\n",
1627                               newName.c_str());
1628         outs.printf("        xlink:show=\"embed\" xlink:actuate=\"onLoad\">\n");
1629         outs.printf("        <text:p/>\n");
1630         outs.printf("    </draw:image>\n");
1631         outs.printf("</draw:frame>\n");
1632         return true;
1633         }
1634     else if (SP_IS_SHAPE(item))
1635         {
1636         //g_message("### %s is a shape", nodeName.c_str());
1637         curve = sp_shape_get_curve(SP_SHAPE(item));
1638         }
1639     else if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))
1640         {
1641         curve = te_get_layout(item)->convertToCurves();
1642         }
1644     if (curve)
1645         {
1646         //### Default <path> output
1648         outs.printf("<draw:path ");
1649         if (id.size()>0)
1650             outs.printf("id=\"%s\" ", id.c_str());
1652         std::map<std::string, std::string>::iterator siter;
1653         siter = styleLookupTable.find(id);
1654         if (siter != styleLookupTable.end())
1655             {
1656             std::string styleName = siter->second;
1657             outs.printf("draw:style-name=\"%s\" ", styleName.c_str());
1658             }
1660         std::map<std::string, std::string>::iterator giter;
1661         giter = gradientLookupTable.find(id);
1662         if (giter != gradientLookupTable.end())
1663             {
1664             std::string gradientName = giter->second;
1665             outs.printf("draw:fill-gradient-name=\"%s\" ",
1666                  gradientName.c_str());
1667             }
1669         outs.printf("draw:layer=\"layout\" svg:x=\"%.3fcm\" svg:y=\"%.3fcm\" ",
1670                        bbox_x, bbox_y);
1671         outs.printf("svg:width=\"%.3fcm\" svg:height=\"%.3fcm\" ",
1672                        bbox_width, bbox_height);
1673         outs.printf("svg:viewBox=\"0.0 0.0 %.3f %.3f\"\n",
1674                        bbox_width * 1000.0, bbox_height * 1000.0);
1676         outs.printf("    svg:d=\"");
1677         writePath(outs, SP_CURVE_BPATH(curve), tf, bbox_x, bbox_y);
1678         outs.printf("\"");
1680         outs.printf(">\n");
1681         outs.printf("</draw:path>\n");
1684         sp_curve_unref(curve);
1685         }
1687     return true;
1692 /**
1693  * Write the content.xml file.  Writes the namesspace headers, then
1694  * calls writeStyle() and writeTree().
1695  */
1696 bool OdfOutput::writeContent(ZipFile &zf, Inkscape::XML::Node *node)
1698     BufferOutputStream bouts;
1699     OutputStreamWriter outs(bouts);
1701     time_t tim;
1702     time(&tim);
1704     outs.printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1705     outs.printf("\n");
1706     outs.printf("\n");
1707     outs.printf("<!--\n");
1708     outs.printf("*************************************************************************\n");
1709     outs.printf("  file:  content.xml\n");
1710     outs.printf("  Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
1711     outs.printf("  http://www.inkscape.org\n");
1712     outs.printf("*************************************************************************\n");
1713     outs.printf("-->\n");
1714     outs.printf("\n");
1715     outs.printf("\n");
1716     outs.printf("<office:document-content\n");
1717     outs.printf("    xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
1718     outs.printf("    xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"\n");
1719     outs.printf("    xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"\n");
1720     outs.printf("    xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"\n");
1721     outs.printf("    xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"\n");
1722     outs.printf("    xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"\n");
1723     outs.printf("    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
1724     outs.printf("    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
1725     outs.printf("    xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
1726     outs.printf("    xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"\n");
1727     outs.printf("    xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
1728     outs.printf("    xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"\n");
1729     outs.printf("    xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"\n");
1730     outs.printf("    xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"\n");
1731     outs.printf("    xmlns:math=\"http://www.w3.org/1998/Math/MathML\"\n");
1732     outs.printf("    xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"\n");
1733     outs.printf("    xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"\n");
1734     outs.printf("    xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
1735     outs.printf("    xmlns:ooow=\"http://openoffice.org/2004/writer\"\n");
1736     outs.printf("    xmlns:oooc=\"http://openoffice.org/2004/calc\"\n");
1737     outs.printf("    xmlns:dom=\"http://www.w3.org/2001/xml-events\"\n");
1738     outs.printf("    xmlns:xforms=\"http://www.w3.org/2002/xforms\"\n");
1739     outs.printf("    xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n");
1740     outs.printf("    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
1741     outs.printf("    xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
1742     outs.printf("    xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
1743     outs.printf("    office:version=\"1.0\">\n");
1744     outs.printf("\n");
1745     outs.printf("\n");
1746     outs.printf("<office:scripts/>\n");
1747     outs.printf("\n");
1748     outs.printf("\n");
1749     outs.printf("<!-- ######### CONVERSION FROM SVG STARTS ######## -->\n");
1750     outs.printf("<!--\n");
1751     outs.printf("*************************************************************************\n");
1752     outs.printf("  S T Y L E S\n");
1753     outs.printf("  Style entries have been pulled from the svg style and\n");
1754     outs.printf("  representation attributes in the SVG tree.  The tree elements\n");
1755     outs.printf("  then refer to them by name, in the ODF manner\n");
1756     outs.printf("*************************************************************************\n");
1757     outs.printf("-->\n");
1758     outs.printf("\n");
1759     outs.printf("\n");
1761     if (!writeStyle(outs))
1762         {
1763         g_warning("Failed to write styles");
1764         return false;
1765         }
1767     outs.printf("\n");
1768     outs.printf("\n");
1769     outs.printf("\n");
1770     outs.printf("\n");
1771     outs.printf("<!--\n");
1772     outs.printf("*************************************************************************\n");
1773     outs.printf("  D R A W I N G\n");
1774     outs.printf("  This section is the heart of SVG-ODF conversion.  We are\n");
1775     outs.printf("  starting with simple conversions, and will slowly evolve\n");
1776     outs.printf("  into a 'smarter' translation as time progresses.  Any help\n");
1777     outs.printf("  in improving .odg export is welcome.\n");
1778     outs.printf("*************************************************************************\n");
1779     outs.printf("-->\n");
1780     outs.printf("\n");
1781     outs.printf("\n");
1782     outs.printf("<office:body>\n");
1783     outs.printf("<office:drawing>\n");
1784     outs.printf("<draw:page draw:name=\"page1\" draw:style-name=\"dp1\"\n");
1785     outs.printf("        draw:master-page-name=\"Default\">\n");
1786     outs.printf("\n");
1787     outs.printf("\n");
1789     if (!writeTree(outs, node))
1790         {
1791         g_warning("Failed to convert SVG tree");
1792         return false;
1793         }
1795     outs.printf("\n");
1796     outs.printf("\n");
1798     outs.printf("</draw:page>\n");
1799     outs.printf("</office:drawing>\n");
1801     outs.printf("\n");
1802     outs.printf("\n");
1803     outs.printf("<!-- ######### CONVERSION FROM SVG ENDS ######## -->\n");
1804     outs.printf("\n");
1805     outs.printf("\n");
1807     outs.printf("</office:body>\n");
1808     outs.printf("</office:document-content>\n");
1809     outs.printf("\n");
1810     outs.printf("\n");
1811     outs.printf("\n");
1812     outs.printf("<!--\n");
1813     outs.printf("*************************************************************************\n");
1814     outs.printf("  E N D    O F    F I L E\n");
1815     outs.printf("  Have a nice day  - ishmal\n");
1816     outs.printf("*************************************************************************\n");
1817     outs.printf("-->\n");
1818     outs.printf("\n");
1819     outs.printf("\n");
1823     //Make our entry
1824     ZipEntry *ze = zf.newEntry("content.xml", "ODF master content file");
1825     ze->setUncompressedData(bouts.getBuffer());
1826     ze->finish();
1828     return true;
1832 /**
1833  * Resets class to its pristine condition, ready to use again
1834  */
1835 void
1836 OdfOutput::reset()
1838     styleTable.clear();
1839     styleLookupTable.clear();
1840     gradientTable.clear();
1841     gradientLookupTable.clear();
1842     imageTable.clear();
1848 /**
1849  * Descends into the SVG tree, mapping things to ODF when appropriate
1850  */
1851 void
1852 OdfOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *uri)
1854     reset();
1856     //g_message("native file:%s\n", uri);
1857     documentUri = URI(uri);
1859     ZipFile zf;
1860     preprocess(zf, doc->rroot);
1862     if (!writeManifest(zf))
1863         {
1864         g_warning("Failed to write manifest");
1865         return;
1866         }
1868     if (!writeMeta(zf))
1869         {
1870         g_warning("Failed to write metafile");
1871         return;
1872         }
1874     if (!writeContent(zf, doc->rroot))
1875         {
1876         g_warning("Failed to write content");
1877         return;
1878         }
1880     if (!zf.writeFile(uri))
1881         {
1882         return;
1883         }
1887 /**
1888  * This is the definition of PovRay output.  This function just
1889  * calls the extension system with the memory allocated XML that
1890  * describes the data.
1891 */
1892 void
1893 OdfOutput::init()
1895     Inkscape::Extension::build_from_mem(
1896         "<inkscape-extension>\n"
1897             "<name>" N_("OpenDocument Drawing Output") "</name>\n"
1898             "<id>org.inkscape.output.odf</id>\n"
1899             "<output>\n"
1900                 "<extension>.odg</extension>\n"
1901                 "<mimetype>text/x-povray-script</mimetype>\n"
1902                 "<filetypename>" N_("OpenDocument drawing (*.odg)") "</filetypename>\n"
1903                 "<filetypetooltip>" N_("OpenDocument drawing file") "</filetypetooltip>\n"
1904             "</output>\n"
1905         "</inkscape-extension>",
1906         new OdfOutput());
1909 /**
1910  * Make sure that we are in the database
1911  */
1912 bool
1913 OdfOutput::check (Inkscape::Extension::Extension *module)
1915     /* We don't need a Key
1916     if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_POV))
1917         return FALSE;
1918     */
1920     return TRUE;
1925 //########################################################################
1926 //# I N P U T
1927 //########################################################################
1931 //#######################
1932 //# L A T E R  !!!  :-)
1933 //#######################
1947 }  //namespace Internal
1948 }  //namespace Extension
1949 }  //namespace Inkscape
1952 //########################################################################
1953 //# E N D    O F    F I L E
1954 //########################################################################
1956 /*
1957   Local Variables:
1958   mode:c++
1959   c-file-style:"stroustrup"
1960   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1961   indent-tabs-mode:nil
1962   fill-column:99
1963   End:
1964 */
1965 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :