26#if defined (HAVE_CONFIG_H)
32#if defined (OCTAVE_USE_WINDOWS_API)
41#include <QApplication>
42#include <QFontDatabase>
59 m_printer.setOutputFormat (QPrinter::PdfFormat);
63#if defined (HAVE_QPRINTER_SETPAGESIZE)
64 m_printer.setPageSize (QPageSize (sz.size (), QPageSize::Point,
66 QPageSize::ExactMatch));
68 m_printer.setPaperSize (sz.size (), QPrinter::Point);
73 setWindow (sz.toRect ());
86 QStringList coords = str.split (
",");
87 for (QStringList::iterator p = coords.begin (); p != coords.end (); p += 1)
89 double pt = (*p).toDouble ();
98 QStringList coords = str.split (
",");
99 for (QStringList::iterator p = coords.begin (); p != coords.end (); p += 1)
101 double pt = (*p).toDouble ();
110 QVector<QPointF> pts;
111 str = str.trimmed ();
112 str.replace (
" ",
",");
113 QStringList coords = str.split (
",");
114 for (QStringList::iterator p = coords.begin (); p != coords.end (); p += 2)
116 QPointF pt ((*p).toDouble (), (*(p+1)).toDouble ());
125 str = str.trimmed ();
126 str.replace (
" ",
",");
127 QStringList coords = str.split (
",");
128 for (QStringList::iterator p = coords.begin (); p != coords.end (); p += 2)
130 QPoint pt ((*p).toDouble (), (*(p+1)).toDouble ());
140 QRegExp rx (field +
"\\(([^\\)]*)\\)");
142 pos = rx.indexIn (str, pos);
173 QVector<bool> unused;
175 unused.push_back (
false);
182 for (
auto ii = 0; ii <
m_polygons.count (); ii++)
187 for (
auto jj = ii+1; jj <
m_polygons.count (); jj++)
192 if (newpoly.count ())
206 for (
auto ii = 0; ii <
m_polygons.count (); ii++)
209 tryagain = ! unused[ii];
211 while (tryagain && polygon.count () > 4)
216 for (
auto jj = 1; jj < (polygon.count () - 1); jj++)
217 if (polygon[jj-1] == polygon[jj+1])
219 if (! del.contains (jj))
222 del.push_front (jj+1);
226 polygon.remove (idx);
247 for (
int ii = 0; ii <
m_polygons.count (); ii++)
250 if (! unused[ii] && polygon.count () > 2)
251 retval.push_back (polygon);
258 bool eq (QPointF p1, QPointF p2)
260 return ((qAbs (p1.x () - p2.x ())
261 <= 0.00001 * qMin (qAbs (p1.x ()), qAbs (p2.x ())))
262 && (qAbs (p1.y () - p2.y ())
263 <= 0.00001 * qMin (qAbs (p1.y ()), qAbs (p2.y ()))));
270 poly1.push_back (poly1[0]);
271 poly2.push_back (poly2[0]);
273 for (
int ii = 0; ii < (poly1.size () - 1); ii++)
275 for (
int jj = 0; jj < (poly2.size () - 1); jj++)
277 bool forward = (
eq (poly1[ii], poly2[jj])
278 &&
eq (poly1[ii+1], poly2[jj+1]));
279 bool backward = ! forward && (
eq (poly1[ii], poly2[jj+1])
280 &&
eq (poly1[ii+1], poly2[jj]));
282 if (forward || backward)
289 for (
int kk = 0; kk < (ii+1); kk++)
290 merged.push_back (poly1[kk]);
293 std::rotate (poly2.begin (), poly2.begin () + jj, poly2.end ());
294 poly2.erase (poly2.begin ());
295 poly2.erase (poly2.begin ());
298 for (
int kk = poly2.size (); kk > 0; kk--)
299 merged.push_back (poly2[kk-1]);
301 for (
int kk = 0; kk < poly2.size (); kk++)
302 merged.push_back (poly2[kk]);
304 for (
int kk = ii+1; kk < poly1.size (); kk++)
305 merged.push_back (poly1[kk]);
308 QPolygonF out (merged.size ());
309 for (
int kk = 0; kk < merged.size (); kk++)
310 out[kk] = merged[kk];
339 QDomNodeList nodes = parent_elt.childNodes ();
341 static QString clippath_id;
347 static double dx = 0, dy = 0;
350 static bool in_defs =
false;
353 for (
int i = 0; i < nodes.count (); i++)
355 QDomNode node = nodes.at (i);
356 if (! node.isElement ())
359 QDomElement elt = node.toElement ();
361 if (elt.tagName () ==
"clipPath")
363 clippath_id =
"#" + elt.attribute (
"id");
365 clippath_id = QString ();
367 else if (elt.tagName () ==
"g")
369 QString str = elt.attribute (
"font-family");
370 if (! str.isEmpty ())
374 font.setFamily (elt.attribute (
"font-family"));
376 str = elt.attribute (
"font-weight");
377 if (! str.isEmpty () && str !=
"normal")
378 font.setWeight (QFont::Bold);
380 str = elt.attribute (
"font-style");
381 if (! str.isEmpty () && str !=
"normal")
382 font.setStyle (QFont::StyleItalic);
384 str = elt.attribute (
"font-size");
385 if (! str.isEmpty ())
386 font.setPixelSize (str.toDouble ());
388 painter.setFont (font);
392 str =
get_field (elt.attribute (
"transform"),
"translate");
393 if (! str.isEmpty ())
395 QStringList trans = str.split (
",");
396 dx = trans[0].toDouble ();
397 dy = trans[1].toDouble ();
399 str =
get_field (elt.attribute (
"transform"),
"rotate");
400 if (! str.isEmpty ())
402 QStringList rot = str.split (
",");
403 painter.translate (dx+rot[1].
toDouble (),
405 painter.rotate (rot[0].
toDouble ());
406 dx = rot[1].toDouble ();
407 dy = rot[2].toDouble ();
411 painter.translate (dx, dy);
422 bool current_clipstate = painter.hasClipping ();
423 QRegion current_clippath = painter.clipRegion ();
425 str = elt.attribute (
"clip-path");
426 if (! str.isEmpty ())
428 QVector<QPoint> pts = clippath[
get_field (str,
"url")];
429 if (! pts.isEmpty ())
431 painter.setClipRegion (QRegion (QPolygon (pts)));
432 painter.setClipping (
true);
437 str =
get_field (elt.attribute (
"fill"),
"rgb");
438 if (! str.isEmpty ())
440 QStringList clist = str.split (
",");
441 painter.setBrush (QColor (clist[0].toInt (),
447 str = elt.attribute (
"transform");
449 if (! str.isEmpty ())
451 QStringRef tf (&str);
454 painter.setTransform (tform);
461 painter.setClipRegion (current_clippath);
462 painter.setClipping (current_clipstate);
465 else if (elt.tagName () ==
"defs")
471 else if (elt.tagName () ==
"path")
474 QString
id = elt.attribute (
"id");
477 QString
d = elt.attribute (
"d");
481 QStringRef data (&
d);
485 else if (
path.isEmpty ())
486 std::cout <<
"Empty path for data:"
487 <<
d.toStdString () << std::endl;
489 path_map[
"#" + id] =
path;
491 painter.drawPath (
path);
494 if (path_map[
"#" +
id].isEmpty ())
495 std::cout <<
"Empty path for data:"
496 <<
d.toStdString () << std::endl;
500 else if (elt.tagName () ==
"use")
502 painter.setPen (Qt::NoPen);
504 QString str = elt.attribute (
"xlink:href");
505 if (! str.isEmpty () && str.size () > 2)
507 QPainterPath
path = path_map[str];
508 if (!
path.isEmpty ())
510 str = elt.attribute (
"x");
511 double x = elt.attribute (
"x").toDouble ();
512 str = elt.attribute (
"y");
513 double y = elt.attribute (
"y").toDouble ();
514 painter.translate (
x, y);
515 painter.drawPath (
path);
516 painter.translate (-
x, -y);
520 else if (elt.tagName () ==
"text")
523 QFont saved_font (font);
525 QString str = elt.attribute (
"font-family");
526 if (! str.isEmpty ())
527 font.setFamily (elt.attribute (
"font-family"));
529 str = elt.attribute (
"font-weight");
530 if (! str.isEmpty ())
533 font.setWeight (QFont::Bold);
535 font.setWeight (QFont::Normal);
538 str = elt.attribute (
"font-style");
539 if (! str.isEmpty ())
542 font.setStyle (QFont::StyleItalic);
544 font.setStyle (QFont::StyleNormal);
547 str = elt.attribute (
"font-size");
548 if (! str.isEmpty ())
549 font.setPixelSize (str.toDouble ());
551 painter.setFont (font);
554 str =
get_field (elt.attribute (
"fill"),
"rgb");
555 if (! str.isEmpty ())
557 QStringList clist = str.split (
",");
558 painter.setPen (QColor (clist[0].toInt (), clist[1].toInt (),
562 QStringList xx = elt.attribute (
"x").split (
" ");
563 int y = elt.attribute (
"y").toInt ();
565 if (! str.isEmpty ())
568 foreach (QString s, xx)
569 if (ii < str.size ())
570 painter.drawText (s.toInt ()-dx, y-dy, str.at (ii++));
576 else if (elt.tagName () ==
"polyline")
579 QColor c (elt.attribute (
"stroke"));
580 QString str = elt.attribute (
"stroke-opacity");
581 if (! str.isEmpty () && str.toDouble () != 1.0
582 && str.toDouble () >= 0.0)
583 c.setAlphaF (str.toDouble ());
589 str = elt.attribute (
"stroke-width");
590 if (! str.isEmpty ())
592 double w = str.toDouble ();
597 str = elt.attribute (
"stroke-linecap");
598 pen.setCapStyle (Qt::SquareCap);
600 pen.setCapStyle (Qt::RoundCap);
601 else if (str ==
"butt")
602 pen.setCapStyle (Qt::FlatCap);
604 str = elt.attribute (
"stroke-linejoin");
605 pen.setJoinStyle (Qt::MiterJoin);
607 pen.setJoinStyle (Qt::RoundJoin);
608 else if (str ==
"bevel")
609 pen.setJoinStyle (Qt::BevelJoin);
611 str = elt.attribute (
"stroke-dasharray");
612 pen.setStyle (Qt::SolidLine);
613 if (! str.isEmpty ())
616 if (pat.count () != 2 || pat[1] != 0)
622 pen.setDashPattern (pat);
626 painter.setPen (pen);
629 else if (elt.tagName () ==
"image")
632 QString href_att = elt.attribute (
"xlink:href");
633 QString prefix (
"data:image/png;base64,");
635 = QByteArray::fromBase64 (href_att.mid (prefix.length ()).toLatin1 ());
637 if (img.loadFromData (data,
"PNG"))
639 QRect pos(elt.attribute (
"x").toInt (),
640 elt.attribute (
"y").toInt (),
641 elt.attribute (
"width").toInt (),
642 elt.attribute (
"height").toInt ());
646 QString str =
get_field (elt.attribute (
"transform"),
"matrix");
647 if (! str.isEmpty ())
650 QTransform tform(m[0], m[1], m[2],
652 painter.setTransform (tform);
655 painter.setRenderHint (QPainter::Antialiasing,
false);
656 painter.drawImage (pos, img);
657 painter.setRenderHint (QPainter::Antialiasing,
true);
661 else if (elt.tagName () ==
"rect")
664 QColor col (Qt::black);
665 QString str = elt.attribute (
"fill");
666 if (! str.isEmpty ())
670 double x = elt.attribute (
"x").toDouble ();
671 double y = elt.attribute (
"y").toDouble ();
674 double wd = elt.attribute (
"width").toDouble ();
675 double hg = elt.attribute (
"height").toDouble ();
677 painter.setBrush (col);
678 painter.setPen (Qt::NoPen);
680 painter.drawRect (QRectF (
x, y, wd, hg));
682 else if (elt.tagName () ==
"polygon")
684 if (! clippath_id.isEmpty ())
688 QString str = elt.attribute (
"fill");
689 if (! str.isEmpty ())
693 str = elt.attribute (
"fill-opacity");
694 if (! str.isEmpty () && str.toDouble () != 1.0
695 && str.toDouble () >= 0.0)
696 color.setAlphaF (str.toDouble ());
702 painter.setBrush (color);
703 painter.setPen (Qt::NoPen);
705 painter.setRenderHint (QPainter::Antialiasing,
false);
706 painter.drawPolygon (p);
707 painter.setRenderHint (QPainter::Antialiasing,
true);
721 if (! orig.count () || (orig.count () == polygons.count ()))
724 QDomNode last = orig.last ();
725 for (
int ii = 0; ii < polygons.count (); ii++)
727 QPolygonF polygon = polygons[ii];
729 QDomNode node = last.cloneNode ();
733 for (
int jj = 0; jj < polygon.count (); jj++)
735 pts += QString (
"%1,%2 ").arg (polygon[jj].
x ())
736 .arg (polygon[jj].y ());
739 node.toElement ().setAttribute (
"points", pts.trimmed ());
741 if (! last.isNull ())
742 last = parent_elt.insertAfter (node, last);
745 for (
int ii = 0; ii < orig.count (); ii++)
746 parent_elt.removeChild (orig.at (ii));
751 QDomNodeList nodes = parent_elt.childNodes ();
752 QColor current_color;
759 for (
int ii = 0; ii < nodes.count (); ii++)
761 QDomNode node = nodes.at (ii);
762 if (! node.isElement ())
765 QDomElement elt = node.toElement ();
767 if (elt.tagName () ==
"polygon")
769 QString str = elt.attribute (
"fill");
770 if (! str.isEmpty ())
773 str = elt.attribute (
"fill-opacity");
774 if (! str.isEmpty ())
776 double alpha = str.toDouble ();
777 if (alpha != 1.0 && alpha >= 0.0)
778 color.setAlphaF (alpha);
781 if (! current_polygon.
count ())
782 current_color = color;
784 if (color != current_color)
789 (replaced_nodes, polygons));
791 replaced_nodes.clear ();
792 current_polygon.
reset ();
794 current_color = color;
798 current_polygon.
add (p);
799 replaced_nodes.push_back (node);
804 if (current_polygon.
count ())
808 (replaced_nodes, polygons));
809 replaced_nodes.clear ();
810 current_polygon.
reset ();
820 for (
int ii = 0; ii < collection.count (); ii++)
824#if defined (OCTAVE_USE_WINDOWS_API) && defined (_UNICODE)
827wmain (
int argc,
wchar_t **wargv)
829 static char **argv =
new char * [argc + 1];
830 std::vector<std::string> argv_str;
833 std::wstring_convert<std::codecvt_utf8<wchar_t>,
wchar_t> wchar_conv;
834 for (
int i_arg = 0; i_arg < argc; i_arg++)
835 argv_str.push_back (wchar_conv.to_bytes (wargv[i_arg]));
838 for (
int i_arg = 0; i_arg < argc; i_arg++)
839 argv[i_arg] = &argv_str[i_arg][0];
840 argv[argc] =
nullptr;
847 const char *doc =
"See \"octave-svgconvert -h\"";
848 const char *help =
"Usage:\n\
849octave-svgconvert infile fmt dpi font reconstruct outfile\n\n\
850Convert svg file to pdf, or svg. All arguments are mandatory:\n\
851* infile: input svg file or \"-\" to indicate that the input svg file should be \
853* fmt: format of the output file. May be one of pdf or svg\n\
854* dpi: device dependent resolution in screen pixel per inch\n\
855* font: specify a file name for the default FreeSans font\n\
856* reconstruct: specify whether to reconstruct triangle to polygons (0 or 1)\n\
857* outfile: output file name\n";
859 if (
strcmp (argv[1],
"-h") == 0)
872 if (
strcmp (argv[1],
"-") != 0)
875 file.setFileName (argv[1]);
876 if (! file.open (QIODevice::ReadOnly | QIODevice::Text))
878 std::cerr <<
"Unable to open file " << argv[1] <<
"\n";
886 if (! file.open (stdin, QIODevice::ReadOnly | QIODevice::Text))
888 std::cerr <<
"Unable to read from stdin\n";
895 QDomDocument document;
897 if (! document.setContent (&file,
false, &msg))
899 std::cerr <<
"Failed to parse XML contents" << std::endl
900 << msg.toStdString () << std::endl;
908 if (
strcmp (argv[2],
"pdf") != 0 &&
strcmp (argv[2],
"svg") != 0)
910 std::cerr <<
"Unhandled output file format " << argv[2] <<
"\n";
920 QDomElement root = document.firstChildElement();
921 double x0, y0, dx, dy;
922 QString s = root.attribute (
"viewBox");
923 QTextStream (&s) >> x0 >> y0 >> dx >> dy;
924 QRectF vp (x0, y0, dx, dy);
930 if (!
strcmp (argv[2],
"pdf"))
932 QFont font (
"FreeSans");
933 if (! font.exactMatch ())
935 QString fontpath (argv[4]);
936 if (! fontpath.isEmpty ())
938 int id = QFontDatabase::addApplicationFont (fontpath);
940 std::cerr <<
"warning: print: "
941 "Unable to add default font to database\n";
944 std::cerr <<
"warning: print: FreeSans font not found\n";
952 std::cerr <<
"Could not open temporary file\n";
957 if (QString (argv[5]).toInt ())
961 if (!
strcmp (argv[2],
"pdf"))
966 draw (root, painter);
971 QTextStream out (&fout);
972 out.setCodec (
"UTF-8");
973 out << document.toByteArray ();
977 if (QFile::exists (argv[6]))
978 if (! QFile::remove (argv[6]))
980 std::cerr <<
"Unable to replace existing file " << argv[6] <<
"\n";
static bool eq(QPointF p1, QPointF p2)
QList< QPolygonF > reconstruct(void)
octave_polygon(QPolygonF p)
static QPolygonF mergepoly(QPolygonF poly1, QPolygonF poly2)
QList< QPolygonF > m_polygons
pdfpainter(QString fname, QRectF sz)
F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE * d
F77_RET_T const F77_DBLE * x
std::complex< double > w(std::complex< double > z, double relerr=0)
OCTAVE_API bool strcmp(const T &str_a, const T &str_b)
True if strings are the same.
static qreal toDouble(const QChar *&str)
static bool parsePathDataFast(const QStringRef &dataStr, QPainterPath &path)
static QTransform parseTransformationMatrix(const QStringRef &value)
QString get_field(QString str, QString field)
QVector< double > qstr2vectord(QString str)
int main(int argc, char **argv)
void replace_polygons(QDomElement &parent_elt, QList< QDomNode > orig, QList< QPolygonF > polygons)
QVector< QPoint > qstr2ptsvectord(QString str)
QVector< double > qstr2vectorf(QString str)
QVector< QPointF > qstr2ptsvector(QString str)
void reconstruct_polygons(QDomElement &parent_elt)
void draw(QDomElement &parent_elt, pdfpainter &painter)