Code

Filters. Some custom predefined filters fixes and tweaks (Silhouette and Neon Draw).
[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  *   Abhishek Sharma
18  *
19  * Copyright (C) 2006, 2007 Bob Jamison
20  *
21  *  This library is free software; you can redistribute it and/or
22  *  modify it under the terms of the GNU Lesser General Public
23  *  License as published by the Free Software Foundation; either
24  *  version 2.1 of the License, or (at your option) any later version.
25  *
26  *  This library is distributed in the hope that it will be useful,
27  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
28  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
29  *  Lesser General Public License for more details.
30  *
31  *  You should have received a copy of the GNU Lesser General Public
32  *  License along with this library; if not, write to the Free Software
33  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
34  */
38 #ifdef HAVE_CONFIG_H
39 # include <config.h>
40 #endif
42 #include "odf.h"
44 //# System includes
45 #include <stdio.h>
46 #include <time.h>
47 #include <vector>
50 //# Inkscape includes
51 #include "clear-n_.h"
52 #include "inkscape.h"
53 #include <style.h>
54 #include "display/curve.h"
55 #include <2geom/pathvector.h>
56 #include <2geom/bezier-curve.h>
57 #include <2geom/hvlinesegment.h>
58 #include <2geom/transforms.h>
59 #include <helper/geom.h>
60 #include "helper/geom-curves.h"
61 #include "extension/system.h"
63 #include "xml/repr.h"
64 #include "xml/attribute-record.h"
65 #include "sp-image.h"
66 #include "sp-gradient.h"
67 #include "sp-stop.h"
68 #include "gradient-chemistry.h"
69 #include "sp-linear-gradient.h"
70 #include "sp-radial-gradient.h"
71 #include "sp-path.h"
72 #include "sp-text.h"
73 #include "sp-flowtext.h"
74 #include "svg/svg.h"
75 #include "text-editing.h"
78 //# DOM-specific includes
79 #include "dom/dom.h"
80 #include "dom/util/ziptool.h"
81 #include "dom/io/domstream.h"
82 #include "dom/io/bufferstream.h"
83 #include "dom/io/stringstream.h"
90 namespace Inkscape
91 {
92 namespace Extension
93 {
94 namespace Internal
95 {
99 //# Shorthand notation
100 typedef org::w3c::dom::DOMString DOMString;
101 typedef org::w3c::dom::XMLCh XMLCh;
102 typedef org::w3c::dom::io::OutputStreamWriter OutputStreamWriter;
103 typedef org::w3c::dom::io::BufferOutputStream BufferOutputStream;
104 typedef org::w3c::dom::io::StringOutputStream StringOutputStream;
106 //########################################################################
107 //# C L A S S    SingularValueDecomposition
108 //########################################################################
109 #include <math.h>
111 class SVDMatrix
113 public:
115     SVDMatrix()
116         {
117         init();
118         }
120     SVDMatrix(unsigned int rowSize, unsigned int colSize)
121         {
122         init();
123         rows = rowSize;
124         cols = colSize;
125         size = rows * cols;
126         d    = new double[size];
127         for (unsigned int i=0 ; i<size ; i++)
128             d[i] = 0.0;
129         }
131     SVDMatrix(double *vals, unsigned int rowSize, unsigned int colSize)
132         {
133         init();
134         rows = rowSize;
135         cols = colSize;
136         size = rows * cols;
137         d    = new double[size];
138         for (unsigned int i=0 ; i<size ; i++)
139             d[i] = vals[i];
140         }
143     SVDMatrix(const SVDMatrix &other)
144         {
145         init();
146         assign(other);
147         }
149     SVDMatrix &operator=(const SVDMatrix &other)
150         {
151         assign(other);
152         return *this;
153         }
155     virtual ~SVDMatrix()
156         {
157         delete[] d;
158         }
160      double& operator() (unsigned int row, unsigned int col)
161          {
162          if (row >= rows || col >= cols)
163              return badval;
164          return d[cols*row + col];
165          }
167      double operator() (unsigned int row, unsigned int col) const
168          {
169          if (row >= rows || col >= cols)
170              return badval;
171          return d[cols*row + col];
172          }
174      unsigned int getRows()
175          {
176          return rows;
177          }
179      unsigned int getCols()
180          {
181          return cols;
182          }
184      SVDMatrix multiply(const SVDMatrix &other)
185          {
186          if (cols != other.rows)
187              {
188              SVDMatrix dummy;
189              return dummy;
190              }
191          SVDMatrix result(rows, other.cols);
192          for (unsigned int i=0 ; i<rows ; i++)
193              {
194              for (unsigned int j=0 ; j<other.cols ; j++)
195              {
196                  double sum = 0.0;
197                  for (unsigned int k=0 ; k<cols ; k++)
198                      {
199                      //sum += a[i][k] * b[k][j];
200                      sum += d[i*cols +k] * other(k, j);
201                      }
202                  result(i, j) = sum;
203                  }
205              }
206          return result;
207          }
209      SVDMatrix transpose()
210          {
211          SVDMatrix result(cols, rows);
212          for (unsigned int i=0 ; i<rows ; i++)
213              for (unsigned int j=0 ; j<cols ; j++)
214                  result(j, i) = d[i*cols + j];
215          return result;
216          }
218 private:
221     virtual void init()
222         {
223         badval = 0.0;
224         d      = NULL;
225         rows   = 0;
226         cols   = 0;
227         size   = 0;
228         }
230      void assign(const SVDMatrix &other)
231         {
232         if (d)
233             {
234             delete[] d;
235             d = 0;
236             }
237         rows = other.rows;
238         cols = other.cols;
239         size = other.size;
240         d = new double[size];
241         for (unsigned int i=0 ; i<size ; i++)
242             d[i] = other.d[i];
243         }
245     double badval;
247     double *d;
248     unsigned int rows;
249     unsigned int cols;
250     unsigned int size;
251 };
255 /**
256  *
257  * ====================================================
258  *
259  * NOTE:
260  * This class is ported almost verbatim from the public domain
261  * JAMA Matrix package.  It is modified to handle only 3x3 matrices
262  * and our Geom::Matrix affine transform class.  We give full
263  * attribution to them, along with many thanks.  JAMA can be found at:
264  *     http://math.nist.gov/javanumerics/jama
265  *
266  * ====================================================
267  *
268  * Singular Value Decomposition.
269  * <P>
270  * For an m-by-n matrix A with m >= n, the singular value decomposition is
271  * an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and
272  * an n-by-n orthogonal matrix V so that A = U*S*V'.
273  * <P>
274  * The singular values, sigma[k] = S[k][k], are ordered so that
275  * sigma[0] >= sigma[1] >= ... >= sigma[n-1].
276  * <P>
277  * The singular value decompostion always exists, so the constructor will
278  * never fail.  The matrix condition number and the effective numerical
279  * rank can be computed from this decomposition.
280  */
281 class SingularValueDecomposition
283 public:
285    /** Construct the singular value decomposition
286    @param A    Rectangular matrix
287    @return     Structure to access U, S and V.
288    */
290     SingularValueDecomposition (const SVDMatrix &mat)
291         {
292         A      = mat;
293         s      = NULL;
294         s_size = 0;
295         calculate();
296         }
298     virtual ~SingularValueDecomposition()
299         {
300         delete[] s;
301         }
303     /**
304      * Return the left singular vectors
305      * @return     U
306      */
307     SVDMatrix &getU();
309     /**
310      * Return the right singular vectors
311      * @return     V
312      */
313     SVDMatrix &getV();
315     /**
316      *  Return the s[index] value
317      */    double getS(unsigned int index);
319     /**
320      * Two norm
321      * @return max(S)
322      */
323     double norm2();
325     /**
326      * Two norm condition number
327      *  @return max(S)/min(S)
328      */
329     double cond();
331     /**
332      *  Effective numerical matrix rank
333      *  @return     Number of nonnegligible singular values.
334      */
335     int rank();
337 private:
339       void calculate();
341       SVDMatrix A;
342       SVDMatrix U;
343       double *s;
344       unsigned int s_size;
345       SVDMatrix V;
347 };
350 static double svd_hypot(double a, double b)
352     double r;
354     if (fabs(a) > fabs(b))
355         {
356         r = b/a;
357         r = fabs(a) * sqrt(1+r*r);
358         }
359     else if (b != 0)
360         {
361         r = a/b;
362         r = fabs(b) * sqrt(1+r*r);
363         }
364     else
365         {
366         r = 0.0;
367         }
368     return r;
373 void SingularValueDecomposition::calculate()
375       // Initialize.
376       int m = A.getRows();
377       int n = A.getCols();
379       int nu = (m > n) ? m : n;
380       s_size = (m+1 < n) ? m+1 : n;
381       s = new double[s_size];
382       U = SVDMatrix(m, nu);
383       V = SVDMatrix(n, n);
384       double *e = new double[n];
385       double *work = new double[m];
386       bool wantu = true;
387       bool wantv = true;
389       // Reduce A to bidiagonal form, storing the diagonal elements
390       // in s and the super-diagonal elements in e.
392       int nct = (m-1<n) ? m-1 : n;
393       int nrtx = (n-2<m) ? n-2 : m;
394       int nrt = (nrtx>0) ? nrtx : 0;
395       for (int k = 0; k < 2; k++) {
396          if (k < nct) {
398             // Compute the transformation for the k-th column and
399             // place the k-th diagonal in s[k].
400             // Compute 2-norm of k-th column without under/overflow.
401             s[k] = 0;
402             for (int i = k; i < m; i++) {
403                s[k] = svd_hypot(s[k],A(i, k));
404             }
405             if (s[k] != 0.0) {
406                if (A(k, k) < 0.0) {
407                   s[k] = -s[k];
408                }
409                for (int i = k; i < m; i++) {
410                   A(i, k) /= s[k];
411                }
412                A(k, k) += 1.0;
413             }
414             s[k] = -s[k];
415          }
416          for (int j = k+1; j < n; j++) {
417             if ((k < nct) & (s[k] != 0.0))  {
419             // Apply the transformation.
421                double t = 0;
422                for (int i = k; i < m; i++) {
423                   t += A(i, k) * A(i, j);
424                }
425                t = -t/A(k, k);
426                for (int i = k; i < m; i++) {
427                   A(i, j) += t*A(i, k);
428                }
429             }
431             // Place the k-th row of A into e for the
432             // subsequent calculation of the row transformation.
434             e[j] = A(k, j);
435          }
436          if (wantu & (k < nct)) {
438             // Place the transformation in U for subsequent back
439             // multiplication.
441             for (int i = k; i < m; i++) {
442                U(i, k) = A(i, k);
443             }
444          }
445          if (k < nrt) {
447             // Compute the k-th row transformation and place the
448             // k-th super-diagonal in e[k].
449             // Compute 2-norm without under/overflow.
450             e[k] = 0;
451             for (int i = k+1; i < n; i++) {
452                e[k] = svd_hypot(e[k],e[i]);
453             }
454             if (e[k] != 0.0) {
455                if (e[k+1] < 0.0) {
456                   e[k] = -e[k];
457                }
458                for (int i = k+1; i < n; i++) {
459                   e[i] /= e[k];
460                }
461                e[k+1] += 1.0;
462             }
463             e[k] = -e[k];
464             if ((k+1 < m) & (e[k] != 0.0)) {
466             // Apply the transformation.
468                for (int i = k+1; i < m; i++) {
469                   work[i] = 0.0;
470                }
471                for (int j = k+1; j < n; j++) {
472                   for (int i = k+1; i < m; i++) {
473                      work[i] += e[j]*A(i, j);
474                   }
475                }
476                for (int j = k+1; j < n; j++) {
477                   double t = -e[j]/e[k+1];
478                   for (int i = k+1; i < m; i++) {
479                      A(i, j) += t*work[i];
480                   }
481                }
482             }
483             if (wantv) {
485             // Place the transformation in V for subsequent
486             // back multiplication.
488                for (int i = k+1; i < n; i++) {
489                   V(i, k) = e[i];
490                }
491             }
492          }
493       }
495       // Set up the final bidiagonal matrix or order p.
497       int p = (n < m+1) ? n : m+1;
498       if (nct < n) {
499          s[nct] = A(nct, nct);
500       }
501       if (m < p) {
502          s[p-1] = 0.0;
503       }
504       if (nrt+1 < p) {
505          e[nrt] = A(nrt, p-1);
506       }
507       e[p-1] = 0.0;
509       // If required, generate U.
511       if (wantu) {
512          for (int j = nct; j < nu; j++) {
513             for (int i = 0; i < m; i++) {
514                U(i, j) = 0.0;
515             }
516             U(j, j) = 1.0;
517          }
518          for (int k = nct-1; k >= 0; k--) {
519             if (s[k] != 0.0) {
520                for (int j = k+1; j < nu; j++) {
521                   double t = 0;
522                   for (int i = k; i < m; i++) {
523                      t += U(i, k)*U(i, j);
524                   }
525                   t = -t/U(k, k);
526                   for (int i = k; i < m; i++) {
527                      U(i, j) += t*U(i, k);
528                   }
529                }
530                for (int i = k; i < m; i++ ) {
531                   U(i, k) = -U(i, k);
532                }
533                U(k, k) = 1.0 + U(k, k);
534                for (int i = 0; i < k-1; i++) {
535                   U(i, k) = 0.0;
536                }
537             } else {
538                for (int i = 0; i < m; i++) {
539                   U(i, k) = 0.0;
540                }
541                U(k, k) = 1.0;
542             }
543          }
544       }
546       // If required, generate V.
548       if (wantv) {
549          for (int k = n-1; k >= 0; k--) {
550             if ((k < nrt) & (e[k] != 0.0)) {
551                for (int j = k+1; j < nu; j++) {
552                   double t = 0;
553                   for (int i = k+1; i < n; i++) {
554                      t += V(i, k)*V(i, j);
555                   }
556                   t = -t/V(k+1, k);
557                   for (int i = k+1; i < n; i++) {
558                      V(i, j) += t*V(i, k);
559                   }
560                }
561             }
562             for (int i = 0; i < n; i++) {
563                V(i, k) = 0.0;
564             }
565             V(k, k) = 1.0;
566          }
567       }
569       // Main iteration loop for the singular values.
571       int pp = p-1;
572       int iter = 0;
573       //double eps = pow(2.0,-52.0);
574       //double tiny = pow(2.0,-966.0);
575       //let's just calculate these now
576       //a double can be e Â± 308.25, so this is safe
577       double eps = 2.22e-16;
578       double tiny = 1.6e-291;
579       while (p > 0) {
580          int k,kase;
582          // Here is where a test for too many iterations would go.
584          // This section of the program inspects for
585          // negligible elements in the s and e arrays.  On
586          // completion the variables kase and k are set as follows.
588          // kase = 1     if s(p) and e[k-1] are negligible and k<p
589          // kase = 2     if s(k) is negligible and k<p
590          // kase = 3     if e[k-1] is negligible, k<p, and
591          //              s(k), ..., s(p) are not negligible (qr step).
592          // kase = 4     if e(p-1) is negligible (convergence).
594          for (k = p-2; k >= -1; k--) {
595             if (k == -1) {
596                break;
597             }
598             if (fabs(e[k]) <=
599                   tiny + eps*(fabs(s[k]) + fabs(s[k+1]))) {
600                e[k] = 0.0;
601                break;
602             }
603          }
604          if (k == p-2) {
605             kase = 4;
606          } else {
607             int ks;
608             for (ks = p-1; ks >= k; ks--) {
609                if (ks == k) {
610                   break;
611                }
612                double t = (ks != p ? fabs(e[ks]) : 0.) +
613                           (ks != k+1 ? fabs(e[ks-1]) : 0.);
614                if (fabs(s[ks]) <= tiny + eps*t)  {
615                   s[ks] = 0.0;
616                   break;
617                }
618             }
619             if (ks == k) {
620                kase = 3;
621             } else if (ks == p-1) {
622                kase = 1;
623             } else {
624                kase = 2;
625                k = ks;
626             }
627          }
628          k++;
630          // Perform the task indicated by kase.
632          switch (kase) {
634             // Deflate negligible s(p).
636             case 1: {
637                double f = e[p-2];
638                e[p-2] = 0.0;
639                for (int j = p-2; j >= k; j--) {
640                   double t = svd_hypot(s[j],f);
641                   double cs = s[j]/t;
642                   double sn = f/t;
643                   s[j] = t;
644                   if (j != k) {
645                      f = -sn*e[j-1];
646                      e[j-1] = cs*e[j-1];
647                   }
648                   if (wantv) {
649                      for (int i = 0; i < n; i++) {
650                         t = cs*V(i, j) + sn*V(i, p-1);
651                         V(i, p-1) = -sn*V(i, j) + cs*V(i, p-1);
652                         V(i, j) = t;
653                      }
654                   }
655                }
656             }
657             break;
659             // Split at negligible s(k).
661             case 2: {
662                double f = e[k-1];
663                e[k-1] = 0.0;
664                for (int j = k; j < p; j++) {
665                   double t = svd_hypot(s[j],f);
666                   double cs = s[j]/t;
667                   double sn = f/t;
668                   s[j] = t;
669                   f = -sn*e[j];
670                   e[j] = cs*e[j];
671                   if (wantu) {
672                      for (int i = 0; i < m; i++) {
673                         t = cs*U(i, j) + sn*U(i, k-1);
674                         U(i, k-1) = -sn*U(i, j) + cs*U(i, k-1);
675                         U(i, j) = t;
676                      }
677                   }
678                }
679             }
680             break;
682             // Perform one qr step.
684             case 3: {
686                // Calculate the shift.
688                double scale = fabs(s[p-1]);
689                double d = fabs(s[p-2]);
690                if (d>scale) scale=d;
691                d = fabs(e[p-2]);
692                if (d>scale) scale=d;
693                d = fabs(s[k]);
694                if (d>scale) scale=d;
695                d = fabs(e[k]);
696                if (d>scale) scale=d;
697                double sp = s[p-1]/scale;
698                double spm1 = s[p-2]/scale;
699                double epm1 = e[p-2]/scale;
700                double sk = s[k]/scale;
701                double ek = e[k]/scale;
702                double b = ((spm1 + sp)*(spm1 - sp) + epm1*epm1)/2.0;
703                double c = (sp*epm1)*(sp*epm1);
704                double shift = 0.0;
705                if ((b != 0.0) | (c != 0.0)) {
706                   shift = sqrt(b*b + c);
707                   if (b < 0.0) {
708                      shift = -shift;
709                   }
710                   shift = c/(b + shift);
711                }
712                double f = (sk + sp)*(sk - sp) + shift;
713                double g = sk*ek;
715                // Chase zeros.
717                for (int j = k; j < p-1; j++) {
718                   double t = svd_hypot(f,g);
719                   double cs = f/t;
720                   double sn = g/t;
721                   if (j != k) {
722                      e[j-1] = t;
723                   }
724                   f = cs*s[j] + sn*e[j];
725                   e[j] = cs*e[j] - sn*s[j];
726                   g = sn*s[j+1];
727                   s[j+1] = cs*s[j+1];
728                   if (wantv) {
729                      for (int i = 0; i < n; i++) {
730                         t = cs*V(i, j) + sn*V(i, j+1);
731                         V(i, j+1) = -sn*V(i, j) + cs*V(i, j+1);
732                         V(i, j) = t;
733                      }
734                   }
735                   t = svd_hypot(f,g);
736                   cs = f/t;
737                   sn = g/t;
738                   s[j] = t;
739                   f = cs*e[j] + sn*s[j+1];
740                   s[j+1] = -sn*e[j] + cs*s[j+1];
741                   g = sn*e[j+1];
742                   e[j+1] = cs*e[j+1];
743                   if (wantu && (j < m-1)) {
744                      for (int i = 0; i < m; i++) {
745                         t = cs*U(i, j) + sn*U(i, j+1);
746                         U(i, j+1) = -sn*U(i, j) + cs*U(i, j+1);
747                         U(i, j) = t;
748                      }
749                   }
750                }
751                e[p-2] = f;
752                iter = iter + 1;
753             }
754             break;
756             // Convergence.
758             case 4: {
760                // Make the singular values positive.
762                if (s[k] <= 0.0) {
763                   s[k] = (s[k] < 0.0 ? -s[k] : 0.0);
764                   if (wantv) {
765                      for (int i = 0; i <= pp; i++) {
766                         V(i, k) = -V(i, k);
767                      }
768                   }
769                }
771                // Order the singular values.
773                while (k < pp) {
774                   if (s[k] >= s[k+1]) {
775                      break;
776                   }
777                   double t = s[k];
778                   s[k] = s[k+1];
779                   s[k+1] = t;
780                   if (wantv && (k < n-1)) {
781                      for (int i = 0; i < n; i++) {
782                         t = V(i, k+1); V(i, k+1) = V(i, k); V(i, k) = t;
783                      }
784                   }
785                   if (wantu && (k < m-1)) {
786                      for (int i = 0; i < m; i++) {
787                         t = U(i, k+1); U(i, k+1) = U(i, k); U(i, k) = t;
788                      }
789                   }
790                   k++;
791                }
792                iter = 0;
793                p--;
794             }
795             break;
796          }
797       }
799     delete [] e;
800     delete [] work;
806 /**
807  * Return the left singular vectors
808  * @return     U
809  */
810 SVDMatrix &SingularValueDecomposition::getU()
812     return U;
815 /**
816  * Return the right singular vectors
817  * @return     V
818  */
820 SVDMatrix &SingularValueDecomposition::getV()
822     return V;
825 /**
826  *  Return the s[0] value
827  */
828 double SingularValueDecomposition::getS(unsigned int index)
830     if (index >= s_size)
831         return 0.0;
832     return s[index];
835 /**
836  * Two norm
837  * @return     max(S)
838  */
839 double SingularValueDecomposition::norm2()
841     return s[0];
844 /**
845  * Two norm condition number
846  *  @return     max(S)/min(S)
847  */
849 double SingularValueDecomposition::cond()
851     return s[0]/s[2];
854 /**
855  *  Effective numerical matrix rank
856  *  @return     Number of nonnegligible singular values.
857  */
858 int SingularValueDecomposition::rank()
860     double eps = pow(2.0,-52.0);
861     double tol = 3.0*s[0]*eps;
862     int r = 0;
863     for (int i = 0; i < 3; i++)
864         {
865         if (s[i] > tol)
866             r++;
867         }
868     return r;
871 //########################################################################
872 //# E N D    C L A S S    SingularValueDecomposition
873 //########################################################################
879 #define pi 3.14159
880 //#define pxToCm  0.0275
881 #define pxToCm  0.03
882 #define piToRad 0.0174532925
883 #define docHeightCm 22.86
886 //########################################################################
887 //# O U T P U T
888 //########################################################################
890 /**
891  * Get the value of a node/attribute pair
892  */
893 static Glib::ustring getAttribute( Inkscape::XML::Node *node, char const *attrName)
895     Glib::ustring val;
896     char const *valstr = node->attribute(attrName);
897     if (valstr)
898         val = valstr;
899     return val;
904 /**
905  * Get the extension suffix from the end of a file name
906  */
907 static Glib::ustring getExtension(const Glib::ustring &fname)
909     Glib::ustring ext;
911     std::string::size_type pos = fname.rfind('.');
912     if (pos == fname.npos)
913         {
914         ext = "";
915         }
916     else
917         {
918         ext = fname.substr(pos);
919         }
920     return ext;
924 static Glib::ustring formatTransform(Geom::Matrix &tf)
926     Glib::ustring str;
927     if (!tf.isIdentity())
928         {
929         StringOutputStream outs;
930         OutputStreamWriter out(outs);
931         out.printf("matrix(%.3f %.3f %.3f %.3f %.3f %.3f)",
932                 tf[0], tf[1], tf[2], tf[3], tf[4], tf[5]);
933         str = outs.getString();
934         }
935     return str;
942 /**
943  * Get the general transform from SVG pixels to
944  * ODF cm
945  */
946 static Geom::Matrix getODFTransform(const SPItem *item)
948     //### Get SVG-to-ODF transform
949     Geom::Matrix tf (item->i2d_affine());
950     //Flip Y into document coordinates
951     double doc_height    = SP_ACTIVE_DOCUMENT->getHeight();
952     Geom::Matrix doc2dt_tf = Geom::Matrix(Geom::Scale(1.0, -1.0));
953     doc2dt_tf            = doc2dt_tf * Geom::Matrix(Geom::Translate(0, doc_height));
954     tf                   = tf * doc2dt_tf;
955     tf                   = tf * Geom::Matrix(Geom::Scale(pxToCm));
956     return tf;
962 /**
963  * Get the bounding box of an item, as mapped onto
964  * an ODF document, in cm.
965  */
966 static Geom::OptRect getODFBoundingBox(const SPItem *item)
968     Geom::OptRect bbox_temp = ((SPItem *)item)->getBboxDesktop();
969     Geom::OptRect bbox;
970     if (bbox_temp) {
971         bbox = *bbox_temp;
972         double doc_height    = SP_ACTIVE_DOCUMENT->getHeight();
973         Geom::Matrix doc2dt_tf = Geom::Matrix(Geom::Scale(1.0, -1.0));
974         doc2dt_tf            = doc2dt_tf * Geom::Matrix(Geom::Translate(0, doc_height));
975         bbox                 = *bbox * doc2dt_tf;
976         bbox                 = *bbox * Geom::Matrix(Geom::Scale(pxToCm));
977     }
978     return bbox;
983 /**
984  * Get the transform for an item, correcting for
985  * handedness reversal
986  */
987 static Geom::Matrix getODFItemTransform(const SPItem *item)
989     Geom::Matrix itemTransform (Geom::Scale(1, -1));
990     itemTransform = itemTransform * (Geom::Matrix)item->transform;
991     itemTransform = itemTransform * Geom::Scale(1, -1);
992     return itemTransform;
997 /**
998  * Get some fun facts from the transform
999  */
1000 static void analyzeTransform(Geom::Matrix &tf,
1001                              double &rotate, double &/*xskew*/, double &/*yskew*/,
1002                              double &xscale, double &yscale)
1004     SVDMatrix mat(2, 2);
1005     mat(0, 0) = tf[0];
1006     mat(0, 1) = tf[1];
1007     mat(1, 0) = tf[2];
1008     mat(1, 1) = tf[3];
1010     SingularValueDecomposition svd(mat);
1012     SVDMatrix U = svd.getU();
1013     SVDMatrix V = svd.getV();
1014     SVDMatrix Vt = V.transpose();
1015     SVDMatrix UVt = U.multiply(Vt);
1016     double s0 = svd.getS(0);
1017     double s1 = svd.getS(1);
1018     xscale = s0;
1019     yscale = s1;
1020     //g_message("## s0:%.3f s1:%.3f", s0, s1);
1021     //g_message("## u:%.3f %.3f %.3f %.3f", U(0,0), U(0,1), U(1,0), U(1,1));
1022     //g_message("## v:%.3f %.3f %.3f %.3f", V(0,0), V(0,1), V(1,0), V(1,1));
1023     //g_message("## vt:%.3f %.3f %.3f %.3f", Vt(0,0), Vt(0,1), Vt(1,0), Vt(1,1));
1024     //g_message("## uvt:%.3f %.3f %.3f %.3f", UVt(0,0), UVt(0,1), UVt(1,0), UVt(1,1));
1025     rotate = UVt(0,0);
1030 static void gatherText(Inkscape::XML::Node *node, Glib::ustring &buf)
1032     if (node->type() == Inkscape::XML::TEXT_NODE)
1033         {
1034         char *s = (char *)node->content();
1035         if (s)
1036             buf.append(s);
1037         }
1039     for (Inkscape::XML::Node *child = node->firstChild() ;
1040                 child != NULL; child = child->next())
1041         {
1042         gatherText(child, buf);
1043         }
1047 /**
1048  * FIRST PASS.
1049  * Method descends into the repr tree, converting image, style, and gradient info
1050  * into forms compatible in ODF.
1051  */
1052 void
1053 OdfOutput::preprocess(ZipFile &zf, Inkscape::XML::Node *node)
1056     Glib::ustring nodeName = node->name();
1057     Glib::ustring id       = getAttribute(node, "id");
1059     //### First, check for metadata
1060     if (nodeName == "metadata" || nodeName == "svg:metadata")
1061         {
1062         Inkscape::XML::Node *mchild = node->firstChild() ;
1063         if (!mchild || strcmp(mchild->name(), "rdf:RDF"))
1064             return;
1065         Inkscape::XML::Node *rchild = mchild->firstChild() ;
1066         if (!rchild || strcmp(rchild->name(), "cc:Work"))
1067             return;
1068         for (Inkscape::XML::Node *cchild = rchild->firstChild() ;
1069             cchild ; cchild = cchild->next())
1070             {
1071             Glib::ustring ccName = cchild->name();
1072             Glib::ustring ccVal;
1073             gatherText(cchild, ccVal);
1074             //g_message("ccName: %s  ccVal:%s", ccName.c_str(), ccVal.c_str());
1075             metadata[ccName] = ccVal;
1076             }
1077         return;
1078         }
1080     //Now consider items.
1081     SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(node);
1082     if (!reprobj)
1083         return;
1084     if (!SP_IS_ITEM(reprobj))
1085         {
1086         return;
1087         }
1088     SPItem *item  = SP_ITEM(reprobj);
1089     //### Get SVG-to-ODF transform
1090     Geom::Matrix tf = getODFTransform(item);
1092     if (nodeName == "image" || nodeName == "svg:image")
1093         {
1094         //g_message("image");
1095         Glib::ustring href = getAttribute(node, "xlink:href");
1096         if (href.size() > 0)
1097             {
1098             Glib::ustring oldName = href;
1099             Glib::ustring ext = getExtension(oldName);
1100             if (ext == ".jpeg")
1101                 ext = ".jpg";
1102             if (imageTable.find(oldName) == imageTable.end())
1103                 {
1104                 char buf[64];
1105                 snprintf(buf, sizeof(buf), "Pictures/image%u%s",
1106                          static_cast<unsigned int>(imageTable.size()), ext.c_str());
1107                 Glib::ustring newName = buf;
1108                 imageTable[oldName] = newName;
1109                 Glib::ustring comment = "old name was: ";
1110                 comment.append(oldName);
1111                 URI oldUri(oldName);
1112                 //g_message("oldpath:%s", oldUri.getNativePath().c_str());
1113                 //# if relative to the documentURI, get proper path
1114                 URI resUri = documentUri.resolve(oldUri);
1115                 DOMString pathName = resUri.getNativePath();
1116                 //g_message("native path:%s", pathName.c_str());
1117                 ZipEntry *ze = zf.addFile(pathName, comment);
1118                 if (ze)
1119                     {
1120                     ze->setFileName(newName);
1121                     }
1122                 else
1123                     {
1124                     g_warning("Could not load image file '%s'", pathName.c_str());
1125                     }
1126                 }
1127             }
1128         }
1130     for (Inkscape::XML::Node *child = node->firstChild() ;
1131             child ; child = child->next())
1132         preprocess(zf, child);
1137 /**
1138  * Writes the manifest.  Currently it only changes according to the
1139  * file names of images packed into the zip file.
1140  */
1141 bool OdfOutput::writeManifest(ZipFile &zf)
1143     BufferOutputStream bouts;
1144     OutputStreamWriter outs(bouts);
1146     time_t tim;
1147     time(&tim);
1149     outs.printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1150     outs.printf("<!DOCTYPE manifest:manifest PUBLIC \"-//OpenOffice.org//DTD Manifest 1.0//EN\" \"Manifest.dtd\">\n");
1151     outs.printf("\n");
1152     outs.printf("\n");
1153     outs.printf("<!--\n");
1154     outs.printf("*************************************************************************\n");
1155     outs.printf("  file:  manifest.xml\n");
1156     outs.printf("  Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
1157     outs.printf("  http://www.inkscape.org\n");
1158     outs.printf("*************************************************************************\n");
1159     outs.printf("-->\n");
1160     outs.printf("\n");
1161     outs.printf("\n");
1162     outs.printf("<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\">\n");
1163     outs.printf("    <manifest:file-entry manifest:media-type=\"application/vnd.oasis.opendocument.graphics\" manifest:full-path=\"/\"/>\n");
1164     outs.printf("    <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/>\n");
1165     outs.printf("    <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"styles.xml\"/>\n");
1166     outs.printf("    <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"meta.xml\"/>\n");
1167     outs.printf("    <!--List our images here-->\n");
1168     std::map<Glib::ustring, Glib::ustring>::iterator iter;
1169     for (iter = imageTable.begin() ; iter!=imageTable.end() ; iter++)
1170         {
1171         Glib::ustring oldName = iter->first;
1172         Glib::ustring newName = iter->second;
1174         Glib::ustring ext = getExtension(oldName);
1175         if (ext == ".jpeg")
1176             ext = ".jpg";
1177         outs.printf("    <manifest:file-entry manifest:media-type=\"");
1178         if (ext == ".gif")
1179             outs.printf("image/gif");
1180         else if (ext == ".png")
1181             outs.printf("image/png");
1182         else if (ext == ".jpg")
1183             outs.printf("image/jpeg");
1184         outs.printf("\" manifest:full-path=\"");
1185         outs.printf(newName.c_str());
1186         outs.printf("\"/>\n");
1187         }
1188     outs.printf("</manifest:manifest>\n");
1190     outs.close();
1192     //Make our entry
1193     ZipEntry *ze = zf.newEntry("META-INF/manifest.xml", "ODF file manifest");
1194     ze->setUncompressedData(bouts.getBuffer());
1195     ze->finish();
1197     return true;
1201 /**
1202  * This writes the document meta information to meta.xml
1203  */
1204 bool OdfOutput::writeMeta(ZipFile &zf)
1206     BufferOutputStream bouts;
1207     OutputStreamWriter outs(bouts);
1209     time_t tim;
1210     time(&tim);
1212     std::map<Glib::ustring, Glib::ustring>::iterator iter;
1213     Glib::ustring creator = "unknown";
1214     iter = metadata.find("dc:creator");
1215     if (iter != metadata.end())
1216         creator = iter->second;
1217     Glib::ustring date = "";
1218     iter = metadata.find("dc:date");
1219     if (iter != metadata.end())
1220         date = iter->second;
1222     outs.printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1223     outs.printf("\n");
1224     outs.printf("\n");
1225     outs.printf("<!--\n");
1226     outs.printf("*************************************************************************\n");
1227     outs.printf("  file:  meta.xml\n");
1228     outs.printf("  Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
1229     outs.printf("  http://www.inkscape.org\n");
1230     outs.printf("*************************************************************************\n");
1231     outs.printf("-->\n");
1232     outs.printf("\n");
1233     outs.printf("\n");
1234     outs.printf("<office:document-meta\n");
1235     outs.printf("xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
1236     outs.printf("xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
1237     outs.printf("xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
1238     outs.printf("xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
1239     outs.printf("xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
1240     outs.printf("xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
1241     outs.printf("xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
1242     outs.printf("xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
1243     outs.printf("office:version=\"1.0\">\n");
1244     outs.printf("<office:meta>\n");
1245     outs.printf("    <meta:generator>Inkscape.org - 0.45</meta:generator>\n");
1246     outs.printf("    <meta:initial-creator>%#s</meta:initial-creator>\n",
1247                                   creator.c_str());
1248     outs.printf("    <meta:creation-date>%#s</meta:creation-date>\n", date.c_str());
1249     for (iter = metadata.begin() ; iter != metadata.end() ; iter++)
1250         {
1251         Glib::ustring name  = iter->first;
1252         Glib::ustring value = iter->second;
1253         if (name.size() > 0 && value.size()>0)
1254             {
1255             outs.printf("    <%#s>%#s</%#s>\n",
1256                       name.c_str(), value.c_str(), name.c_str());
1257             }
1258         }
1259     outs.printf("    <meta:editing-cycles>2</meta:editing-cycles>\n");
1260     outs.printf("    <meta:editing-duration>PT56S</meta:editing-duration>\n");
1261     outs.printf("    <meta:user-defined meta:name=\"Info 1\"/>\n");
1262     outs.printf("    <meta:user-defined meta:name=\"Info 2\"/>\n");
1263     outs.printf("    <meta:user-defined meta:name=\"Info 3\"/>\n");
1264     outs.printf("    <meta:user-defined meta:name=\"Info 4\"/>\n");
1265     outs.printf("    <meta:document-statistic meta:object-count=\"2\"/>\n");
1266     outs.printf("</office:meta>\n");
1267     outs.printf("</office:document-meta>\n");
1268     outs.printf("\n");
1269     outs.printf("\n");
1272     outs.close();
1274     //Make our entry
1275     ZipEntry *ze = zf.newEntry("meta.xml", "ODF info file");
1276     ze->setUncompressedData(bouts.getBuffer());
1277     ze->finish();
1279     return true;
1285 /**
1286  * This is called just before writeTree(), since it will write style and
1287  * gradient information above the <draw> tag in the content.xml file
1288  */
1289 bool OdfOutput::writeStyle(ZipFile &zf)
1291     BufferOutputStream bouts;
1292     OutputStreamWriter outs(bouts);
1294     /*
1295     ==========================================================
1296     Dump our style table.  Styles should have a general layout
1297     something like the following.  Look in:
1298     http://books.evc-cit.info/odbook/ch06.html#draw-style-file-section
1299     for style and gradient information.
1300     <style:style style:name="gr13"
1301       style:family="graphic" style:parent-style-name="standard">
1302         <style:graphic-properties draw:stroke="solid"
1303             svg:stroke-width="0.1cm"
1304             svg:stroke-color="#ff0000"
1305             draw:fill="solid" draw:fill-color="#e6e6ff"/>
1306     </style:style>
1307     ==========================================================
1308     */
1309     outs.printf("<!-- ####### Styles from Inkscape document ####### -->\n");
1310     std::vector<StyleInfo>::iterator iter;
1311     for (iter = styleTable.begin() ; iter != styleTable.end() ; iter++)
1312         {
1313         outs.printf("<style:style style:name=\"%s\"", iter->name.c_str());
1314         StyleInfo s(*iter);
1315         outs.printf(" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");
1316         outs.printf("  <style:graphic-properties");
1317         outs.printf(" draw:fill=\"%s\" ", s.fill.c_str());
1318         if (s.fill != "none")
1319             {
1320             outs.printf(" draw:fill-color=\"%s\" ", s.fillColor.c_str());
1321             outs.printf(" draw:fill-opacity=\"%s\" ", s.fillOpacity.c_str());
1322             }
1323         outs.printf(" draw:stroke=\"%s\" ", s.stroke.c_str());
1324         if (s.stroke != "none")
1325             {
1326             outs.printf(" svg:stroke-width=\"%s\" ", s.strokeWidth.c_str());
1327             outs.printf(" svg:stroke-color=\"%s\" ", s.strokeColor.c_str());
1328             outs.printf(" svg:stroke-opacity=\"%s\" ", s.strokeOpacity.c_str());
1329             }
1330         outs.printf("/>\n");
1331         outs.printf("</style:style>\n");
1332         }
1334     //##  Dump our gradient table
1335     int gradientCount = 0;
1336     outs.printf("\n");
1337     outs.printf("<!-- ####### Gradients from Inkscape document ####### -->\n");
1338     std::vector<GradientInfo>::iterator giter;
1339     for (giter = gradientTable.begin() ; giter != gradientTable.end() ; giter++)
1340         {
1341         GradientInfo gi(*giter);
1342         if (gi.style == "linear")
1343             {
1344             /*
1345             ===================================================================
1346             LINEAR gradient.  We need something that looks like this:
1347             <draw:gradient draw:name="Gradient_20_7"
1348                 draw:display-name="Gradient 7"
1349                 draw:style="linear"
1350                 draw:start-color="#008080" draw:end-color="#993366"
1351                 draw:start-intensity="100%" draw:end-intensity="100%"
1352                 draw:angle="150" draw:border="0%"/>
1353             ===================================================================
1354             */
1355             if (gi.stops.size() < 2)
1356                 {
1357                 g_warning("Need at least 2 tops for a linear gradient");
1358                 continue;
1359                 }
1360             outs.printf("<svg:linearGradient ");
1361             outs.printf("id=\"%#s_g\" ", gi.name.c_str());
1362             outs.printf("draw:name=\"%#s_g\"\n", gi.name.c_str());
1363             outs.printf("    draw:display-name=\"imported linear %u\"\n",
1364                         gradientCount);
1365             outs.printf("    svg:x1=\"%05.3fcm\" svg:y1=\"%05.3fcm\"\n",
1366                         gi.x1, gi.y1);
1367             outs.printf("    svg:x2=\"%05.3fcm\" svg:y2=\"%05.3fcm\"\n",
1368                         gi.x2, gi.y2);
1369             outs.printf("    svg:gradientUnits=\"objectBoundingBox\">\n");
1370             outs.printf("    <svg:stop\n");
1371             outs.printf("        svg:stop-color=\"#%06lx\"\n",
1372                         gi.stops[0].rgb);
1373             outs.printf("        svg:stop-opacity=\"%f%%\"\n",
1374                         gi.stops[0].opacity * 100.0);
1375             outs.printf("        svg:offset=\"0\"/>\n");
1376             outs.printf("    <svg:stop\n");
1377             outs.printf("        svg:stop-color=\"#%06lx\"\n",
1378                         gi.stops[1].rgb);
1379             outs.printf("        svg:stop-opacity=\"%f%%\"\n",
1380                         gi.stops[1].opacity * 100.0);
1381             outs.printf("        svg:offset=\"1\"/>\n");
1382             outs.printf("</svg:linearGradient>\n");
1383             }
1384         else if (gi.style == "radial")
1385             {
1386             /*
1387             ===================================================================
1388             RADIAL gradient.  We need something that looks like this:
1389             <!-- radial gradient, light gray to white, centered, 0% border -->
1390             <draw:gradient draw:name="radial_20_borderless"
1391                 draw:display-name="radial borderless"
1392                 draw:style="radial"
1393                 draw:cx="50%" draw:cy="50%"
1394                 draw:start-color="#999999" draw:end-color="#ffffff"
1395                 draw:border="0%"/>
1396             ===================================================================
1397             */
1398             if (gi.stops.size() < 2)
1399                 {
1400                 g_warning("Need at least 2 tops for a radial gradient");
1401                 continue;
1402                 }
1403             outs.printf("<svg:radialGradient ");
1404             outs.printf("id=\"%#s_g\" ", gi.name.c_str());
1405             outs.printf("draw:name=\"%#s_g\"\n", gi.name.c_str());
1406             outs.printf("    draw:display-name=\"imported radial %d\"\n",
1407                         gradientCount);
1408             outs.printf("    svg:cx=\"%05.3f\" svg:cy=\"%05.3f\"\n",
1409                         gi.cx, gi.cy);
1410             outs.printf("    svg:fx=\"%05.3f\" svg:fy=\"%05.3f\"\n",
1411                         gi.fx, gi.fy);
1412             outs.printf("    svg:r=\"%05.3f\"\n",
1413                         gi.r);
1414             outs.printf("    svg:gradientUnits=\"objectBoundingBox\">\n");
1415             outs.printf("    <svg:stop\n");
1416             outs.printf("        svg:stop-color=\"#%06lx\"\n",
1417                         gi.stops[0].rgb);
1418             outs.printf("        svg:stop-opacity=\"%f%%\"\n",
1419                         gi.stops[0].opacity * 100.0);
1420             outs.printf("        svg:offset=\"0\"/>\n");
1421             outs.printf("    <svg:stop\n");
1422             outs.printf("        svg:stop-color=\"#%06lx\"\n",
1423                         gi.stops[1].rgb);
1424             outs.printf("        svg:stop-opacity=\"%f%%\"\n",
1425                         gi.stops[1].opacity * 100.0);
1426             outs.printf("        svg:offset=\"1\"/>\n");
1427             outs.printf("</svg:radialGradient>\n");
1428             }
1429         else
1430             {
1431             g_warning("unsupported gradient style '%s'", gi.style.c_str());
1432             }
1433         outs.printf("<style:style style:name=\"%#s\" style:family=\"graphic\" ",
1434                   gi.name.c_str());
1435         outs.printf("style:parent-style-name=\"standard\">\n");
1436         outs.printf("    <style:graphic-properties draw:fill=\"gradient\" ");
1437         outs.printf("draw:fill-gradient-name=\"%#s_g\"\n",
1438                   gi.name.c_str());
1439         outs.printf("        draw:textarea-horizontal-align=\"center\" ");
1440         outs.printf("draw:textarea-vertical-align=\"middle\"/>\n");
1441         outs.printf("</style:style>\n\n");
1443         gradientCount++;
1444         }
1446     outs.printf("\n");
1447     outs.printf("</office:automatic-styles>\n");
1448     outs.printf("\n");
1449     outs.printf("\n");
1450     outs.printf("<office:master-styles>\n");
1451     outs.printf("<draw:layer-set>\n");
1452     outs.printf("    <draw:layer draw:name=\"layout\"/>\n");
1453     outs.printf("    <draw:layer draw:name=\"background\"/>\n");
1454     outs.printf("    <draw:layer draw:name=\"backgroundobjects\"/>\n");
1455     outs.printf("    <draw:layer draw:name=\"controls\"/>\n");
1456     outs.printf("    <draw:layer draw:name=\"measurelines\"/>\n");
1457     outs.printf("</draw:layer-set>\n");
1458     outs.printf("\n");
1459     outs.printf("<style:master-page style:name=\"Default\"\n");
1460     outs.printf("    style:page-master-name=\"PM1\" draw:style-name=\"dp1\"/>\n");
1461     outs.printf("</office:master-styles>\n");
1462     outs.printf("\n");
1463     outs.printf("\n");
1464     outs.printf("\n");
1465     outs.printf("</office:document-styles>\n");
1466     outs.printf("\n");
1467     outs.printf("<!--\n");
1468     outs.printf("*************************************************************************\n");
1469     outs.printf("  E N D    O F    F I L E\n");
1470     outs.printf("  Have a nice day  - ishmal\n");
1471     outs.printf("*************************************************************************\n");
1472     outs.printf("-->\n");
1473     outs.printf("\n");
1475     //Make our entry
1476     ZipEntry *ze = zf.newEntry("styles.xml", "ODF style file");
1477     ze->setUncompressedData(bouts.getBuffer());
1478     ze->finish();
1480     return true;
1485 /**
1486  * Writes an SVG path as an ODF <draw:path> and returns the number of points written
1487  */
1488 static int
1489 writePath(Writer &outs, Geom::PathVector const &pathv,
1490           Geom::Matrix const &tf, double xoff, double yoff)
1492     using Geom::X;
1493     using Geom::Y;
1495     int nrPoints  = 0;
1497     // convert the path to only lineto's and cubic curveto's:
1498     Geom::PathVector pv = pathv_to_linear_and_cubic_beziers(pathv * tf * Geom::Translate(xoff, yoff) * Geom::Scale(1000.));
1500         for (Geom::PathVector::const_iterator pit = pv.begin(); pit != pv.end(); ++pit) {
1502             double destx = pit->initialPoint()[X];
1503             double desty = pit->initialPoint()[Y];
1504             if (fabs(destx)<1.0) destx = 0.0;   // Why is this needed? Shouldn't we just round all numbers then?
1505             if (fabs(desty)<1.0) desty = 0.0;
1506             outs.printf("M %.3f %.3f ", destx, desty);
1507             nrPoints++;
1509             for (Geom::Path::const_iterator cit = pit->begin(); cit != pit->end_closed(); ++cit) {
1511                 if( is_straight_curve(*cit) )
1512                 {
1513                     double destx = cit->finalPoint()[X];
1514                     double desty = cit->finalPoint()[Y];
1515                     if (fabs(destx)<1.0) destx = 0.0;   // Why is this needed? Shouldn't we just round all numbers then?
1516                     if (fabs(desty)<1.0) desty = 0.0;
1517                     outs.printf("L %.3f %.3f ",  destx, desty);
1518                 }
1519                 else if(Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
1520                     std::vector<Geom::Point> points = cubic->points();
1521                     for (unsigned i = 1; i <= 3; i++) {
1522                         if (fabs(points[i][X])<1.0) points[i][X] = 0.0;   // Why is this needed? Shouldn't we just round all numbers then?
1523                         if (fabs(points[i][Y])<1.0) points[i][Y] = 0.0;
1524                     }
1525                     outs.printf("C %.3f %.3f %.3f %.3f %.3f %.3f ", points[1][X],points[1][Y], points[2][X],points[2][Y], points[3][X],points[3][Y]);
1526                 }
1527                 else {
1528                     g_error ("logical error, because pathv_to_linear_and_cubic_beziers was used");
1529                 }
1531                 nrPoints++;
1532             }
1534             if (pit->closed()) {
1535                 outs.printf("Z");
1536             }
1537         }
1539     return nrPoints;
1544 bool OdfOutput::processStyle(Writer &outs, SPItem *item,
1545                              const Glib::ustring &id)
1547     SPStyle *style = item->style;
1549     StyleInfo si;
1551     //## FILL
1552     if (style->fill.isColor())
1553         {
1554         guint32 fillCol = style->fill.value.color.toRGBA32( 0 );
1555         char buf[16];
1556         int r = (fillCol >> 24) & 0xff;
1557         int g = (fillCol >> 16) & 0xff;
1558         int b = (fillCol >>  8) & 0xff;
1559         //g_message("## %s %lx", id.c_str(), (unsigned int)fillCol);
1560         snprintf(buf, 15, "#%02x%02x%02x", r, g, b);
1561         si.fillColor = buf;
1562         si.fill      = "solid";
1563         double opacityPercent = 100.0 *
1564              (SP_SCALE24_TO_FLOAT(style->fill_opacity.value));
1565         snprintf(buf, 15, "%.3f%%", opacityPercent);
1566         si.fillOpacity = buf;
1567         }
1569     //## STROKE
1570     if (style->stroke.isColor())
1571         {
1572         guint32 strokeCol = style->stroke.value.color.toRGBA32( 0 );
1573         char buf[16];
1574         int r = (strokeCol >> 24) & 0xff;
1575         int g = (strokeCol >> 16) & 0xff;
1576         int b = (strokeCol >>  8) & 0xff;
1577         snprintf(buf, 15, "#%02x%02x%02x", r, g, b);
1578         si.strokeColor = buf;
1579         snprintf(buf, 15, "%.3fpt", style->stroke_width.value);
1580         si.strokeWidth = buf;
1581         si.stroke      = "solid";
1582         double opacityPercent = 100.0 *
1583              (SP_SCALE24_TO_FLOAT(style->stroke_opacity.value));
1584         snprintf(buf, 15, "%.3f%%", opacityPercent);
1585         si.strokeOpacity = buf;
1586         }
1588     //Look for existing identical style;
1589     bool styleMatch = false;
1590     std::vector<StyleInfo>::iterator iter;
1591     for (iter=styleTable.begin() ; iter!=styleTable.end() ; iter++)
1592         {
1593         if (si.equals(*iter))
1594             {
1595             //map to existing styleTable entry
1596             Glib::ustring styleName = iter->name;
1597             //g_message("found duplicate style:%s", styleName.c_str());
1598             styleLookupTable[id] = styleName;
1599             styleMatch = true;
1600             break;
1601             }
1602         }
1604     //## Dont need a new style
1605     if (styleMatch)
1606         return false;
1608     char buf[16];
1609     snprintf(buf, 15, "style%d", (int)styleTable.size());
1610     Glib::ustring styleName = buf;
1611     si.name = styleName;
1612     styleTable.push_back(si);
1613     styleLookupTable[id] = styleName;
1615     outs.printf("<style:style style:name=\"%s\"", si.name.c_str());
1616     outs.printf(" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");
1617     outs.printf("  <style:graphic-properties");
1618     outs.printf(" draw:fill=\"%s\" ", si.fill.c_str());
1619     if (si.fill != "none")
1620         {
1621         outs.printf(" draw:fill-color=\"%s\" ", si.fillColor.c_str());
1622         outs.printf(" draw:fill-opacity=\"%s\" ", si.fillOpacity.c_str());
1623         }
1624     outs.printf(" draw:stroke=\"%s\" ", si.stroke.c_str());
1625     if (si.stroke != "none")
1626         {
1627         outs.printf(" svg:stroke-width=\"%s\" ", si.strokeWidth.c_str());
1628         outs.printf(" svg:stroke-color=\"%s\" ", si.strokeColor.c_str());
1629         outs.printf(" svg:stroke-opacity=\"%s\" ", si.strokeOpacity.c_str());
1630         }
1631     outs.printf("/>\n");
1632     outs.printf("</style:style>\n");
1634     return true;
1640 bool OdfOutput::processGradient(Writer &outs, SPItem *item,
1641                                 const Glib::ustring &id, Geom::Matrix &/*tf*/)
1643     if (!item)
1644         return false;
1646     SPStyle *style = item->style;
1648     if (!style)
1649         return false;
1651     if (!style->fill.isPaintserver())
1652         return false;
1654     //## Gradient.  Look in writeStyle() below to see what info
1655     //   we need to read into GradientInfo.
1656     if (!SP_IS_GRADIENT(SP_STYLE_FILL_SERVER(style)))
1657         return false;
1659     SPGradient *gradient = SP_GRADIENT(SP_STYLE_FILL_SERVER(style));
1661     GradientInfo gi;
1663     SPGradient *grvec = gradient->getVector(FALSE);
1664     for (SPStop *stop = grvec->getFirstStop() ;
1665          stop ; stop = stop->getNextStop())
1666         {
1667         unsigned long rgba = sp_stop_get_rgba32(stop);
1668         unsigned long rgb  = (rgba >> 8) & 0xffffff;
1669         double opacity     = ((double)(rgba & 0xff)) / 256.0;
1670         GradientStop gs(rgb, opacity);
1671         gi.stops.push_back(gs);
1672         }
1674     if (SP_IS_LINEARGRADIENT(gradient))
1675         {
1676         gi.style = "linear";
1677         SPLinearGradient *linGrad = SP_LINEARGRADIENT(gradient);
1678         /*
1679         Geom::Point p1(linGrad->x1.value, linGrad->y1.value);
1680         p1 = p1 * tf;
1681         gi.x1 = p1[Geom::X];
1682         gi.y1 = p1[Geom::Y];
1683         Geom::Point p2(linGrad->x2.value, linGrad->y2.value);
1684         p2 = p2 * tf;
1685         gi.x2 = p2[Geom::X];
1686         gi.y2 = p2[Geom::Y];
1687         */
1688         gi.x1 = linGrad->x1.value;
1689         gi.y1 = linGrad->y1.value;
1690         gi.x2 = linGrad->x2.value;
1691         gi.y2 = linGrad->y2.value;
1692         }
1693     else if (SP_IS_RADIALGRADIENT(gradient))
1694         {
1695         gi.style = "radial";
1696         SPRadialGradient *radGrad = SP_RADIALGRADIENT(gradient);
1697         gi.cx = radGrad->cx.computed * 100.0;//ODG cx is percentages
1698         gi.cy = radGrad->cy.computed * 100.0;
1699         }
1700     else
1701         {
1702         g_warning("not a supported gradient type");
1703         return false;
1704         }
1706     //Look for existing identical style;
1707     bool gradientMatch = false;
1708     std::vector<GradientInfo>::iterator iter;
1709     for (iter=gradientTable.begin() ; iter!=gradientTable.end() ; iter++)
1710         {
1711         if (gi.equals(*iter))
1712             {
1713             //map to existing gradientTable entry
1714             Glib::ustring gradientName = iter->name;
1715             //g_message("found duplicate style:%s", gradientName.c_str());
1716             gradientLookupTable[id] = gradientName;
1717             gradientMatch = true;
1718             break;
1719             }
1720         }
1722     if (gradientMatch)
1723         return true;
1725     //## No match, let us write a new entry
1726     char buf[16];
1727     snprintf(buf, 15, "gradient%d", (int)gradientTable.size());
1728     Glib::ustring gradientName = buf;
1729     gi.name = gradientName;
1730     gradientTable.push_back(gi);
1731     gradientLookupTable[id] = gradientName;
1733     int gradientCount = gradientTable.size();
1735     if (gi.style == "linear")
1736         {
1737         /*
1738         ===================================================================
1739         LINEAR gradient.  We need something that looks like this:
1740         <draw:gradient draw:name="Gradient_20_7"
1741             draw:display-name="Gradient 7"
1742             draw:style="linear"
1743             draw:start-color="#008080" draw:end-color="#993366"
1744             draw:start-intensity="100%" draw:end-intensity="100%"
1745             draw:angle="150" draw:border="0%"/>
1746         ===================================================================
1747         */
1748         if (gi.stops.size() < 2)
1749             {
1750             g_warning("Need at least 2 stops for a linear gradient");
1751             return false;;
1752             }
1753         outs.printf("<svg:linearGradient ");
1754         outs.printf("id=\"%#s_g\" ", gi.name.c_str());
1755         outs.printf("draw:name=\"%#s_g\"\n", gi.name.c_str());
1756         outs.printf("    draw:display-name=\"imported linear %d\"\n",
1757                     gradientCount);
1758         outs.printf("    svg:gradientUnits=\"objectBoundingBox\"\n");
1759         outs.printf("    svg:x1=\"%05.3fcm\" svg:y1=\"%05.3fcm\"\n",
1760                     gi.x1 * pxToCm, gi.y1 * pxToCm);
1761         outs.printf("    svg:x2=\"%05.3fcm\" svg:y2=\"%05.3fcm\">\n",
1762                     gi.x2 * pxToCm, gi.y2 * pxToCm);
1763         outs.printf("    <svg:stop\n");
1764         outs.printf("        svg:stop-color=\"#%06lx\"\n",
1765                     gi.stops[0].rgb);
1766         outs.printf("        svg:stop-opacity=\"%f%%\"\n",
1767                     gi.stops[0].opacity * 100.0);
1768         outs.printf("        svg:offset=\"0\"/>\n");
1769         outs.printf("    <svg:stop\n");
1770         outs.printf("        svg:stop-color=\"#%06lx\"\n",
1771                     gi.stops[1].rgb);
1772         outs.printf("        svg:stop-opacity=\"%f%%\"\n",
1773                     gi.stops[1].opacity * 100.0);
1774         outs.printf("        svg:offset=\"1\"/>\n");
1775         outs.printf("</svg:linearGradient>\n");
1776         }
1777     else if (gi.style == "radial")
1778         {
1779         /*
1780         ===================================================================
1781         RADIAL gradient.  We need something that looks like this:
1782         <!-- radial gradient, light gray to white, centered, 0% border -->
1783         <draw:gradient draw:name="radial_20_borderless"
1784             draw:display-name="radial borderless"
1785             draw:style="radial"
1786             draw:cx="50%" draw:cy="50%"
1787             draw:start-color="#999999" draw:end-color="#ffffff"
1788             draw:border="0%"/>
1789         ===================================================================
1790         */
1791         if (gi.stops.size() < 2)
1792             {
1793             g_warning("Need at least 2 stops for a radial gradient");
1794             return false;
1795             }
1796         outs.printf("<svg:radialGradient ");
1797         outs.printf("id=\"%#s_g\" ", gi.name.c_str());
1798         outs.printf("draw:name=\"%#s_g\"\n", gi.name.c_str());
1799         outs.printf("    draw:display-name=\"imported radial %d\"\n",
1800                     gradientCount);
1801         outs.printf("    svg:gradientUnits=\"objectBoundingBox\"\n");
1802         outs.printf("    svg:cx=\"%05.3f\" svg:cy=\"%05.3f\"\n",
1803                     gi.cx, gi.cy);
1804         outs.printf("    svg:fx=\"%05.3f\" svg:fy=\"%05.3f\"\n",
1805                     gi.fx, gi.fy);
1806         outs.printf("    svg:r=\"%05.3f\">\n",
1807                     gi.r);
1808         outs.printf("    <svg:stop\n");
1809         outs.printf("        svg:stop-color=\"#%06lx\"\n",
1810                     gi.stops[0].rgb);
1811         outs.printf("        svg:stop-opacity=\"%f%%\"\n",
1812                     gi.stops[0].opacity * 100.0);
1813         outs.printf("        svg:offset=\"0\"/>\n");
1814         outs.printf("    <svg:stop\n");
1815         outs.printf("        svg:stop-color=\"#%06lx\"\n",
1816                     gi.stops[1].rgb);
1817         outs.printf("        svg:stop-opacity=\"%f%%\"\n",
1818                     gi.stops[1].opacity * 100.0);
1819         outs.printf("        svg:offset=\"1\"/>\n");
1820         outs.printf("</svg:radialGradient>\n");
1821         }
1822     else
1823         {
1824         g_warning("unsupported gradient style '%s'", gi.style.c_str());
1825         return false;
1826         }
1827     outs.printf("<style:style style:name=\"%#s\" style:family=\"graphic\" ",
1828               gi.name.c_str());
1829     outs.printf("style:parent-style-name=\"standard\">\n");
1830     outs.printf("    <style:graphic-properties draw:fill=\"gradient\" ");
1831     outs.printf("draw:fill-gradient-name=\"%#s_g\"\n",
1832               gi.name.c_str());
1833     outs.printf("        draw:textarea-horizontal-align=\"center\" ");
1834     outs.printf("draw:textarea-vertical-align=\"middle\"/>\n");
1835     outs.printf("</style:style>\n\n");
1837     return true;
1843 /**
1844  * SECOND PASS.
1845  * This is the main SPObject tree output to ODF.  preprocess()
1846  * must be called prior to this, as elements will often reference
1847  * data parsed and tabled in preprocess().
1848  */
1849 bool OdfOutput::writeTree(Writer &couts, Writer &souts,
1850                           Inkscape::XML::Node *node)
1852     //# Get the SPItem, if applicable
1853     SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(node);
1854     if (!reprobj)
1855         return true;
1856     if (!SP_IS_ITEM(reprobj))
1857         {
1858         return true;
1859         }
1860     SPItem *item = SP_ITEM(reprobj);
1863     Glib::ustring nodeName = node->name();
1864     Glib::ustring id       = getAttribute(node, "id");
1866     //### Get SVG-to-ODF transform
1867     Geom::Matrix tf        = getODFTransform(item);
1869     //### Get ODF bounding box params for item
1870     Geom::OptRect bbox = getODFBoundingBox(item);
1871     if (!bbox) {
1872         return true;
1873     }
1875     double bbox_x        = bbox->min()[Geom::X];
1876     double bbox_y        = bbox->min()[Geom::Y];
1877     double bbox_width    = (*bbox)[Geom::X].extent();
1878     double bbox_height   = (*bbox)[Geom::Y].extent();
1880     double rotate;
1881     double xskew;
1882     double yskew;
1883     double xscale;
1884     double yscale;
1885     analyzeTransform(tf, rotate, xskew, yskew, xscale, yscale);
1887     //# Do our stuff
1888     SPCurve *curve = NULL;
1892     if (nodeName == "svg" || nodeName == "svg:svg")
1893         {
1894         //# Iterate through the children
1895         for (Inkscape::XML::Node *child = node->firstChild() ;
1896                child ; child = child->next())
1897             {
1898             if (!writeTree(couts, souts, child))
1899                 return false;
1900             }
1901         return true;
1902         }
1903     else if (nodeName == "g" || nodeName == "svg:g")
1904         {
1905         if (id.size() > 0)
1906             couts.printf("<draw:g id=\"%s\">\n", id.c_str());
1907         else
1908             couts.printf("<draw:g>\n");
1909         //# Iterate through the children
1910         for (Inkscape::XML::Node *child = node->firstChild() ;
1911                child ; child = child->next())
1912             {
1913             if (!writeTree(couts, souts, child))
1914                 return false;
1915             }
1916         if (id.size() > 0)
1917             couts.printf("</draw:g> <!-- id=\"%s\" -->\n", id.c_str());
1918         else
1919             couts.printf("</draw:g>\n");
1920         return true;
1921         }
1923     //######################################
1924     //# S T Y L E
1925     //######################################
1926     processStyle(souts, item, id);
1928     //######################################
1929     //# G R A D I E N T
1930     //######################################
1931     processGradient(souts, item, id, tf);
1936     //######################################
1937     //# I T E M    D A T A
1938     //######################################
1939     //g_message("##### %s #####", nodeName.c_str());
1940     if (nodeName == "image" || nodeName == "svg:image")
1941         {
1942         if (!SP_IS_IMAGE(item))
1943             {
1944             g_warning("<image> is not an SPImage.  Why?  ;-)");
1945             return false;
1946             }
1948         SPImage *img   = SP_IMAGE(item);
1949         double ix      = img->x.value;
1950         double iy      = img->y.value;
1951         double iwidth  = img->width.value;
1952         double iheight = img->height.value;
1954         Geom::Rect ibbox(Geom::Point(ix, iy), Geom::Point(ix+iwidth, iy+iheight));
1955         ibbox = ibbox * tf;
1956         ix      = ibbox.min()[Geom::X];
1957         iy      = ibbox.min()[Geom::Y];
1958         //iwidth  = ibbox.max()[Geom::X] - ibbox.min()[Geom::X];
1959         //iheight = ibbox.max()[Geom::Y] - ibbox.min()[Geom::Y];
1960         iwidth  = xscale * iwidth;
1961         iheight = yscale * iheight;
1963         Geom::Matrix itemTransform = getODFItemTransform(item);
1965         Glib::ustring itemTransformString = formatTransform(itemTransform);
1967         Glib::ustring href = getAttribute(node, "xlink:href");
1968         std::map<Glib::ustring, Glib::ustring>::iterator iter = imageTable.find(href);
1969         if (iter == imageTable.end())
1970             {
1971             g_warning("image '%s' not in table", href.c_str());
1972             return false;
1973             }
1974         Glib::ustring newName = iter->second;
1976         couts.printf("<draw:frame ");
1977         if (id.size() > 0)
1978             couts.printf("id=\"%s\" ", id.c_str());
1979         couts.printf("draw:style-name=\"gr1\" draw:text-style-name=\"P1\" draw:layer=\"layout\" ");
1980         //no x or y.  make them the translate transform, last one
1981         couts.printf("svg:width=\"%.3fcm\" svg:height=\"%.3fcm\" ",
1982                                   iwidth, iheight);
1983         if (itemTransformString.size() > 0)
1984             {
1985             couts.printf("draw:transform=\"%s translate(%.3fcm, %.3fcm)\" ",
1986                            itemTransformString.c_str(), ix, iy);
1987             }
1988         else
1989             {
1990             couts.printf("draw:transform=\"translate(%.3fcm, %.3fcm)\" ",
1991                                 ix, iy);
1992             }
1994         couts.printf(">\n");
1995         couts.printf("    <draw:image xlink:href=\"%s\" xlink:type=\"simple\"\n",
1996                               newName.c_str());
1997         couts.printf("        xlink:show=\"embed\" xlink:actuate=\"onLoad\">\n");
1998         couts.printf("        <text:p/>\n");
1999         couts.printf("    </draw:image>\n");
2000         couts.printf("</draw:frame>\n");
2001         return true;
2002         }
2003     else if (SP_IS_SHAPE(item))
2004         {
2005         //g_message("### %s is a shape", nodeName.c_str());
2006         curve = SP_SHAPE(item)->getCurve();
2007         }
2008     else if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))
2009         {
2010         curve = te_get_layout(item)->convertToCurves();
2011         }
2013     if (curve)
2014         {
2015         //### Default <path> output
2017         couts.printf("<draw:path ");
2018         if (id.size()>0)
2019             couts.printf("id=\"%s\" ", id.c_str());
2021         std::map<Glib::ustring, Glib::ustring>::iterator siter;
2022         siter = styleLookupTable.find(id);
2023         if (siter != styleLookupTable.end())
2024             {
2025             Glib::ustring styleName = siter->second;
2026             couts.printf("draw:style-name=\"%s\" ", styleName.c_str());
2027             }
2029         std::map<Glib::ustring, Glib::ustring>::iterator giter;
2030         giter = gradientLookupTable.find(id);
2031         if (giter != gradientLookupTable.end())
2032             {
2033             Glib::ustring gradientName = giter->second;
2034             couts.printf("draw:fill-gradient-name=\"%s\" ",
2035                  gradientName.c_str());
2036             }
2038         couts.printf("draw:layer=\"layout\" svg:x=\"%.3fcm\" svg:y=\"%.3fcm\" ",
2039                        bbox_x, bbox_y);
2040         couts.printf("svg:width=\"%.3fcm\" svg:height=\"%.3fcm\" ",
2041                        bbox_width, bbox_height);
2042         couts.printf("svg:viewBox=\"0.0 0.0 %.3f %.3f\"\n",
2043                        bbox_width * 1000.0, bbox_height * 1000.0);
2045         couts.printf("    svg:d=\"");
2046         int nrPoints = writePath(couts, curve->get_pathvector(),
2047                              tf, bbox_x, bbox_y);
2048         couts.printf("\"");
2050         couts.printf(">\n");
2051         couts.printf("    <!-- %d nodes -->\n", nrPoints);
2052         couts.printf("</draw:path>\n\n");
2055         curve->unref();
2056         }
2058     return true;
2063 /**
2064  * Write the header for the content.xml file
2065  */
2066 bool OdfOutput::writeStyleHeader(Writer &outs)
2068     time_t tim;
2069     time(&tim);
2071     outs.printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
2072     outs.printf("\n");
2073     outs.printf("\n");
2074     outs.printf("<!--\n");
2075     outs.printf("*************************************************************************\n");
2076     outs.printf("  file:  styles.xml\n");
2077     outs.printf("  Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
2078     outs.printf("  http://www.inkscape.org\n");
2079     outs.printf("*************************************************************************\n");
2080     outs.printf("-->\n");
2081     outs.printf("\n");
2082     outs.printf("\n");
2083     outs.printf("<office:document-styles\n");
2084     outs.printf("    xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
2085     outs.printf("    xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"\n");
2086     outs.printf("    xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"\n");
2087     outs.printf("    xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"\n");
2088     outs.printf("    xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"\n");
2089     outs.printf("    xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"\n");
2090     outs.printf("    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
2091     outs.printf("    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
2092     outs.printf("    xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
2093     outs.printf("    xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"\n");
2094     outs.printf("    xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
2095     outs.printf("    xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"\n");
2096     outs.printf("    xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"\n");
2097     outs.printf("    xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"\n");
2098     outs.printf("    xmlns:math=\"http://www.w3.org/1998/Math/MathML\"\n");
2099     outs.printf("    xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"\n");
2100     outs.printf("    xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"\n");
2101     outs.printf("    xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
2102     outs.printf("    xmlns:ooow=\"http://openoffice.org/2004/writer\"\n");
2103     outs.printf("    xmlns:oooc=\"http://openoffice.org/2004/calc\"\n");
2104     outs.printf("    xmlns:dom=\"http://www.w3.org/2001/xml-events\"\n");
2105     outs.printf("    xmlns:xforms=\"http://www.w3.org/2002/xforms\"\n");
2106     outs.printf("    xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n");
2107     outs.printf("    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
2108     outs.printf("    xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
2109     outs.printf("    xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
2110     outs.printf("    office:version=\"1.0\">\n");
2111     outs.printf("\n");
2112     outs.printf("\n");
2113     outs.printf("<!--\n");
2114     outs.printf("*************************************************************************\n");
2115     outs.printf("  S T Y L E S\n");
2116     outs.printf("  Style entries have been pulled from the svg style and\n");
2117     outs.printf("  representation attributes in the SVG tree.  The tree elements\n");
2118     outs.printf("  then refer to them by name, in the ODF manner\n");
2119     outs.printf("*************************************************************************\n");
2120     outs.printf("-->\n");
2121     outs.printf("\n");
2122     outs.printf("<office:styles>\n");
2123     outs.printf("\n");
2125     return true;
2129 /**
2130  * Write the footer for the style.xml file
2131  */
2132 bool OdfOutput::writeStyleFooter(Writer &outs)
2134     outs.printf("\n");
2135     outs.printf("</office:styles>\n");
2136     outs.printf("\n");
2137     outs.printf("\n");
2138     outs.printf("<office:automatic-styles>\n");
2139     outs.printf("<!-- ####### 'Standard' styles ####### -->\n");
2140     outs.printf("<style:style style:name=\"dp1\" style:family=\"drawing-page\"/>\n");
2141     outs.printf("<style:style style:name=\"gr1\" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");
2142     outs.printf("  <style:graphic-properties draw:stroke=\"none\" draw:fill=\"none\"\n");
2143     outs.printf("       draw:textarea-horizontal-align=\"center\"\n");
2144     outs.printf("       draw:textarea-vertical-align=\"middle\" draw:color-mode=\"standard\"\n");
2145     outs.printf("       draw:luminance=\"0%%\" draw:contrast=\"0%%\" draw:gamma=\"100%%\" draw:red=\"0%%\"\n");
2146     outs.printf("       draw:green=\"0%%\" draw:blue=\"0%%\" fo:clip=\"rect(0cm 0cm 0cm 0cm)\"\n");
2147     outs.printf("       draw:image-opacity=\"100%%\" style:mirror=\"none\"/>\n");
2148     outs.printf("</style:style>\n");
2149     outs.printf("<style:style style:name=\"P1\" style:family=\"paragraph\">\n");
2150     outs.printf("  <style:paragraph-properties fo:text-align=\"center\"/>\n");
2151     outs.printf("</style:style>\n");
2152     outs.printf("</office:automatic-styles>\n");
2153     outs.printf("\n");
2154     outs.printf("\n");
2155     outs.printf("<office:master-styles>\n");
2156     outs.printf("<draw:layer-set>\n");
2157     outs.printf("    <draw:layer draw:name=\"layout\"/>\n");
2158     outs.printf("    <draw:layer draw:name=\"background\"/>\n");
2159     outs.printf("    <draw:layer draw:name=\"backgroundobjects\"/>\n");
2160     outs.printf("    <draw:layer draw:name=\"controls\"/>\n");
2161     outs.printf("    <draw:layer draw:name=\"measurelines\"/>\n");
2162     outs.printf("</draw:layer-set>\n");
2163     outs.printf("\n");
2164     outs.printf("<style:master-page style:name=\"Default\"\n");
2165     outs.printf("    style:page-master-name=\"PM1\" draw:style-name=\"dp1\"/>\n");
2166     outs.printf("</office:master-styles>\n");
2167     outs.printf("\n");
2168     outs.printf("\n");
2169     outs.printf("\n");
2170     outs.printf("</office:document-styles>\n");
2171     outs.printf("\n");
2172     outs.printf("<!--\n");
2173     outs.printf("*************************************************************************\n");
2174     outs.printf("  E N D    O F    F I L E\n");
2175     outs.printf("  Have a nice day  - ishmal\n");
2176     outs.printf("*************************************************************************\n");
2177     outs.printf("-->\n");
2178     outs.printf("\n");
2180     return true;
2186 /**
2187  * Write the header for the content.xml file
2188  */
2189 bool OdfOutput::writeContentHeader(Writer &outs)
2191     time_t tim;
2192     time(&tim);
2194     outs.printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
2195     outs.printf("\n");
2196     outs.printf("\n");
2197     outs.printf("<!--\n");
2198     outs.printf("*************************************************************************\n");
2199     outs.printf("  file:  content.xml\n");
2200     outs.printf("  Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
2201     outs.printf("  http://www.inkscape.org\n");
2202     outs.printf("*************************************************************************\n");
2203     outs.printf("-->\n");
2204     outs.printf("\n");
2205     outs.printf("\n");
2206     outs.printf("<office:document-content\n");
2207     outs.printf("    xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
2208     outs.printf("    xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"\n");
2209     outs.printf("    xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"\n");
2210     outs.printf("    xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"\n");
2211     outs.printf("    xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"\n");
2212     outs.printf("    xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"\n");
2213     outs.printf("    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
2214     outs.printf("    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
2215     outs.printf("    xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
2216     outs.printf("    xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"\n");
2217     outs.printf("    xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
2218     outs.printf("    xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"\n");
2219     outs.printf("    xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"\n");
2220     outs.printf("    xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"\n");
2221     outs.printf("    xmlns:math=\"http://www.w3.org/1998/Math/MathML\"\n");
2222     outs.printf("    xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"\n");
2223     outs.printf("    xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"\n");
2224     outs.printf("    xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
2225     outs.printf("    xmlns:ooow=\"http://openoffice.org/2004/writer\"\n");
2226     outs.printf("    xmlns:oooc=\"http://openoffice.org/2004/calc\"\n");
2227     outs.printf("    xmlns:dom=\"http://www.w3.org/2001/xml-events\"\n");
2228     outs.printf("    xmlns:xforms=\"http://www.w3.org/2002/xforms\"\n");
2229     outs.printf("    xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n");
2230     outs.printf("    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
2231     outs.printf("    xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
2232     outs.printf("    xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
2233     outs.printf("    office:version=\"1.0\">\n");
2234     outs.printf("\n");
2235     outs.printf("\n");
2236     outs.printf("<office:scripts/>\n");
2237     outs.printf("\n");
2238     outs.printf("\n");
2239     outs.printf("<!--\n");
2240     outs.printf("*************************************************************************\n");
2241     outs.printf("  D R A W I N G\n");
2242     outs.printf("  This section is the heart of SVG-ODF conversion.  We are\n");
2243     outs.printf("  starting with simple conversions, and will slowly evolve\n");
2244     outs.printf("  into a 'smarter' translation as time progresses.  Any help\n");
2245     outs.printf("  in improving .odg export is welcome.\n");
2246     outs.printf("*************************************************************************\n");
2247     outs.printf("-->\n");
2248     outs.printf("\n");
2249     outs.printf("\n");
2250     outs.printf("<office:body>\n");
2251     outs.printf("<office:drawing>\n");
2252     outs.printf("<draw:page draw:name=\"page1\" draw:style-name=\"dp1\"\n");
2253     outs.printf("        draw:master-page-name=\"Default\">\n");
2254     outs.printf("\n");
2255     outs.printf("\n");
2257     return true;
2261 /**
2262  * Write the footer for the content.xml file
2263  */
2264 bool OdfOutput::writeContentFooter(Writer &outs)
2266     outs.printf("\n");
2267     outs.printf("\n");
2269     outs.printf("</draw:page>\n");
2270     outs.printf("</office:drawing>\n");
2272     outs.printf("\n");
2273     outs.printf("\n");
2274     outs.printf("<!-- ######### CONVERSION FROM SVG ENDS ######## -->\n");
2275     outs.printf("\n");
2276     outs.printf("\n");
2278     outs.printf("</office:body>\n");
2279     outs.printf("</office:document-content>\n");
2280     outs.printf("\n");
2281     outs.printf("\n");
2282     outs.printf("\n");
2283     outs.printf("<!--\n");
2284     outs.printf("*************************************************************************\n");
2285     outs.printf("  E N D    O F    F I L E\n");
2286     outs.printf("  Have a nice day  - ishmal\n");
2287     outs.printf("*************************************************************************\n");
2288     outs.printf("-->\n");
2289     outs.printf("\n");
2290     outs.printf("\n");
2292     return true;
2297 /**
2298  * Write the content.xml file.  Writes the namesspace headers, then
2299  * calls writeTree().
2300  */
2301 bool OdfOutput::writeContent(ZipFile &zf, Inkscape::XML::Node *node)
2303     //Content.xml stream
2304     BufferOutputStream cbouts;
2305     OutputStreamWriter couts(cbouts);
2307     if (!writeContentHeader(couts))
2308         return false;
2310     //Style.xml stream
2311     BufferOutputStream sbouts;
2312     OutputStreamWriter souts(sbouts);
2314     if (!writeStyleHeader(souts))
2315         return false;
2318     //# Descend into the tree, doing all of our conversions
2319     //# to both files as the same time
2320     if (!writeTree(couts, souts, node))
2321         {
2322         g_warning("Failed to convert SVG tree");
2323         return false;
2324         }
2328     //# Finish content file
2329     if (!writeContentFooter(couts))
2330         return false;
2332     ZipEntry *ze = zf.newEntry("content.xml", "ODF master content file");
2333     ze->setUncompressedData(cbouts.getBuffer());
2334     ze->finish();
2338     //# Finish style file
2339     if (!writeStyleFooter(souts))
2340         return false;
2342     ze = zf.newEntry("styles.xml", "ODF style file");
2343     ze->setUncompressedData(sbouts.getBuffer());
2344     ze->finish();
2346     return true;
2350 /**
2351  * Resets class to its pristine condition, ready to use again
2352  */
2353 void
2354 OdfOutput::reset()
2356     metadata.clear();
2357     styleTable.clear();
2358     styleLookupTable.clear();
2359     gradientTable.clear();
2360     gradientLookupTable.clear();
2361     imageTable.clear();
2367 /**
2368  * Descends into the SVG tree, mapping things to ODF when appropriate
2369  */
2370 void
2371 OdfOutput::save(Inkscape::Extension::Output */*mod*/, SPDocument *doc, gchar const *filename)
2373     reset();
2375     //g_message("native file:%s\n", filename);
2376     documentUri = URI(filename);
2377     /* fixme: It looks like we really are using documentUri as a URI, so we ought to call
2378      * g_filename_to_uri for the URI constructor. */
2380     ZipFile zf;
2381     preprocess(zf, doc->rroot);
2383     if (!writeManifest(zf))
2384         {
2385         g_warning("Failed to write manifest");
2386         return;
2387         }
2389     if (!writeContent(zf, doc->rroot))
2390         {
2391         g_warning("Failed to write content");
2392         return;
2393         }
2395     if (!writeMeta(zf))
2396         {
2397         g_warning("Failed to write metafile");
2398         return;
2399         }
2401     if (!zf.writeFile(filename))
2402         {
2403         return;
2404         }
2408 /**
2409  * This is the definition of PovRay output.  This function just
2410  * calls the extension system with the memory allocated XML that
2411  * describes the data.
2412 */
2413 void
2414 OdfOutput::init()
2416     Inkscape::Extension::build_from_mem(
2417         "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
2418             "<name>" N_("OpenDocument Drawing Output") "</name>\n"
2419             "<id>org.inkscape.output.odf</id>\n"
2420             "<output>\n"
2421                 "<extension>.odg</extension>\n"
2422                 "<mimetype>text/x-povray-script</mimetype>\n"
2423                 "<filetypename>" N_("OpenDocument drawing (*.odg)") "</filetypename>\n"
2424                 "<filetypetooltip>" N_("OpenDocument drawing file") "</filetypetooltip>\n"
2425             "</output>\n"
2426         "</inkscape-extension>",
2427         new OdfOutput());
2430 /**
2431  * Make sure that we are in the database
2432  */
2433 bool
2434 OdfOutput::check (Inkscape::Extension::Extension */*module*/)
2436     /* We don't need a Key
2437     if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_POV))
2438         return FALSE;
2439     */
2441     return TRUE;
2446 //########################################################################
2447 //# I N P U T
2448 //########################################################################
2452 //#######################
2453 //# L A T E R  !!!  :-)
2454 //#######################
2468 }  //namespace Internal
2469 }  //namespace Extension
2470 }  //namespace Inkscape
2473 //########################################################################
2474 //# E N D    O F    F I L E
2475 //########################################################################
2477 /*
2478   Local Variables:
2479   mode:c++
2480   c-file-style:"stroustrup"
2481   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2482   indent-tabs-mode:nil
2483   fill-column:99
2484   End:
2485 */
2486 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :