1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 /** @file
3  * OpenDocument (drawing) input and output
4  *//*
5  * Authors:
6  *   Bob Jamison
7  *   Abhishek Sharma
8  *   Kris De Gussem
9  *
10  * Copyright (C) 2018 Authors
11  * Released under GNU LGPL v2.1+, read the file 'COPYING' for more information.
12  */
13 /*
14  * This is an an entry in the extensions mechanism to begin to enable
15  * the inputting and outputting of OpenDocument Format (ODF) files from
16  * within Inkscape.  Although the initial implementations will be very lossy
17  * due to the differences in the models of SVG and ODF, they will hopefully
18  * improve greatly with time.  People should consider this to be a framework
19  * that can be continuously upgraded for ever improving fidelity.  Potential
20  * developers should especially look in preprocess() and writeTree() to see how
21  * the SVG tree is scanned, read, translated, and then written to ODF.
22  *
23  * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html
24  *
25  */
26 
27 #include "odf.h"
28 
29 //# System includes
30 #include <cstdio>
31 #include <ctime>
32 #include <vector>
33 #include <cmath>
34 
35 //# Inkscape includes
36 #include "clear-n_.h"
37 #include "inkscape.h"
38 #include "display/curve.h"
39 #include <2geom/pathvector.h>
40 #include <2geom/curves.h>
41 #include <2geom/transforms.h>
42 #include <helper/geom.h>
43 #include "helper/geom-curves.h"
44 #include "extension/system.h"
45 
46 #include "xml/repr.h"
47 #include "xml/attribute-record.h"
48 #include "object/sp-image.h"
49 #include "object/sp-gradient.h"
50 #include "object/sp-stop.h"
51 #include "object/sp-linear-gradient.h"
52 #include "object/sp-radial-gradient.h"
53 #include "object/sp-root.h"
54 #include "object/sp-path.h"
55 #include "object/sp-text.h"
56 #include "object/sp-flowtext.h"
57 #include "object/uri.h"
58 #include "style.h"
59 
60 #include "svg/svg.h"
61 #include "text-editing.h"
62 #include "util/units.h"
63 
64 
65 #include "inkscape-version.h"
66 #include "document.h"
67 #include "extension/extension.h"
68 
69 #include "io/stream/bufferstream.h"
70 #include "io/stream/stringstream.h"
71 #include "io/sys.h"
72 #include <util/ziptool.h>
73 #include <iomanip>
74 namespace Inkscape
75 {
76 namespace Extension
77 {
78 namespace Internal
79 {
80 //# Shorthand notation
81 typedef Inkscape::IO::BufferOutputStream BufferOutputStream;
82 typedef Inkscape::IO::OutputStreamWriter OutputStreamWriter;
83 typedef Inkscape::IO::StringOutputStream StringOutputStream;
84 
85 
86 //########################################################################
87 //# C L A S S    SingularValueDecomposition
88 //########################################################################
89 #include <cmath>
90 
91 class SVDMatrix
92 {
93 public:
94 
SVDMatrix()95     SVDMatrix()
96         {
97         init();
98         }
99 
SVDMatrix(unsigned int rowSize,unsigned int colSize)100     SVDMatrix(unsigned int rowSize, unsigned int colSize)
101         {
102         init();
103         rows = rowSize;
104         cols = colSize;
105         size = rows * cols;
106         d    = new double[size];
107         for (unsigned int i=0 ; i<size ; i++)
108             d[i] = 0.0;
109         }
110 
SVDMatrix(double * vals,unsigned int rowSize,unsigned int colSize)111     SVDMatrix(double *vals, unsigned int rowSize, unsigned int colSize)
112         {
113         init();
114         rows = rowSize;
115         cols = colSize;
116         size = rows * cols;
117         d    = new double[size];
118         for (unsigned int i=0 ; i<size ; i++)
119             d[i] = vals[i];
120         }
121 
122 
SVDMatrix(const SVDMatrix & other)123     SVDMatrix(const SVDMatrix &other)
124         {
125         init();
126         assign(other);
127         }
128 
operator =(const SVDMatrix & other)129     SVDMatrix &operator=(const SVDMatrix &other)
130         {
131         assign(other);
132         return *this;
133         }
134 
~SVDMatrix()135     virtual ~SVDMatrix()
136         {
137         delete[] d;
138         }
139 
operator ()(unsigned int row,unsigned int col)140      double& operator() (unsigned int row, unsigned int col)
141          {
142          if (row >= rows || col >= cols)
143              return badval;
144          return d[cols*row + col];
145          }
146 
operator ()(unsigned int row,unsigned int col) const147      double operator() (unsigned int row, unsigned int col) const
148          {
149          if (row >= rows || col >= cols)
150              return badval;
151          return d[cols*row + col];
152          }
153 
getRows()154      unsigned int getRows()
155          {
156          return rows;
157          }
158 
getCols()159      unsigned int getCols()
160          {
161          return cols;
162          }
163 
multiply(const SVDMatrix & other)164      SVDMatrix multiply(const SVDMatrix &other)
165          {
166          if (cols != other.rows)
167              {
168              SVDMatrix dummy;
169              return dummy;
170              }
171          SVDMatrix result(rows, other.cols);
172          for (unsigned int i=0 ; i<rows ; i++)
173              {
174              for (unsigned int j=0 ; j<other.cols ; j++)
175              {
176                  double sum = 0.0;
177                  for (unsigned int k=0 ; k<cols ; k++)
178                      {
179                      //sum += a[i][k] * b[k][j];
180                      sum += d[i*cols +k] * other(k, j);
181                      }
182                  result(i, j) = sum;
183                  }
184 
185              }
186          return result;
187          }
188 
transpose()189      SVDMatrix transpose()
190          {
191          SVDMatrix result(cols, rows);
192          for (unsigned int i=0 ; i<rows ; i++){
193              for (unsigned int j=0 ; j<cols ; j++){
194                  result(j, i) = d[i*cols + j];
195              }
196          }
197          return result;
198          }
199 
200 private:
201 
202 
init()203     virtual void init()
204         {
205         badval = 0.0;
206         d      = nullptr;
207         rows   = 0;
208         cols   = 0;
209         size   = 0;
210         }
211 
assign(const SVDMatrix & other)212      void assign(const SVDMatrix &other)
213         {
214         if (d)
215             {
216             delete[] d;
217             d = nullptr;
218             }
219         rows = other.rows;
220         cols = other.cols;
221         size = other.size;
222         badval = other.badval;
223         d = new double[size];
224         for (unsigned int i=0 ; i<size ; i++){
225             d[i] = other.d[i];
226             }
227         }
228 
229     double badval;
230 
231     double *d;
232     unsigned int rows;
233     unsigned int cols;
234     unsigned int size;
235 };
236 
237 
238 
239 /**
240  *
241  * ====================================================
242  *
243  * NOTE:
244  * This class is ported almost verbatim from the public domain
245  * JAMA Matrix package.  It is modified to handle only 3x3 matrices
246  * and our Geom::Affine affine transform class.  We give full
247  * attribution to them, along with many thanks.  JAMA can be found at:
248  *     http://math.nist.gov/javanumerics/jama
249  *
250  * ====================================================
251  *
252  * Singular Value Decomposition.
253  * <P>
254  * For an m-by-n matrix A with m >= n, the singular value decomposition is
255  * an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and
256  * an n-by-n orthogonal matrix V so that A = U*S*V'.
257  * <P>
258  * The singular values, sigma[k] = S[k][k], are ordered so that
259  * sigma[0] >= sigma[1] >= ... >= sigma[n-1].
260  * <P>
261  * The singular value decomposition always exists, so the constructor will
262  * never fail.  The matrix condition number and the effective numerical
263  * rank can be computed from this decomposition.
264  */
265 class SingularValueDecomposition
266 {
267 public:
268 
269    /** Construct the singular value decomposition
270    @param A    Rectangular matrix
271    @return     Structure to access U, S and V.
272    */
273 
SingularValueDecomposition(const SVDMatrix & mat)274     SingularValueDecomposition (const SVDMatrix &mat) :
275         A (mat),
276         U (),
277         s (nullptr),
278         s_size (0),
279         V ()
280         {
281         calculate();
282         }
283 
~SingularValueDecomposition()284     virtual ~SingularValueDecomposition()
285         {
286         delete[] s;
287         }
288 
289     /**
290      * Return the left singular vectors
291      * @return     U
292      */
293     SVDMatrix &getU();
294 
295     /**
296      * Return the right singular vectors
297      * @return     V
298      */
299     SVDMatrix &getV();
300 
301     /**
302      *  Return the s[index] value
303      */    double getS(unsigned int index);
304 
305     /**
306      * Two norm
307      * @return max(S)
308      */
309     double norm2();
310 
311     /**
312      * Two norm condition number
313      *  @return max(S)/min(S)
314      */
315     double cond();
316 
317     /**
318      *  Effective numerical matrix rank
319      *  @return     Number of nonnegligible singular values.
320      */
321     int rank();
322 
323 private:
324 
325       void calculate();
326 
327       SVDMatrix A;
328       SVDMatrix U;
329       double *s;
330       unsigned int s_size;
331       SVDMatrix V;
332 
333 };
334 
335 
svd_hypot(double a,double b)336 static double svd_hypot(double a, double b)
337 {
338     double r;
339 
340     if (fabs(a) > fabs(b))
341         {
342         r = b/a;
343         r = fabs(a) * sqrt(1+r*r);
344         }
345     else if (b != 0)
346         {
347         r = a/b;
348         r = fabs(b) * sqrt(1+r*r);
349         }
350     else
351         {
352         r = 0.0;
353         }
354     return r;
355 }
356 
357 
358 
calculate()359 void SingularValueDecomposition::calculate()
360 {
361       // Initialize.
362       int m = A.getRows();
363       int n = A.getCols();
364 
365       int nu = (m > n) ? m : n;
366       s_size = (m+1 < n) ? m+1 : n;
367       s = new double[s_size];
368       U = SVDMatrix(m, nu);
369       V = SVDMatrix(n, n);
370       double *e = new double[n];
371       double *work = new double[m];
372       bool wantu = true;
373       bool wantv = true;
374 
375       // Reduce A to bidiagonal form, storing the diagonal elements
376       // in s and the super-diagonal elements in e.
377 
378       int nct = (m-1<n) ? m-1 : n;
379       int nrtx = (n-2<m) ? n-2 : m;
380       int nrt = (nrtx>0) ? nrtx : 0;
381       for (int k = 0; k < 2; k++) {
382          if (k < nct) {
383 
384             // Compute the transformation for the k-th column and
385             // place the k-th diagonal in s[k].
386             // Compute 2-norm of k-th column without under/overflow.
387             s[k] = 0;
388             for (int i = k; i < m; i++) {
389                s[k] = svd_hypot(s[k],A(i, k));
390             }
391             if (s[k] != 0.0) {
392                if (A(k, k) < 0.0) {
393                   s[k] = -s[k];
394                }
395                for (int i = k; i < m; i++) {
396                   A(i, k) /= s[k];
397                }
398                A(k, k) += 1.0;
399             }
400             s[k] = -s[k];
401          }
402          for (int j = k+1; j < n; j++) {
403             if ((k < nct) & (s[k] != 0.0))  {
404 
405             // Apply the transformation.
406 
407                double t = 0;
408                for (int i = k; i < m; i++) {
409                   t += A(i, k) * A(i, j);
410                }
411                t = -t/A(k, k);
412                for (int i = k; i < m; i++) {
413                   A(i, j) += t*A(i, k);
414                }
415             }
416 
417             // Place the k-th row of A into e for the
418             // subsequent calculation of the row transformation.
419 
420             e[j] = A(k, j);
421          }
422          if (wantu & (k < nct)) {
423 
424             // Place the transformation in U for subsequent back
425             // multiplication.
426 
427             for (int i = k; i < m; i++) {
428                U(i, k) = A(i, k);
429             }
430          }
431          if (k < nrt) {
432 
433             // Compute the k-th row transformation and place the
434             // k-th super-diagonal in e[k].
435             // Compute 2-norm without under/overflow.
436             e[k] = 0;
437             for (int i = k+1; i < n; i++) {
438                e[k] = svd_hypot(e[k],e[i]);
439             }
440             if (e[k] != 0.0) {
441                if (e[k+1] < 0.0) {
442                   e[k] = -e[k];
443                }
444                for (int i = k+1; i < n; i++) {
445                   e[i] /= e[k];
446                }
447                e[k+1] += 1.0;
448             }
449             e[k] = -e[k];
450             if ((k+1 < m) & (e[k] != 0.0)) {
451 
452             // Apply the transformation.
453 
454                for (int i = k+1; i < m; i++) {
455                   work[i] = 0.0;
456                }
457                for (int j = k+1; j < n; j++) {
458                   for (int i = k+1; i < m; i++) {
459                      work[i] += e[j]*A(i, j);
460                   }
461                }
462                for (int j = k+1; j < n; j++) {
463                   double t = -e[j]/e[k+1];
464                   for (int i = k+1; i < m; i++) {
465                      A(i, j) += t*work[i];
466                   }
467                }
468             }
469             if (wantv) {
470 
471             // Place the transformation in V for subsequent
472             // back multiplication.
473 
474                for (int i = k+1; i < n; i++) {
475                   V(i, k) = e[i];
476                }
477             }
478          }
479       }
480 
481       // Set up the final bidiagonal matrix or order p.
482 
483       int p = (n < m+1) ? n : m+1;
484       if (nct < n) {
485          s[nct] = A(nct, nct);
486       }
487       if (m < p) {
488          s[p-1] = 0.0;
489       }
490       if (nrt+1 < p) {
491          e[nrt] = A(nrt, p-1);
492       }
493       e[p-1] = 0.0;
494 
495       // If required, generate U.
496 
497       if (wantu) {
498          for (int j = nct; j < nu; j++) {
499             for (int i = 0; i < m; i++) {
500                U(i, j) = 0.0;
501             }
502             U(j, j) = 1.0;
503          }
504          for (int k = nct-1; k >= 0; k--) {
505             if (s[k] != 0.0) {
506                for (int j = k+1; j < nu; j++) {
507                   double t = 0;
508                   for (int i = k; i < m; i++) {
509                      t += U(i, k)*U(i, j);
510                   }
511                   t = -t/U(k, k);
512                   for (int i = k; i < m; i++) {
513                      U(i, j) += t*U(i, k);
514                   }
515                }
516                for (int i = k; i < m; i++ ) {
517                   U(i, k) = -U(i, k);
518                }
519                U(k, k) = 1.0 + U(k, k);
520                for (int i = 0; i < k-1; i++) {
521                   U(i, k) = 0.0;
522                }
523             } else {
524                for (int i = 0; i < m; i++) {
525                   U(i, k) = 0.0;
526                }
527                U(k, k) = 1.0;
528             }
529          }
530       }
531 
532       // If required, generate V.
533 
534       if (wantv) {
535          for (int k = n-1; k >= 0; k--) {
536             if ((k < nrt) & (e[k] != 0.0)) {
537                for (int j = k+1; j < nu; j++) {
538                   double t = 0;
539                   for (int i = k+1; i < n; i++) {
540                      t += V(i, k)*V(i, j);
541                   }
542                   t = -t/V(k+1, k);
543                   for (int i = k+1; i < n; i++) {
544                      V(i, j) += t*V(i, k);
545                   }
546                }
547             }
548             for (int i = 0; i < n; i++) {
549                V(i, k) = 0.0;
550             }
551             V(k, k) = 1.0;
552          }
553       }
554 
555       // Main iteration loop for the singular values.
556 
557       int pp = p-1;
558       //double eps = pow(2.0,-52.0);
559       //double tiny = pow(2.0,-966.0);
560       //let's just calculate these now
561       //a double can be e ± 308.25, so this is safe
562       double eps = 2.22e-16;
563       double tiny = 1.6e-291;
564       while (p > 0) {
565          int k,kase;
566 
567          // Here is where a test for too many iterations would go.
568 
569          // This section of the program inspects for
570          // negligible elements in the s and e arrays.  On
571          // completion the variables kase and k are set as follows.
572 
573          // kase = 1     if s(p) and e[k-1] are negligible and k<p
574          // kase = 2     if s(k) is negligible and k<p
575          // kase = 3     if e[k-1] is negligible, k<p, and
576          //              s(k), ..., s(p) are not negligible (qr step).
577          // kase = 4     if e(p-1) is negligible (convergence).
578 
579          for (k = p-2; k >= -1; k--) {
580             if (k == -1) {
581                break;
582             }
583             if (fabs(e[k]) <=
584                   tiny + eps*(fabs(s[k]) + fabs(s[k+1]))) {
585                e[k] = 0.0;
586                break;
587             }
588          }
589          if (k == p-2) {
590             kase = 4;
591          } else {
592             int ks;
593             for (ks = p-1; ks >= k; ks--) {
594                if (ks == k) {
595                   break;
596                }
597                double t = (ks != p ? fabs(e[ks]) : 0.) +
598                           (ks != k+1 ? fabs(e[ks-1]) : 0.);
599                if (fabs(s[ks]) <= tiny + eps*t)  {
600                   s[ks] = 0.0;
601                   break;
602                }
603             }
604             if (ks == k) {
605                kase = 3;
606             } else if (ks == p-1) {
607                kase = 1;
608             } else {
609                kase = 2;
610                k = ks;
611             }
612          }
613          k++;
614 
615          // Perform the task indicated by kase.
616 
617          switch (kase) {
618 
619             // Deflate negligible s(p).
620 
621             case 1: {
622                double f = e[p-2];
623                e[p-2] = 0.0;
624                for (int j = p-2; j >= k; j--) {
625                   double t = svd_hypot(s[j],f);
626                   double cs = s[j]/t;
627                   double sn = f/t;
628                   s[j] = t;
629                   if (j != k) {
630                      f = -sn*e[j-1];
631                      e[j-1] = cs*e[j-1];
632                   }
633                   if (wantv) {
634                      for (int i = 0; i < n; i++) {
635                         t = cs*V(i, j) + sn*V(i, p-1);
636                         V(i, p-1) = -sn*V(i, j) + cs*V(i, p-1);
637                         V(i, j) = t;
638                      }
639                   }
640                }
641             }
642             break;
643 
644             // Split at negligible s(k).
645 
646             case 2: {
647                double f = e[k-1];
648                e[k-1] = 0.0;
649                for (int j = k; j < p; j++) {
650                   double t = svd_hypot(s[j],f);
651                   double cs = s[j]/t;
652                   double sn = f/t;
653                   s[j] = t;
654                   f = -sn*e[j];
655                   e[j] = cs*e[j];
656                   if (wantu) {
657                      for (int i = 0; i < m; i++) {
658                         t = cs*U(i, j) + sn*U(i, k-1);
659                         U(i, k-1) = -sn*U(i, j) + cs*U(i, k-1);
660                         U(i, j) = t;
661                      }
662                   }
663                }
664             }
665             break;
666 
667             // Perform one qr step.
668 
669             case 3: {
670 
671                // Calculate the shift.
672 
673                double scale = fabs(s[p-1]);
674                double d = fabs(s[p-2]);
675                if (d>scale) scale=d;
676                d = fabs(e[p-2]);
677                if (d>scale) scale=d;
678                d = fabs(s[k]);
679                if (d>scale) scale=d;
680                d = fabs(e[k]);
681                if (d>scale) scale=d;
682                double sp = s[p-1]/scale;
683                double spm1 = s[p-2]/scale;
684                double epm1 = e[p-2]/scale;
685                double sk = s[k]/scale;
686                double ek = e[k]/scale;
687                double b = ((spm1 + sp)*(spm1 - sp) + epm1*epm1)/2.0;
688                double c = (sp*epm1)*(sp*epm1);
689                double shift = 0.0;
690                if ((b != 0.0) | (c != 0.0)) {
691                   shift = sqrt(b*b + c);
692                   if (b < 0.0) {
693                      shift = -shift;
694                   }
695                   shift = c/(b + shift);
696                }
697                double f = (sk + sp)*(sk - sp) + shift;
698                double g = sk*ek;
699 
700                // Chase zeros.
701 
702                for (int j = k; j < p-1; j++) {
703                   double t = svd_hypot(f,g);
704                   double cs = f/t;
705                   double sn = g/t;
706                   if (j != k) {
707                      e[j-1] = t;
708                   }
709                   f = cs*s[j] + sn*e[j];
710                   e[j] = cs*e[j] - sn*s[j];
711                   g = sn*s[j+1];
712                   s[j+1] = cs*s[j+1];
713                   if (wantv) {
714                      for (int i = 0; i < n; i++) {
715                         t = cs*V(i, j) + sn*V(i, j+1);
716                         V(i, j+1) = -sn*V(i, j) + cs*V(i, j+1);
717                         V(i, j) = t;
718                      }
719                   }
720                   t = svd_hypot(f,g);
721                   cs = f/t;
722                   sn = g/t;
723                   s[j] = t;
724                   f = cs*e[j] + sn*s[j+1];
725                   s[j+1] = -sn*e[j] + cs*s[j+1];
726                   g = sn*e[j+1];
727                   e[j+1] = cs*e[j+1];
728                   if (wantu && (j < m-1)) {
729                      for (int i = 0; i < m; i++) {
730                         t = cs*U(i, j) + sn*U(i, j+1);
731                         U(i, j+1) = -sn*U(i, j) + cs*U(i, j+1);
732                         U(i, j) = t;
733                      }
734                   }
735                }
736                e[p-2] = f;
737             }
738             break;
739 
740             // Convergence.
741 
742             case 4: {
743 
744                // Make the singular values positive.
745 
746                if (s[k] <= 0.0) {
747                   s[k] = (s[k] < 0.0 ? -s[k] : 0.0);
748                   if (wantv) {
749                      for (int i = 0; i <= pp; i++) {
750                         V(i, k) = -V(i, k);
751                      }
752                   }
753                }
754 
755                // Order the singular values.
756 
757                while (k < pp) {
758                   if (s[k] >= s[k+1]) {
759                      break;
760                   }
761                   double t = s[k];
762                   s[k] = s[k+1];
763                   s[k+1] = t;
764                   if (wantv && (k < n-1)) {
765                      for (int i = 0; i < n; i++) {
766                         t = V(i, k+1); V(i, k+1) = V(i, k); V(i, k) = t;
767                      }
768                   }
769                   if (wantu && (k < m-1)) {
770                      for (int i = 0; i < m; i++) {
771                         t = U(i, k+1); U(i, k+1) = U(i, k); U(i, k) = t;
772                      }
773                   }
774                   k++;
775                }
776                p--;
777             }
778             break;
779          }
780       }
781 
782     delete [] e;
783     delete [] work;
784 
785 }
786 
787 
788 /**
789  * Return the left singular vectors
790  * @return     U
791  */
getU()792 SVDMatrix &SingularValueDecomposition::getU()
793 {
794     return U;
795 }
796 
797 /**
798  * Return the right singular vectors
799  * @return     V
800  */
801 
getV()802 SVDMatrix &SingularValueDecomposition::getV()
803 {
804     return V;
805 }
806 
807 /**
808  *  Return the s[0] value
809  */
getS(unsigned int index)810 double SingularValueDecomposition::getS(unsigned int index)
811 {
812     if (index >= s_size)
813         return 0.0;
814     return s[index];
815 }
816 
817 /**
818  * Two norm
819  * @return     max(S)
820  */
norm2()821 double SingularValueDecomposition::norm2()
822 {
823     return s[0];
824 }
825 
826 /**
827  * Two norm condition number
828  *  @return     max(S)/min(S)
829  */
830 
cond()831 double SingularValueDecomposition::cond()
832 {
833     return s[0]/s[2];
834 }
835 
836 /**
837  *  Effective numerical matrix rank
838  *  @return     Number of nonnegligible singular values.
839  */
rank()840 int SingularValueDecomposition::rank()
841 {
842     double eps = pow(2.0,-52.0);
843     double tol = 3.0*s[0]*eps;
844     int r = 0;
845     for (int i = 0; i < 3; i++)
846         {
847         if (s[i] > tol)
848             r++;
849         }
850     return r;
851 }
852 
853 //########################################################################
854 //# E N D    C L A S S    SingularValueDecomposition
855 //########################################################################
856 
857 
858 
859 
860 
861 //#define pxToCm  0.0275
862 #define pxToCm  0.03
863 
864 
865 //########################################################################
866 //# O U T P U T
867 //########################################################################
868 
869 /**
870  * Get the value of a node/attribute pair
871  */
getAttribute(Inkscape::XML::Node * node,char const * attrName)872 static Glib::ustring getAttribute( Inkscape::XML::Node *node, char const *attrName)
873 {
874     Glib::ustring val;
875     char const *valstr = node->attribute(attrName);
876     if (valstr)
877         val = valstr;
878     return val;
879 }
880 
881 
formatTransform(Geom::Affine & tf)882 static Glib::ustring formatTransform(Geom::Affine &tf)
883 {
884     Glib::ustring str;
885     if (!tf.isIdentity())
886         {
887         StringOutputStream outs;
888         OutputStreamWriter out(outs);
889         out.printf("matrix(%.3f %.3f %.3f %.3f %.3f %.3f)",
890                 tf[0], tf[1], tf[2], tf[3], tf[4], tf[5]);
891         str = outs.getString();
892         }
893     return str;
894 }
895 
896 
897 /**
898  * Get the general transform from SVG pixels to
899  * ODF cm
900  */
getODFTransform(const SPItem * item)901 static Geom::Affine getODFTransform(const SPItem *item)
902 {
903     //### Get SVG-to-ODF transform
904     Geom::Affine tf (item->i2doc_affine());
905     tf                   = tf * Geom::Affine(Geom::Scale(pxToCm));
906     return tf;
907 }
908 
909 
910 /**
911  * Get the bounding box of an item, as mapped onto
912  * an ODF document, in cm.
913  */
getODFBoundingBox(const SPItem * item)914 static Geom::OptRect getODFBoundingBox(const SPItem *item)
915 {
916     // TODO: geometric or visual?
917     Geom::OptRect bbox = item->documentVisualBounds();
918     if (bbox) {
919         *bbox *= Geom::Affine(Geom::Scale(pxToCm));
920     }
921     return bbox;
922 }
923 
924 
925 /**
926  * Get the transform for an item, including parents, but without
927  * root viewBox transformation.
928  */
getODFItemTransform(const SPItem * item)929 static Geom::Affine getODFItemTransform(const SPItem *item)
930 {
931     Geom::Affine itemTransform (item->i2doc_affine() *
932             SP_ACTIVE_DOCUMENT->getRoot()->c2p.inverse());
933     return itemTransform;
934 }
935 
936 
937 /**
938  * Get some fun facts from the transform
939  */
analyzeTransform(Geom::Affine & tf,double & rotate,double &,double &,double & xscale,double & yscale)940 static void analyzeTransform(Geom::Affine &tf,
941                              double &rotate, double &/*xskew*/, double &/*yskew*/,
942                              double &xscale, double &yscale)
943 {
944     SVDMatrix mat(2, 2);
945     mat(0, 0) = tf[0];
946     mat(0, 1) = tf[1];
947     mat(1, 0) = tf[2];
948     mat(1, 1) = tf[3];
949 
950     SingularValueDecomposition svd(mat);
951 
952     SVDMatrix U = svd.getU();
953     SVDMatrix V = svd.getV();
954     SVDMatrix Vt = V.transpose();
955     SVDMatrix UVt = U.multiply(Vt);
956     double s0 = svd.getS(0);
957     double s1 = svd.getS(1);
958     xscale = s0;
959     yscale = s1;
960     rotate = UVt(0,0);
961 }
962 
gatherText(Inkscape::XML::Node * node,Glib::ustring & buf)963 static void gatherText(Inkscape::XML::Node *node, Glib::ustring &buf)
964 {
965     if (node->type() == Inkscape::XML::NodeType::TEXT_NODE)
966         {
967         char *s = (char *)node->content();
968         if (s)
969             buf.append(s);
970         }
971 
972     for (Inkscape::XML::Node *child = node->firstChild() ;
973                 child != nullptr; child = child->next())
974         {
975         gatherText(child, buf);
976         }
977 
978 }
979 
980 
981 /**
982  * FIRST PASS.
983  * Method descends into the repr tree, converting image, style, and gradient info
984  * into forms compatible in ODF.
985  */
preprocess(ZipFile & zf,Inkscape::XML::Node * node)986 void OdfOutput::preprocess(ZipFile &zf, Inkscape::XML::Node *node)
987 {
988     Glib::ustring nodeName = node->name();
989     Glib::ustring id       = getAttribute(node, "id");
990 
991     //### First, check for metadata
992     if (nodeName == "metadata" || nodeName == "svg:metadata")
993         {
994         Inkscape::XML::Node *mchild = node->firstChild() ;
995         if (!mchild || strcmp(mchild->name(), "rdf:RDF"))
996             return;
997         Inkscape::XML::Node *rchild = mchild->firstChild() ;
998         if (!rchild || strcmp(rchild->name(), "cc:Work"))
999             return;
1000         for (Inkscape::XML::Node *cchild = rchild->firstChild() ;
1001             cchild ; cchild = cchild->next())
1002             {
1003             Glib::ustring ccName = cchild->name();
1004             Glib::ustring ccVal;
1005             gatherText(cchild, ccVal);
1006             //g_message("ccName: %s  ccVal:%s", ccName.c_str(), ccVal.c_str());
1007             metadata[ccName] = ccVal;
1008             }
1009         return;
1010         }
1011 
1012     //Now consider items.
1013     SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(node);
1014     if (!reprobj)
1015     {
1016         return;
1017     }
1018     if (!SP_IS_ITEM(reprobj))
1019     {
1020         return;
1021     }
1022 
1023     if (nodeName == "image" || nodeName == "svg:image") {
1024         Glib::ustring href = getAttribute(node, "xlink:href");
1025         if (href.size() > 0 && imageTable.count(href) == 0) {
1026             try {
1027                 auto uri = Inkscape::URI(href.c_str(), docBaseUri.c_str());
1028                 auto mimetype = uri.getMimeType();
1029 
1030                 if (mimetype.substr(0, 6) != "image/") {
1031                     return;
1032                 }
1033 
1034                 auto ext = mimetype.substr(6);
1035                 auto newName = Glib::ustring("Pictures/image") + std::to_string(imageTable.size()) + "." + ext;
1036 
1037                 imageTable[href] = newName;
1038 
1039                 auto ze = zf.newEntry(newName.raw(), "");
1040                 ze->setUncompressedData(uri.getContents());
1041                 ze->finish();
1042             } catch (...) {
1043                 g_warning("Could not handle URI '%.100s'", href.c_str());
1044             }
1045         }
1046     }
1047 
1048     for (Inkscape::XML::Node *child = node->firstChild() ;
1049             child ; child = child->next())
1050         preprocess(zf, child);
1051 }
1052 
1053 
1054 /**
1055  * Writes the manifest.  Currently it only changes according to the
1056  * file names of images packed into the zip file.
1057  */
writeManifest(ZipFile & zf)1058 bool OdfOutput::writeManifest(ZipFile &zf)
1059 {
1060     BufferOutputStream bouts;
1061     OutputStreamWriter outs(bouts);
1062 
1063     time_t tim;
1064     time(&tim);
1065 
1066     outs.writeString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1067     outs.writeString("<!DOCTYPE manifest:manifest PUBLIC \"-//OpenOffice.org//DTD Manifest 1.0//EN\" \"Manifest.dtd\">\n");
1068     outs.writeString("\n");
1069     outs.writeString("\n");
1070     outs.writeString("<!--\n");
1071     outs.writeString("*************************************************************************\n");
1072     outs.writeString("  file:  manifest.xml\n");
1073     outs.printf     ("  Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
1074     outs.writeString("  http://www.inkscape.org\n");
1075     outs.writeString("*************************************************************************\n");
1076     outs.writeString("-->\n");
1077     outs.writeString("\n");
1078     outs.writeString("\n");
1079     outs.writeString("<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\">\n");
1080     outs.writeString("    <manifest:file-entry manifest:media-type=\"application/vnd.oasis.opendocument.graphics\" manifest:full-path=\"/\"/>\n");
1081     outs.writeString("    <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/>\n");
1082     outs.writeString("    <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"styles.xml\"/>\n");
1083     outs.writeString("    <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"meta.xml\"/>\n");
1084     outs.writeString("    <!--List our images here-->\n");
1085     std::map<Glib::ustring, Glib::ustring>::iterator iter;
1086     for (iter = imageTable.begin() ; iter!=imageTable.end() ; ++iter)
1087         {
1088         Glib::ustring newName = iter->second;
1089 
1090         // note: mime subtype was added as file extension in OdfOutput::preprocess
1091         Glib::ustring mimesubtype = Inkscape::IO::get_file_extension(newName);
1092 
1093         outs.printf("    <manifest:file-entry manifest:media-type=\"");
1094         outs.printf("image/");
1095         outs.printf("%s", mimesubtype.c_str());
1096         outs.printf("\" manifest:full-path=\"");
1097         outs.writeString(newName.c_str());
1098         outs.printf("\"/>\n");
1099         }
1100     outs.printf("</manifest:manifest>\n");
1101 
1102     outs.close();
1103 
1104     //Make our entry
1105     ZipEntry *ze = zf.newEntry("META-INF/manifest.xml", "ODF file manifest");
1106     ze->setUncompressedData(bouts.getBuffer());
1107     ze->finish();
1108 
1109     return true;
1110 }
1111 
1112 
1113 /**
1114  * This writes the document meta information to meta.xml
1115  */
writeMeta(ZipFile & zf)1116 bool OdfOutput::writeMeta(ZipFile &zf)
1117 {
1118     BufferOutputStream bouts;
1119     OutputStreamWriter outs(bouts);
1120 
1121     time_t tim;
1122     time(&tim);
1123 
1124     std::map<Glib::ustring, Glib::ustring>::iterator iter;
1125     Glib::ustring InkscapeVersion = Glib::ustring("Inkscape.org - ") + Inkscape::version_string;
1126     Glib::ustring creator = InkscapeVersion;
1127     iter = metadata.find("dc:creator");
1128     if (iter != metadata.end())
1129     {
1130         creator = iter->second;
1131     }
1132 
1133     Glib::ustring date;
1134     Glib::ustring moddate;
1135     char buf [80];
1136     time_t rawtime;
1137     struct tm * timeinfo;
1138     time (&rawtime);
1139     timeinfo = localtime (&rawtime);
1140     strftime (buf,80,"%Y-%m-%d %H:%M:%S",timeinfo);
1141     moddate = Glib::ustring(buf);
1142 
1143     iter = metadata.find("dc:date");
1144     if (iter != metadata.end())
1145     {
1146         date = iter->second;
1147     }
1148     else
1149     {
1150         date = moddate;
1151     }
1152 
1153     outs.writeString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1154     outs.writeString("\n");
1155     outs.writeString("<!--\n");
1156     outs.writeString("*************************************************************************\n");
1157     outs.writeString("  file:  meta.xml\n");
1158     outs.printf     ("  Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
1159     outs.writeString("  http://www.inkscape.org\n");
1160     outs.writeString("*************************************************************************\n");
1161     outs.writeString("-->\n");
1162     outs.writeString("\n");
1163     outs.writeString("<office:document-meta\n");
1164     outs.writeString("xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
1165     outs.writeString("xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
1166     outs.writeString("xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
1167     outs.writeString("xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
1168     outs.writeString("xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
1169     outs.writeString("xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
1170     outs.writeString("xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
1171     outs.writeString("xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
1172     outs.writeString("office:version=\"1.0\">\n");
1173     outs.writeString("<office:meta>\n");
1174     Glib::ustring tmp = Glib::ustring::compose("    <meta:generator>%1</meta:generator>\n", InkscapeVersion);
1175     tmp += Glib::ustring::compose("    <meta:initial-creator>%1</meta:initial-creator>\n", creator);
1176     tmp += Glib::ustring::compose("    <meta:creation-date>%1</meta:creation-date>\n", date);
1177     tmp += Glib::ustring::compose("    <dc:date>%1</dc:date>\n", moddate);
1178     outs.writeUString(tmp);
1179     for (iter = metadata.begin() ; iter != metadata.end() ; ++iter)
1180     {
1181         Glib::ustring name  = iter->first;
1182         Glib::ustring value = iter->second;
1183         if (!name.empty() && !value.empty())
1184         {
1185             tmp = Glib::ustring::compose("    <%1>%2</%3>\n", name, value, name);
1186             outs.writeUString(tmp);
1187         }
1188     }
1189     // outs.writeString("    <meta:editing-cycles>2</meta:editing-cycles>\n");
1190     // outs.writeString("    <meta:editing-duration>PT56S</meta:editing-duration>\n");
1191     // outs.writeString("    <meta:user-defined meta:name=\"Info 1\"/>\n");
1192     // outs.writeString("    <meta:user-defined meta:name=\"Info 2\"/>\n");
1193     // outs.writeString("    <meta:user-defined meta:name=\"Info 3\"/>\n");
1194     // outs.writeString("    <meta:user-defined meta:name=\"Info 4\"/>\n");
1195     // outs.writeString("    <meta:document-statistic meta:object-count=\"2\"/>\n");
1196     outs.writeString("</office:meta>\n");
1197     outs.writeString("</office:document-meta>\n");
1198     outs.close();
1199 
1200     //Make our entry
1201     ZipEntry *ze = zf.newEntry("meta.xml", "ODF info file");
1202     ze->setUncompressedData(bouts.getBuffer());
1203     ze->finish();
1204 
1205     return true;
1206 }
1207 
1208 
1209 /**
1210  * Writes an SVG path as an ODF <draw:path> and returns the number of points written
1211  */
1212 static int
writePath(Writer & outs,Geom::PathVector const & pathv,Geom::Affine const & tf,double xoff,double yoff)1213 writePath(Writer &outs, Geom::PathVector const &pathv,
1214           Geom::Affine const &tf, double xoff, double yoff)
1215 {
1216     using Geom::X;
1217     using Geom::Y;
1218 
1219     int nrPoints  = 0;
1220 
1221     // convert the path to only lineto's and cubic curveto's:
1222     Geom::PathVector pv = pathv_to_linear_and_cubic_beziers(pathv * tf * Geom::Translate(xoff, yoff) * Geom::Scale(1000.));
1223 
1224         for (const auto & pit : pv) {
1225 
1226             double destx = pit.initialPoint()[X];
1227             double desty = pit.initialPoint()[Y];
1228             if (fabs(destx)<1.0) destx = 0.0;   // Why is this needed? Shouldn't we just round all numbers then?
1229             if (fabs(desty)<1.0) desty = 0.0;
1230             outs.printf("M %.3f %.3f ", destx, desty);
1231             nrPoints++;
1232 
1233             for (Geom::Path::const_iterator cit = pit.begin(); cit != pit.end_closed(); ++cit) {
1234 
1235                 if( is_straight_curve(*cit) )
1236                 {
1237                     double destx = cit->finalPoint()[X];
1238                     double desty = cit->finalPoint()[Y];
1239                     if (fabs(destx)<1.0) destx = 0.0;   // Why is this needed? Shouldn't we just round all numbers then?
1240                     if (fabs(desty)<1.0) desty = 0.0;
1241                     outs.printf("L %.3f %.3f ",  destx, desty);
1242                 }
1243                 else if(Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const*>(&*cit)) {
1244                     std::vector<Geom::Point> points = cubic->controlPoints();
1245                     for (unsigned i = 1; i <= 3; i++) {
1246                         if (fabs(points[i][X])<1.0) points[i][X] = 0.0;   // Why is this needed? Shouldn't we just round all numbers then?
1247                         if (fabs(points[i][Y])<1.0) points[i][Y] = 0.0;
1248                     }
1249                     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]);
1250                 }
1251                 else {
1252                     g_error ("logical error, because pathv_to_linear_and_cubic_beziers was used");
1253                 }
1254 
1255                 nrPoints++;
1256             }
1257 
1258             if (pit.closed()) {
1259                 outs.printf("Z");
1260             }
1261         }
1262 
1263     return nrPoints;
1264 }
1265 
processStyle(SPItem * item,const Glib::ustring & id,const Glib::ustring & gradientNameFill,const Glib::ustring & gradientNameStroke,Glib::ustring & output)1266 bool OdfOutput::processStyle(SPItem *item, const Glib::ustring &id, const Glib::ustring &gradientNameFill, const Glib::ustring &gradientNameStroke, Glib::ustring& output)
1267 {
1268     output.clear();
1269     if (!item)
1270     {
1271         return false;
1272     }
1273 
1274     SPStyle *style = item->style;
1275     if (!style)
1276     {
1277         return false;
1278     }
1279 
1280     StyleInfo si;
1281 
1282     // FILL
1283     if (style->fill.isColor())
1284     {
1285         guint32 fillCol = style->fill.value.color.toRGBA32( 0 );
1286         char buf[16];
1287         int r = (fillCol >> 24) & 0xff;
1288         int g = (fillCol >> 16) & 0xff;
1289         int b = (fillCol >>  8) & 0xff;
1290         snprintf(buf, 15, "#%02x%02x%02x", r, g, b);
1291         si.fillColor = buf;
1292         si.fill      = "solid";
1293         double opacityPercent = 100.0 *
1294              (SP_SCALE24_TO_FLOAT(style->fill_opacity.value));
1295         snprintf(buf, 15, "%.3f%%", opacityPercent);
1296         si.fillOpacity = buf;
1297     }
1298     else if (style->fill.isPaintserver())
1299     {
1300         SPGradient *gradient = SP_GRADIENT(SP_STYLE_FILL_SERVER(style));
1301         if (gradient)
1302         {
1303             si.fill = "gradient";
1304         }
1305     }
1306 
1307     // STROKE
1308     if (style->stroke.isColor())
1309     {
1310         guint32 strokeCol = style->stroke.value.color.toRGBA32( 0 );
1311         char buf[16];
1312         int r = (strokeCol >> 24) & 0xff;
1313         int g = (strokeCol >> 16) & 0xff;
1314         int b = (strokeCol >>  8) & 0xff;
1315         snprintf(buf, 15, "#%02x%02x%02x", r, g, b);
1316         si.strokeColor = buf;
1317         snprintf(buf, 15, "%.3fpt", style->stroke_width.value);
1318         si.strokeWidth = buf;
1319         si.stroke      = "solid";
1320         double opacityPercent = 100.0 *
1321              (SP_SCALE24_TO_FLOAT(style->stroke_opacity.value));
1322         snprintf(buf, 15, "%.3f%%", opacityPercent);
1323         si.strokeOpacity = buf;
1324     }
1325     else if (style->stroke.isPaintserver())
1326     {
1327         SPGradient *gradient = SP_GRADIENT(SP_STYLE_STROKE_SERVER(style));
1328         if (gradient)
1329         {
1330             si.stroke = "gradient";
1331         }
1332     }
1333 
1334     //Look for existing identical style;
1335     bool styleMatch = false;
1336     std::vector<StyleInfo>::iterator iter;
1337     for (iter=styleTable.begin() ; iter!=styleTable.end() ; ++iter)
1338     {
1339         if (si.equals(*iter))
1340         {
1341             //map to existing styleTable entry
1342             Glib::ustring styleName = iter->name;
1343             styleLookupTable[id] = styleName;
1344             styleMatch = true;
1345             break;
1346         }
1347     }
1348 
1349     // Don't need a new style
1350     if (styleMatch)
1351     {
1352         return false;
1353     }
1354 
1355     Glib::ustring styleName = Glib::ustring::compose("style%1", styleTable.size());
1356     si.name = styleName;
1357     styleTable.push_back(si);
1358     styleLookupTable[id] = styleName;
1359 
1360     output = Glib::ustring::compose ("<style:style style:name=\"%1\" style:family=\"graphic\" style:parent-style-name=\"standard\">\n", si.name);
1361     output += "<style:graphic-properties";
1362     if (si.fill == "gradient")
1363     {
1364         output += Glib::ustring::compose (" draw:fill=\"gradient\" draw:fill-gradient-name=\"%1\"", gradientNameFill);
1365     }
1366     else
1367     {
1368         output += Glib::ustring(" draw:fill=\"") + si.fill + "\"";
1369         if(si.fill != "none")
1370         {
1371             output += Glib::ustring::compose(" draw:fill-color=\"%1\"", si.fillColor);
1372         }
1373     }
1374     if (si.stroke == "gradient")
1375     {
1376         //does not seem to be supported by Open Office.org
1377         output += Glib::ustring::compose (" draw:stroke=\"gradient\" draw:stroke-gradient-name=\"%1\"", gradientNameStroke);
1378     }
1379     else
1380     {
1381         output += Glib::ustring(" draw:stroke=\"") + si.stroke + "\"";
1382         if (si.stroke != "none")
1383         {
1384             output += Glib::ustring::compose (" svg:stroke-width=\"%1\" svg:stroke-color=\"%2\" ", si.strokeWidth, si.strokeColor);
1385         }
1386     }
1387     output += "/>\n</style:style>\n";
1388 
1389     return true;
1390 }
1391 
processGradient(SPItem * item,const Glib::ustring & id,Geom::Affine &,Glib::ustring & gradientName,Glib::ustring & output,bool checkFillGradient)1392 bool OdfOutput::processGradient(SPItem *item,
1393                                 const Glib::ustring &id, Geom::Affine &/*tf*/,
1394                                 Glib::ustring& gradientName, Glib::ustring& output, bool checkFillGradient)
1395 {
1396     output.clear();
1397     if (!item)
1398     {
1399         return false;
1400     }
1401 
1402     SPStyle *style = item->style;
1403     if (!style)
1404     {
1405         return false;
1406     }
1407 
1408     if ((checkFillGradient? (!style->fill.isPaintserver()) : (!style->stroke.isPaintserver())))
1409     {
1410         return false;
1411     }
1412 
1413     //## Gradient
1414     SPGradient *gradient = SP_GRADIENT((checkFillGradient?(SP_STYLE_FILL_SERVER(style)) :(SP_STYLE_STROKE_SERVER(style))));
1415 
1416     if (gradient == nullptr)
1417     {
1418         return false;
1419     }
1420     GradientInfo gi;
1421     SPGradient *grvec = gradient->getVector(FALSE);
1422     for (SPStop *stop = grvec->getFirstStop();
1423          stop ; stop = stop->getNextStop())
1424     {
1425         unsigned long rgba = stop->get_rgba32();
1426         unsigned long rgb  = (rgba >> 8) & 0xffffff;
1427         double opacity     = (static_cast<double>(rgba & 0xff)) / 256.0;
1428         GradientStop gs(rgb, opacity);
1429         gi.stops.push_back(gs);
1430     }
1431 
1432     Glib::ustring gradientName2;
1433     if (SP_IS_LINEARGRADIENT(gradient))
1434     {
1435         gi.style = "linear";
1436         SPLinearGradient *linGrad = SP_LINEARGRADIENT(gradient);
1437         gi.x1 = linGrad->x1.value;
1438         gi.y1 = linGrad->y1.value;
1439         gi.x2 = linGrad->x2.value;
1440         gi.y2 = linGrad->y2.value;
1441         gradientName2 = Glib::ustring::compose("ImportedLinearGradient%1", gradientTable.size());
1442     }
1443     else if (SP_IS_RADIALGRADIENT(gradient))
1444     {
1445         gi.style = "radial";
1446         SPRadialGradient *radGrad = SP_RADIALGRADIENT(gradient);
1447         Geom::OptRect bbox = item->documentVisualBounds();
1448         gi.cx = (radGrad->cx.value-bbox->left())/bbox->width();
1449         gi.cy = (radGrad->cy.value-bbox->top())/bbox->height();
1450         gradientName2 = Glib::ustring::compose("ImportedRadialGradient%1", gradientTable.size());
1451     }
1452     else
1453     {
1454         g_warning("not a supported gradient type");
1455         return false;
1456     }
1457 
1458     //Look for existing identical style;
1459     bool gradientMatch = false;
1460     std::vector<GradientInfo>::iterator iter;
1461     for (iter=gradientTable.begin() ; iter!=gradientTable.end() ; ++iter)
1462     {
1463         if (gi.equals(*iter))
1464         {
1465             //map to existing gradientTable entry
1466             gradientName = iter->name;
1467             gradientLookupTable[id] = gradientName;
1468             gradientMatch = true;
1469             break;
1470         }
1471     }
1472 
1473     if (gradientMatch)
1474     {
1475         return true;
1476     }
1477 
1478     // No match, let us write a new entry
1479     gradientName = gradientName2;
1480     gi.name = gradientName;
1481     gradientTable.push_back(gi);
1482     gradientLookupTable[id] = gradientName;
1483 
1484     // int gradientCount = gradientTable.size();
1485     char buf[128];
1486     if (gi.style == "linear")
1487     {
1488         /*
1489         ===================================================================
1490         LINEAR gradient.  We need something that looks like this:
1491         <draw:gradient draw:name="Gradient_20_7"
1492             draw:display-name="Gradient 7"
1493             draw:style="linear"
1494             draw:start-color="#008080" draw:end-color="#993366"
1495             draw:start-intensity="100%" draw:end-intensity="100%"
1496             draw:angle="150" draw:border="0%"/>
1497         ===================================================================
1498         */
1499         if (gi.stops.size() < 2)
1500         {
1501             g_warning("Need at least 2 stops for a linear gradient");
1502             return false;
1503         }
1504         output += Glib::ustring::compose("<draw:gradient draw:name=\"%1\"", gi.name);
1505         output += Glib::ustring::compose(" draw:display-name=\"%1\"", gi.name);
1506         output += " draw:style=\"linear\"";
1507         snprintf(buf, 127, " draw:start-color=\"#%06lx\" draw:end-color=\"#%06lx\"", gi.stops[0].rgb, gi.stops[1].rgb);
1508         output += buf;
1509         //TODO: apply maths, to define begin of gradient, taking gradient begin and end, as well as object boundary into account
1510         double angle = (gi.y2-gi.y1);
1511         angle = (angle != 0.) ? (atan((gi.x2-gi.x1)/(gi.y2-gi.y1))* 180. / M_PI) : 90;
1512         angle = (angle < 0)?(180+angle):angle;
1513         angle = angle * 10; //why do we need this: precision?????????????
1514         output += Glib::ustring::compose(" draw:start-intensity=\"%1\" draw:end-intensity=\"%2\" draw:angle=\"%3\"/>\n",
1515             gi.stops[0].opacity * 100.0, gi.stops[1].opacity * 100.0, angle);// draw:border=\"0%%\"
1516     }
1517     else if (gi.style == "radial")
1518     {
1519         /*
1520         ===================================================================
1521         RADIAL gradient.  We need something that looks like this:
1522         <!-- radial gradient, light gray to white, centered, 0% border -->
1523         <draw:gradient draw:name="radial_20_borderless"
1524             draw:display-name="radial borderless"
1525             draw:style="radial"
1526             draw:cx="50%" draw:cy="50%"
1527             draw:start-color="#999999" draw:end-color="#ffffff"
1528             draw:border="0%"/>
1529         ===================================================================
1530         */
1531         if (gi.stops.size() < 2)
1532         {
1533             g_warning("Need at least 2 stops for a radial gradient");
1534             return false;
1535         }
1536         output += Glib::ustring::compose("<draw:gradient draw:name=\"%1\" draw:display-name=\"%1\" ", gi.name);
1537         snprintf(buf, 127, "draw:cx=\"%05.3f\" draw:cy=\"%05.3f\" ", gi.cx*100, gi.cy*100);
1538         output += Glib::ustring("draw:style=\"radial\" ") + buf;
1539         snprintf(buf, 127, "draw:start-color=\"#%06lx\" draw:end-color=\"#%06lx\" ", gi.stops[0].rgb, gi.stops[1].rgb);
1540         output += buf;
1541         snprintf(buf, 127, "draw:start-intensity=\"%f%%\" draw:end-intensity=\"%f%%\" ", gi.stops[0].opacity*100.0, gi.stops[1].opacity*100.0);
1542         output += buf;
1543         output += "/>\n";//draw:border=\"0%\"
1544     }
1545     else
1546     {
1547         g_warning("unsupported gradient style '%s'", gi.style.c_str());
1548         return false;
1549     }
1550     return true;
1551 }
1552 
1553 
1554 /**
1555  * SECOND PASS.
1556  * This is the main SPObject tree output to ODF.
1557  */
writeTree(Writer & couts,Writer & souts,Inkscape::XML::Node * node)1558 bool OdfOutput::writeTree(Writer &couts, Writer &souts,
1559                           Inkscape::XML::Node *node)
1560 {
1561     //# Get the SPItem, if applicable
1562     SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(node);
1563     if (!reprobj)
1564     {
1565         return true;
1566     }
1567     if (!SP_IS_ITEM(reprobj))
1568     {
1569         return true;
1570     }
1571     SPItem *item = SP_ITEM(reprobj);
1572 
1573     Glib::ustring nodeName = node->name();
1574     Glib::ustring id       = getAttribute(node, "id");
1575     Geom::Affine tf        = getODFTransform(item);//Get SVG-to-ODF transform
1576     Geom::OptRect bbox = getODFBoundingBox(item);//Get ODF bounding box params for item
1577     if (!bbox) {
1578         return true;
1579     }
1580 
1581     double bbox_x        = bbox->min()[Geom::X];
1582     double bbox_y        = bbox->min()[Geom::Y];
1583     double bbox_width    = (*bbox)[Geom::X].extent();
1584     double bbox_height   = (*bbox)[Geom::Y].extent();
1585 
1586     double rotate;
1587     double xskew;
1588     double yskew;
1589     double xscale;
1590     double yscale;
1591     analyzeTransform(tf, rotate, xskew, yskew, xscale, yscale);
1592 
1593     //# Do our stuff
1594 
1595     if (nodeName == "svg" || nodeName == "svg:svg")
1596     {
1597         //# Iterate through the children
1598         for (Inkscape::XML::Node *child = node->firstChild() ;
1599                child ; child = child->next())
1600         {
1601             if (!writeTree(couts, souts, child))
1602             {
1603                 return false;
1604             }
1605         }
1606         return true;
1607     }
1608     else if (nodeName == "g" || nodeName == "svg:g")
1609     {
1610         if (!id.empty())
1611         {
1612             couts.printf("<draw:g id=\"%s\">\n", id.c_str());
1613         }
1614         else
1615         {
1616             couts.printf("<draw:g>\n");
1617         }
1618         //# Iterate through the children
1619         for (Inkscape::XML::Node *child = node->firstChild() ;
1620                child ; child = child->next())
1621         {
1622             if (!writeTree(couts, souts, child))
1623             {
1624                 return false;
1625             }
1626         }
1627         if (!id.empty())
1628         {
1629             couts.printf("</draw:g> <!-- id=\"%s\" -->\n", id.c_str());
1630         }
1631         else
1632         {
1633             couts.printf("</draw:g>\n");
1634         }
1635         return true;
1636     }
1637 
1638     //# GRADIENT
1639     Glib::ustring gradientNameFill;
1640     Glib::ustring gradientNameStroke;
1641     Glib::ustring outputFill;
1642     Glib::ustring outputStroke;
1643     Glib::ustring outputStyle;
1644 
1645     processGradient(item, id, tf, gradientNameFill, outputFill, true);
1646     processGradient(item, id, tf, gradientNameStroke, outputStroke, false);
1647     souts.writeUString(outputFill);
1648     souts.writeUString(outputStroke);
1649 
1650     //# STYLE
1651     processStyle(item, id, gradientNameFill, gradientNameStroke, outputStyle);
1652     souts.writeUString(outputStyle);
1653 
1654     //# ITEM DATA
1655     if (nodeName == "image" || nodeName == "svg:image")
1656     {
1657         if (!SP_IS_IMAGE(item))
1658         {
1659             g_warning("<image> is not an SPImage.");
1660             return false;
1661         }
1662 
1663         SPImage *img   = SP_IMAGE(item);
1664         double ix      = img->x.value;
1665         double iy      = img->y.value;
1666         double iwidth  = img->width.value;
1667         double iheight = img->height.value;
1668 
1669         Geom::Point ibbox_min = Geom::Point(ix, iy) * tf;
1670         ix      = ibbox_min.x();
1671         iy      = ibbox_min.y();
1672         iwidth  = xscale * iwidth;
1673         iheight = yscale * iheight;
1674 
1675         Geom::Affine itemTransform = getODFItemTransform(item);
1676 
1677         Glib::ustring itemTransformString = formatTransform(itemTransform);
1678 
1679         Glib::ustring href = getAttribute(node, "xlink:href");
1680         std::map<Glib::ustring, Glib::ustring>::iterator iter = imageTable.find(href);
1681         if (iter == imageTable.end())
1682         {
1683             g_warning("image '%s' not in table", href.c_str());
1684             return false;
1685         }
1686         Glib::ustring newName = iter->second;
1687 
1688         couts.printf("<draw:frame ");
1689         if (!id.empty())
1690         {
1691             couts.printf("id=\"%s\" ", id.c_str());
1692         }
1693         couts.printf("draw:style-name=\"gr1\" draw:text-style-name=\"P1\" draw:layer=\"layout\" ");
1694         //no x or y.  make them the translate transform, last one
1695         couts.printf("svg:width=\"%.3fcm\" svg:height=\"%.3fcm\" ",
1696                                   iwidth, iheight);
1697         if (!itemTransformString.empty())
1698         {
1699             couts.printf("draw:transform=\"%s translate(%.3fcm, %.3fcm)\" ",
1700                            itemTransformString.c_str(), ix, iy);
1701         }
1702         else
1703         {
1704             couts.printf("draw:transform=\"translate(%.3fcm, %.3fcm)\" ", ix, iy);
1705         }
1706 
1707         couts.writeString(">\n");
1708         couts.printf("    <draw:image xlink:href=\"%s\" xlink:type=\"simple\"\n",
1709                               newName.c_str());
1710         couts.writeString("        xlink:show=\"embed\" xlink:actuate=\"onLoad\">\n");
1711         couts.writeString("        <text:p/>\n");
1712         couts.writeString("    </draw:image>\n");
1713         couts.writeString("</draw:frame>\n");
1714         return true;
1715     }
1716 
1717     std::unique_ptr<SPCurve> curve;
1718 
1719     if (auto shape = dynamic_cast<SPShape const *>(item)) {
1720         curve = SPCurve::copy(shape->curve());
1721     } else if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item)) {
1722         curve = te_get_layout(item)->convertToCurves();
1723     }
1724 
1725     if (curve)
1726     {
1727         //### Default <path> output
1728         couts.writeString("<draw:path ");
1729         if (!id.empty())
1730         {
1731             couts.printf("id=\"%s\" ", id.c_str());
1732         }
1733 
1734         std::map<Glib::ustring, Glib::ustring>::iterator siter;
1735         siter = styleLookupTable.find(id);
1736         if (siter != styleLookupTable.end())
1737         {
1738             Glib::ustring styleName = siter->second;
1739             couts.printf("draw:style-name=\"%s\" ", styleName.c_str());
1740         }
1741 
1742         couts.printf("draw:layer=\"layout\" svg:x=\"%.3fcm\" svg:y=\"%.3fcm\" ",
1743                        bbox_x, bbox_y);
1744         couts.printf("svg:width=\"%.3fcm\" svg:height=\"%.3fcm\" ",
1745                        bbox_width, bbox_height);
1746         couts.printf("svg:viewBox=\"0.0 0.0 %.3f %.3f\"",
1747                        bbox_width * 1000.0, bbox_height * 1000.0);
1748 
1749         couts.printf(" svg:d=\"");
1750         int nrPoints = writePath(couts, curve->get_pathvector(),
1751                              tf, bbox_x, bbox_y);
1752         couts.writeString("\"");
1753 
1754         couts.writeString(">\n");
1755         couts.printf("    <!-- %d nodes -->\n", nrPoints);
1756         couts.writeString("</draw:path>\n\n");
1757     }
1758 
1759     return true;
1760 }
1761 
1762 
1763 /**
1764  * Write the header for the content.xml file
1765  */
writeStyleHeader(Writer & outs)1766 bool OdfOutput::writeStyleHeader(Writer &outs)
1767 {
1768     time_t tim;
1769     time(&tim);
1770 
1771     outs.writeString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1772     outs.writeString("\n");
1773     outs.writeString("<!--\n");
1774     outs.writeString("*************************************************************************\n");
1775     outs.writeString("  file:  styles.xml\n");
1776     outs.printf     ("  Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
1777     outs.writeString("  http://www.inkscape.org\n");
1778     outs.writeString("*************************************************************************\n");
1779     outs.writeString("-->\n");
1780     outs.writeString("\n");
1781     outs.writeString("<office:document-styles\n");
1782     outs.writeString("    xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
1783     outs.writeString("    xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"\n");
1784     outs.writeString("    xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"\n");
1785     outs.writeString("    xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"\n");
1786     outs.writeString("    xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"\n");
1787     outs.writeString("    xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"\n");
1788     outs.writeString("    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
1789     outs.writeString("    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
1790     outs.writeString("    xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
1791     outs.writeString("    xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"\n");
1792     outs.writeString("    xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
1793     outs.writeString("    xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"\n");
1794     outs.writeString("    xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"\n");
1795     outs.writeString("    xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"\n");
1796     outs.writeString("    xmlns:math=\"http://www.w3.org/1998/Math/MathML\"\n");
1797     outs.writeString("    xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"\n");
1798     outs.writeString("    xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"\n");
1799     outs.writeString("    xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
1800     outs.writeString("    xmlns:ooow=\"http://openoffice.org/2004/writer\"\n");
1801     outs.writeString("    xmlns:oooc=\"http://openoffice.org/2004/calc\"\n");
1802     outs.writeString("    xmlns:dom=\"http://www.w3.org/2001/xml-events\"\n");
1803     outs.writeString("    xmlns:xforms=\"http://www.w3.org/2002/xforms\"\n");
1804     outs.writeString("    xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n");
1805     outs.writeString("    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
1806     outs.writeString("    xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
1807     outs.writeString("    xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
1808     outs.writeString("    office:version=\"1.0\">\n");
1809     outs.writeString("\n");
1810     outs.writeString("<!--\n");
1811     outs.writeString("*************************************************************************\n");
1812     outs.writeString("  S T Y L E S\n");
1813     outs.writeString("  Style entries have been pulled from the svg style and\n");
1814     outs.writeString("  representation attributes in the SVG tree.  The tree elements\n");
1815     outs.writeString("  then refer to them by name, in the ODF manner\n");
1816     outs.writeString("*************************************************************************\n");
1817     outs.writeString("-->\n");
1818     outs.writeString("\n");
1819     outs.writeString("<office:styles>\n");
1820     outs.writeString("\n");
1821 
1822     return true;
1823 }
1824 
1825 
1826 /**
1827  * Write the footer for the style.xml file
1828  */
writeStyleFooter(Writer & outs)1829 bool OdfOutput::writeStyleFooter(Writer &outs)
1830 {
1831     outs.writeString("\n");
1832     outs.writeString("</office:styles>\n");
1833     outs.writeString("\n");
1834     outs.writeString("<office:automatic-styles>\n");
1835     outs.writeString("<!-- ####### 'Standard' styles ####### -->\n");
1836     outs.writeString("<style:style style:name=\"dp1\" style:family=\"drawing-page\"/>\n");
1837     outs.writeString("<style:style style:name=\"standard\" style:family=\"graphic\">\n");
1838 
1839 ///TODO: add default document style here
1840 
1841     outs.writeString("</style:style>\n");
1842     outs.writeString("<style:style style:name=\"gr1\" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");
1843     outs.writeString("  <style:graphic-properties draw:stroke=\"none\" draw:fill=\"none\"\n");
1844     outs.writeString("       draw:textarea-horizontal-align=\"center\"\n");
1845     outs.writeString("       draw:textarea-vertical-align=\"middle\" draw:color-mode=\"standard\"\n");
1846     outs.writeString("       draw:luminance=\"0%\" draw:contrast=\"0%\" draw:gamma=\"100%\" draw:red=\"0%\"\n");
1847     outs.writeString("       draw:green=\"0%\" draw:blue=\"0%\" fo:clip=\"rect(0cm 0cm 0cm 0cm)\"\n");
1848     outs.writeString("       draw:image-opacity=\"100%\" style:mirror=\"none\"/>\n");
1849     outs.writeString("</style:style>\n");
1850     outs.writeString("<style:style style:name=\"P1\" style:family=\"paragraph\">\n");
1851     outs.writeString("  <style:paragraph-properties fo:text-align=\"center\"/>\n");
1852     outs.writeString("</style:style>\n");
1853     outs.writeString("</office:automatic-styles>\n");
1854     outs.writeString("\n");
1855     outs.writeString("<office:master-styles>\n");
1856     outs.writeString("<draw:layer-set>\n");
1857     outs.writeString("    <draw:layer draw:name=\"layout\"/>\n");
1858     outs.writeString("    <draw:layer draw:name=\"background\"/>\n");
1859     outs.writeString("    <draw:layer draw:name=\"backgroundobjects\"/>\n");
1860     outs.writeString("    <draw:layer draw:name=\"controls\"/>\n");
1861     outs.writeString("    <draw:layer draw:name=\"measurelines\"/>\n");
1862     outs.writeString("</draw:layer-set>\n");
1863     outs.writeString("\n");
1864     outs.writeString("<style:master-page style:name=\"Default\"\n");
1865     outs.writeString("    style:page-master-name=\"PM1\" draw:style-name=\"dp1\"/>\n");
1866     outs.writeString("</office:master-styles>\n");
1867     outs.writeString("\n");
1868     outs.writeString("</office:document-styles>\n");
1869 
1870     return true;
1871 }
1872 
1873 
1874 /**
1875  * Write the header for the content.xml file
1876  */
writeContentHeader(Writer & outs)1877 bool OdfOutput::writeContentHeader(Writer &outs)
1878 {
1879     time_t tim;
1880     time(&tim);
1881 
1882     outs.writeString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1883     outs.writeString("\n");
1884     outs.writeString("<!--\n");
1885     outs.writeString("*************************************************************************\n");
1886     outs.writeString("  file:  content.xml\n");
1887     outs.printf     ("  Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
1888     outs.writeString("  http://www.inkscape.org\n");
1889     outs.writeString("*************************************************************************\n");
1890     outs.writeString("-->\n");
1891     outs.writeString("\n");
1892     outs.writeString("<office:document-content\n");
1893     outs.writeString("    xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
1894     outs.writeString("    xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"\n");
1895     outs.writeString("    xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"\n");
1896     outs.writeString("    xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"\n");
1897     outs.writeString("    xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"\n");
1898     outs.writeString("    xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"\n");
1899     outs.writeString("    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
1900     outs.writeString("    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
1901     outs.writeString("    xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
1902     outs.writeString("    xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"\n");
1903     outs.writeString("    xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
1904     outs.writeString("    xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"\n");
1905     outs.writeString("    xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"\n");
1906     outs.writeString("    xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"\n");
1907     outs.writeString("    xmlns:math=\"http://www.w3.org/1998/Math/MathML\"\n");
1908     outs.writeString("    xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"\n");
1909     outs.writeString("    xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"\n");
1910     outs.writeString("    xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
1911     outs.writeString("    xmlns:ooow=\"http://openoffice.org/2004/writer\"\n");
1912     outs.writeString("    xmlns:oooc=\"http://openoffice.org/2004/calc\"\n");
1913     outs.writeString("    xmlns:dom=\"http://www.w3.org/2001/xml-events\"\n");
1914     outs.writeString("    xmlns:xforms=\"http://www.w3.org/2002/xforms\"\n");
1915     outs.writeString("    xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n");
1916     outs.writeString("    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
1917     outs.writeString("    xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
1918     outs.writeString("    xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
1919     outs.writeString("    office:version=\"1.0\">\n");
1920     outs.writeString("<office:scripts/>\n");
1921     outs.writeString("\n");
1922     outs.writeString("<!--\n");
1923     outs.writeString("*************************************************************************\n");
1924     outs.writeString("  D R A W I N G\n");
1925     outs.writeString("  This section is the heart of SVG-ODF conversion.  We are\n");
1926     outs.writeString("  starting with simple conversions, and will slowly evolve\n");
1927     outs.writeString("  into a 'smarter' translation as time progresses.  Any help\n");
1928     outs.writeString("  in improving .odg export is welcome.\n");
1929     outs.writeString("*************************************************************************\n");
1930     outs.writeString("-->\n");
1931     outs.writeString("\n");
1932     outs.writeString("<office:body>\n");
1933     outs.writeString("<office:drawing>\n");
1934     outs.writeString("<draw:page draw:name=\"page1\" draw:style-name=\"dp1\"\n");
1935     outs.writeString("        draw:master-page-name=\"Default\">\n");
1936     outs.writeString("\n");
1937     return true;
1938 }
1939 
1940 
1941 /**
1942  * Write the footer for the content.xml file
1943  */
writeContentFooter(Writer & outs)1944 bool OdfOutput::writeContentFooter(Writer &outs)
1945 {
1946     outs.writeString("\n");
1947     outs.writeString("</draw:page>\n");
1948     outs.writeString("</office:drawing>\n");
1949     outs.writeString("\n");
1950     outs.writeString("<!-- ######### CONVERSION FROM SVG ENDS ######## -->\n");
1951     outs.writeString("\n");
1952     outs.writeString("</office:body>\n");
1953     outs.writeString("</office:document-content>\n");
1954     return true;
1955 }
1956 
1957 
1958 /**
1959  * Write the content.xml file.  Writes the namesspace headers, then
1960  * calls writeTree().
1961  */
writeContent(ZipFile & zf,Inkscape::XML::Node * node)1962 bool OdfOutput::writeContent(ZipFile &zf, Inkscape::XML::Node *node)
1963 {
1964     //Content.xml stream
1965     BufferOutputStream cbouts;
1966     OutputStreamWriter couts(cbouts);
1967 
1968     if (!writeContentHeader(couts))
1969     {
1970         return false;
1971     }
1972 
1973     //Style.xml stream
1974     BufferOutputStream sbouts;
1975     OutputStreamWriter souts(sbouts);
1976 
1977     if (!writeStyleHeader(souts))
1978     {
1979         return false;
1980     }
1981 
1982     //# Descend into the tree, doing all of our conversions
1983     //# to both files at the same time
1984     char *oldlocale = g_strdup (setlocale (LC_NUMERIC, nullptr));
1985     setlocale (LC_NUMERIC, "C");
1986     if (!writeTree(couts, souts, node))
1987     {
1988         g_warning("Failed to convert SVG tree");
1989         setlocale (LC_NUMERIC, oldlocale);
1990         g_free (oldlocale);
1991         return false;
1992     }
1993     setlocale (LC_NUMERIC, oldlocale);
1994     g_free (oldlocale);
1995 
1996     //# Finish content file
1997     if (!writeContentFooter(couts))
1998     {
1999         return false;
2000     }
2001 
2002     ZipEntry *ze = zf.newEntry("content.xml", "ODF master content file");
2003     ze->setUncompressedData(cbouts.getBuffer());
2004     ze->finish();
2005 
2006     //# Finish style file
2007     if (!writeStyleFooter(souts))
2008     {
2009         return false;
2010     }
2011 
2012     ze = zf.newEntry("styles.xml", "ODF style file");
2013     ze->setUncompressedData(sbouts.getBuffer());
2014     ze->finish();
2015 
2016     return true;
2017 }
2018 
2019 
2020 /**
2021  * Resets class to its pristine condition, ready to use again
2022  */
reset()2023 void OdfOutput::reset()
2024 {
2025     metadata.clear();
2026     styleTable.clear();
2027     styleLookupTable.clear();
2028     gradientTable.clear();
2029     gradientLookupTable.clear();
2030     imageTable.clear();
2031 }
2032 
2033 
2034 /**
2035  * Descends into the SVG tree, mapping things to ODF when appropriate
2036  */
save(Inkscape::Extension::Output *,SPDocument * doc,gchar const * filename)2037 void OdfOutput::save(Inkscape::Extension::Output */*mod*/, SPDocument *doc, gchar const *filename)
2038 {
2039     if (doc != SP_ACTIVE_DOCUMENT) {
2040         g_warning("OdfOutput can only save the active document");
2041         return;
2042     }
2043 
2044     reset();
2045 
2046     docBaseUri = Inkscape::URI::from_dirname(doc->getDocumentBase()).str();
2047 
2048     ZipFile zf;
2049     preprocess(zf, doc->getReprRoot());
2050 
2051     if (!writeManifest(zf))
2052         {
2053         g_warning("Failed to write manifest");
2054         return;
2055         }
2056 
2057     if (!writeContent(zf, doc->getReprRoot()))
2058         {
2059         g_warning("Failed to write content");
2060         return;
2061         }
2062 
2063     if (!writeMeta(zf))
2064         {
2065         g_warning("Failed to write metafile");
2066         return;
2067         }
2068 
2069     if (!zf.writeFile(filename))
2070         {
2071         return;
2072         }
2073 }
2074 
2075 
2076 /**
2077  * This is the definition of PovRay output.  This function just
2078  * calls the extension system with the memory allocated XML that
2079  * describes the data.
2080 */
init()2081 void OdfOutput::init()
2082 {
2083     // clang-format off
2084     Inkscape::Extension::build_from_mem(
2085         "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
2086             "<name>" N_("OpenDocument Drawing Output") "</name>\n"
2087             "<id>org.inkscape.output.odf</id>\n"
2088             "<output>\n"
2089                 "<extension>.odg</extension>\n"
2090                 "<mimetype>text/x-povray-script</mimetype>\n"
2091                 "<filetypename>" N_("OpenDocument drawing (*.odg)") "</filetypename>\n"
2092                 "<filetypetooltip>" N_("OpenDocument drawing file") "</filetypetooltip>\n"
2093             "</output>\n"
2094         "</inkscape-extension>",
2095         new OdfOutput());
2096     // clang-format on
2097 }
2098 
2099 /**
2100  * Make sure that we are in the database
2101  */
check(Inkscape::Extension::Extension *)2102 bool OdfOutput::check (Inkscape::Extension::Extension */*module*/)
2103 {
2104     /* We don't need a Key
2105     if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_POV))
2106         return FALSE;
2107     */
2108 
2109     return TRUE;
2110 }
2111 
2112 }  //namespace Internal
2113 }  //namespace Extension
2114 }  //namespace Inkscape
2115 
2116 
2117 //########################################################################
2118 //# E N D    O F    F I L E
2119 //########################################################################
2120 
2121 /*
2122   Local Variables:
2123   mode:c++
2124   c-file-style:"stroustrup"
2125   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
2126   indent-tabs-mode:nil
2127   fill-column:99
2128   End:
2129 */
2130 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
2131