Code

Fix for delete[] and binary comment
[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         badval(0),
106         d(0),
107         rows(0),
108         cols(0),
109         size(0)
110         {
111         }
113     SVDMatrix(unsigned int rowSize, unsigned int colSize) :
114         badval(0),
115         rows(rowSize),
116         cols(colSize),
117         size(rows * cols)
118         {
119         d = new double[size];
120         for (unsigned int i=0 ; i<size ; i++)
121             d[i] = 0.0;
122         }
124     SVDMatrix(double *vals, unsigned int rowSize, unsigned int colSize) :
125         badval(0),
126         rows(rowSize),
127         cols(colSize),
128         size(rows * cols)
129         {
130         d = new double[size];
131         for (unsigned int i=0 ; i<size ; i++)
132             d[i] = vals[i];
133         }
135     virtual ~SVDMatrix()
136         {
137         delete[] d;
138         d = 0;
139         }
141     SVDMatrix(const SVDMatrix &other) :
142         badval(0),
143         d(0),
144         rows(0),
145         cols(0),
146         size(0)
147         {
148         assign(other);
149         }
151     SVDMatrix &operator=(const SVDMatrix &other)
152         {
153         assign(other);
154         return *this;
155         }
157      double& operator() (unsigned int row, unsigned int col)
158          {
159          if (row >= rows || col >= cols)
160              return badval;
161          return d[cols*row + col];
162          }
164      double operator() (unsigned int row, unsigned int col) const
165          {
166          if (row >= rows || col >= cols)
167              return badval;
168          return d[cols*row + col];
169          }
171      unsigned int getRows()
172          {
173          return rows;
174          }
176      unsigned int getCols()
177          {
178          return cols;
179          }
181      SVDMatrix multiply(const SVDMatrix &other)
182          {
183          if (cols != other.rows)
184              {
185              SVDMatrix dummy;
186              return dummy;
187              }
188          SVDMatrix result(rows, other.cols);
189          for (unsigned int i=0 ; i<rows ; i++)
190              {
191              for (unsigned int j=0 ; j<other.cols ; j++)
192                  {
193                  double sum = 0.0;
194                  for (unsigned int k=0 ; k<cols ; k++)
195                      {
196                      //sum += a[i][k] * b[k][j];
197                      sum += d[i*cols +k] * other(k, j);
198                      }
199                  result(i, j) = sum;
200                  }
202              }
203          return result;
204          }
206      SVDMatrix transpose()
207          {
208          SVDMatrix result(cols, rows);
209          for (unsigned int i=0 ; i<rows ; i++)
210              for (unsigned int j=0 ; j<cols ; j++)
211                  result(j, i) = d[i*cols + j];
212          return result;
213          }
215 private:
218      void assign(const SVDMatrix &other)
219         {
220         if (d)
221             {
222             delete[] d;
223             d = 0;
224             }
225         rows = other.rows;
226         cols = other.cols;
227         size = other.size;
228         d = new double[size];
229         for (unsigned int i=0 ; i<size ; i++)
230             d[i] = other.d[i];
231         }
233     double badval;
235     double *d;
236     unsigned int rows;
237     unsigned int cols;
238     unsigned int size;
239 };
241 /**
242  *
243  * ====================================================
244  *
245  * NOTE:
246  * This class is ported almost verbatim from the public domain
247  * JAMA Matrix package.  It is modified to handle only 3x3 matrices
248  * and our NR::Matrix affine transform class.  We give full
249  * attribution to them, along with many thanks.  JAMA can be found at:
250  *     http://math.nist.gov/javanumerics/jama
251  *
252  * ====================================================
253  *
254  * Singular Value Decomposition.
255  * <P>
256  * For an m-by-n matrix A with m >= n, the singular value decomposition is
257  * an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and
258  * an n-by-n orthogonal matrix V so that A = U*S*V'.
259  * <P>
260  * The singular values, sigma[k] = S[k][k], are ordered so that
261  * sigma[0] >= sigma[1] >= ... >= sigma[n-1].
262  * <P>
263  * The singular value decompostion always exists, so the constructor will
264  * never fail.  The matrix condition number and the effective numerical
265  * rank can be computed from this decomposition.
266  */
267 class SingularValueDecomposition
269 public:
271    /** Construct the singular value decomposition
272    @param A    Rectangular matrix
273    @return     Structure to access U, S and V.
274    */
276     SingularValueDecomposition (const SVDMatrix &mat) :
277         A(mat),
278         U(),
279         s(0),
280         s_size(0),
281         V()
282         {
283         calculate();
284         }
286     virtual ~SingularValueDecomposition()
287         {
288         delete s;
289         }
291     /**
292      * Return the left singular vectors
293      * @return     U
294      */
295     SVDMatrix &getU();
297     /**
298      * Return the right singular vectors
299      * @return     V
300      */
301     SVDMatrix &getV();
303     /**
304      *  Return the s[index] value
305      */    double getS(unsigned int index);
307     /**
308      * Two norm
309      * @return max(S)
310      */
311     double norm2();
313     /**
314      * Two norm condition number
315      *  @return max(S)/min(S)
316      */
317     double cond();
319     /**
320      *  Effective numerical matrix rank
321      *  @return     Number of nonnegligible singular values.
322      */
323     int rank();
325 private:
327       void calculate();
329       SVDMatrix A;
330       SVDMatrix U;
331       double *s;
332       unsigned int s_size;
333       SVDMatrix V;
335 };
338 static double svd_hypot(double a, double b)
340     double r;
342     if (fabs(a) > fabs(b))
343         {
344         r = b/a;
345         r = fabs(a) * sqrt(1+r*r);
346         }
347     else if (b != 0)
348         {
349         r = a/b;
350         r = fabs(b) * sqrt(1+r*r);
351         }
352     else
353         {
354         r = 0.0;
355         }
356     return r;
361 void SingularValueDecomposition::calculate()
363       // Initialize.
364       int m = A.getRows();
365       int n = A.getCols();
367       int nu = (m > n) ? m : n;
368       s_size = (m+1 < n) ? m+1 : n;
369       s = new double[s_size];
370       U = SVDMatrix(m, nu);
371       V = SVDMatrix(n, n);
372       double *e = new double[n];
373       double *work = new double[m];
374       bool wantu = true;
375       bool wantv = true;
377       // Reduce A to bidiagonal form, storing the diagonal elements
378       // in s and the super-diagonal elements in e.
380       int nct = (m-1<n) ? m-1 : n;
381       int nrtx = (n-2<m) ? n-2 : m;
382       int nrt = (nrtx>0) ? nrtx : 0;
383       for (int k = 0; k < 2; k++) {
384          if (k < nct) {
386             // Compute the transformation for the k-th column and
387             // place the k-th diagonal in s[k].
388             // Compute 2-norm of k-th column without under/overflow.
389             s[k] = 0;
390             for (int i = k; i < m; i++) {
391                s[k] = svd_hypot(s[k],A(i, k));
392             }
393             if (s[k] != 0.0) {
394                if (A(k, k) < 0.0) {
395                   s[k] = -s[k];
396                }
397                for (int i = k; i < m; i++) {
398                   A(i, k) /= s[k];
399                }
400                A(k, k) += 1.0;
401             }
402             s[k] = -s[k];
403          }
404          for (int j = k+1; j < n; j++) {
405             if ((k < nct) & (s[k] != 0.0))  {
407             // Apply the transformation.
409                double t = 0;
410                for (int i = k; i < m; i++) {
411                   t += A(i, k) * A(i, j);
412                }
413                t = -t/A(k, k);
414                for (int i = k; i < m; i++) {
415                   A(i, j) += t*A(i, k);
416                }
417             }
419             // Place the k-th row of A into e for the
420             // subsequent calculation of the row transformation.
422             e[j] = A(k, j);
423          }
424          if (wantu & (k < nct)) {
426             // Place the transformation in U for subsequent back
427             // multiplication.
429             for (int i = k; i < m; i++) {
430                U(i, k) = A(i, k);
431             }
432          }
433          if (k < nrt) {
435             // Compute the k-th row transformation and place the
436             // k-th super-diagonal in e[k].
437             // Compute 2-norm without under/overflow.
438             e[k] = 0;
439             for (int i = k+1; i < n; i++) {
440                e[k] = svd_hypot(e[k],e[i]);
441             }
442             if (e[k] != 0.0) {
443                if (e[k+1] < 0.0) {
444                   e[k] = -e[k];
445                }
446                for (int i = k+1; i < n; i++) {
447                   e[i] /= e[k];
448                }
449                e[k+1] += 1.0;
450             }
451             e[k] = -e[k];
452             if ((k+1 < m) & (e[k] != 0.0)) {
454             // Apply the transformation.
456                for (int i = k+1; i < m; i++) {
457                   work[i] = 0.0;
458                }
459                for (int j = k+1; j < n; j++) {
460                   for (int i = k+1; i < m; i++) {
461                      work[i] += e[j]*A(i, j);
462                   }
463                }
464                for (int j = k+1; j < n; j++) {
465                   double t = -e[j]/e[k+1];
466                   for (int i = k+1; i < m; i++) {
467                      A(i, j) += t*work[i];
468                   }
469                }
470             }
471             if (wantv) {
473             // Place the transformation in V for subsequent
474             // back multiplication.
476                for (int i = k+1; i < n; i++) {
477                   V(i, k) = e[i];
478                }
479             }
480          }
481       }
483       // Set up the final bidiagonal matrix or order p.
485       int p = (n < m+1) ? n : m+1;
486       if (nct < n) {
487          s[nct] = A(nct, nct);
488       }
489       if (m < p) {
490          s[p-1] = 0.0;
491       }
492       if (nrt+1 < p) {
493          e[nrt] = A(nrt, p-1);
494       }
495       e[p-1] = 0.0;
497       // If required, generate U.
499       if (wantu) {
500          for (int j = nct; j < nu; j++) {
501             for (int i = 0; i < m; i++) {
502                U(i, j) = 0.0;
503             }
504             U(j, j) = 1.0;
505          }
506          for (int k = nct-1; k >= 0; k--) {
507             if (s[k] != 0.0) {
508                for (int j = k+1; j < nu; j++) {
509                   double t = 0;
510                   for (int i = k; i < m; i++) {
511                      t += U(i, k)*U(i, j);
512                   }
513                   t = -t/U(k, k);
514                   for (int i = k; i < m; i++) {
515                      U(i, j) += t*U(i, k);
516                   }
517                }
518                for (int i = k; i < m; i++ ) {
519                   U(i, k) = -U(i, k);
520                }
521                U(k, k) = 1.0 + U(k, k);
522                for (int i = 0; i < k-1; i++) {
523                   U(i, k) = 0.0;
524                }
525             } else {
526                for (int i = 0; i < m; i++) {
527                   U(i, k) = 0.0;
528                }
529                U(k, k) = 1.0;
530             }
531          }
532       }
534       // If required, generate V.
536       if (wantv) {
537          for (int k = n-1; k >= 0; k--) {
538             if ((k < nrt) & (e[k] != 0.0)) {
539                for (int j = k+1; j < nu; j++) {
540                   double t = 0;
541                   for (int i = k+1; i < n; i++) {
542                      t += V(i, k)*V(i, j);
543                   }
544                   t = -t/V(k+1, k);
545                   for (int i = k+1; i < n; i++) {
546                      V(i, j) += t*V(i, k);
547                   }
548                }
549             }
550             for (int i = 0; i < n; i++) {
551                V(i, k) = 0.0;
552             }
553             V(k, k) = 1.0;
554          }
555       }
557       // Main iteration loop for the singular values.
559       int pp = p-1;
560       int iter = 0;
561       //double eps = pow(2.0,-52.0);
562       //double tiny = pow(2.0,-966.0);
563       //let's just calculate these now
564       //a double can be e Â± 308.25, so this is safe
565       double eps = 2.22e-16;
566       double tiny = 1.6e-291;
567       while (p > 0) {
568          int k,kase;
570          // Here is where a test for too many iterations would go.
572          // This section of the program inspects for
573          // negligible elements in the s and e arrays.  On
574          // completion the variables kase and k are set as follows.
576          // kase = 1     if s(p) and e[k-1] are negligible and k<p
577          // kase = 2     if s(k) is negligible and k<p
578          // kase = 3     if e[k-1] is negligible, k<p, and
579          //              s(k), ..., s(p) are not negligible (qr step).
580          // kase = 4     if e(p-1) is negligible (convergence).
582          for (k = p-2; k >= -1; k--) {
583             if (k == -1) {
584                break;
585             }
586             if (fabs(e[k]) <=
587                   tiny + eps*(fabs(s[k]) + fabs(s[k+1]))) {
588                e[k] = 0.0;
589                break;
590             }
591          }
592          if (k == p-2) {
593             kase = 4;
594          } else {
595             int ks;
596             for (ks = p-1; ks >= k; ks--) {
597                if (ks == k) {
598                   break;
599                }
600                double t = (ks != p ? fabs(e[ks]) : 0.) +
601                           (ks != k+1 ? fabs(e[ks-1]) : 0.);
602                if (fabs(s[ks]) <= tiny + eps*t)  {
603                   s[ks] = 0.0;
604                   break;
605                }
606             }
607             if (ks == k) {
608                kase = 3;
609             } else if (ks == p-1) {
610                kase = 1;
611             } else {
612                kase = 2;
613                k = ks;
614             }
615          }
616          k++;
618          // Perform the task indicated by kase.
620          switch (kase) {
622             // Deflate negligible s(p).
624             case 1: {
625                double f = e[p-2];
626                e[p-2] = 0.0;
627                for (int j = p-2; j >= k; j--) {
628                   double t = svd_hypot(s[j],f);
629                   double cs = s[j]/t;
630                   double sn = f/t;
631                   s[j] = t;
632                   if (j != k) {
633                      f = -sn*e[j-1];
634                      e[j-1] = cs*e[j-1];
635                   }
636                   if (wantv) {
637                      for (int i = 0; i < n; i++) {
638                         t = cs*V(i, j) + sn*V(i, p-1);
639                         V(i, p-1) = -sn*V(i, j) + cs*V(i, p-1);
640                         V(i, j) = t;
641                      }
642                   }
643                }
644             }
645             break;
647             // Split at negligible s(k).
649             case 2: {
650                double f = e[k-1];
651                e[k-1] = 0.0;
652                for (int j = k; j < p; j++) {
653                   double t = svd_hypot(s[j],f);
654                   double cs = s[j]/t;
655                   double sn = f/t;
656                   s[j] = t;
657                   f = -sn*e[j];
658                   e[j] = cs*e[j];
659                   if (wantu) {
660                      for (int i = 0; i < m; i++) {
661                         t = cs*U(i, j) + sn*U(i, k-1);
662                         U(i, k-1) = -sn*U(i, j) + cs*U(i, k-1);
663                         U(i, j) = t;
664                      }
665                   }
666                }
667             }
668             break;
670             // Perform one qr step.
672             case 3: {
674                // Calculate the shift.
676                double scale = fabs(s[p-1]);
677                double d = fabs(s[p-2]);
678                if (d>scale) scale=d;
679                d = fabs(e[p-2]);
680                if (d>scale) scale=d;
681                d = fabs(s[k]);
682                if (d>scale) scale=d;
683                d = fabs(e[k]);
684                if (d>scale) scale=d;
685                double sp = s[p-1]/scale;
686                double spm1 = s[p-2]/scale;
687                double epm1 = e[p-2]/scale;
688                double sk = s[k]/scale;
689                double ek = e[k]/scale;
690                double b = ((spm1 + sp)*(spm1 - sp) + epm1*epm1)/2.0;
691                double c = (sp*epm1)*(sp*epm1);
692                double shift = 0.0;
693                if ((b != 0.0) | (c != 0.0)) {
694                   shift = sqrt(b*b + c);
695                   if (b < 0.0) {
696                      shift = -shift;
697                   }
698                   shift = c/(b + shift);
699                }
700                double f = (sk + sp)*(sk - sp) + shift;
701                double g = sk*ek;
703                // Chase zeros.
705                for (int j = k; j < p-1; j++) {
706                   double t = svd_hypot(f,g);
707                   double cs = f/t;
708                   double sn = g/t;
709                   if (j != k) {
710                      e[j-1] = t;
711                   }
712                   f = cs*s[j] + sn*e[j];
713                   e[j] = cs*e[j] - sn*s[j];
714                   g = sn*s[j+1];
715                   s[j+1] = cs*s[j+1];
716                   if (wantv) {
717                      for (int i = 0; i < n; i++) {
718                         t = cs*V(i, j) + sn*V(i, j+1);
719                         V(i, j+1) = -sn*V(i, j) + cs*V(i, j+1);
720                         V(i, j) = t;
721                      }
722                   }
723                   t = svd_hypot(f,g);
724                   cs = f/t;
725                   sn = g/t;
726                   s[j] = t;
727                   f = cs*e[j] + sn*s[j+1];
728                   s[j+1] = -sn*e[j] + cs*s[j+1];
729                   g = sn*e[j+1];
730                   e[j+1] = cs*e[j+1];
731                   if (wantu && (j < m-1)) {
732                      for (int i = 0; i < m; i++) {
733                         t = cs*U(i, j) + sn*U(i, j+1);
734                         U(i, j+1) = -sn*U(i, j) + cs*U(i, j+1);
735                         U(i, j) = t;
736                      }
737                   }
738                }
739                e[p-2] = f;
740                iter = iter + 1;
741             }
742             break;
744             // Convergence.
746             case 4: {
748                // Make the singular values positive.
750                if (s[k] <= 0.0) {
751                   s[k] = (s[k] < 0.0 ? -s[k] : 0.0);
752                   if (wantv) {
753                      for (int i = 0; i <= pp; i++) {
754                         V(i, k) = -V(i, k);
755                      }
756                   }
757                }
759                // Order the singular values.
761                while (k < pp) {
762                   if (s[k] >= s[k+1]) {
763                      break;
764                   }
765                   double t = s[k];
766                   s[k] = s[k+1];
767                   s[k+1] = t;
768                   if (wantv && (k < n-1)) {
769                      for (int i = 0; i < n; i++) {
770                         t = V(i, k+1); V(i, k+1) = V(i, k); V(i, k) = t;
771                      }
772                   }
773                   if (wantu && (k < m-1)) {
774                      for (int i = 0; i < m; i++) {
775                         t = U(i, k+1); U(i, k+1) = U(i, k); U(i, k) = t;
776                      }
777                   }
778                   k++;
779                }
780                iter = 0;
781                p--;
782             }
783             break;
784          }
785       }
787     delete e;
788     delete work;
794 /**
795  * Return the left singular vectors
796  * @return     U
797  */
798 SVDMatrix &SingularValueDecomposition::getU()
800     return U;
803 /**
804  * Return the right singular vectors
805  * @return     V
806  */
808 SVDMatrix &SingularValueDecomposition::getV()
810     return V;
813 /**
814  *  Return the s[0] value
815  */
816 double SingularValueDecomposition::getS(unsigned int index)
818     if (index >= s_size)
819         return 0.0;
820     return s[index];
823 /**
824  * Two norm
825  * @return     max(S)
826  */
827 double SingularValueDecomposition::norm2()
829     return s[0];
832 /**
833  * Two norm condition number
834  *  @return     max(S)/min(S)
835  */
837 double SingularValueDecomposition::cond()
839     return s[0]/s[2];
842 /**
843  *  Effective numerical matrix rank
844  *  @return     Number of nonnegligible singular values.
845  */
846 int SingularValueDecomposition::rank()
848     double eps = pow(2.0,-52.0);
849     double tol = 3.0*s[0]*eps;
850     int r = 0;
851     for (int i = 0; i < 3; i++)
852         {
853         if (s[i] > tol)
854             r++;
855         }
856     return r;
859 //########################################################################
860 //# E N D    C L A S S    SingularValueDecomposition
861 //########################################################################
867 #define pi 3.14159
868 //#define pxToCm  0.0275
869 #define pxToCm  0.04
870 #define piToRad 0.0174532925
871 #define docHeightCm 22.86
874 //########################################################################
875 //# O U T P U T
876 //########################################################################
878 /**
879  * Get the value of a node/attribute pair
880  */
881 static std::string getAttribute( Inkscape::XML::Node *node, char *attrName)
883     std::string val;
884     char *valstr = (char *)node->attribute(attrName);
885     if (valstr)
886         val = (const char *)valstr;
887     return val;
892 /**
893  * Get the extension suffix from the end of a file name
894  */
895 static std::string getExtension(const std::string &fname)
897     std::string ext;
899     unsigned int pos = fname.rfind('.');
900     if (pos == fname.npos)
901         {
902         ext = "";
903         }
904     else
905         {
906         ext = fname.substr(pos);
907         }
908     return ext;
912 static std::string formatTransform(NR::Matrix &tf)
914     std::string str;
915     if (!tf.test_identity())
916         {
917         char buf[128];
918         snprintf(buf, 127, "matrix(%.3f %.3f %.3f %.3f %.3f %.3f)",
919                 tf[0], tf[1], tf[2], tf[3], tf[4], tf[5]);
920         str = buf;
921         }
922     return str;
929 /**
930  * Get the general transform from SVG pixels to
931  * ODF cm
932  */
933 static NR::Matrix getODFTransform(const SPItem *item)
935     //### Get SVG-to-ODF transform
936     NR::Matrix tf;
937     tf                   = sp_item_i2d_affine(item);
938     //Flip Y into document coordinates
939     double doc_height    = sp_document_height(SP_ACTIVE_DOCUMENT);
940     NR::Matrix doc2dt_tf = NR::Matrix(NR::scale(1.0, -1.0));
941     doc2dt_tf            = doc2dt_tf * NR::Matrix(NR::translate(0, doc_height));
942     tf                   = tf * doc2dt_tf;
943     tf                   = tf * NR::Matrix(NR::scale(pxToCm));
944     return tf;
950 /**
951  * Get the bounding box of an item, as mapped onto
952  * an ODF document, in cm.
953  */
954 static NR::Rect getODFBoundingBox(const SPItem *item)
956     NR::Rect bbox        = sp_item_bbox_desktop((SPItem *)item);
957     double doc_height    = sp_document_height(SP_ACTIVE_DOCUMENT);
958     NR::Matrix doc2dt_tf = NR::Matrix(NR::scale(1.0, -1.0));
959     doc2dt_tf            = doc2dt_tf * NR::Matrix(NR::translate(0, doc_height));
960     bbox                 = bbox * doc2dt_tf;
961     bbox                 = bbox * NR::Matrix(NR::scale(pxToCm));
962     return bbox;
967 /**
968  * Get the transform for an item, correcting for
969  * handedness reversal
970  */
971 static NR::Matrix getODFItemTransform(const SPItem *item)
973     NR::Matrix itemTransform = NR::Matrix(NR::scale(1, -1));
974     itemTransform = itemTransform * item->transform;
975     itemTransform = itemTransform * NR::Matrix(NR::scale(1, -1));
976     return itemTransform;
981 /**
982  * Get some fun facts from the transform
983  */
984 static void analyzeTransform(NR::Matrix &tf,
985            double &rotate, double &xskew, double &yskew,
986            double &xscale, double &yscale)
988     SVDMatrix mat(2, 2);
989     mat(0, 0) = tf[0];
990     mat(0, 1) = tf[1];
991     mat(1, 0) = tf[2];
992     mat(1, 1) = tf[3];
994     SingularValueDecomposition svd(mat);
996     SVDMatrix U = svd.getU();
997     SVDMatrix V = svd.getV();
998     SVDMatrix Vt = V.transpose();
999     SVDMatrix UVt = U.multiply(Vt);
1000     double s0 = svd.getS(0);
1001     double s1 = svd.getS(1);
1002     xscale = s0;
1003     yscale = s1;
1004     //g_message("## s0:%.3f s1:%.3f", s0, s1);
1005     //g_message("## u:%.3f %.3f %.3f %.3f", U(0,0), U(0,1), U(1,0), U(1,1));
1006     //g_message("## v:%.3f %.3f %.3f %.3f", V(0,0), V(0,1), V(1,0), V(1,1));
1007     //g_message("## vt:%.3f %.3f %.3f %.3f", Vt(0,0), Vt(0,1), Vt(1,0), Vt(1,1));
1008     //g_message("## uvt:%.3f %.3f %.3f %.3f", UVt(0,0), UVt(0,1), UVt(1,0), UVt(1,1));
1009     rotate = UVt(0,0);
1015 /**
1016  * FIRST PASS.
1017  * Method descends into the repr tree, converting image, style, and gradient info
1018  * into forms compatible in ODF.
1019  */
1020 void
1021 OdfOutput::preprocess(ZipFile &zf, Inkscape::XML::Node *node)
1024     std::string nodeName = node->name();
1025     std::string id       = getAttribute(node, "id");
1027     SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(node);
1028     if (!reprobj)
1029         return;
1030     if (!SP_IS_ITEM(reprobj))
1031         {
1032         return;
1033         }
1034     SPItem *item  = SP_ITEM(reprobj);
1035     //### Get SVG-to-ODF transform
1036     NR::Matrix tf = getODFTransform(item);
1039     if (nodeName == "image" || nodeName == "svg:image")
1040         {
1041         //g_message("image");
1042         std::string href = getAttribute(node, "xlink:href");
1043         if (href.size() > 0)
1044             {
1045             std::string oldName = href;
1046             std::string ext = getExtension(oldName);
1047             if (ext == ".jpeg")
1048                 ext = ".jpg";
1049             if (imageTable.find(oldName) == imageTable.end())
1050                 {
1051                 char buf[64];
1052                 snprintf(buf, 63, "Pictures/image%d%s",
1053                     (int)imageTable.size(), ext.c_str());
1054                 std::string newName = buf;
1055                 imageTable[oldName] = newName;
1056                 std::string comment = "old name was: ";
1057                 comment.append(oldName);
1058                 URI oldUri(oldName);
1059                 //g_message("oldpath:%s", oldUri.getNativePath().c_str());
1060                 //# if relative to the documentURI, get proper path
1061                 URI resUri = documentUri.resolve(oldUri);
1062                 DOMString pathName = resUri.getNativePath();
1063                 //g_message("native path:%s", pathName.c_str());
1064                 ZipEntry *ze = zf.addFile(pathName, comment);
1065                 if (ze)
1066                     {
1067                     ze->setFileName(newName);
1068                     }
1069                 else
1070                     {
1071                     g_warning("Could not load image file '%s'", pathName.c_str());
1072                     }
1073                 }
1074             }
1075         }
1079     //###### Get style
1080     SPStyle *style = SP_OBJECT_STYLE(item);
1081     if (style && id.size()>0)
1082         {
1083         bool isGradient = false;
1085         StyleInfo si;
1086         //## Style.  Look in writeStyle() below to see what info
1087         //   we need to read into StyleInfo.  Note that we need to
1088         //   determine whether information goes into a style element
1089         //   or a gradient element.
1090         //## FILL
1091         if (style->fill.type == SP_PAINT_TYPE_COLOR)
1092             {
1093             guint32 fillCol =
1094                 sp_color_get_rgba32_ualpha(&style->fill.value.color, 0);
1095             char buf[16];
1096             int r = (fillCol >> 24) & 0xff;
1097             int g = (fillCol >> 16) & 0xff;
1098             int b = (fillCol >>  8) & 0xff;
1099             //g_message("## %s %lx", id.c_str(), (unsigned int)fillCol);
1100             snprintf(buf, 15, "#%02x%02x%02x", r, g, b);
1101             si.fillColor = buf;
1102             si.fill      = "solid";
1103             double opacityPercent = 100.0 *
1104                  (SP_SCALE24_TO_FLOAT(style->fill_opacity.value));
1105             snprintf(buf, 15, "%.3f%%", opacityPercent);
1106             si.fillOpacity = buf;
1107             }
1108         else if (style->fill.type == SP_PAINT_TYPE_PAINTSERVER)
1109             {
1110             //## Gradient.  Look in writeStyle() below to see what info
1111             //   we need to read into GradientInfo.
1112             if (!SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)))
1113                 return;
1114             isGradient = true;
1115             GradientInfo gi;
1116             SPGradient *gradient = SP_GRADIENT(SP_STYLE_FILL_SERVER(style));
1117             if (SP_IS_LINEARGRADIENT(gradient))
1118                 {
1119                 gi.style = "linear";
1120                 SPLinearGradient *linGrad = SP_LINEARGRADIENT(gradient);
1121                 gi.x1 = linGrad->x1.value;
1122                 gi.y1 = linGrad->y1.value;
1123                 gi.x2 = linGrad->x2.value;
1124                 gi.y2 = linGrad->y2.value;
1125                 }
1126             else if (SP_IS_RADIALGRADIENT(gradient))
1127                 {
1128                 gi.style = "radial";
1129                 SPRadialGradient *radGrad = SP_RADIALGRADIENT(gradient);
1130                 gi.cx = radGrad->cx.computed * 100.0;//ODG cx is percentages
1131                 gi.cy = radGrad->cy.computed * 100.0;
1132                 }
1133             else
1134                 {
1135                 g_warning("not a supported gradient type");
1136                 }
1138             //Look for existing identical style;
1139             bool gradientMatch = false;
1140             std::vector<GradientInfo>::iterator iter;
1141             for (iter=gradientTable.begin() ; iter!=gradientTable.end() ; iter++)
1142                 {
1143                 if (gi.equals(*iter))
1144                     {
1145                     //map to existing gradientTable entry
1146                     std::string gradientName = iter->name;
1147                     //g_message("found duplicate style:%s", gradientName.c_str());
1148                     gradientLookupTable[id] = gradientName;
1149                     gradientMatch = true;
1150                     break;
1151                     }
1152                 }
1153             //None found, make a new pair or entries
1154             if (!gradientMatch)
1155                 {
1156                 char buf[16];
1157                 snprintf(buf, 15, "gradient%d", (int)gradientTable.size());
1158                 std::string gradientName = buf;
1159                 gi.name = gradientName;
1160                 gradientTable.push_back(gi);
1161                 gradientLookupTable[id] = gradientName;
1162                 }
1163             }
1165         //## STROKE
1166         if (style->stroke.type == SP_PAINT_TYPE_COLOR)
1167             {
1168             guint32 strokeCol =
1169                 sp_color_get_rgba32_ualpha(&style->stroke.value.color, 0);
1170             char buf[16];
1171             int r = (strokeCol >> 24) & 0xff;
1172             int g = (strokeCol >> 16) & 0xff;
1173             int b = (strokeCol >>  8) & 0xff;
1174             snprintf(buf, 15, "#%02x%02x%02x", r, g, b);
1175             si.strokeColor = buf;
1176             snprintf(buf, 15, "%.3fpt", style->stroke_width.value);
1177             si.strokeWidth = buf;
1178             si.stroke      = "solid";
1179             double opacityPercent = 100.0 *
1180                  (SP_SCALE24_TO_FLOAT(style->stroke_opacity.value));
1181             snprintf(buf, 15, "%.3f%%", opacityPercent);
1182             si.strokeOpacity = buf;
1183             }
1185         if (!isGradient)
1186             {
1187             //Look for existing identical style;
1188             bool styleMatch = false;
1189             std::vector<StyleInfo>::iterator iter;
1190             for (iter=styleTable.begin() ; iter!=styleTable.end() ; iter++)
1191                 {
1192                 if (si.equals(*iter))
1193                     {
1194                     //map to existing styleTable entry
1195                     std::string styleName = iter->name;
1196                     //g_message("found duplicate style:%s", styleName.c_str());
1197                     styleLookupTable[id] = styleName;
1198                     styleMatch = true;
1199                     break;
1200                     }
1201                 }
1202             //None found, make a new pair or entries
1203             if (!styleMatch)
1204                 {
1205                 char buf[16];
1206                 snprintf(buf, 15, "style%d", (int)styleTable.size());
1207                 std::string styleName = buf;
1208                 si.name = styleName;
1209                 styleTable.push_back(si);
1210                 styleLookupTable[id] = styleName;
1211                 }
1212             }
1213         }
1215     for (Inkscape::XML::Node *child = node->firstChild() ;
1216             child ; child = child->next())
1217         preprocess(zf, child);
1222 /**
1223  * Writes the manifest.  Currently it only changes according to the
1224  * file names of images packed into the zip file.
1225  */
1226 bool OdfOutput::writeManifest(ZipFile &zf)
1228     BufferOutputStream bouts;
1229     OutputStreamWriter outs(bouts);
1231     time_t tim;
1232     time(&tim);
1234     outs.printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1235     outs.printf("<!DOCTYPE manifest:manifest PUBLIC \"-//OpenOffice.org//DTD Manifest 1.0//EN\" \"Manifest.dtd\">\n");
1236     outs.printf("\n");
1237     outs.printf("\n");
1238     outs.printf("<!--\n");
1239     outs.printf("*************************************************************************\n");
1240     outs.printf("  file:  manifest.xml\n");
1241     outs.printf("  Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
1242     outs.printf("  http://www.inkscape.org\n");
1243     outs.printf("*************************************************************************\n");
1244     outs.printf("-->\n");
1245     outs.printf("\n");
1246     outs.printf("\n");
1247     outs.printf("<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\">\n");
1248     outs.printf("    <manifest:file-entry manifest:media-type=\"application/vnd.oasis.opendocument.graphics\" manifest:full-path=\"/\"/>\n");
1249     outs.printf("    <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/>\n");
1250     outs.printf("    <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"meta.xml\"/>\n");
1251     outs.printf("    <!--List our images here-->\n");
1252     std::map<std::string, std::string>::iterator iter;
1253     for (iter = imageTable.begin() ; iter!=imageTable.end() ; iter++)
1254         {
1255         std::string oldName = iter->first;
1256         std::string newName = iter->second;
1258         std::string ext = getExtension(oldName);
1259         if (ext == ".jpeg")
1260             ext = ".jpg";
1261         outs.printf("    <manifest:file-entry manifest:media-type=\"");
1262         if (ext == ".gif")
1263             outs.printf("image/gif");
1264         else if (ext == ".png")
1265             outs.printf("image/png");
1266         else if (ext == ".jpg")
1267             outs.printf("image/jpeg");
1268         outs.printf("\" manifest:full-path=\"");
1269         outs.printf((char *)newName.c_str());
1270         outs.printf("\"/>\n");
1271         }
1272     outs.printf("</manifest:manifest>\n");
1274     outs.close();
1276     //Make our entry
1277     ZipEntry *ze = zf.newEntry("META-INF/manifest.xml", "ODF file manifest");
1278     ze->setUncompressedData(bouts.getBuffer());
1279     ze->finish();
1281     return true;
1285 /**
1286  * This writes the document meta information to meta.xml
1287  */
1288 bool OdfOutput::writeMeta(ZipFile &zf)
1290     BufferOutputStream bouts;
1291     OutputStreamWriter outs(bouts);
1293     time_t tim;
1294     time(&tim);
1296     outs.printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1297     outs.printf("\n");
1298     outs.printf("\n");
1299     outs.printf("<!--\n");
1300     outs.printf("*************************************************************************\n");
1301     outs.printf("  file:  meta.xml\n");
1302     outs.printf("  Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
1303     outs.printf("  http://www.inkscape.org\n");
1304     outs.printf("*************************************************************************\n");
1305     outs.printf("-->\n");
1306     outs.printf("\n");
1307     outs.printf("\n");
1308     outs.printf("<office:document-meta\n");
1309     outs.printf("xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
1310     outs.printf("xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
1311     outs.printf("xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
1312     outs.printf("xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
1313     outs.printf("xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
1314     outs.printf("xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
1315     outs.printf("xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
1316     outs.printf("xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
1317     outs.printf("office:version=\"1.0\">\n");
1318     outs.printf("<office:meta>\n");
1319     outs.printf("    <meta:generator>Inkscape.org - 0.44</meta:generator>\n");
1320     outs.printf("    <meta:initial-creator>clark kent</meta:initial-creator>\n");
1321     outs.printf("    <meta:creation-date>2006-04-13T17:12:29</meta:creation-date>\n");
1322     outs.printf("    <dc:creator>clark kent</dc:creator>\n");
1323     outs.printf("    <dc:date>2006-04-13T17:13:20</dc:date>\n");
1324     outs.printf("    <dc:language>en-US</dc:language>\n");
1325     outs.printf("    <meta:editing-cycles>2</meta:editing-cycles>\n");
1326     outs.printf("    <meta:editing-duration>PT56S</meta:editing-duration>\n");
1327     outs.printf("    <meta:user-defined meta:name=\"Info 1\"/>\n");
1328     outs.printf("    <meta:user-defined meta:name=\"Info 2\"/>\n");
1329     outs.printf("    <meta:user-defined meta:name=\"Info 3\"/>\n");
1330     outs.printf("    <meta:user-defined meta:name=\"Info 4\"/>\n");
1331     outs.printf("    <meta:document-statistic meta:object-count=\"2\"/>\n");
1332     outs.printf("</office:meta>\n");
1333     outs.printf("</office:document-meta>\n");
1334     outs.printf("\n");
1335     outs.printf("\n");
1338     outs.close();
1340     //Make our entry
1341     ZipEntry *ze = zf.newEntry("meta.xml", "ODF info file");
1342     ze->setUncompressedData(bouts.getBuffer());
1343     ze->finish();
1345     return true;
1351 /**
1352  * This is called just before writeTree(), since it will write style and
1353  * gradient information above the <draw> tag in the content.xml file
1354  */
1355 bool OdfOutput::writeStyle(Writer &outs)
1357     outs.printf("<office:automatic-styles>\n");
1358     outs.printf("<!-- ####### 'Standard' styles ####### -->\n");
1359     outs.printf("<style:style style:name=\"dp1\" style:family=\"drawing-page\"/>\n");
1360     outs.printf("<style:style style:name=\"gr1\" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");
1361     outs.printf("  <style:graphic-properties draw:stroke=\"none\" draw:fill=\"none\"\n");
1362     outs.printf("       draw:textarea-horizontal-align=\"center\"\n");
1363     outs.printf("       draw:textarea-vertical-align=\"middle\" draw:color-mode=\"standard\"\n");
1364     outs.printf("       draw:luminance=\"0%\" draw:contrast=\"0%\" draw:gamma=\"100%\" draw:red=\"0%\"\n");
1365     outs.printf("       draw:green=\"0%\" draw:blue=\"0%\" fo:clip=\"rect(0cm 0cm 0cm 0cm)\"\n");
1366     outs.printf("       draw:image-opacity=\"100%\" style:mirror=\"none\"/>\n");
1367     outs.printf("</style:style>\n");
1368     outs.printf("<style:style style:name=\"P1\" style:family=\"paragraph\">\n");
1369     outs.printf("  <style:paragraph-properties fo:text-align=\"center\"/>\n");
1370     outs.printf("</style:style>\n");
1372     /*
1373     ==========================================================
1374     Dump our style table.  Styles should have a general layout
1375     something like the following.  Look in:
1376     http://books.evc-cit.info/odbook/ch06.html#draw-style-file-section
1377     for style and gradient information.
1378     <style:style style:name="gr13"
1379       style:family="graphic" style:parent-style-name="standard">
1380         <style:graphic-properties draw:stroke="solid"
1381             svg:stroke-width="0.1cm"
1382             svg:stroke-color="#ff0000"
1383             draw:fill="solid" draw:fill-color="#e6e6ff"/>
1384     </style:style>
1385     ==========================================================
1386     */
1387     outs.printf("<!-- ####### Styles from Inkscape document ####### -->\n");
1388     std::vector<StyleInfo>::iterator iter;
1389     for (iter = styleTable.begin() ; iter != styleTable.end() ; iter++)
1390         {
1391         outs.printf("<style:style style:name=\"%s\"", iter->name.c_str());
1392         StyleInfo s(*iter);
1393         outs.printf(" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");
1394         outs.printf("  <style:graphic-properties");
1395         outs.printf(" draw:fill=\"%s\" ", s.fill.c_str());
1396         if (s.fill != "none")
1397             {
1398             outs.printf(" draw:fill-color=\"%s\" ", s.fillColor.c_str());
1399             outs.printf(" draw:fill-opacity=\"%s\" ", s.fillOpacity.c_str());
1400             }
1401         outs.printf(" draw:stroke=\"%s\" ", s.stroke.c_str());
1402         if (s.stroke != "none")
1403             {
1404             outs.printf(" svg:stroke-width=\"%s\" ", s.strokeWidth.c_str());
1405             outs.printf(" svg:stroke-color=\"%s\" ", s.strokeColor.c_str());
1406             outs.printf(" svg:stroke-opacity=\"%s\" ", s.strokeOpacity.c_str());
1407             }
1408         outs.printf("/>\n");
1409         outs.printf("</style:style>\n");
1410         }
1412     //##  Dump our gradient table
1413     outs.printf("\n");
1414     outs.printf("<!-- ####### Gradients from Inkscape document ####### -->\n");
1415     std::vector<GradientInfo>::iterator giter;
1416     for (giter = gradientTable.begin() ; giter != gradientTable.end() ; giter++)
1417         {
1418         GradientInfo gi(*giter);
1419         outs.printf("<draw:gradient draw:name=\"%s\" ", gi.name.c_str());
1420         outs.printf("draw:style=\"%s\" ", gi.style.c_str());
1421         if (gi.style == "linear")
1422             {
1423             /*
1424             ===================================================================
1425             LINEAR gradient.  We need something that looks like this:
1426             <draw:gradient draw:name="Gradient_20_7"
1427                 draw:display-name="Gradient 7"
1428                 draw:style="linear"
1429                 draw:start-color="#008080" draw:end-color="#993366"
1430                 draw:start-intensity="100%" draw:end-intensity="100%"
1431                 draw:angle="150" draw:border="0%"/>
1432             ===================================================================
1433             */
1434             outs.printf("draw:display-name=\"linear borderless\" ");
1435             }
1436         else if (gi.style == "radial")
1437             {
1438             /*
1439             ===================================================================
1440             RADIAL gradient.  We need something that looks like this:
1441             <!-- radial gradient, light gray to white, centered, 0% border -->
1442             <draw:gradient draw:name="radial_20_borderless"
1443                 draw:display-name="radial borderless"
1444                 draw:style="radial"
1445                 draw:cx="50%" draw:cy="50%"
1446                 draw:start-color="#999999" draw:end-color="#ffffff"
1447                 draw:border="0%"/>
1448             ===================================================================
1449             */
1450             outs.printf("draw:display-name=\"radial borderless\" ");
1451             outs.printf("draw:cx=\".2f%%\" draw:cy=\".2f%%\" ", gi.cx, gi.cy);
1452             }
1453         else
1454             {
1455             g_warning("unsupported gradient style '%s'", gi.style.c_str());
1456             }
1457         outs.printf("/>\n");
1458         }
1460     outs.printf("\n");
1461     outs.printf("</office:automatic-styles>\n");
1462     outs.printf("\n");
1464     return true;
1469 /**
1470  * Writes an SVG path as an ODF <draw:path>
1471  */
1472 static void
1473 writePath(Writer &outs, NArtBpath const *bpath,
1474           NR::Matrix &tf, double xoff, double yoff)
1476     bool closed = false;
1477     NArtBpath *bp = (NArtBpath *)bpath;
1478     for (  ; bp->code != NR_END; bp++)
1479         {
1480         NR::Point const p1(bp->c(1) * tf);
1481         NR::Point const p2(bp->c(2) * tf);
1482         NR::Point const p3(bp->c(3) * tf);
1483         double x1 = (p1[NR::X] - xoff) * 1000.0;
1484         double y1 = (p1[NR::Y] - yoff) * 1000.0;
1485         double x2 = (p2[NR::X] - xoff) * 1000.0;
1486         double y2 = (p2[NR::Y] - yoff) * 1000.0;
1487         double x3 = (p3[NR::X] - xoff) * 1000.0;
1488         double y3 = (p3[NR::Y] - yoff) * 1000.0;
1490         switch (bp->code)
1491             {
1492             case NR_LINETO:
1493                 outs.printf("L %.3f,%.3f ",  x3 , y3);
1494                 break;
1496             case NR_CURVETO:
1497                 outs.printf("C %.3f,%.3f %.3f,%.3f %.3f,%.3f ",
1498                               x1, y1, x2, y2, x3, y3);
1499                 break;
1501             case NR_MOVETO_OPEN:
1502             case NR_MOVETO:
1503                 if (closed)
1504                     outs.printf("z ");
1505                 closed = ( bp->code == NR_MOVETO );
1506                 outs.printf("M %.3f,%.3f ",  x3 , y3);
1507                 break;
1509             default:
1510                 break;
1512             }
1514         }
1516     if (closed)
1517         outs.printf("z");;
1523 /**
1524  * SECOND PASS.
1525  * This is the main SPObject tree output to ODF.  preprocess()
1526  * must be called prior to this, as elements will often reference
1527  * data parsed and tabled in preprocess().
1528  */
1529 bool OdfOutput::writeTree(Writer &outs, Inkscape::XML::Node *node)
1531     //# Get the SPItem, if applicable
1532     SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(node);
1533     if (!reprobj)
1534         return true;
1535     if (!SP_IS_ITEM(reprobj))
1536         {
1537         return true;
1538         }
1539     SPItem *item = SP_ITEM(reprobj);
1542     std::string nodeName = node->name();
1543     std::string id       = getAttribute(node, "id");
1545     //### Get SVG-to-ODF transform
1546     NR::Matrix tf        = getODFTransform(item);
1548     //### Get ODF bounding box params for item
1549     NR::Rect bbox        = getODFBoundingBox(item);
1550     double bbox_x        = bbox.min()[NR::X];
1551     double bbox_y        = bbox.min()[NR::Y];
1552     double bbox_width    = bbox.max()[NR::X] - bbox.min()[NR::X];
1553     double bbox_height   = bbox.max()[NR::Y] - bbox.min()[NR::Y];
1555     double rotate;
1556     double xskew;
1557     double yskew;
1558     double xscale;
1559     double yscale;
1560     analyzeTransform(tf, rotate, xskew, yskew, xscale, yscale);
1562     //# Do our stuff
1563     SPCurve *curve = NULL;
1565     //g_message("##### %s #####", nodeName.c_str());
1567     if (nodeName == "svg" || nodeName == "svg:svg")
1568         {
1569         //# Iterate through the children
1570         for (Inkscape::XML::Node *child = node->firstChild() ; child ; child = child->next())
1571             {
1572             if (!writeTree(outs, child))
1573                 return false;
1574             }
1575         return true;
1576         }
1577     else if (nodeName == "g" || nodeName == "svg:g")
1578         {
1579         if (id.size() > 0)
1580             outs.printf("<draw:g id=\"%s\">\n", id.c_str());
1581         else
1582             outs.printf("<draw:g>\n");
1583         //# Iterate through the children
1584         for (Inkscape::XML::Node *child = node->firstChild() ; child ; child = child->next())
1585             {
1586             if (!writeTree(outs, child))
1587                 return false;
1588             }
1589         if (id.size() > 0)
1590             outs.printf("</draw:g> <!-- id=\"%s\" -->\n", id.c_str());
1591         else
1592             outs.printf("</draw:g>\n");
1593         return true;
1594         }
1595     else if (nodeName == "image" || nodeName == "svg:image")
1596         {
1597         if (!SP_IS_IMAGE(item))
1598             {
1599             g_warning("<image> is not an SPImage.  Why?  ;-)");
1600             return false;
1601             }
1603         SPImage *img   = SP_IMAGE(item);
1604         double ix      = img->x.value;
1605         double iy      = img->y.value;
1606         double iwidth  = img->width.value;
1607         double iheight = img->height.value;
1609         NR::Rect ibbox(NR::Point(ix, iy), NR::Point(ix+iwidth, iy+iheight));
1610         ibbox = ibbox * tf;
1611         ix      = ibbox.min()[NR::X];
1612         iy      = ibbox.min()[NR::Y];
1613         //iwidth  = ibbox.max()[NR::X] - ibbox.min()[NR::X];
1614         //iheight = ibbox.max()[NR::Y] - ibbox.min()[NR::Y];
1615         iwidth  = xscale * iwidth;
1616         iheight = yscale * iheight;
1618         NR::Matrix itemTransform = getODFItemTransform(item);
1620         std::string itemTransformString = formatTransform(itemTransform);
1622         std::string href = getAttribute(node, "xlink:href");
1623         std::map<std::string, std::string>::iterator iter = imageTable.find(href);
1624         if (iter == imageTable.end())
1625             {
1626             g_warning("image '%s' not in table", href.c_str());
1627             return false;
1628             }
1629         std::string newName = iter->second;
1631         outs.printf("<draw:frame ");
1632         if (id.size() > 0)
1633             outs.printf("id=\"%s\" ", id.c_str());
1634         outs.printf("draw:style-name=\"gr1\" draw:text-style-name=\"P1\" draw:layer=\"layout\" ");
1635         //no x or y.  make them the translate transform, last one
1636         outs.printf("svg:width=\"%.3fcm\" svg:height=\"%.3fcm\" ",
1637                                   iwidth, iheight);
1638         if (itemTransformString.size() > 0)
1639             outs.printf("draw:transform=\"%s translate(%.3fcm, %.3fcm)\" ",
1640                 itemTransformString.c_str(), ix, iy);
1642         outs.printf(">\n");
1643         outs.printf("    <draw:image xlink:href=\"%s\" xlink:type=\"simple\"\n",
1644                               newName.c_str());
1645         outs.printf("        xlink:show=\"embed\" xlink:actuate=\"onLoad\">\n");
1646         outs.printf("        <text:p/>\n");
1647         outs.printf("    </draw:image>\n");
1648         outs.printf("</draw:frame>\n");
1649         return true;
1650         }
1651     else if (SP_IS_SHAPE(item))
1652         {
1653         //g_message("### %s is a shape", nodeName.c_str());
1654         curve = sp_shape_get_curve(SP_SHAPE(item));
1655         }
1656     else if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))
1657         {
1658         curve = te_get_layout(item)->convertToCurves();
1659         }
1661     if (curve)
1662         {
1663         //### Default <path> output
1665         outs.printf("<draw:path ");
1666         if (id.size()>0)
1667             outs.printf("id=\"%s\" ", id.c_str());
1669         std::map<std::string, std::string>::iterator siter;
1670         siter = styleLookupTable.find(id);
1671         if (siter != styleLookupTable.end())
1672             {
1673             std::string styleName = siter->second;
1674             outs.printf("draw:style-name=\"%s\" ", styleName.c_str());
1675             }
1677         std::map<std::string, std::string>::iterator giter;
1678         giter = gradientLookupTable.find(id);
1679         if (giter != gradientLookupTable.end())
1680             {
1681             std::string gradientName = giter->second;
1682             outs.printf("draw:fill-gradient-name=\"%s\" ",
1683                  gradientName.c_str());
1684             }
1686         outs.printf("draw:layer=\"layout\" svg:x=\"%.3fcm\" svg:y=\"%.3fcm\" ",
1687                        bbox_x, bbox_y);
1688         outs.printf("svg:width=\"%.3fcm\" svg:height=\"%.3fcm\" ",
1689                        bbox_width, bbox_height);
1690         outs.printf("svg:viewBox=\"0.0 0.0 %.3f %.3f\"\n",
1691                        bbox_width * 1000.0, bbox_height * 1000.0);
1693         outs.printf("    svg:d=\"");
1694         writePath(outs, SP_CURVE_BPATH(curve), tf, bbox_x, bbox_y);
1695         outs.printf("\"");
1697         outs.printf(">\n");
1698         outs.printf("</draw:path>\n");
1701         sp_curve_unref(curve);
1702         }
1704     return true;
1709 /**
1710  * Write the content.xml file.  Writes the namesspace headers, then
1711  * calls writeStyle() and writeTree().
1712  */
1713 bool OdfOutput::writeContent(ZipFile &zf, Inkscape::XML::Node *node)
1715     BufferOutputStream bouts;
1716     OutputStreamWriter outs(bouts);
1718     time_t tim;
1719     time(&tim);
1721     outs.printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1722     outs.printf("\n");
1723     outs.printf("\n");
1724     outs.printf("<!--\n");
1725     outs.printf("*************************************************************************\n");
1726     outs.printf("  file:  content.xml\n");
1727     outs.printf("  Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
1728     outs.printf("  http://www.inkscape.org\n");
1729     outs.printf("*************************************************************************\n");
1730     outs.printf("-->\n");
1731     outs.printf("\n");
1732     outs.printf("\n");
1733     outs.printf("<office:document-content\n");
1734     outs.printf("    xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
1735     outs.printf("    xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"\n");
1736     outs.printf("    xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"\n");
1737     outs.printf("    xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"\n");
1738     outs.printf("    xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"\n");
1739     outs.printf("    xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"\n");
1740     outs.printf("    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
1741     outs.printf("    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
1742     outs.printf("    xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
1743     outs.printf("    xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"\n");
1744     outs.printf("    xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
1745     outs.printf("    xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"\n");
1746     outs.printf("    xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"\n");
1747     outs.printf("    xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"\n");
1748     outs.printf("    xmlns:math=\"http://www.w3.org/1998/Math/MathML\"\n");
1749     outs.printf("    xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"\n");
1750     outs.printf("    xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"\n");
1751     outs.printf("    xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
1752     outs.printf("    xmlns:ooow=\"http://openoffice.org/2004/writer\"\n");
1753     outs.printf("    xmlns:oooc=\"http://openoffice.org/2004/calc\"\n");
1754     outs.printf("    xmlns:dom=\"http://www.w3.org/2001/xml-events\"\n");
1755     outs.printf("    xmlns:xforms=\"http://www.w3.org/2002/xforms\"\n");
1756     outs.printf("    xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n");
1757     outs.printf("    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
1758     outs.printf("    xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
1759     outs.printf("    xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
1760     outs.printf("    office:version=\"1.0\">\n");
1761     outs.printf("\n");
1762     outs.printf("\n");
1763     outs.printf("<office:scripts/>\n");
1764     outs.printf("\n");
1765     outs.printf("\n");
1766     outs.printf("<!-- ######### CONVERSION FROM SVG STARTS ######## -->\n");
1767     outs.printf("<!--\n");
1768     outs.printf("*************************************************************************\n");
1769     outs.printf("  S T Y L E S\n");
1770     outs.printf("  Style entries have been pulled from the svg style and\n");
1771     outs.printf("  representation attributes in the SVG tree.  The tree elements\n");
1772     outs.printf("  then refer to them by name, in the ODF manner\n");
1773     outs.printf("*************************************************************************\n");
1774     outs.printf("-->\n");
1775     outs.printf("\n");
1776     outs.printf("\n");
1778     if (!writeStyle(outs))
1779         {
1780         g_warning("Failed to write styles");
1781         return false;
1782         }
1784     outs.printf("\n");
1785     outs.printf("\n");
1786     outs.printf("\n");
1787     outs.printf("\n");
1788     outs.printf("<!--\n");
1789     outs.printf("*************************************************************************\n");
1790     outs.printf("  D R A W I N G\n");
1791     outs.printf("  This section is the heart of SVG-ODF conversion.  We are\n");
1792     outs.printf("  starting with simple conversions, and will slowly evolve\n");
1793     outs.printf("  into a 'smarter' translation as time progresses.  Any help\n");
1794     outs.printf("  in improving .odg export is welcome.\n");
1795     outs.printf("*************************************************************************\n");
1796     outs.printf("-->\n");
1797     outs.printf("\n");
1798     outs.printf("\n");
1799     outs.printf("<office:body>\n");
1800     outs.printf("<office:drawing>\n");
1801     outs.printf("<draw:page draw:name=\"page1\" draw:style-name=\"dp1\"\n");
1802     outs.printf("        draw:master-page-name=\"Default\">\n");
1803     outs.printf("\n");
1804     outs.printf("\n");
1806     if (!writeTree(outs, node))
1807         {
1808         g_warning("Failed to convert SVG tree");
1809         return false;
1810         }
1812     outs.printf("\n");
1813     outs.printf("\n");
1815     outs.printf("</draw:page>\n");
1816     outs.printf("</office:drawing>\n");
1818     outs.printf("\n");
1819     outs.printf("\n");
1820     outs.printf("<!-- ######### CONVERSION FROM SVG ENDS ######## -->\n");
1821     outs.printf("\n");
1822     outs.printf("\n");
1824     outs.printf("</office:body>\n");
1825     outs.printf("</office:document-content>\n");
1826     outs.printf("\n");
1827     outs.printf("\n");
1828     outs.printf("\n");
1829     outs.printf("<!--\n");
1830     outs.printf("*************************************************************************\n");
1831     outs.printf("  E N D    O F    F I L E\n");
1832     outs.printf("  Have a nice day  - ishmal\n");
1833     outs.printf("*************************************************************************\n");
1834     outs.printf("-->\n");
1835     outs.printf("\n");
1836     outs.printf("\n");
1840     //Make our entry
1841     ZipEntry *ze = zf.newEntry("content.xml", "ODF master content file");
1842     ze->setUncompressedData(bouts.getBuffer());
1843     ze->finish();
1845     return true;
1849 /**
1850  * Resets class to its pristine condition, ready to use again
1851  */
1852 void
1853 OdfOutput::reset()
1855     styleTable.clear();
1856     styleLookupTable.clear();
1857     gradientTable.clear();
1858     gradientLookupTable.clear();
1859     imageTable.clear();
1865 /**
1866  * Descends into the SVG tree, mapping things to ODF when appropriate
1867  */
1868 void
1869 OdfOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *uri)
1871     reset();
1873     //g_message("native file:%s\n", uri);
1874     documentUri = URI(uri);
1876     ZipFile zf;
1877     preprocess(zf, doc->rroot);
1879     if (!writeManifest(zf))
1880         {
1881         g_warning("Failed to write manifest");
1882         return;
1883         }
1885     if (!writeMeta(zf))
1886         {
1887         g_warning("Failed to write metafile");
1888         return;
1889         }
1891     if (!writeContent(zf, doc->rroot))
1892         {
1893         g_warning("Failed to write content");
1894         return;
1895         }
1897     if (!zf.writeFile(uri))
1898         {
1899         return;
1900         }
1904 /**
1905  * This is the definition of PovRay output.  This function just
1906  * calls the extension system with the memory allocated XML that
1907  * describes the data.
1908 */
1909 void
1910 OdfOutput::init()
1912     Inkscape::Extension::build_from_mem(
1913         "<inkscape-extension>\n"
1914             "<name>" N_("OpenDocument Drawing Output") "</name>\n"
1915             "<id>org.inkscape.output.odf</id>\n"
1916             "<output>\n"
1917                 "<extension>.odg</extension>\n"
1918                 "<mimetype>text/x-povray-script</mimetype>\n"
1919                 "<filetypename>" N_("OpenDocument drawing (*.odg)") "</filetypename>\n"
1920                 "<filetypetooltip>" N_("OpenDocument drawing file") "</filetypetooltip>\n"
1921             "</output>\n"
1922         "</inkscape-extension>",
1923         new OdfOutput());
1926 /**
1927  * Make sure that we are in the database
1928  */
1929 bool
1930 OdfOutput::check (Inkscape::Extension::Extension *module)
1932     /* We don't need a Key
1933     if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_POV))
1934         return FALSE;
1935     */
1937     return TRUE;
1942 //########################################################################
1943 //# I N P U T
1944 //########################################################################
1948 //#######################
1949 //# L A T E R  !!!  :-)
1950 //#######################
1964 }  //namespace Internal
1965 }  //namespace Extension
1966 }  //namespace Inkscape
1969 //########################################################################
1970 //# E N D    O F    F I L E
1971 //########################################################################
1973 /*
1974   Local Variables:
1975   mode:c++
1976   c-file-style:"stroustrup"
1977   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1978   indent-tabs-mode:nil
1979   fill-column:99
1980   End:
1981 */
1982 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :