26 #if defined (HAVE_CONFIG_H)
33 #if defined (OCTAVE_USE_WINDOWS_API)
42 #include <QApplication>
43 #include <QFontDatabase>
60 m_printer.setOutputFormat (QPrinter::PdfFormat);
64 #if defined (HAVE_QPRINTER_SETPAGESIZE)
65 m_printer.setPageSize (QPageSize (sz.size (), QPageSize::Point,
67 QPageSize::ExactMatch));
69 m_printer.setPaperSize (sz.size (), QPrinter::Point);
74 setWindow (sz.toRect ());
87 QStringList coords = str.split (
",");
88 for (QStringList::iterator p = coords.begin (); p != coords.end (); p += 1)
90 double pt = (*p).toDouble ();
99 QStringList coords = str.split (
",");
100 for (QStringList::iterator p = coords.begin (); p != coords.end (); p += 1)
102 double pt = (*p).toDouble ();
111 QVector<QPointF> pts;
112 str = str.trimmed ();
113 str.replace (
" ",
",");
114 QStringList coords = str.split (
",");
115 for (QStringList::iterator p = coords.begin (); p != coords.end (); p += 2)
117 QPointF pt ((*p).toDouble (), (*(p+1)).toDouble ());
126 str = str.trimmed ();
127 str.replace (
" ",
",");
128 QStringList coords = str.split (
",");
129 for (QStringList::iterator p = coords.begin (); p != coords.end (); p += 2)
131 QPoint pt ((*p).toDouble (), (*(p+1)).toDouble ());
141 QRegExp rx (field +
"\\(([^\\)]*)\\)");
143 pos = rx.indexIn (str, pos);
172 else if (reconstruct_level < 2)
176 QVector<bool> unused;
178 unused.push_back (
false);
185 for (
auto ii = 0; ii <
m_polygons.count (); ii++)
190 for (
auto jj = ii+1; jj <
m_polygons.count (); jj++)
195 if (newpoly.count ())
209 for (
auto ii = 0; ii <
m_polygons.count (); ii++)
212 tryagain = ! unused[ii];
214 while (tryagain && polygon.count () > 4)
219 for (
auto jj = 1; jj < (polygon.count () - 1); jj++)
220 if (polygon[jj-1] == polygon[jj+1])
222 if (! del.contains (jj))
225 del.push_front (jj+1);
229 polygon.remove (idx);
250 for (
int ii = 0; ii <
m_polygons.count (); ii++)
253 if (! unused[ii] && polygon.count () > 2)
254 retval.push_back (polygon);
261 bool eq (QPointF p1, QPointF p2)
263 return ((qAbs (p1.x () - p2.x ())
264 <= 0.00001 * qMin (qAbs (p1.x ()), qAbs (p2.x ())))
265 && (qAbs (p1.y () - p2.y ())
266 <= 0.00001 * qMin (qAbs (p1.y ()), qAbs (p2.y ()))));
273 poly1.push_back (poly1[0]);
274 poly2.push_back (poly2[0]);
276 for (
int ii = 0; ii < (poly1.size () - 1); ii++)
278 for (
int jj = 0; jj < (poly2.size () - 1); jj++)
280 bool forward = (
eq (poly1[ii], poly2[jj])
281 &&
eq (poly1[ii+1], poly2[jj+1]));
282 bool backward = ! forward && (
eq (poly1[ii], poly2[jj+1])
283 &&
eq (poly1[ii+1], poly2[jj]));
285 if (forward || backward)
292 for (
int kk = 0; kk < (ii+1); kk++)
293 merged.push_back (poly1[kk]);
296 std::rotate (poly2.begin (), poly2.begin () + jj, poly2.end ());
297 poly2.erase (poly2.begin ());
298 poly2.erase (poly2.begin ());
301 for (
int kk = poly2.size (); kk > 0; kk--)
302 merged.push_back (poly2[kk-1]);
304 for (
int kk = 0; kk < poly2.size (); kk++)
305 merged.push_back (poly2[kk]);
307 for (
int kk = ii+1; kk < poly1.size (); kk++)
308 merged.push_back (poly1[kk]);
311 QPolygonF out (merged.size ());
312 for (
int kk = 0; kk < merged.size (); kk++)
313 out[kk] = merged[kk];
342 QDomNodeList nodes = parent_elt.childNodes ();
344 static QString clippath_id;
350 static double dx = 0, dy = 0;
353 static bool in_defs =
false;
356 for (
int i = 0; i < nodes.count (); i++)
358 QDomNode node = nodes.at (i);
359 if (! node.isElement ())
362 QDomElement elt = node.toElement ();
364 if (elt.tagName () ==
"clipPath")
366 clippath_id =
"#" + elt.attribute (
"id");
368 clippath_id = QString ();
370 else if (elt.tagName () ==
"g")
372 QString str = elt.attribute (
"font-family");
373 if (! str.isEmpty ())
377 font.setFamily (elt.attribute (
"font-family"));
379 str = elt.attribute (
"font-weight");
380 if (! str.isEmpty () && str !=
"normal")
381 font.setWeight (QFont::Bold);
383 str = elt.attribute (
"font-style");
384 if (! str.isEmpty () && str !=
"normal")
385 font.setStyle (QFont::StyleItalic);
387 str = elt.attribute (
"font-size");
388 if (! str.isEmpty ())
389 font.setPixelSize (str.toDouble ());
391 painter.setFont (font);
395 str =
get_field (elt.attribute (
"transform"),
"translate");
396 if (! str.isEmpty ())
398 QStringList trans = str.split (
",");
399 dx = trans[0].toDouble ();
400 dy = trans[1].toDouble ();
402 str =
get_field (elt.attribute (
"transform"),
"rotate");
403 if (! str.isEmpty ())
405 QStringList rot = str.split (
",");
406 painter.translate (dx+rot[1].
toDouble (),
408 painter.rotate (rot[0].
toDouble ());
409 dx = rot[1].toDouble ();
410 dy = rot[2].toDouble ();
414 painter.translate (dx, dy);
425 bool current_clipstate = painter.hasClipping ();
426 QRegion current_clippath = painter.clipRegion ();
428 str = elt.attribute (
"clip-path");
429 if (! str.isEmpty ())
431 QVector<QPoint> pts = clippath[
get_field (str,
"url")];
432 if (! pts.isEmpty ())
434 painter.setClipRegion (QRegion (QPolygon (pts)));
435 painter.setClipping (
true);
440 str =
get_field (elt.attribute (
"fill"),
"rgb");
441 if (! str.isEmpty ())
443 QStringList clist = str.split (
",");
444 painter.setBrush (QColor (clist[0].toInt (),
450 str = elt.attribute (
"transform");
452 if (! str.isEmpty ())
454 QStringRef tf (&str);
457 painter.setTransform (tform);
464 painter.setClipRegion (current_clippath);
465 painter.setClipping (current_clipstate);
468 else if (elt.tagName () ==
"defs")
474 else if (elt.tagName () ==
"path")
477 QString
id = elt.attribute (
"id");
480 QString
d = elt.attribute (
"d");
484 QStringRef data (&
d);
488 else if (path.isEmpty ())
489 std::cout <<
"Empty path for data:"
490 <<
d.toStdString () << std::endl;
492 path_map[
"#" + id] = path;
494 painter.drawPath (path);
497 if (path_map[
"#" +
id].isEmpty ())
498 std::cout <<
"Empty path for data:"
499 <<
d.toStdString () << std::endl;
503 else if (elt.tagName () ==
"use")
505 painter.setPen (Qt::NoPen);
507 QString str = elt.attribute (
"xlink:href");
508 if (! str.isEmpty () && str.size () > 2)
510 QPainterPath path = path_map[str];
511 if (! path.isEmpty ())
513 str = elt.attribute (
"x");
514 double x = elt.attribute (
"x").toDouble ();
515 str = elt.attribute (
"y");
516 double y = elt.attribute (
"y").toDouble ();
517 painter.translate (
x, y);
518 painter.drawPath (path);
519 painter.translate (-
x, -y);
523 else if (elt.tagName () ==
"text")
526 QFont saved_font (font);
528 QString str = elt.attribute (
"font-family");
529 if (! str.isEmpty ())
530 font.setFamily (elt.attribute (
"font-family"));
532 str = elt.attribute (
"font-weight");
533 if (! str.isEmpty ())
536 font.setWeight (QFont::Bold);
538 font.setWeight (QFont::Normal);
541 str = elt.attribute (
"font-style");
542 if (! str.isEmpty ())
545 font.setStyle (QFont::StyleItalic);
547 font.setStyle (QFont::StyleNormal);
550 str = elt.attribute (
"font-size");
551 if (! str.isEmpty ())
552 font.setPixelSize (str.toDouble ());
554 painter.setFont (font);
557 str =
get_field (elt.attribute (
"fill"),
"rgb");
558 if (! str.isEmpty ())
560 QStringList clist = str.split (
",");
561 painter.setPen (QColor (clist[0].toInt (), clist[1].toInt (),
565 QStringList xx = elt.attribute (
"x").split (
" ");
566 int y = elt.attribute (
"y").toInt ();
568 if (! str.isEmpty ())
571 foreach (QString s, xx)
572 if (ii < str.size ())
573 painter.drawText (s.toInt ()-dx, y-dy, str.at (ii++));
579 else if (elt.tagName () ==
"polyline")
582 QColor c (elt.attribute (
"stroke"));
583 QString str = elt.attribute (
"stroke-opacity");
584 if (! str.isEmpty () && str.toDouble () != 1.0
585 && str.toDouble () >= 0.0)
586 c.setAlphaF (str.toDouble ());
592 str = elt.attribute (
"stroke-width");
593 if (! str.isEmpty ())
595 double w = str.toDouble ();
600 str = elt.attribute (
"stroke-linecap");
601 pen.setCapStyle (Qt::SquareCap);
603 pen.setCapStyle (Qt::RoundCap);
604 else if (str ==
"butt")
605 pen.setCapStyle (Qt::FlatCap);
607 str = elt.attribute (
"stroke-linejoin");
608 pen.setJoinStyle (Qt::MiterJoin);
610 pen.setJoinStyle (Qt::RoundJoin);
611 else if (str ==
"bevel")
612 pen.setJoinStyle (Qt::BevelJoin);
614 str = elt.attribute (
"stroke-dasharray");
615 pen.setStyle (Qt::SolidLine);
616 if (! str.isEmpty ())
619 if (pat.count () != 2 || pat[1] != 0)
625 pen.setDashPattern (pat);
629 painter.setPen (pen);
632 else if (elt.tagName () ==
"image")
635 QString href_att = elt.attribute (
"xlink:href");
636 QString prefix (
"data:image/png;base64,");
638 = QByteArray::fromBase64 (href_att.mid (prefix.length ()).toLatin1 ());
640 if (img.loadFromData (data,
"PNG"))
642 QRect pos(elt.attribute (
"x").toInt (),
643 elt.attribute (
"y").toInt (),
644 elt.attribute (
"width").toInt (),
645 elt.attribute (
"height").toInt ());
649 QString str =
get_field (elt.attribute (
"transform"),
"matrix");
650 if (! str.isEmpty ())
653 QTransform tform(
m[0],
m[1],
m[2],
655 painter.setTransform (tform);
658 painter.setRenderHint (QPainter::Antialiasing,
false);
659 #if defined (HAVE_QPAINTER_RENDERHINT_LOSSLESS)
660 painter.setRenderHint (QPainter::LosslessImageRendering);
662 painter.drawImage (pos, img);
663 painter.setRenderHint (QPainter::Antialiasing,
true);
667 else if (elt.tagName () ==
"rect")
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 ();
678 QColor saved_color = painter.brush ().color ();
680 QString str = elt.attribute (
"fill");
681 if (! str.isEmpty ())
682 painter.setBrush (QColor (str));
684 painter.setPen (Qt::NoPen);
686 painter.drawRect (QRectF (
x, y, wd, hg));
688 if (! str.isEmpty ())
689 painter.setBrush (saved_color);
691 else if (elt.tagName () ==
"polygon")
693 if (! clippath_id.isEmpty ())
697 QString str = elt.attribute (
"fill");
698 if (! str.isEmpty ())
702 str = elt.attribute (
"fill-opacity");
703 if (! str.isEmpty () && str.toDouble () != 1.0
704 && str.toDouble () >= 0.0)
705 color.setAlphaF (str.toDouble ());
711 painter.setBrush (color);
712 painter.setPen (Qt::NoPen);
714 painter.setRenderHint (QPainter::Antialiasing,
false);
715 painter.drawPolygon (p);
716 painter.setRenderHint (QPainter::Antialiasing,
true);
730 if (! orig.count () || (orig.count () == polygons.count ()))
733 QDomNode last = orig.last ();
734 for (
int ii = 0; ii < polygons.count (); ii++)
736 QPolygonF polygon = polygons[ii];
738 QDomNode node = last.cloneNode ();
742 for (
int jj = 0; jj < polygon.count (); jj++)
744 pts += QString (
"%1,%2 ").arg (polygon[jj].
x ())
745 .arg (polygon[jj].y ());
748 node.toElement ().setAttribute (
"points", pts.trimmed ());
750 if (! last.isNull ())
751 last = parent_elt.insertAfter (node, last);
754 for (
int ii = 0; ii < orig.count (); ii++)
755 parent_elt.removeChild (orig.at (ii));
760 QDomNodeList nodes = parent_elt.childNodes ();
761 QColor current_color;
768 for (
int ii = 0; ii < nodes.count (); ii++)
770 QDomNode node = nodes.at (ii);
771 if (! node.isElement ())
774 QDomElement elt = node.toElement ();
776 if (elt.tagName () ==
"polygon")
778 QString str = elt.attribute (
"fill");
779 if (! str.isEmpty ())
782 str = elt.attribute (
"fill-opacity");
783 if (! str.isEmpty ())
785 double alpha = str.toDouble ();
786 if (alpha != 1.0 && alpha >= 0.0)
787 color.setAlphaF (alpha);
790 if (! current_polygon.
count ())
791 current_color = color;
793 if (color != current_color)
799 (replaced_nodes, polygons));
801 replaced_nodes.clear ();
802 current_polygon.
reset ();
804 current_color = color;
808 current_polygon.
add (p);
809 replaced_nodes.push_back (node);
814 if (current_polygon.
count ())
818 (replaced_nodes, polygons));
819 replaced_nodes.clear ();
820 current_polygon.
reset ();
831 for (
int ii = 0; ii < collection.count (); ii++)
837 QDomNodeList nodes = parent_elt.childNodes ();
839 for (
int ii = 0; ii < nodes.count (); ii++)
841 QDomNode node = nodes.at (ii);
842 if (! node.isElement ())
845 QDomElement elt = node.toElement ();
847 if (elt.tagName () ==
"image")
848 elt.setAttribute (
"image-rendering",
"optimizeSpeed");
855 #if defined (OCTAVE_USE_WINDOWS_API) && defined (_UNICODE)
858 wmain (
int argc,
wchar_t **wargv)
860 static char **argv =
new char * [argc + 1];
861 std::vector<std::string> argv_str;
864 std::wstring_convert<std::codecvt_utf8<wchar_t>,
wchar_t> wchar_conv;
865 for (
int i_arg = 0; i_arg < argc; i_arg++)
866 argv_str.push_back (wchar_conv.to_bytes (wargv[i_arg]));
869 for (
int i_arg = 0; i_arg < argc; i_arg++)
870 argv[i_arg] = &argv_str[i_arg][0];
871 argv[argc] =
nullptr;
878 const char *doc =
"See \"octave-svgconvert -h\"";
879 const char *help =
"Usage:\n\
880 octave-svgconvert infile fmt dpi font reconstruct outfile\n\n\
881 Convert svg file to pdf, or svg. All arguments are mandatory:\n\
882 * infile: input svg file or \"-\" to indicate that the input svg file should be \
884 * fmt: format of the output file. May be one of pdf or svg\n\
885 * dpi: device dependent resolution in screen pixel per inch\n\
886 * font: specify a file name for the default FreeSans font\n\
887 * reconstruct: specify whether to reconstruct triangle to polygons\n\
888 0: no reconstruction (merging) of polygons\n\
889 1: merge consecutive triangles if they share an edge\n\
890 2: merge all triangles that share edges (might take a long time)\n\
891 * outfile: output file name\n";
893 if (
strcmp (argv[1],
"-h") == 0)
906 if (
strcmp (argv[1],
"-") != 0)
909 file.setFileName (argv[1]);
910 if (! file.open (QIODevice::ReadOnly | QIODevice::Text))
912 std::cerr <<
"Unable to open file " << argv[1] <<
"\n";
920 if (! file.open (stdin, QIODevice::ReadOnly | QIODevice::Text))
922 std::cerr <<
"Unable to read from stdin\n";
929 QDomDocument document;
931 if (! document.setContent (&file,
false, &msg))
933 std::cerr <<
"Failed to parse XML contents" << std::endl
934 << msg.toStdString () << std::endl;
942 if (
strcmp (argv[2],
"pdf") != 0 &&
strcmp (argv[2],
"svg") != 0)
944 std::cerr <<
"Unhandled output file format " << argv[2] <<
"\n";
954 QDomElement root = document.firstChildElement();
955 double x0, y0, dx, dy;
956 QString s = root.attribute (
"viewBox");
957 QTextStream (&s) >> x0 >> y0 >> dx >> dy;
958 QRectF vp (x0, y0, dx, dy);
964 if (!
strcmp (argv[2],
"pdf"))
966 QFont font (
"FreeSans");
967 if (! font.exactMatch ())
969 QString fontpath (argv[4]);
970 if (! fontpath.isEmpty ())
972 int id = QFontDatabase::addApplicationFont (fontpath);
974 std::cerr <<
"warning: print: "
975 "Unable to add default font to database\n";
978 std::cerr <<
"warning: print: FreeSans font not found\n";
986 std::cerr <<
"Could not open temporary file\n";
991 int reconstruct_level = QString (argv[5]).toInt ();
992 if (reconstruct_level)
999 if (!
strcmp (argv[2],
"pdf"))
1004 draw (root, painter);
1009 QTextStream out (&fout);
1010 out.setCodec (
"UTF-8");
1011 out << document.toByteArray ();
1015 if (QFile::exists (argv[6]))
1016 if (! QFile::remove (argv[6]))
1018 std::cerr <<
"Unable to replace existing file " << argv[6] <<
"\n";
1022 fout.copy (argv[6]);
static bool eq(QPointF p1, QPointF p2)
QList< QPolygonF > reconstruct(int reconstruct_level)
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)
Octave string utility functions.
static qreal toDouble(const QChar *&str)
static bool parsePathDataFast(const QStringRef &dataStr, QPainterPath &path)
static QTransform parseTransformationMatrix(const QStringRef &value)
void reconstruct_polygons(QDomElement &parent_elt, int reconstruct_level)
QString get_field(QString str, QString field)
int main(int argc, char **argv)
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 add_custom_properties(QDomElement &parent_elt)
QVector< QPoint > qstr2ptsvectord(QString str)
void draw(QDomElement &parent_elt, pdfpainter &painter)