diff --git a/src/Mod/TechDraw/App/DrawSVGTemplate.cpp b/src/Mod/TechDraw/App/DrawSVGTemplate.cpp index 71c6498b76..75360395de 100644 --- a/src/Mod/TechDraw/App/DrawSVGTemplate.cpp +++ b/src/Mod/TechDraw/App/DrawSVGTemplate.cpp @@ -50,6 +50,7 @@ #include +#include "DrawUtil.h" #include "DrawPage.h" #include "DrawSVGTemplate.h" @@ -290,7 +291,6 @@ std::map DrawSVGTemplate::getEditableTextsFromTemplate return editables; } - QDomDocument templateDocument; if (!templateDocument.setContent(&templateFile)) { Base::Console().Message("DrawSVGTemplate::getEditableTextsFromTemplate() - failed to parse file: %s\n", diff --git a/src/Mod/TechDraw/App/DrawSVGTemplate.h b/src/Mod/TechDraw/App/DrawSVGTemplate.h index c8a11eb42e..c233687201 100644 --- a/src/Mod/TechDraw/App/DrawSVGTemplate.h +++ b/src/Mod/TechDraw/App/DrawSVGTemplate.h @@ -29,9 +29,6 @@ #include #include "DrawTemplate.h" -#define SVG_NS_URI "http://www.w3.org/2000/svg" -#define FREECAD_SVG_NS_URI "http://www.freecadweb.org/wiki/index.php?title=Svg_Namespace" - namespace TechDraw { diff --git a/src/Mod/TechDraw/App/DrawUtil.h b/src/Mod/TechDraw/App/DrawUtil.h index 972816c081..a97b2fa5ce 100644 --- a/src/Mod/TechDraw/App/DrawUtil.h +++ b/src/Mod/TechDraw/App/DrawUtil.h @@ -56,6 +56,9 @@ #define VERTEXTOLERANCE (2.0 * Precision::Confusion()) +#define SVG_NS_URI "http://www.w3.org/2000/svg" +#define FREECAD_SVG_NS_URI "http://www.freecadweb.org/wiki/index.php?title=Svg_Namespace" + namespace TechDraw { diff --git a/src/Mod/TechDraw/App/DrawViewSymbol.cpp b/src/Mod/TechDraw/App/DrawViewSymbol.cpp index 8fe8b7a7ce..5b1f560dae 100644 --- a/src/Mod/TechDraw/App/DrawViewSymbol.cpp +++ b/src/Mod/TechDraw/App/DrawViewSymbol.cpp @@ -25,16 +25,23 @@ #ifndef _PreComp_ # include +#include #endif #include #include + #include +#include +#include + #include #include #include +#include "QDomNodeModel.h" +#include "DrawUtil.h" #include "DrawPage.h" #include "DrawViewSymbol.h" @@ -68,26 +75,45 @@ void DrawViewSymbol::onChanged(const App::Property* prop) { // Base::Console().Message("DVS::onChanged(%s) \n",prop->getName()); if (prop == &Symbol) { - if (!isRestoring()) { + if (!isRestoring() && Symbol.getValue()[0]) { //this pulls the initial values from svg into editabletexts // should only happen first time?? extra loop onChanged->execute->onChanged - std::vector eds; - std::string svg = Symbol.getValue(); - if (!svg.empty()) { - boost::regex e ("(.*?)"); - std::string::const_iterator tbegin, tend; - tbegin = svg.begin(); - tend = svg.end(); - boost::match_results twhat; - while (boost::regex_search(tbegin, tend, twhat, e)) { - eds.push_back(twhat[2]); - tbegin = twhat[0].second; + + std::vector editables; + QDomDocument symbolDocument; + + if (symbolDocument.setContent(QString::fromUtf8(Symbol.getValue()))) { + QDomElement symbolDocElem = symbolDocument.documentElement(); + + QXmlQuery query(QXmlQuery::XQuery10); + QDomNodeModel model(query.namePool(), symbolDocument); + query.setFocus(QXmlItem(model.fromDomNode(symbolDocElem))); + + // XPath query to select all nodes whose parent + // has "freecad:editable" attribute + query.setQuery(QString::fromUtf8( + "declare default element namespace \"" SVG_NS_URI "\"; " + "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " + "//text[@freecad:editable]/tspan")); + + QXmlResultItems queryResult; + query.evaluateTo(&queryResult); + + while (!queryResult.next().isNull()) + { + QDomElement tspanElement = model.toDomNode(queryResult.current().toNodeModelIndex()).toElement(); + editables.push_back(tspanElement.text().toStdString()); } - EditableTexts.setValues(eds); -// requestPaint(); } + else { + Base::Console().Warning("DrawViewSymbol:onChanged - SVG for Symbol is not a valid document\n"); + } + + EditableTexts.setValues(editables); +// requestPaint(); } } + TechDraw::DrawView::onChanged(prop); } @@ -102,27 +128,51 @@ App::DocumentObjectExecReturn *DrawViewSymbol::execute(void) std::string svg = Symbol.getValue(); const std::vector& editText = EditableTexts.getValues(); - //this pushes the editabletexts into the svg - std::string newsvg = svg; if (!editText.empty()) { - boost::regex e1 ("(.*?)"); - string::const_iterator begin, end; - begin = svg.begin(); - end = svg.end(); - boost::match_results what; - std::size_t count = 0; + QDomDocument symbolDocument; - while (boost::regex_search(begin, end, what, e1)) { - if (count < editText.size()) { - boost::regex e2 ("((.*?)()"); - newsvg = boost::regex_replace(newsvg, e2, "$1>" + editText[count] + "$3"); + if (symbolDocument.setContent(QString::fromUtf8(Symbol.getValue()))) { + QDomElement symbolDocElem = symbolDocument.documentElement(); + + QXmlQuery query(QXmlQuery::XQuery10); + QDomNodeModel model(query.namePool(), symbolDocument); + query.setFocus(QXmlItem(model.fromDomNode(symbolDocElem))); + + // XPath query to select all nodes whose parent + // has "freecad:editable" attribute + query.setQuery(QString::fromUtf8( + "declare default element namespace \"" SVG_NS_URI "\"; " + "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " + "//text[@freecad:editable]/tspan")); + + QXmlResultItems queryResult; + query.evaluateTo(&queryResult); + + unsigned int count = 0; + while (!queryResult.next().isNull()) + { + QDomElement tspanElement = model.toDomNode(queryResult.current().toNodeModelIndex()).toElement(); + + // Keep all spaces in the text node + tspanElement.setAttribute(QString::fromUtf8("xml:space"), QString::fromUtf8("preserve")); + + // Remove all child nodes (if any) + while (!tspanElement.lastChild().isNull()) { + tspanElement.removeChild(tspanElement.lastChild()); + } + + // Finally append text node with editable replacement as the only descendant + tspanElement.appendChild(symbolDocument.createTextNode(QString::fromUtf8(editText[count].c_str()))); + ++count; } - count++; - begin = what[0].second; - } + Symbol.setValue(symbolDocument.toString(1).toStdString()); + } + else { + Base::Console().Warning("DrawViewSymbol:execute - SVG for Symbol is not a valid document\n"); + } } - Symbol.setValue(newsvg); + // requestPaint(); return DrawView::execute(); } diff --git a/src/Mod/TechDraw/Gui/QGISVGTemplate.cpp b/src/Mod/TechDraw/Gui/QGISVGTemplate.cpp index 6233c9d71a..5f09dd15ef 100644 --- a/src/Mod/TechDraw/Gui/QGISVGTemplate.cpp +++ b/src/Mod/TechDraw/Gui/QGISVGTemplate.cpp @@ -28,10 +28,12 @@ #include #include #include -#include +#include #endif // #ifndef _PreComp_ #include +#include +#include #include #include @@ -39,6 +41,8 @@ #include #include +#include +#include #include #include @@ -142,90 +146,87 @@ void QGISVGTemplate::updateView(bool update) void QGISVGTemplate::createClickHandles(void) { - TechDraw::DrawSVGTemplate *tmplte = getSVGTemplate(); - std::string temp = tmplte->PageResult.getValue(); + TechDraw::DrawSVGTemplate *svgTemplate = getSVGTemplate(); + QString templateFilename(QString::fromUtf8(svgTemplate->PageResult.getValue())); - if (temp.empty()) + if (templateFilename.isEmpty()) { return; - - Base::FileInfo fi(temp); - - std::ostringstream oStream; - std::string tempendl = "--endOfLine--"; - std::string line; - - //read all of PageResult into oStream (except the DrawingContent marker comment - why??) - std::ifstream ifile (fi.filePath().c_str()); - while (std::getline(ifile,line)) - { - // check if the marker in the template is found - if(line.find("") == std::string::npos) { - // if not - write line to oStream - oStream << line << tempendl; - } } - std::string outfragment(oStream.str()); + QFile file(templateFilename); + if (!file.open(QIODevice::ReadOnly)) { + Base::Console().Error("QGISVGTemplate::createClickHandles - error opening template file %s\n", + svgTemplate->PageResult.getValue()); + return; + } - // Find text tags with freecad:editable attribute and their matching tspans - // keep tagRegex in sync with App/DrawSVGTemplate.cpp - boost::regex tagRegex("]*freecad:editable=[^>]*)>[^<]*]*>([^<]*)"); + QDomDocument templateDocument; + if (!templateDocument.setContent(&file)) { + Base::Console().Message("QGISVGTemplate::createClickHandles - xml loading error\n"); + return; + } + file.close(); - // Smaller regexes for parsing matches to tagRegex - boost::regex editableNameRegex("freecad:editable=\"(.*?)\""); - boost::regex xRegex("x=\"([\\d.-]+)\""); - boost::regex yRegex("y=\"([\\d.-]+)\""); - //Note: some templates have fancy Transform clauses and don't use absolute x,y to position editableFields. - // editableFields will be in the wrong place in this case. + QDomElement templateDocElem = templateDocument.documentElement(); - std::string::const_iterator begin, end; - begin = outfragment.begin(); - end = outfragment.end(); - boost::match_results tagMatch, nameMatch, xMatch, yMatch; + QXmlQuery query(QXmlQuery::XQuery10); + QDomNodeModel model(query.namePool(), templateDocument); + query.setFocus(QXmlItem(model.fromDomNode(templateDocElem))); + + // XPath query to select all nodes with "freecad:editable" attribute + query.setQuery(QString::fromUtf8( + "declare default element namespace \"" SVG_NS_URI "\"; " + "declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; " + "//text[@freecad:editable]")); + + QXmlResultItems queryResult; + query.evaluateTo(&queryResult); //TODO: Find location of special fields (first/third angle) and make graphics items for them Base::Reference hGrp = App::GetApplication().GetUserParameter() .GetGroup("BaseApp")->GetGroup("Preferences")->GetGroup("Mod/TechDraw/General"); - double dotSize = hGrp->GetFloat("TemplateDotSize", 3.0); + double editClickBoxSize = Rez::guiX(hGrp->GetFloat("TemplateDotSize", 3.0)); - while (boost::regex_search(begin, end, tagMatch, tagRegex)) { - if ( boost::regex_search(tagMatch[1].first, tagMatch[1].second, nameMatch, editableNameRegex) && - boost::regex_search(tagMatch[1].first, tagMatch[1].second, xMatch, xRegex) && - boost::regex_search(tagMatch[1].first, tagMatch[1].second, yMatch, yRegex) ) { + QColor editClickBoxColor = Qt::green; + editClickBoxColor.setAlpha(128); //semi-transparent - QString xStr = QString::fromStdString(xMatch[1].str()); - QString yStr = QString::fromStdString(yMatch[1].str()); + double width = editClickBoxSize; + double height = editClickBoxSize; - double x = Rez::guiX(xStr.toDouble()); - double y = Rez::guiX(yStr.toDouble()); + while (!queryResult.next().isNull()) + { + QDomElement textElement = model.toDomNode(queryResult.current().toNodeModelIndex()).toElement(); - double editClickBoxSize = Rez::guiX(dotSize); - QColor editClickBoxColor = Qt::green; - editClickBoxColor.setAlpha(128); //semi-transparent + QString name = textElement.attribute(QString::fromUtf8("freecad:editable")); + double x = Rez::guiX(textElement.attribute(QString::fromUtf8("x"), QString::fromUtf8("0.0")).toDouble()); + double y = Rez::guiX(textElement.attribute(QString::fromUtf8("y"), QString::fromUtf8("0.0")).toDouble()); - double width = editClickBoxSize; - double height = editClickBoxSize; - - auto item( new TemplateTextField(this, tmplte, nameMatch[1].str()) ); - float pad = 1; - - item->setRect(x - pad, Rez::guiX(-tmplte->getHeight()) + y - height - pad, - width + 2 * pad, height + 2 * pad); - QPen myPen; - QBrush myBrush(editClickBoxColor,Qt::SolidPattern); - myPen.setStyle(Qt::SolidLine); - myPen.setColor(editClickBoxColor); - myPen.setWidth(0); // 0 means "cosmetic pen" - always 1px - item->setPen(myPen); - item->setBrush(myBrush); - - item->setZValue(ZVALUE::SVGTEMPLATE + 1); - addToGroup(item); - textFields.push_back(item); + if (name.isEmpty()) { + Base::Console().Warning("QGISVGTemplate::createClickHandles - no name for editable text at %f, %f\n", + x, y); + continue; } - begin = tagMatch[0].second; + auto item(new TemplateTextField(this, svgTemplate, name.toStdString())); + + double pad = 1.0; + item->setRect(x - pad, Rez::guiX(-svgTemplate->getHeight()) + y - height - pad, + width + 2.0*pad, height + 2.0*pad); + + QPen myPen; + myPen.setStyle(Qt::SolidLine); + myPen.setColor(editClickBoxColor); + myPen.setWidth(0); // 0 means "cosmetic pen" - always 1px + item->setPen(myPen); + + QBrush myBrush(editClickBoxColor,Qt::SolidPattern); + item->setBrush(myBrush); + + item->setZValue(ZVALUE::SVGTEMPLATE + 1); + addToGroup(item); + + textFields.push_back(item); } } diff --git a/src/Mod/TechDraw/Gui/QGVPage.cpp b/src/Mod/TechDraw/Gui/QGVPage.cpp index 129519dabf..781b0f9d9d 100644 --- a/src/Mod/TechDraw/Gui/QGVPage.cpp +++ b/src/Mod/TechDraw/Gui/QGVPage.cpp @@ -79,6 +79,7 @@ #include #include #include +#include #include "Rez.h" #include "QGIDrawingTemplate.h" @@ -867,29 +868,13 @@ void QGVPage::postProcessXml(QTemporaryFile& temporaryFile, QString fileName, QS QDomElement exportDocElem = exportDoc.documentElement(); //root - 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); - // Insert Freecad SVG namespace into namespace declarations exportDocElem.setAttribute(QString::fromUtf8("xmlns:freecad"), QString::fromUtf8(FREECAD_SVG_NS_URI)); - // Set the first group's id to page name - QDomElement g; - if (!queryResult.next().isNull()) { - g = model.toDomNode(queryResult.current().toNodeModelIndex()).toElement(); - g.setAttribute(QString::fromUtf8("id"), pageName); - } + // Create the root group which will host the drawing group and the template group + QDomElement rootGroup = exportDoc.createElement(QString::fromUtf8("g")); + rootGroup.setAttribute(QString::fromUtf8("id"), pageName); // Now insert our template QGISVGTemplate *svgTemplate = dynamic_cast(pageTemplate); @@ -903,33 +888,51 @@ void QGVPage::postProcessXml(QTemporaryFile& temporaryFile, QString fileName, QS QDomElement templateDocElem = templateResultDoc.documentElement(); // Insert the template into a new group with id set to template name - QDomElement groupWrapper = exportDoc.createElement(QString::fromUtf8("g")); + QDomElement templateGroup = exportDoc.createElement(QString::fromUtf8("g")); Base::FileInfo fi(drawTemplate->Template.getValue()); - groupWrapper.setAttribute(QString::fromUtf8("id"), - QString::fromUtf8(fi.fileName().c_str())); - groupWrapper.setAttribute(QString::fromUtf8("style"), - QString::fromUtf8("stroke: none;")); + templateGroup.setAttribute(QString::fromUtf8("id"), + QString::fromUtf8(fi.fileName().c_str())); + templateGroup.setAttribute(QString::fromUtf8("style"), + QString::fromUtf8("stroke: none;")); // Scale the template group correctly - groupWrapper.setAttribute(QString::fromUtf8("transform"), + templateGroup.setAttribute(QString::fromUtf8("transform"), QString().sprintf("scale(%f, %f)", Rez::guiX(1.0), Rez::guiX(1.0))); - // Finally, transfer all template document child nodes under the wrapper group + // Finally, transfer all template document child nodes under the template group while (!templateDocElem.firstChild().isNull()) { - groupWrapper.appendChild(templateDocElem.firstChild()); + templateGroup.appendChild(templateDocElem.firstChild()); } - if (!g.isNull()) { - g.insertBefore(groupWrapper, QDomNode()); - } - else { - exportDocElem.insertBefore(groupWrapper, QDomNode()); - } + rootGroup.appendChild(templateGroup); } } } } + 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(); + 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( @@ -939,7 +942,7 @@ void QGVPage::postProcessXml(QTemporaryFile& temporaryFile, QString fileName, QS query.evaluateTo(&queryResult); while (!queryResult.next().isNull()) { - g = model.toDomNode(queryResult.current().toNodeModelIndex()).toElement(); + QDomElement g(model.toDomNode(queryResult.current().toNodeModelIndex()).toElement()); g.parentNode().removeChild(g); } diff --git a/src/Mod/TechDraw/Gui/Resources/ToleranceSymbols/flatness_editable.svg b/src/Mod/TechDraw/Gui/Resources/ToleranceSymbols/flatness_editable.svg index 9ad0d66448..83635eaeb9 100644 --- a/src/Mod/TechDraw/Gui/Resources/ToleranceSymbols/flatness_editable.svg +++ b/src/Mod/TechDraw/Gui/Resources/ToleranceSymbols/flatness_editable.svg @@ -2,6 +2,7 @@ x="17.047827" y="1041.8041" id="text3801" - xml:space="preserve" style="font-size:5px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:100%;letter-spacing:0px;word-spacing:0px;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.28200001;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:osifont;-inkscape-font-specification:osifont">0.000 + id="tspan3803" + xml:space="preserve">0.000 x="33.975941" y="1041.7771" id="text5288" - xml:space="preserve" style="font-size:5.07499981px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;text-align:center;line-height:100%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial Narrow;-inkscape-font-specification:'Arial Narrow, Condensed'">A diff --git a/src/Mod/TechDraw/Gui/Resources/ToleranceSymbols/tolerance_editable.svg b/src/Mod/TechDraw/Gui/Resources/ToleranceSymbols/tolerance_editable.svg index f890bbe338..f2b41ca769 100644 --- a/src/Mod/TechDraw/Gui/Resources/ToleranceSymbols/tolerance_editable.svg +++ b/src/Mod/TechDraw/Gui/Resources/ToleranceSymbols/tolerance_editable.svg @@ -2,6 +2,7 @@ + 0.01