From 043e6c8ba34529ea5f40c1b1aaf555f4f1410259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Br=C3=BCns?= Date: Mon, 21 Mar 2022 01:19:24 +0100 Subject: [PATCH] [TechDraw] Replace XQuery based SVG cleanup with DOM traversal Evaluation of the XQuery has a complexity of more than O(3), as the underlying QDomNodeModel::compareOrder is quite expensive. Traversing the DOM tree directly is significantly faster, i.e. it now takes constantly less time than the actual drawing via QPainter, while previously it could take several magnitudes more time. Fixes #6638 Also replace the XQuery used for reparenting the drawing group. Not performance critical, but significantly simpler code. --- src/Mod/TechDraw/Gui/QGVPage.cpp | 48 +++++++++++--------------------- 1 file changed, 16 insertions(+), 32 deletions(-) diff --git a/src/Mod/TechDraw/Gui/QGVPage.cpp b/src/Mod/TechDraw/Gui/QGVPage.cpp index 012ad6cdbf..8295cb99d8 100644 --- a/src/Mod/TechDraw/Gui/QGVPage.cpp +++ b/src/Mod/TechDraw/Gui/QGVPage.cpp @@ -44,9 +44,6 @@ #include #endif -#include -#include - #include #include #include @@ -86,7 +83,6 @@ #include #include #include -#include #include #include "Rez.h" @@ -915,6 +911,19 @@ void QGVPage::saveSvg(QString filename) postProcessXml(temporaryFile, filename, pageName); } +static void removeEmptyGroups(QDomElement e) +{ + while (!e.isNull()) { + QDomElement next = e.nextSiblingElement(); + if (e.hasChildNodes()) { + removeEmptyGroups(e.firstChildElement()); + } else if (e.tagName() == QLatin1String("g")) { + e.parentNode().removeChild(e); + } + e = next; + } +} + void QGVPage::postProcessXml(QTemporaryFile& temporaryFile, QString fileName, QString pageName) { QDomDocument exportDoc(QString::fromUtf8("SvgDoc")); @@ -996,41 +1005,16 @@ void QGVPage::postProcessXml(QTemporaryFile& temporaryFile, QString fileName, QS } } - QXmlQuery query(QXmlQuery::XQuery10); - QDomNodeModel model(query.namePool(), exportDoc); - query.setFocus(QXmlItem(model.fromDomNode(exportDocElem))); - - // XPath query to select first node as direct element descendant - query.setQuery(QString::fromUtf8( - "declare default element namespace \"" SVG_NS_URI "\"; " - "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " - "/svg/g[1]")); - - QXmlResultItems queryResult; - query.evaluateTo(&queryResult); - // Obtain the drawing group element, move it under root node and set its id to "DrawingContent" - QDomElement drawingGroup; - if (!queryResult.next().isNull()) { - drawingGroup = model.toDomNode(queryResult.current().toNodeModelIndex()).toElement(); + QDomElement drawingGroup = exportDocElem.firstChildElement(QLatin1String("g")); + if (!drawingGroup.isNull()) { drawingGroup.setAttribute(QString::fromUtf8("id"), QString::fromUtf8("DrawingContent")); rootGroup.appendChild(drawingGroup); } - exportDocElem.appendChild(rootGroup); // As icing on the cake, get rid of the empty 's Qt SVG generator painting inserts. - // XPath query to select any element anywhere with no child nodes whatsoever - query.setQuery(QString::fromUtf8( - "declare default element namespace \"" SVG_NS_URI "\"; " - "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " - "//g[not(*)]")); - - query.evaluateTo(&queryResult); - while (!queryResult.next().isNull()) { - QDomElement g(model.toDomNode(queryResult.current().toNodeModelIndex()).toElement()); - g.parentNode().removeChild(g); - } + removeEmptyGroups(exportDocElem); // Time to save our product QFile outFile( fileName );