GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
octave-svgconvert.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2017-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software; you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation; either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <http://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #include <iostream>
27 
28 #include <QtCore>
29 #include <QtXml>
30 
31 #include <QApplication>
32 #include <QFontDatabase>
33 #include <QImage>
34 #include <QPainter>
35 #include <QPrinter>
36 #include <QRegExp>
37 
38 class pdfpainter : public QPainter
39 {
40 public:
41  pdfpainter (QString fname, QRectF sizepix, double dpi)
42  : m_fname (fname), m_sizef (sizepix), m_dpi (dpi), m_printer ()
43  {
44  double scl = get_scale ();
45  m_sizef.setWidth (m_sizef.width () * scl);
46  m_sizef.setHeight (m_sizef.height () * scl);
47 
48  // Printer settings
49  m_printer.setOutputFormat (QPrinter::PdfFormat);
50  m_printer.setFontEmbeddingEnabled (true);
51  m_printer.setOutputFileName (get_fname ());
52  m_printer.setFullPage (true);
53  m_printer.setPaperSize (get_rectf ().size (), QPrinter::DevicePixel);
54 
55  // Painter settings
56  begin (&m_printer);
57  setViewport (get_rect ());
58  scale (get_scale (), get_scale ());
59  }
60 
61  ~pdfpainter (void) { }
62 
63  QString get_fname (void) const { return m_fname; }
64 
65  QRectF get_rectf (void) const { return m_sizef; }
66 
67  QRect get_rect (void) const { return m_sizef.toRect (); }
68 
69  double get_scale (void) const { return m_dpi / 72.0; }
70 
71  void finish (void) { end (); }
72 
73 private:
74  QString m_fname;
75  QRectF m_sizef;
76  double m_dpi;
77  QPrinter m_printer;
78 };
79 
80 // String conversion functions
81 QVector<double> qstr2vectorf (QString str)
82 {
83  QVector<double> pts;
84  QStringList coords = str.split (",");
85  for (QStringList::iterator p = coords.begin (); p != coords.end (); p += 1)
86  {
87  double pt = (*p).toDouble ();
88  pts.append (pt);
89  }
90  return pts;
91 }
92 
93 QVector<double> qstr2vectord (QString str)
94 {
95  QVector<double> pts;
96  QStringList coords = str.split (",");
97  for (QStringList::iterator p = coords.begin (); p != coords.end (); p += 1)
98  {
99  double pt = (*p).toDouble ();
100  pts.append (pt);
101  }
102 
103  return pts;
104 }
105 
106 QVector<QPointF> qstr2ptsvector (QString str)
107 {
108  QVector<QPointF> pts;
109  str = str.trimmed ();
110  str.replace (" ", ",");
111  QStringList coords = str.split (",");
112  for (QStringList::iterator p = coords.begin (); p != coords.end (); p += 2)
113  {
114  QPointF pt ((*p).toDouble (), (*(p+1)).toDouble ());
115  pts.append (pt);
116  }
117  return pts;
118 }
119 
120 QVector<QPoint> qstr2ptsvectord (QString str)
121 {
122  QVector<QPoint> pts;
123  str = str.trimmed ();
124  str.replace (" ", ",");
125  QStringList coords = str.split (",");
126  for (QStringList::iterator p = coords.begin (); p != coords.end (); p += 2)
127  {
128  QPoint pt ((*p).toDouble (), (*(p+1)).toDouble ());
129  pts.append (pt);
130  }
131  return pts;
132 }
133 
134 // Extract field arguments in a style-like string, e.g. "bla field(1,34,56) bla"
135 QString get_field (QString str, QString field)
136 {
137  QString retval;
138  QRegExp rx (field + "\\(([^\\)]*)\\)");
139  int pos = 0;
140  pos = rx.indexIn (str, pos);
141  if (pos > -1)
142  retval = rx.cap (1);
143 
144  return retval;
145 }
146 
147 // Polygon reconstruction class
149 {
150 public:
152  { }
153 
154  octave_polygon (QPolygonF p)
155  { m_polygons.push_back (p); }
156 
157  ~octave_polygon (void) { }
158 
159  int count (void) const
160  { return m_polygons.count (); }
161 
162  void reset (void)
163  { m_polygons.clear (); }
164 
166  {
167  if (m_polygons.isEmpty ())
168  return QList<QPolygonF> ();
169 
170  // Once a polygon has been merged to another, it is marked unsuded
171  QVector<bool> unused;
172  for (auto it = m_polygons.begin (); it != m_polygons.end (); it++)
173  unused.push_back (false);
174 
175  bool tryagain = (m_polygons.count () > 1);
176 
177  while (tryagain)
178  {
179  tryagain = false;
180  for (auto ii = 0; ii < m_polygons.count (); ii++)
181  {
182  if (! unused[ii])
183  {
184  QPolygonF polygon = m_polygons[ii];
185  for (auto jj = ii+1; jj < m_polygons.count (); jj++)
186  {
187  if (! unused[jj])
188  {
189  QPolygonF newpoly = mergepoly (polygon, m_polygons[jj]);
190  if (newpoly.count ())
191  {
192  polygon = newpoly;
193  m_polygons[ii] = newpoly;
194  unused[jj] = true;
195  tryagain = true;
196  }
197  }
198  }
199  }
200  }
201  }
202 
203  // Try to remove cracks in polygons
204  for (auto ii = 0; ii < m_polygons.count (); ii++)
205  {
206  QPolygonF polygon = m_polygons[ii];
207  tryagain = ! unused[ii];
208 
209  while (tryagain && polygon.count () > 4)
210  {
211  tryagain = false;
212  QVector<int> del;
213 
214  for (auto jj = 1; jj < (polygon.count () - 1); jj++)
215  if (polygon[jj-1] == polygon[jj+1])
216  {
217  if (! del.contains (jj))
218  del.push_front (jj);
219 
220  del.push_front (jj+1);
221  }
222 
223  for (auto idx : del)
224  polygon.remove (idx);
225 
226  if (del.count ())
227  tryagain = true;
228  }
229  m_polygons[ii] = polygon;
230  }
231 
232  // FIXME: There may still be residual cracks, we should do something like
233  // resetloop = 2;
234  // while (resetloop)
235  // currface = shift (currface, 1);
236  // if (currface(1) == currface(3))
237  // currface([2 3]) = [];
238  // resetloop = 2;
239  // else
240  // resetloop--;
241  // endif
242  // endwhile
243 
245  for (int ii = 0; ii < m_polygons.count (); ii++)
246  {
247  QPolygonF polygon = m_polygons[ii];
248  if (! unused[ii] && polygon.count () > 2)
249  retval.push_back (polygon);
250  }
251 
252  return retval;
253  }
254 
255  static inline
256  bool eq (QPointF p1, QPointF p2)
257  {
258  return ((qAbs (p1.x () - p2.x ())
259  <= 0.00001 * qMin (qAbs (p1.x ()), qAbs (p2.x ())))
260  && (qAbs (p1.y () - p2.y ())
261  <= 0.00001 * qMin (qAbs (p1.y ()), qAbs (p2.y ()))));
262  }
263 
264  static
265  QPolygonF mergepoly (QPolygonF poly1, QPolygonF poly2)
266  {
267  // Close polygon contour
268  poly1.push_back (poly1[0]);
269  poly2.push_back (poly2[0]);
270 
271  for (int ii = 0; ii < (poly1.size () - 1); ii++)
272  {
273  for (int jj = 0; jj < (poly2.size () - 1); jj++)
274  {
275  bool forward = (eq (poly1[ii], poly2[jj])
276  && eq (poly1[ii+1], poly2[jj+1]));
277  bool backward = ! forward && (eq (poly1[ii], poly2[jj+1])
278  && eq (poly1[ii+1], poly2[jj]));
279 
280  if (forward || backward)
281  {
282  // Unclose contour
283  poly1.pop_back ();
284  poly2.pop_back ();
285 
286  QPolygonF merged;
287  for (int kk = 0; kk < (ii+1); kk++)
288  merged.push_back (poly1[kk]);
289 
290  // Shift vertices and eliminate the common edge
291  std::rotate (poly2.begin (), poly2.begin () + jj, poly2.end ());
292  poly2.erase (poly2.begin ());
293  poly2.erase (poly2.begin ());
294 
295  if (forward)
296  for (int kk = poly2.size (); kk > 0; kk--)
297  merged.push_back (poly2[kk-1]);
298  else
299  for (int kk = 0; kk < poly2.size (); kk++)
300  merged.push_back (poly2[kk]);
301 
302  for (int kk = ii+1; kk < poly1.size (); kk++)
303  merged.push_back (poly1[kk]);
304 
305  // Return row vector
306  QPolygonF out (merged.size ());
307  for (int kk = 0; kk < merged.size (); kk++)
308  out[kk] = merged[kk];
309 
310  return out;
311  }
312  }
313  }
314  return QPolygonF ();
315  }
316 
317  void add (QPolygonF p)
318  {
319  if (m_polygons.count () == 0)
320  m_polygons.push_back (p);
321  else
322  {
323  QPolygonF tmp = mergepoly (m_polygons.back (), p);
324  if (tmp.count ())
325  m_polygons.back () = tmp;
326  else
327  m_polygons.push_back (p);
328  }
329  }
330 
331 private:
333 };
334 
335 void draw (QDomElement& parent_elt, pdfpainter& painter)
336 {
337  QDomNodeList nodes = parent_elt.childNodes ();
338 
339  static QString clippath_id;
340  static QMap< QString, QVector<QPoint> > clippath;
341 
342  // tspan elements must have access to the font and position extracted from
343  // their parent text element
344  static QFont font;
345  static double dx = 0, dy = 0;
346 
347  for (int i = 0; i < nodes.count (); i++)
348  {
349  QDomNode node = nodes.at (i);
350  if (! node.isElement ())
351  continue;
352 
353  QDomElement elt = node.toElement ();
354 
355  if (elt.tagName () == "clipPath")
356  {
357  clippath_id = "#" + elt.attribute ("id");
358  draw (elt, painter);
359  clippath_id = QString ();
360  }
361  else if (elt.tagName () == "g")
362  {
363  bool current_clipstate = painter.hasClipping ();
364  QRegion current_clippath = painter.clipRegion ();
365 
366  QString str = elt.attribute ("clip-path");
367  if (! str.isEmpty ())
368  {
369  QVector<QPoint> pts = clippath[get_field (str, "url")];
370  if (! pts.isEmpty ())
371  {
372  painter.setClipRegion (QRegion (QPolygon (pts)));
373  painter.setClipping (true);
374  }
375  }
376 
377  draw (elt, painter);
378 
379  // Restore previous clipping settings
380  painter.setClipRegion (current_clippath);
381  painter.setClipping (current_clipstate);
382  }
383  else if (elt.tagName () == "text")
384  {
385  // Font
386  font = QFont ();
387  QString str = elt.attribute ("font-family");
388  if (! str.isEmpty ())
389  font.setFamily (elt.attribute ("font-family"));
390 
391  str = elt.attribute ("font-weight");
392  if (! str.isEmpty () && str != "normal")
393  font.setWeight (QFont::Bold);
394 
395  str = elt.attribute ("font-style");
396  if (! str.isEmpty () && str != "normal")
397  font.setStyle (QFont::StyleItalic);
398 
399  str = elt.attribute ("font-size");
400  if (! str.isEmpty ())
401  font.setPixelSize (str.toDouble ());
402 
403  painter.setFont (font);
404 
405  // Translation and rotation
406  painter.save ();
407  str = get_field (elt.attribute ("transform"), "translate");
408  if (! str.isEmpty ())
409  {
410  QStringList trans = str.split (",");
411  dx = trans[0].toDouble ();
412  dy = trans[1].toDouble ();
413 
414  str = get_field (elt.attribute ("transform"), "rotate");
415  if (! str.isEmpty ())
416  {
417  QStringList rot = str.split (",");
418  painter.translate (dx+rot[1].toDouble (),
419  dy+rot[2].toDouble ());
420  painter.rotate (rot[0].toDouble ());
421  dx = rot[1].toDouble ();
422  dy = rot[2].toDouble ();
423  }
424  else
425  {
426  painter.translate (dx, dy);
427  dx = 0;
428  dy = 0;
429  }
430  }
431 
432  draw (elt, painter);
433  painter.restore ();
434  }
435  else if (elt.tagName () == "tspan")
436  {
437  // Font
438  QFont saved_font (font);
439 
440  QString str = elt.attribute ("font-family");
441  if (! str.isEmpty ())
442  font.setFamily (elt.attribute ("font-family"));
443 
444  str = elt.attribute ("font-weight");
445  if (! str.isEmpty ())
446  {
447  if (str != "normal")
448  font.setWeight (QFont::Bold);
449  else
450  font.setWeight (QFont::Normal);
451  }
452 
453  str = elt.attribute ("font-style");
454  if (! str.isEmpty ())
455  {
456  if (str != "normal")
457  font.setStyle (QFont::StyleItalic);
458  else
459  font.setStyle (QFont::StyleNormal);
460  }
461 
462  str = elt.attribute ("font-size");
463  if (! str.isEmpty ())
464  font.setPixelSize (str.toDouble ());
465 
466  painter.setFont (font);
467 
468  // Color is specified in rgb
469  str = get_field (elt.attribute ("fill"), "rgb");
470  if (! str.isEmpty ())
471  {
472  QStringList clist = str.split (",");
473  painter.setPen (QColor (clist[0].toInt (), clist[1].toInt (),
474  clist[2].toInt ()));
475  }
476 
477  QStringList xx = elt.attribute ("x").split (" ");
478  int y = elt.attribute ("y").toInt ();
479  str = elt.text ();
480  if (! str.isEmpty ())
481  {
482  int ii = 0;
483  foreach (QString s, xx)
484  if (ii < str.size ())
485  painter.drawText (s.toInt ()-dx, y-dy, str.at (ii++));
486  }
487 
488  draw (elt, painter);
489  font = saved_font;
490  }
491  else if (elt.tagName () == "polyline")
492  {
493  // Color
494  QColor c (elt.attribute ("stroke"));
495  QString str = elt.attribute ("stroke-opacity");
496  if (! str.isEmpty () && str.toDouble () != 1.0
497  && str.toDouble () >= 0.0)
498  c.setAlphaF (str.toDouble ());
499 
500  QPen pen;
501  pen.setColor (c);
502 
503  // Line properties
504  str = elt.attribute ("stroke-width");
505  if (! str.isEmpty ())
506  {
507  double w = str.toDouble () * painter.get_scale ();
508  if (w > 0)
509  pen.setWidthF (w / painter.get_scale ());
510  }
511 
512  str = elt.attribute ("stroke-linecap");
513  pen.setCapStyle (Qt::SquareCap);
514  if (str == "round")
515  pen.setCapStyle (Qt::RoundCap);
516  else if (str == "butt")
517  pen.setCapStyle (Qt::FlatCap);
518 
519  str = elt.attribute ("stroke-linejoin");
520  pen.setJoinStyle (Qt::MiterJoin);
521  if (str == "round")
522  pen.setJoinStyle (Qt::RoundJoin);
523  else if (str == "bevel")
524  pen.setJoinStyle (Qt::BevelJoin);
525 
526  str = elt.attribute ("stroke-dasharray");
527  pen.setStyle (Qt::SolidLine);
528  if (! str.isEmpty ())
529  {
530  QVector<double> pat = qstr2vectord (str);
531  if (pat.count () != 2 || pat[1] != 0)
532  {
533  // Express pattern in linewidth units
534  for (auto& p : pat)
535  p /= pen.widthF ();
536 
537  pen.setDashPattern (pat);
538  }
539  }
540 
541  painter.setPen (pen);
542  painter.drawPolyline (qstr2ptsvector (elt.attribute ("points")));
543  }
544  else if (elt.tagName () == "image")
545  {
546  // Images are represented as a base64 stream of png formatted data
547  QString href_att = elt.attribute ("xlink:href");
548  QString prefix ("data:image/png;base64,");
549  QByteArray data
550  = QByteArray::fromBase64 (href_att.mid (prefix.length ()).toLatin1 ());
551  QImage img;
552  if (img.loadFromData (data, "PNG"))
553  {
554  QRect pos(elt.attribute ("x").toInt (),
555  elt.attribute ("y").toInt (),
556  elt.attribute ("width").toInt (),
557  elt.attribute ("height").toInt ());
558 
559  // Translate
560  painter.save ();
561  QString str = get_field (elt.attribute ("transform"), "matrix");
562  if (! str.isEmpty ())
563  {
564  QVector<double> m = qstr2vectorf (str);
565  double scl = painter.get_scale ();
566  QTransform tform(m[0]*scl, m[1]*scl, m[2]*scl,
567  m[3]*scl, m[4]*scl, m[5]*scl);
568  painter.setTransform (tform);
569  }
570 
571  painter.setRenderHint (QPainter::Antialiasing, false);
572  painter.drawImage (pos, img);
573  painter.setRenderHint (QPainter::Antialiasing, true);
574  painter.restore ();
575  }
576  }
577  else if (elt.tagName () == "polygon")
578  {
579  if (! clippath_id.isEmpty ())
580  clippath[clippath_id] = qstr2ptsvectord (elt.attribute ("points"));
581  else
582  {
583  QString str = elt.attribute ("fill");
584  if (! str.isEmpty ())
585  {
586  QColor color (str);
587 
588  str = elt.attribute ("fill-opacity");
589  if (! str.isEmpty () && str.toDouble () != 1.0
590  && str.toDouble () >= 0.0)
591  color.setAlphaF (str.toDouble ());
592 
593  QPolygonF p (qstr2ptsvector (elt.attribute ("points")));
594 
595  if (p.count () > 2)
596  {
597  painter.setBrush (color);
598  painter.setPen (Qt::NoPen);
599 
600  painter.setRenderHint (QPainter::Antialiasing, false);
601  painter.drawPolygon (p);
602  painter.setRenderHint (QPainter::Antialiasing, true);
603  }
604  }
605  }
606  }
607  }
608 }
609 
610 // Append a list of reconstructed child polygons to a QDomElement and remove
611 // the original nodes
612 
613 void replace_polygons (QDomElement& parent_elt, QList<QDomNode> orig,
614  QList<QPolygonF> polygons)
615 {
616  if (! orig.count () || (orig.count () == polygons.count ()))
617  return;
618 
619  QDomNode last = orig.last ();
620  for (int ii = 0; ii < polygons.count (); ii++)
621  {
622  QPolygonF polygon = polygons[ii];
623 
624  QDomNode node = last.cloneNode ();
625 
626  QString pts;
627 
628  for (int jj = 0; jj < polygon.count (); jj++)
629  {
630  pts += QString ("%1,%2 ").arg (polygon[jj].x ())
631  .arg (polygon[jj].y ());
632  }
633 
634  node.toElement ().setAttribute ("points", pts.trimmed ());
635 
636  if (! last.isNull ())
637  last = parent_elt.insertAfter (node, last);
638  }
639 
640  for (int ii = 0; ii < orig.count (); ii++)
641  parent_elt.removeChild (orig.at (ii));
642 }
643 
644 void reconstruct_polygons (QDomElement& parent_elt)
645 {
646  QDomNodeList nodes = parent_elt.childNodes ();
647  QColor current_color;
648  QList<QDomNode> replaced_nodes;
649  octave_polygon current_polygon;
650 
651  // Collection of child nodes to be removed and polygons to be added
653 
654  for (int ii = 0; ii < nodes.count (); ii++)
655  {
656  QDomNode node = nodes.at (ii);
657  if (! node.isElement ())
658  continue;
659 
660  QDomElement elt = node.toElement ();
661 
662  if (elt.tagName () == "polygon")
663  {
664  QString str = elt.attribute ("fill");
665  if (! str.isEmpty ())
666  {
667  QColor color (str);
668  str = elt.attribute ("fill-opacity");
669  if (! str.isEmpty ())
670  {
671  double alpha = str.toDouble ();
672  if (alpha != 1.0 && str.toDouble () >= 0.0)
673  color.setAlphaF (alpha);
674  }
675 
676  if (! current_polygon.count ())
677  current_color = color;
678 
679  if (color != current_color)
680  {
681  // Reconstruct the previous series of triangle
682  QList<QPolygonF> polygons = current_polygon.reconstruct ();
683  collection.push_back (QPair<QList<QDomNode>,QList<QPolygonF> >
684  (replaced_nodes, polygons));
685 
686  replaced_nodes.clear ();
687  current_polygon.reset ();
688 
689  current_color = color;
690  }
691 
692  QPolygonF p (qstr2ptsvector (elt.attribute ("points")));
693  current_polygon.add (p);
694  replaced_nodes.push_back (node);
695  }
696  }
697  else
698  {
699  if (current_polygon.count ())
700  {
701  QList<QPolygonF> polygons = current_polygon.reconstruct ();
702  collection.push_back (QPair<QList<QDomNode>,QList<QPolygonF> >
703  (replaced_nodes, polygons));
704  replaced_nodes.clear ();
705  current_polygon.reset ();
706  }
707  reconstruct_polygons (elt);
708  }
709  }
710 
711  // Finish
712  collection.push_back (QPair<QList<QDomNode>,QList<QPolygonF> >
713  (replaced_nodes, current_polygon.reconstruct ()));
714 
715  for (int ii = 0; ii < collection.count (); ii++)
716  replace_polygons (parent_elt, collection[ii].first, collection[ii].second);
717 }
718 
719 int main(int argc, char *argv[])
720 {
721  const char *doc = "See \"octave-svgconvert -h\"";
722  const char *help = "Usage:\n\
723 octave-svgconvert infile fmt dpi font reconstruct outfile\n\n\
724 Convert svg file to pdf, or svg. All arguments are mandatory:\n\
725 * infile: input svg file or \"-\" to indicate that the input svg file should be \
726 read from stdin\n\
727 * fmt: format of the output file. May be one of pdf or svg\n\
728 * dpi: device dependent resolution in screen pixel per inch\n\
729 * font: specify a file name for the default FreeSans font\n\
730 * reconstruct: specify whether to reconstruct triangle to polygons (0 or 1)\n\
731 * outfile: output file name\n";
732 
733  if (strcmp (argv[1], "-h") == 0)
734  {
735  std::cout << help;
736  return 0;
737  }
738  else if (argc != 7)
739  {
740  std::cerr << help;
741  return -1;
742  }
743 
744  // Open svg file
745  QFile file;
746  if (strcmp (argv[1], "-") != 0)
747  {
748  // Read from file
749  file.setFileName (argv[1]);
750  if (! file.open (QIODevice::ReadOnly | QIODevice::Text))
751  {
752  std::cerr << "Unable to open file " << argv[1] << "\n";
753  std::cerr << help;
754  return -1;
755  }
756  }
757  else
758  {
759  // Read from stdin
760  if (! file.open (stdin, QIODevice::ReadOnly | QIODevice::Text))
761  {
762  std::cerr << "Unable read from stdin\n";
763  std::cerr << doc;
764  return -1;
765  }
766  }
767 
768  // Create a DOM document and load the svg file
769  QDomDocument document;
770  QString msg;
771  if (! document.setContent (&file, false, &msg))
772  {
773  std::cerr << "Failed to parse XML contents" << std::endl
774  << msg.toStdString ();
775  std::cerr << doc;
776  file.close();
777  return -1;
778  }
779 
780  file.close ();
781 
782  // Format
783  if (strcmp (argv[2], "pdf") != 0 && strcmp (argv[2], "svg") != 0)
784  {
785  std::cerr << "Unhandled output file format " << argv[2] << "\n";
786  std::cerr << doc;
787  return -1;
788  }
789 
790  // Resolution
791  double dpi = QString (argv[3]).toDouble ();
792  if (dpi <= 0.0)
793  {
794  std::cerr << "DPI must be positive\n";
795  return -1;
796  }
797 
798 
799  // Get the viewport from the root element
800  QDomElement root = document.firstChildElement();
801  double x0, y0, dx, dy;
802  QString s = root.attribute ("viewBox");
803  QTextStream (&s) >> x0 >> y0 >> dx >> dy;
804  QRectF vp (x0, y0, dx, dy);
805 
806  // Setup application and add default FreeSans font if needed
807  QApplication a (argc, argv);
808 
809  // When printing to PDF we may need the default FreeSans font
810  if (! strcmp (argv[2], "pdf"))
811  {
812  QFont font ("FreeSans");
813  if (! font.exactMatch ())
814  {
815  QString fontpath (argv[4]);
816  if (! fontpath.isEmpty ())
817  {
818  int id = QFontDatabase::addApplicationFont (fontpath);
819  if (id < 0)
820  std::cerr << "warning: print: "
821  "Unable to add default font to database\n";
822  }
823  else
824  std::cerr << "warning: print: FreeSans font not found\n";
825  }
826  }
827 
828  // First render in a temporary file
829  QTemporaryFile fout;
830  if (! fout.open ())
831  {
832  std::cerr << "Could not open temporary file\n";
833  return -1;
834  }
835 
836  // Do basic polygons reconstruction
837  if (QString (argv[5]).toInt ())
838  reconstruct_polygons (root);
839 
840  // Draw
841  if (! strcmp (argv[2], "pdf"))
842  {
843  // PDF painter
844  pdfpainter painter (fout.fileName (), vp, dpi);
845 
846  draw (root, painter);
847  painter.finish ();
848  }
849  else
850  {
851  // Return modified svg document
852  QTextStream out (&fout);
853  out.setCodec ("UTF-8");
854  out << document.toByteArray ();
855  }
856 
857  // Delete output file before writing with new data
858  if (QFile::exists (argv[6]))
859  if (! QFile::remove (argv[6]))
860  {
861  std::cerr << "Unable to replace existing file " << argv[6] << "\n";
862  return -1;
863  }
864 
865  fout.copy (argv[6]);
866 
867  return 0;
868 }
QList< QPolygonF > reconstruct(void)
int count(void) const
static bool eq(QPointF p1, QPointF p2)
void add(QPolygonF p)
octave_polygon(QPolygonF p)
static QPolygonF mergepoly(QPolygonF poly1, QPolygonF poly2)
QList< QPolygonF > m_polygons
double get_scale(void) const
QPrinter m_printer
pdfpainter(QString fname, QRectF sizepix, double dpi)
QRectF get_rectf(void) const
void finish(void)
QString get_fname(void) const
QRect get_rect(void) const
void scale(Matrix &m, double x, double y, double z)
Definition: graphics.cc:5846
F77_RET_T const F77_DBLE * x
T octave_idx_type m
Definition: mx-inlines.cc:773
std::complex< double > w(std::complex< double > z, double relerr=0)
bool strcmp(const T &str_a, const T &str_b)
True if strings are the same.
Definition: oct-string.cc:122
int main(int argc, char *argv[])
QString get_field(QString str, QString field)
QVector< double > qstr2vectord(QString str)
QVector< double > qstr2vectorf(QString str)
void replace_polygons(QDomElement &parent_elt, QList< QDomNode > orig, QList< QPolygonF > polygons)
QVector< QPointF > qstr2ptsvector(QString str)
void reconstruct_polygons(QDomElement &parent_elt)
QVector< QPoint > qstr2ptsvectord(QString str)
void draw(QDomElement &parent_elt, pdfpainter &painter)
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811