Mercurial > octave
view src/octave-svgconvert.cc @ 31771:21f9b34eb893
maint: Eliminate "(void)" in C++ function prototypes/declarations.
* mk-opts.pl, external.txi, embedded.cc, make_int.cc, standalone.cc,
standalonebuiltin.cc, BaseControl.cc, BaseControl.h, ButtonControl.cc,
ButtonControl.h, ButtonGroup.cc, ButtonGroup.h, Canvas.cc, Canvas.h,
CheckBoxControl.cc, CheckBoxControl.h, Container.cc, Container.h,
ContextMenu.cc, ContextMenu.h, EditControl.cc, EditControl.h, Figure.cc,
Figure.h, FigureWindow.cc, FigureWindow.h, GLCanvas.cc, GLCanvas.h,
GenericEventNotify.h, KeyMap.cc, ListBoxControl.cc, ListBoxControl.h,
Logger.cc, Logger.h, Menu.cc, Menu.h, MenuContainer.h, Object.cc, Object.h,
ObjectProxy.cc, ObjectProxy.h, Panel.cc, Panel.h, PopupMenuControl.cc,
PopupMenuControl.h, PushButtonControl.cc, PushButtonControl.h, PushTool.cc,
PushTool.h, RadioButtonControl.cc, RadioButtonControl.h, SliderControl.cc,
SliderControl.h, Table.cc, Table.h, TextControl.cc, TextControl.h, TextEdit.h,
ToggleButtonControl.cc, ToggleButtonControl.h, ToggleTool.cc, ToggleTool.h,
ToolBar.cc, ToolBar.h, ToolBarButton.cc, ToolBarButton.h, gl-select.cc,
gl-select.h, qopengl-functions.h, qt-graphics-toolkit.h, qdialog.cpp,
qfontdialog.cpp, qprintdialog_win.cpp, liboctgui-build-info.h,
liboctgui-build-info.in.cc, color-picker.cc, color-picker.h, command-widget.cc,
command-widget.h, community-news.cc, community-news.h, dialog.cc, dialog.h,
documentation-bookmarks.cc, documentation-bookmarks.h,
documentation-dock-widget.cc, documentation-dock-widget.h, documentation.cc,
documentation.h, dw-main-window.cc, dw-main-window.h,
external-editor-interface.cc, external-editor-interface.h,
files-dock-widget.cc, files-dock-widget.h, find-files-dialog.cc,
find-files-dialog.h, find-files-model.cc, find-files-model.h,
gui-preferences.cc, gui-preferences.h, gui-settings.cc, gui-settings.h,
history-dock-widget.cc, history-dock-widget.h, interpreter-qobject.cc,
interpreter-qobject.h, file-editor-interface.h, file-editor-tab.cc,
file-editor-tab.h, file-editor.cc, file-editor.h, find-dialog.cc,
find-dialog.h, marker.cc, marker.h, octave-qscintilla.cc, octave-qscintilla.h,
octave-txt-lexer.cc, octave-txt-lexer.h, main-window.cc, main-window.h,
news-reader.cc, news-reader.h, octave-dock-widget.cc, octave-dock-widget.h,
octave-qobject.cc, octave-qobject.h, qt-application.cc, qt-application.h,
qt-interpreter-events.cc, qt-interpreter-events.h, release-notes.cc,
release-notes.h, set-path-dialog.cc, set-path-dialog.h, set-path-model.cc,
set-path-model.h, settings-dialog.cc, settings-dialog.h,
shortcuts-tree-widget.cc, shortcuts-tree-widget.h, tab-bar.cc, tab-bar.h,
terminal-dock-widget.cc, terminal-dock-widget.h, variable-editor-model.cc,
variable-editor-model.h, variable-editor.cc, variable-editor.h,
welcome-wizard.cc, welcome-wizard.h, workspace-model.cc, workspace-model.h,
workspace-view.cc, workspace-view.h, build-env.h, Cell.cc, Cell.h,
__contourc__.cc, __magick_read__.cc, auto-shlib.cc, auto-shlib.h,
base-text-renderer.h, bsxfun.cc, c-file-ptr-stream.cc, c-file-ptr-stream.h,
call-stack.cc, call-stack.h, debug.cc, defaults.cc, defaults.h, defun.cc,
display.cc, display.h, dynamic-ld.cc, dynamic-ld.h, environment.cc,
environment.h, error.cc, error.h, errwarn.cc, errwarn.h, event-manager.cc,
event-manager.h, event-queue.cc, event-queue.h, fcn-info.cc, fcn-info.h,
ft-text-renderer.cc, ft-text-renderer.h, genprops.awk, gh-manager.cc,
gh-manager.h, gl-render.cc, gl-render.h, gl2ps-print.cc, graphics-toolkit.h,
graphics.cc, graphics.in.h, gtk-manager.cc, gtk-manager.h, help.cc, help.h,
hook-fcn.h, input.cc, input.h, interpreter-private.cc, interpreter-private.h,
interpreter.cc, interpreter.h, jsondecode.cc, latex-text-renderer.cc,
latex-text-renderer.h, load-path.cc, load-path.h, load-save.cc, load-save.h,
ls-hdf5.cc, ls-hdf5.h, mxarray.h, oct-errno.h, oct-errno.in.cc, oct-fstrm.cc,
oct-fstrm.h, oct-handle.h, oct-hist.cc, oct-hist.h, oct-iostrm.cc,
oct-iostrm.h, oct-map.cc, oct-map.h, oct-opengl.h, oct-prcstrm.cc,
oct-prcstrm.h, oct-procbuf.cc, oct-procbuf.h, oct-process.h, oct-stdstrm.h,
oct-stream.cc, oct-stream.h, oct-strstrm.cc, oct-strstrm.h,
oct-tex-lexer.in.ll, pager.cc, pager.h, pr-flt-fmt.cc, pr-flt-fmt.h,
pr-output.cc, pr-output.h, procstream.cc, procstream.h, settings.cc,
settings.h, sighandlers.cc, sighandlers.h, stack-frame.cc, stack-frame.h,
svd.cc, syminfo.cc, syminfo.h, symrec.cc, symrec.h, symscope.cc, symscope.h,
symtab.cc, symtab.h, sysdep.cc, sysdep.h, text-engine.cc, text-engine.h,
text-renderer.cc, text-renderer.h, toplev.cc, url-handle-manager.cc,
url-handle-manager.h, variables.cc, xpow.cc, __init_fltk__.cc,
__init_gnuplot__.cc, __ode15__.cc, audiodevinfo.cc, gzip.cc,
liboctinterp-build-info.h, liboctinterp-build-info.in.cc,
mk-build-env-features.sh, mk-builtins.pl, cdef-class.cc, cdef-class.h,
cdef-manager.h, cdef-method.cc, cdef-method.h, cdef-object.cc, cdef-object.h,
cdef-package.cc, cdef-package.h, cdef-property.cc, cdef-property.h,
cdef-utils.cc, cdef-utils.h, ov-base-diag.cc, ov-base-diag.h, ov-base-int.cc,
ov-base-int.h, ov-base-mat.cc, ov-base-mat.h, ov-base-scalar.cc,
ov-base-scalar.h, ov-base-sparse.cc, ov-base-sparse.h, ov-base.cc, ov-base.h,
ov-bool-mat.cc, ov-bool-mat.h, ov-bool-sparse.cc, ov-bool-sparse.h, ov-bool.cc,
ov-bool.h, ov-builtin.cc, ov-builtin.h, ov-cell.cc, ov-cell.h, ov-ch-mat.cc,
ov-ch-mat.h, ov-class.cc, ov-class.h, ov-classdef.cc, ov-classdef.h,
ov-colon.h, ov-complex.cc, ov-complex.h, ov-cs-list.h, ov-cx-diag.cc,
ov-cx-diag.h, ov-cx-mat.cc, ov-cx-mat.h, ov-cx-sparse.cc, ov-cx-sparse.h,
ov-dld-fcn.cc, ov-dld-fcn.h, ov-fcn-handle.cc, ov-fcn-handle.h, ov-fcn.cc,
ov-fcn.h, ov-float.cc, ov-float.h, ov-flt-complex.cc, ov-flt-complex.h,
ov-flt-cx-diag.cc, ov-flt-cx-diag.h, ov-flt-cx-mat.cc, ov-flt-cx-mat.h,
ov-flt-re-diag.cc, ov-flt-re-diag.h, ov-flt-re-mat.cc, ov-flt-re-mat.h,
ov-intx.h, ov-java.cc, ov-java.h, ov-lazy-idx.cc, ov-lazy-idx.h,
ov-legacy-range.cc, ov-legacy-range.h, ov-magic-int.cc, ov-magic-int.h,
ov-mex-fcn.cc, ov-mex-fcn.h, ov-null-mat.cc, ov-null-mat.h, ov-oncleanup.cc,
ov-oncleanup.h, ov-perm.cc, ov-perm.h, ov-range.cc, ov-range.h, ov-re-diag.cc,
ov-re-diag.h, ov-re-mat.cc, ov-re-mat.h, ov-re-sparse.cc, ov-re-sparse.h,
ov-scalar.cc, ov-scalar.h, ov-str-mat.cc, ov-str-mat.h, ov-struct.cc,
ov-struct.h, ov-typeinfo.cc, ov-typeinfo.h, ov-usr-fcn.cc, ov-usr-fcn.h, ov.cc,
ov.h, ovl.cc, ovl.h, octave.cc, octave.h, anon-fcn-validator.h, bp-table.cc,
bp-table.h, comment-list.cc, comment-list.h, filepos.h, lex.h, lex.ll,
oct-lvalue.cc, oct-lvalue.h, oct-parse.yy, parse.h, profiler.cc, profiler.h,
pt-anon-scopes.h, pt-arg-list.cc, pt-arg-list.h, pt-args-block.cc,
pt-args-block.h, pt-array-list.cc, pt-array-list.h, pt-assign.cc, pt-assign.h,
pt-binop.cc, pt-binop.h, pt-bp.h, pt-cbinop.h, pt-cell.h, pt-check.h,
pt-classdef.cc, pt-classdef.h, pt-cmd.h, pt-colon.h, pt-const.h, pt-decl.cc,
pt-decl.h, pt-eval.cc, pt-eval.h, pt-except.cc, pt-except.h, pt-exp.cc,
pt-exp.h, pt-fcn-handle.cc, pt-fcn-handle.h, pt-id.cc, pt-id.h, pt-idx.cc,
pt-idx.h, pt-jump.h, pt-loop.cc, pt-loop.h, pt-mat.h, pt-misc.cc, pt-misc.h,
pt-pr-code.cc, pt-pr-code.h, pt-select.cc, pt-select.h, pt-spmd.cc, pt-spmd.h,
pt-stmt.cc, pt-stmt.h, pt-tm-const.cc, pt-tm-const.h, pt-unop.cc, pt-unop.h,
pt-walk.h, pt.cc, pt.h, token.cc, token.h, usage.h, Array-base.cc, Array.h,
CColVector.cc, CColVector.h, CDiagMatrix.cc, CDiagMatrix.h, CMatrix.cc,
CMatrix.h, CNDArray.cc, CNDArray.h, CRowVector.cc, CRowVector.h, CSparse.cc,
CSparse.h, DiagArray2.cc, DiagArray2.h, MArray.cc, MArray.h, MDiagArray2.h,
MSparse.h, MatrixType.cc, MatrixType.h, PermMatrix.cc, PermMatrix.h, Range.cc,
Range.h, Sparse-b.cc, Sparse.cc, Sparse.h, boolMatrix.cc, boolMatrix.h,
boolNDArray.cc, boolNDArray.h, boolSparse.cc, boolSparse.h, chMatrix.h,
chNDArray.h, dColVector.cc, dColVector.h, dDiagMatrix.cc, dDiagMatrix.h,
dMatrix.cc, dMatrix.h, dNDArray.cc, dNDArray.h, dRowVector.cc, dRowVector.h,
dSparse.cc, dSparse.h, dim-vector.cc, dim-vector.h, fCColVector.cc,
fCColVector.h, fCDiagMatrix.cc, fCDiagMatrix.h, fCMatrix.cc, fCMatrix.h,
fCNDArray.cc, fCNDArray.h, fCRowVector.cc, fCRowVector.h, fColVector.cc,
fColVector.h, fDiagMatrix.cc, fDiagMatrix.h, fMatrix.cc, fMatrix.h,
fNDArray.cc, fNDArray.h, fRowVector.cc, fRowVector.h, idx-vector.cc,
idx-vector.h, intNDArray.cc, intNDArray.h, liboctave-build-info.h,
liboctave-build-info.in.cc, CollocWt.cc, CollocWt.h, DAE.h, DAEFunc.h, DAERT.h,
DAERTFunc.h, DASPK.cc, DASPK.h, DASRT.cc, DASRT.h, DASSL.cc, DASSL.h, DET.h,
EIG.h, LSODE.cc, LSODE.h, ODE.h, ODEFunc.h, ODES.h, ODESFunc.h, Quad.h,
aepbalance.cc, aepbalance.h, base-dae.h, base-de.h, chol.cc, chol.h,
eigs-base.cc, fEIG.h, gepbalance.h, gsvd.cc, gsvd.h, hess.h, lu.cc, lu.h,
oct-fftw.cc, oct-fftw.h, oct-rand.cc, oct-rand.h, oct-spparms.cc,
oct-spparms.h, qr.cc, qr.h, qrp.cc, qrp.h, randmtzig.cc, randmtzig.h, schur.h,
sparse-chol.cc, sparse-chol.h, sparse-lu.cc, sparse-lu.h, sparse-qr.cc,
sparse-qr.h, svd.cc, svd.h, child-list.cc, child-list.h, dir-ops.cc, dir-ops.h,
file-ops.cc, file-ops.h, file-stat.cc, file-stat.h, lo-sysdep.cc, lo-sysdep.h,
lo-sysinfo.cc, lo-sysinfo.h, mach-info.cc, mach-info.h, oct-env.cc, oct-env.h,
oct-group.cc, oct-group.h, oct-password.cc, oct-password.h, oct-syscalls.cc,
oct-syscalls.h, oct-time.cc, oct-time.h, oct-uname.cc, oct-uname.h,
action-container.h, base-list.h, caseless-str.h, cmd-edit.cc, cmd-edit.h,
cmd-hist.cc, cmd-hist.h, data-conv.cc, file-info.h, glob-match.cc,
glob-match.h, kpse.cc, kpse.h, lo-array-errwarn.cc, lo-array-errwarn.h,
lo-hash.cc, lo-hash.h, lo-ieee.cc, lo-ieee.h, lo-regexp.cc, lo-regexp.h,
oct-inttypes.cc, oct-inttypes.h, oct-mutex.cc, oct-mutex.h, oct-refcount.h,
oct-shlib.cc, oct-shlib.h, oct-sort.cc, oct-sort.h, oct-sparse.cc,
octave-preserve-stream-state.h, pathsearch.cc, pathsearch.h, quit.cc,
singleton-cleanup.cc, singleton-cleanup.h, str-vec.cc, str-vec.h,
unwind-prot.cc, unwind-prot.h, url-transfer.cc, url-transfer.h, version.cc,
version.in.h, cxx-signal-helpers.cc, acinclude.m4, main-cli.cc, main-gui.cc,
main.in.cc, mkoctfile.in.cc, octave-build-info.h, octave-build-info.in.cc,
octave-config.in.cc, octave-svgconvert.cc, shared-fcns.h:
maint: Eliminate "(void)" in C++ function prototypes/declarations.
author | Rik <rik@octave.org> |
---|---|
date | Tue, 24 Jan 2023 17:19:44 -0800 |
parents | 9cc509a00b54 |
children | 8825cedf5482 |
line wrap: on
line source
//////////////////////////////////////////////////////////////////////// // // Copyright (C) 2017-2023 The Octave Project Developers // // See the file COPYRIGHT.md in the top-level directory of this // distribution or <https://octave.org/copyright/>. // // This file is part of Octave. // // Octave is free software: you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Octave is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Octave; see the file COPYING. If not, see // <https://www.gnu.org/licenses/>. // //////////////////////////////////////////////////////////////////////// #if defined (HAVE_CONFIG_H) # include "config.h" #endif #include <cstdio> #include <iostream> #if defined (OCTAVE_USE_WINDOWS_API) # include <vector> # include <locale> # include <codecvt> #endif #include <QtCore> #include <QtXml> #include <QApplication> #include <QFontDatabase> #include <QImage> #include <QPainter> #include <QPrinter> #include <QRegExp> // Include a set of path rendering functions extracted from Qt-5.12 source #include "octave-qsvghandler.h" // Render to pdf class pdfpainter : public QPainter { public: pdfpainter (QString fname, QRectF sz) : m_printer () { // Printer settings m_printer.setOutputFormat (QPrinter::PdfFormat); m_printer.setFontEmbeddingEnabled (true); m_printer.setOutputFileName (fname); m_printer.setFullPage (true); m_printer.setPageSize (QPageSize (sz.size (), QPageSize::Point, QString ("custom"), QPageSize::ExactMatch)); // Painter settings begin (&m_printer); setWindow (sz.toRect ()); } ~pdfpainter () { end (); } private: QPrinter m_printer; }; // String conversion functions+QVector<double> qstr2vectorf (QString str) QVector<double> qstr2vectorf (QString str) { QVector<double> pts; QStringList coords = str.split (","); for (QStringList::iterator p = coords.begin (); p != coords.end (); p += 1) { double pt = (*p).toDouble (); pts.append (pt); } return pts; } QVector<double> qstr2vectord (QString str) { QVector<double> pts; QStringList coords = str.split (","); for (QStringList::iterator p = coords.begin (); p != coords.end (); p += 1) { double pt = (*p).toDouble (); pts.append (pt); } return pts; } QVector<QPointF> qstr2ptsvector (QString str) { QVector<QPointF> pts; str = str.trimmed (); str.replace (" ", ","); QStringList coords = str.split (","); for (QStringList::iterator p = coords.begin (); p != coords.end (); p += 2) { QPointF pt ((*p).toDouble (), (*(p+1)).toDouble ()); pts.append (pt); } return pts; } QVector<QPoint> qstr2ptsvectord (QString str) { QVector<QPoint> pts; str = str.trimmed (); str.replace (" ", ","); QStringList coords = str.split (","); for (QStringList::iterator p = coords.begin (); p != coords.end (); p += 2) { QPoint pt ((*p).toDouble (), (*(p+1)).toDouble ()); pts.append (pt); } return pts; } // Extract field arguments in a style-like string, e.g. "bla field(1,34,56) bla" QString get_field (QString str, QString field) { QString retval; QRegExp rx (field + "\\(([^\\)]*)\\)"); int pos = 0; pos = rx.indexIn (str, pos); if (pos > -1) retval = rx.cap (1); return retval; } // Polygon reconstruction class class octave_polygon { public: octave_polygon () { } octave_polygon (QPolygonF p) { m_polygons.push_back (p); } ~octave_polygon () { } int count () const { return m_polygons.count (); } void reset () { m_polygons.clear (); } QList<QPolygonF> reconstruct () { if (m_polygons.isEmpty ()) return QList<QPolygonF> (); // Once a polygon has been merged to another, it is marked unsuded QVector<bool> unused; for (auto it = m_polygons.begin (); it != m_polygons.end (); it++) unused.push_back (false); bool tryagain = (m_polygons.count () > 1); while (tryagain) { tryagain = false; for (auto ii = 0; ii < m_polygons.count (); ii++) { if (! unused[ii]) { QPolygonF polygon = m_polygons[ii]; for (auto jj = ii+1; jj < m_polygons.count (); jj++) { if (! unused[jj]) { QPolygonF newpoly = mergepoly (polygon, m_polygons[jj]); if (newpoly.count ()) { polygon = newpoly; m_polygons[ii] = newpoly; unused[jj] = true; tryagain = true; } } } } } } // Try to remove cracks in polygons for (auto ii = 0; ii < m_polygons.count (); ii++) { QPolygonF polygon = m_polygons[ii]; tryagain = ! unused[ii]; while (tryagain && polygon.count () > 4) { tryagain = false; QVector<int> del; for (auto jj = 1; jj < (polygon.count () - 1); jj++) if (polygon[jj-1] == polygon[jj+1]) { if (! del.contains (jj)) del.push_front (jj); del.push_front (jj+1); } for (auto idx : del) polygon.remove (idx); if (del.count ()) tryagain = true; } m_polygons[ii] = polygon; } // FIXME: There may still be residual cracks, we should do something like // resetloop = 2; // while (resetloop) // currface = shift (currface, 1); // if (currface(1) == currface(3)) // currface([2 3]) = []; // resetloop = 2; // else // resetloop--; // endif // endwhile QList<QPolygonF> retval; for (int ii = 0; ii < m_polygons.count (); ii++) { QPolygonF polygon = m_polygons[ii]; if (! unused[ii] && polygon.count () > 2) retval.push_back (polygon); } return retval; } static inline bool eq (QPointF p1, QPointF p2) { return ((qAbs (p1.x () - p2.x ()) <= 0.00001 * qMin (qAbs (p1.x ()), qAbs (p2.x ()))) && (qAbs (p1.y () - p2.y ()) <= 0.00001 * qMin (qAbs (p1.y ()), qAbs (p2.y ())))); } static QPolygonF mergepoly (QPolygonF poly1, QPolygonF poly2) { // Close polygon contour poly1.push_back (poly1[0]); poly2.push_back (poly2[0]); for (int ii = 0; ii < (poly1.size () - 1); ii++) { for (int jj = 0; jj < (poly2.size () - 1); jj++) { bool forward = (eq (poly1[ii], poly2[jj]) && eq (poly1[ii+1], poly2[jj+1])); bool backward = ! forward && (eq (poly1[ii], poly2[jj+1]) && eq (poly1[ii+1], poly2[jj])); if (forward || backward) { // Unclose contour poly1.pop_back (); poly2.pop_back (); QPolygonF merged; for (int kk = 0; kk < (ii+1); kk++) merged.push_back (poly1[kk]); // Shift vertices and eliminate the common edge std::rotate (poly2.begin (), poly2.begin () + jj, poly2.end ()); poly2.erase (poly2.begin ()); poly2.erase (poly2.begin ()); if (forward) for (int kk = poly2.size (); kk > 0; kk--) merged.push_back (poly2[kk-1]); else for (int kk = 0; kk < poly2.size (); kk++) merged.push_back (poly2[kk]); for (int kk = ii+1; kk < poly1.size (); kk++) merged.push_back (poly1[kk]); // Return row vector QPolygonF out (merged.size ()); for (int kk = 0; kk < merged.size (); kk++) out[kk] = merged[kk]; return out; } } } return QPolygonF (); } void add (QPolygonF p) { if (m_polygons.count () == 0) m_polygons.push_back (p); else { QPolygonF tmp = mergepoly (m_polygons.back (), p); if (tmp.count ()) m_polygons.back () = tmp; else m_polygons.push_back (p); } } private: QList<QPolygonF> m_polygons; }; void draw (QDomElement& parent_elt, pdfpainter& painter) { QDomNodeList nodes = parent_elt.childNodes (); static QString clippath_id; static QMap< QString, QVector<QPoint> > clippath; // tspan elements must have access to the font and position extracted from // their parent text element static QFont font; static double dx = 0, dy = 0; // Store path defined in <defs> in a map static bool in_defs = false; static QMap< QString, QPainterPath> path_map; for (int i = 0; i < nodes.count (); i++) { QDomNode node = nodes.at (i); if (! node.isElement ()) continue; QDomElement elt = node.toElement (); if (elt.tagName () == "clipPath") { clippath_id = "#" + elt.attribute ("id"); draw (elt, painter); clippath_id = QString (); } else if (elt.tagName () == "g") { QString str = elt.attribute ("font-family"); if (! str.isEmpty ()) { // Font font = QFont (); font.setFamily (elt.attribute ("font-family")); str = elt.attribute ("font-weight"); if (! str.isEmpty () && str != "normal") font.setWeight (QFont::Bold); str = elt.attribute ("font-style"); if (! str.isEmpty () && str != "normal") font.setStyle (QFont::StyleItalic); str = elt.attribute ("font-size"); if (! str.isEmpty ()) font.setPixelSize (str.toDouble ()); painter.setFont (font); // Translation and rotation painter.save (); str = get_field (elt.attribute ("transform"), "translate"); if (! str.isEmpty ()) { QStringList trans = str.split (","); dx = trans[0].toDouble (); dy = trans[1].toDouble (); str = get_field (elt.attribute ("transform"), "rotate"); if (! str.isEmpty ()) { QStringList rot = str.split (","); painter.translate (dx+rot[1].toDouble (), dy+rot[2].toDouble ()); painter.rotate (rot[0].toDouble ()); dx = rot[1].toDouble (); dy = rot[2].toDouble (); } else { painter.translate (dx, dy); dx = 0; dy = 0; } } draw (elt, painter); painter.restore (); } else { bool current_clipstate = painter.hasClipping (); QRegion current_clippath = painter.clipRegion (); str = elt.attribute ("clip-path"); if (! str.isEmpty ()) { QVector<QPoint> pts = clippath[get_field (str, "url")]; if (! pts.isEmpty ()) { painter.setClipRegion (QRegion (QPolygon (pts))); painter.setClipping (true); } } // Fill color str = get_field (elt.attribute ("fill"), "rgb"); if (! str.isEmpty ()) { QStringList clist = str.split (","); painter.setBrush (QColor (clist[0].toInt (), clist[1].toInt (), clist[2].toInt ())); } // Transform str = elt.attribute ("transform"); painter.save (); if (! str.isEmpty ()) { QStringRef tf (&str); QTransform tform = parseTransformationMatrix (tf) * painter.transform (); painter.setTransform (tform); } draw (elt, painter); // Restore previous clipping settings painter.restore (); painter.setClipRegion (current_clippath); painter.setClipping (current_clipstate); } } else if (elt.tagName () == "defs") { in_defs = true; draw (elt, painter); in_defs = false; } else if (elt.tagName () == "path") { // Store QPainterPath for latter use QString id = elt.attribute ("id"); if (! id.isEmpty ()) { QString d = elt.attribute ("d"); if (! d.isEmpty ()) { QStringRef data (&d); QPainterPath path; if (! parsePathDataFast (data, path)) continue; // Something went wrong, pass else if (path.isEmpty ()) std::cout << "Empty path for data:" << d.toStdString () << std::endl; else if (in_defs) path_map["#" + id] = path; else painter.drawPath (path); if (path_map["#" + id].isEmpty ()) std::cout << "Empty path for data:" << d.toStdString () << std::endl; } } } else if (elt.tagName () == "use") { painter.setPen (Qt::NoPen); QString str = elt.attribute ("xlink:href"); if (! str.isEmpty () && str.size () > 2) { QPainterPath path = path_map[str]; if (! path.isEmpty ()) { str = elt.attribute ("x"); double x = elt.attribute ("x").toDouble (); str = elt.attribute ("y"); double y = elt.attribute ("y").toDouble (); painter.translate (x, y); painter.drawPath (path); painter.translate (-x, -y); } } } else if (elt.tagName () == "text") { // Font QFont saved_font (font); QString str = elt.attribute ("font-family"); if (! str.isEmpty ()) font.setFamily (elt.attribute ("font-family")); str = elt.attribute ("font-weight"); if (! str.isEmpty ()) { if (str != "normal") font.setWeight (QFont::Bold); else font.setWeight (QFont::Normal); } str = elt.attribute ("font-style"); if (! str.isEmpty ()) { if (str != "normal") font.setStyle (QFont::StyleItalic); else font.setStyle (QFont::StyleNormal); } str = elt.attribute ("font-size"); if (! str.isEmpty ()) font.setPixelSize (str.toDouble ()); painter.setFont (font); // Color is specified in rgb str = get_field (elt.attribute ("fill"), "rgb"); if (! str.isEmpty ()) { QStringList clist = str.split (","); painter.setPen (QColor (clist[0].toInt (), clist[1].toInt (), clist[2].toInt ())); } QStringList xx = elt.attribute ("x").split (" "); int y = elt.attribute ("y").toInt (); str = elt.text (); if (! str.isEmpty ()) { int ii = 0; foreach (QString s, xx) if (ii < str.size ()) painter.drawText (s.toInt ()-dx, y-dy, str.at (ii++)); } draw (elt, painter); font = saved_font; } else if (elt.tagName () == "polyline") { // Color QColor c (elt.attribute ("stroke")); QString str = elt.attribute ("stroke-opacity"); if (! str.isEmpty () && str.toDouble () != 1.0 && str.toDouble () >= 0.0) c.setAlphaF (str.toDouble ()); QPen pen; pen.setColor (c); // Line properties str = elt.attribute ("stroke-width"); if (! str.isEmpty ()) { double w = str.toDouble (); if (w > 0) pen.setWidthF (w); } str = elt.attribute ("stroke-linecap"); pen.setCapStyle (Qt::SquareCap); if (str == "round") pen.setCapStyle (Qt::RoundCap); else if (str == "butt") pen.setCapStyle (Qt::FlatCap); str = elt.attribute ("stroke-linejoin"); pen.setJoinStyle (Qt::MiterJoin); if (str == "round") pen.setJoinStyle (Qt::RoundJoin); else if (str == "bevel") pen.setJoinStyle (Qt::BevelJoin); str = elt.attribute ("stroke-dasharray"); pen.setStyle (Qt::SolidLine); if (! str.isEmpty ()) { QVector<double> pat = qstr2vectord (str); if (pat.count () != 2 || pat[1] != 0) { // Express pattern in linewidth units for (auto& p : pat) p /= pen.widthF (); pen.setDashPattern (pat); } } painter.setPen (pen); painter.drawPolyline (qstr2ptsvector (elt.attribute ("points"))); } else if (elt.tagName () == "image") { // Images are represented as a base64 stream of png formatted data QString href_att = elt.attribute ("xlink:href"); QString prefix ("data:image/png;base64,"); QByteArray data = QByteArray::fromBase64 (href_att.mid (prefix.length ()).toLatin1 ()); QImage img; if (img.loadFromData (data, "PNG")) { QRect pos(elt.attribute ("x").toInt (), elt.attribute ("y").toInt (), elt.attribute ("width").toInt (), elt.attribute ("height").toInt ()); // Translate painter.save (); QString str = get_field (elt.attribute ("transform"), "matrix"); if (! str.isEmpty ()) { QVector<double> m = qstr2vectorf (str); QTransform tform(m[0], m[1], m[2], m[3], m[4], m[5]); painter.setTransform (tform); } painter.setRenderHint (QPainter::Antialiasing, false); #if defined (HAVE_QPAINTER_RENDERHINT_LOSSLESS) painter.setRenderHint (QPainter::LosslessImageRendering); #endif painter.drawImage (pos, img); painter.setRenderHint (QPainter::Antialiasing, true); painter.restore (); } } else if (elt.tagName () == "rect") { // Position double x = elt.attribute ("x").toDouble (); double y = elt.attribute ("y").toDouble (); // Size double wd = elt.attribute ("width").toDouble (); double hg = elt.attribute ("height").toDouble (); // Color QColor saved_color = painter.brush ().color (); QString str = elt.attribute ("fill"); if (! str.isEmpty ()) painter.setBrush (QColor (str)); painter.setPen (Qt::NoPen); painter.drawRect (QRectF (x, y, wd, hg)); if (! str.isEmpty ()) painter.setBrush (saved_color); } else if (elt.tagName () == "polygon") { if (! clippath_id.isEmpty ()) clippath[clippath_id] = qstr2ptsvectord (elt.attribute ("points")); else { QString str = elt.attribute ("fill"); if (! str.isEmpty ()) { QColor color (str); str = elt.attribute ("fill-opacity"); if (! str.isEmpty () && str.toDouble () != 1.0 && str.toDouble () >= 0.0) color.setAlphaF (str.toDouble ()); QPolygonF p (qstr2ptsvector (elt.attribute ("points"))); if (p.count () > 2) { painter.setBrush (color); painter.setPen (Qt::NoPen); painter.setRenderHint (QPainter::Antialiasing, false); painter.drawPolygon (p); painter.setRenderHint (QPainter::Antialiasing, true); } } } } } } // Append a list of reconstructed child polygons to a QDomElement and remove // the original nodes void replace_polygons (QDomElement& parent_elt, QList<QDomNode> orig, QList<QPolygonF> polygons) { if (! orig.count () || (orig.count () == polygons.count ())) return; QDomNode last = orig.last (); for (int ii = 0; ii < polygons.count (); ii++) { QPolygonF polygon = polygons[ii]; QDomNode node = last.cloneNode (); QString pts; for (int jj = 0; jj < polygon.count (); jj++) { pts += QString ("%1,%2 ").arg (polygon[jj].x ()) .arg (polygon[jj].y ()); } node.toElement ().setAttribute ("points", pts.trimmed ()); if (! last.isNull ()) last = parent_elt.insertAfter (node, last); } for (int ii = 0; ii < orig.count (); ii++) parent_elt.removeChild (orig.at (ii)); } void reconstruct_polygons (QDomElement& parent_elt) { QDomNodeList nodes = parent_elt.childNodes (); QColor current_color; QList<QDomNode> replaced_nodes; octave_polygon current_polygon; // Collection of child nodes to be removed and polygons to be added QList< QPair<QList<QDomNode>,QList<QPolygonF> > > collection; for (int ii = 0; ii < nodes.count (); ii++) { QDomNode node = nodes.at (ii); if (! node.isElement ()) continue; QDomElement elt = node.toElement (); if (elt.tagName () == "polygon") { QString str = elt.attribute ("fill"); if (! str.isEmpty ()) { QColor color (str); str = elt.attribute ("fill-opacity"); if (! str.isEmpty ()) { double alpha = str.toDouble (); if (alpha != 1.0 && alpha >= 0.0) color.setAlphaF (alpha); } if (! current_polygon.count ()) current_color = color; if (color != current_color) { // Reconstruct the previous series of triangles QList<QPolygonF> polygons = current_polygon.reconstruct (); collection.push_back (QPair<QList<QDomNode>,QList<QPolygonF> > (replaced_nodes, polygons)); replaced_nodes.clear (); current_polygon.reset (); current_color = color; } QPolygonF p (qstr2ptsvector (elt.attribute ("points"))); current_polygon.add (p); replaced_nodes.push_back (node); } } else { if (current_polygon.count ()) { QList<QPolygonF> polygons = current_polygon.reconstruct (); collection.push_back (QPair<QList<QDomNode>,QList<QPolygonF> > (replaced_nodes, polygons)); replaced_nodes.clear (); current_polygon.reset (); } reconstruct_polygons (elt); } } // Finish collection.push_back (QPair<QList<QDomNode>,QList<QPolygonF> > (replaced_nodes, current_polygon.reconstruct ())); for (int ii = 0; ii < collection.count (); ii++) replace_polygons (parent_elt, collection[ii].first, collection[ii].second); } void add_custom_properties (QDomElement& parent_elt) { QDomNodeList nodes = parent_elt.childNodes (); for (int ii = 0; ii < nodes.count (); ii++) { QDomNode node = nodes.at (ii); if (! node.isElement ()) continue; QDomElement elt = node.toElement (); if (elt.tagName () == "image") elt.setAttribute ("image-rendering", "optimizeSpeed"); else add_custom_properties (elt); } } #if defined (OCTAVE_USE_WINDOWS_API) && defined (_UNICODE) extern "C" int wmain (int argc, wchar_t **wargv) { static char **argv = new char * [argc + 1]; std::vector<std::string> argv_str; // convert wide character strings to multibyte UTF-8 strings std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> wchar_conv; for (int i_arg = 0; i_arg < argc; i_arg++) argv_str.push_back (wchar_conv.to_bytes (wargv[i_arg])); // Get pointers to C strings not before vector is stable. for (int i_arg = 0; i_arg < argc; i_arg++) argv[i_arg] = &argv_str[i_arg][0]; argv[argc] = nullptr; #else int main (int argc, char **argv) { #endif const char *doc = "See \"octave-svgconvert -h\""; const char *help = "Usage:\n\ octave-svgconvert infile fmt dpi font reconstruct outfile\n\n\ Convert svg file to pdf, or svg. All arguments are mandatory:\n\ * infile: input svg file or \"-\" to indicate that the input svg file should be \ read from stdin\n\ * fmt: format of the output file. May be one of pdf or svg\n\ * dpi: device dependent resolution in screen pixel per inch\n\ * font: specify a file name for the default FreeSans font\n\ * reconstruct: specify whether to reconstruct triangle to polygons (0 or 1)\n\ * outfile: output file name\n"; if (strcmp (argv[1], "-h") == 0) { std::cout << help; return 0; } else if (argc != 7) { std::cerr << help; return -1; } // Open svg file QFile file; if (strcmp (argv[1], "-") != 0) { // Read from file file.setFileName (argv[1]); if (! file.open (QIODevice::ReadOnly | QIODevice::Text)) { std::cerr << "Unable to open file " << argv[1] << "\n"; std::cerr << help; return -1; } } else { // Read from stdin if (! file.open (stdin, QIODevice::ReadOnly | QIODevice::Text)) { std::cerr << "Unable to read from stdin\n"; std::cerr << doc; return -1; } } // Create a DOM document and load the svg file QDomDocument document; QString msg; if (! document.setContent (&file, false, &msg)) { std::cerr << "Failed to parse XML contents" << std::endl << msg.toStdString () << std::endl; file.close(); return -1; } file.close (); // Format if (strcmp (argv[2], "pdf") != 0 && strcmp (argv[2], "svg") != 0) { std::cerr << "Unhandled output file format " << argv[2] << "\n"; std::cerr << doc; return -1; } // Resolution (Currently unused). Keep the DPI argument in case // we implement raster outputs. // double dpi = QString (argv[3]).toDouble (); // Get the viewport from the root element QDomElement root = document.firstChildElement(); double x0, y0, dx, dy; QString s = root.attribute ("viewBox"); QTextStream (&s) >> x0 >> y0 >> dx >> dy; QRectF vp (x0, y0, dx, dy); // Setup application and add default FreeSans font if needed QApplication a (argc, argv); // When printing to PDF we may need the default FreeSans font if (! strcmp (argv[2], "pdf")) { QFont font ("FreeSans"); if (! font.exactMatch ()) { QString fontpath (argv[4]); if (! fontpath.isEmpty ()) { int id = QFontDatabase::addApplicationFont (fontpath); if (id < 0) std::cerr << "warning: print: " "Unable to add default font to database\n"; } else std::cerr << "warning: print: FreeSans font not found\n"; } } // First render in a temporary file QTemporaryFile fout; if (! fout.open ()) { std::cerr << "Could not open temporary file\n"; return -1; } // Do basic polygons reconstruction if (QString (argv[5]).toInt ()) reconstruct_polygons (root); // Add custom properties to SVG add_custom_properties (root); // Draw if (! strcmp (argv[2], "pdf")) { // PDF painter pdfpainter painter (fout.fileName (), vp); draw (root, painter); } else { // Return modified svg document QTextStream out (&fout); out.setCodec ("UTF-8"); out << document.toByteArray (); } // Delete output file before writing with new data if (QFile::exists (argv[6])) if (! QFile::remove (argv[6])) { std::cerr << "Unable to replace existing file " << argv[6] << "\n"; return -1; } fout.copy (argv[6]); return 0; }