[TechDraw] Issue #5903 - Autofill template information

This commit is contained in:
pavltom
2024-03-16 10:50:35 +01:00
committed by WandererFan
parent 897e969477
commit aac48eb2f9
13 changed files with 236 additions and 156 deletions

View File

@@ -132,10 +132,10 @@ QString DrawSVGTemplate::processTemplate()
query.processItems(QString::fromUtf8(
"declare default element namespace \"" SVG_NS_URI "\"; "
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
"//text[@freecad:editable]/tspan"),
"//text[@" FREECAD_ATTR_EDITABLE "]/tspan"),
[&substitutions, &templateDocument](QDomElement& tspan) -> bool {
// Replace the editable text spans with new nodes holding actual values
QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8("freecad:editable"));
QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8(FREECAD_ATTR_EDITABLE));
std::map<std::string, std::string>::iterator item =
substitutions.find(editableName.toStdString());
if (item != substitutions.end()) {
@@ -296,15 +296,28 @@ std::map<std::string, std::string> DrawSVGTemplate::getEditableTextsFromTemplate
query.processItems(QString::fromUtf8(
"declare default element namespace \"" SVG_NS_URI "\"; "
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
"//text[@freecad:editable]/tspan"),
[&editables](QDomElement& tspan) -> bool {
QString editableName = tspan.parentNode().toElement().attribute(QString::fromUtf8("freecad:editable"));
QString editableValue = tspan.firstChild().nodeValue();
"//text[@" FREECAD_ATTR_EDITABLE "]/tspan"),
[this, &editables](QDomElement& tspan) -> bool {
QDomElement parent = tspan.parentNode().toElement();
QString editableName = parent.attribute(QString::fromUtf8(FREECAD_ATTR_EDITABLE));
editables[std::string(editableName.toUtf8().constData())] =
std::string(editableValue.toUtf8().constData());
return true;
});
QString editableValue;
if (parent.hasAttribute(QString::fromUtf8(FREECAD_ATTR_AUTOFILL))) {
QString autofillValue = getAutofillValue(parent.attribute(QString::fromUtf8(FREECAD_ATTR_AUTOFILL)));
if (!autofillValue.isNull()) {
editableValue = autofillValue;
}
}
// If the autofill value is not specified or unsupported, use the default text value
if (editableValue.isNull()) {
editableValue = tspan.firstChild().nodeValue();
}
editables[std::string(editableName.toUtf8().constData())] =
std::string(editableValue.toUtf8().constData());
return true;
});
return editables;
}

View File

@@ -24,13 +24,19 @@
#ifndef _PreComp_
# include <sstream>
# include <QCollator>
# include <QDateTime>
#endif
#include <Base/Console.h>
#include <App/Application.h>
#include <App/Document.h>
#include "DrawTemplate.h"
#include "DrawTemplatePy.h"
#include "DrawPage.h"
#include "DrawUtil.h"
using namespace TechDraw;
@@ -94,6 +100,67 @@ DrawPage* DrawTemplate::getParentPage() const
return page;
}
QString DrawTemplate::getAutofillValue(const QString &id) const
{
// author
if (id.compare(QString::fromUtf8(Autofill::Author)) == 0) {
std::string value = App::GetApplication().GetUserParameter().GetGroup("BaseApp")->GetGroup("Preferences")
->GetGroup("Document")->GetASCII("prefAuthor");
if (!value.empty()) {
return QString::fromUtf8(value.c_str());
}
}
// date
else if (id.compare(QString::fromUtf8(Autofill::Date)) == 0) {
QDateTime date = QDateTime::currentDateTime();
return date.toString(QLocale().dateFormat(QLocale::ShortFormat));
}
// organization
else if (id.compare(QString::fromUtf8(Autofill::Organization)) == 0) {
std::string value = App::GetApplication().GetUserParameter().GetGroup("BaseApp")->GetGroup("Preferences")
->GetGroup("Document")->GetASCII("prefCompany");
if (!value.empty()) {
return QString::fromUtf8(value.c_str());
}
}
// scale
else if (id.compare(QString::fromUtf8(Autofill::Scale)) == 0) {
DrawPage *page = getParentPage();
if (page) {
std::pair<int, int> scale = DrawUtil::nearestFraction(page->Scale.getValue());
return QString::asprintf("%d : %d", scale.first, scale.second);
}
}
// sheet
else if (id.compare(QString::fromUtf8(Autofill::Sheet)) == 0) {
std::vector<DocumentObject *> pages = getDocument()->getObjectsOfType(TechDraw::DrawPage::getClassTypeId());
std::vector<QString> pageNames;
for (auto page : pages) {
pageNames.push_back(QString::fromUtf8(page->Label.getValue()));
}
QCollator collator;
std::sort(pageNames.begin(), pageNames.end(), collator);
int pos = 0;
DrawPage *page = getParentPage();
if (page) {
auto it = std::find(pageNames.begin(), pageNames.end(), QString::fromUtf8(page->Label.getValue()));
if (it != pageNames.end()) {
pos = it - pageNames.begin() + 1;
}
}
return QString::asprintf("%d / %d", pos, (int) pageNames.size());
}
// title
else if (id.compare(QString::fromUtf8(Autofill::Title)) == 0) {
return QString::fromUtf8(getDocument()->Label.getValue());
}
return QString();
}
// Python Template feature ---------------------------------------------------------
namespace App {

View File

@@ -57,6 +57,8 @@ public:
virtual DrawPage* getParentPage() const;
virtual QString getAutofillValue(const QString &id) const;
/// returns the type name of the ViewProvider
const char* getViewProviderName(void) const override{
return "TechDrawGui::ViewProviderTemplate";
@@ -65,6 +67,17 @@ public:
// from base class
PyObject *getPyObject(void) override;
class Autofill
{
public:
static constexpr const char *Author = "author";
static constexpr const char *Date = "date";
static constexpr const char *Organization = "organization";
static constexpr const char *Scale = "scale";
static constexpr const char *Sheet = "sheet";
static constexpr const char *Title = "title";
};
private:
static const char* OrientationEnums[];

View File

@@ -1312,6 +1312,80 @@ double DrawUtil::angleDifference(double fi1, double fi2, bool reflex)
return fi1;
}
std::pair<int, int> DrawUtil::nearestFraction(double val, int maxDenom)
{
// Find rational approximation to given real number
// David Eppstein / UC Irvine / 8 Aug 1993
//
// With corrections from Arno Formella, May 2008
// and additional fiddles by WF 2017
// usage: a.out r d
// r is real number to approx
// d is the maximum denominator allowed
//
// Based on the theory of continued fractions
// if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...)))
// then best approximation is found by truncating this series
// (with some adjustments in the last term).
//
// Note the fraction can be recovered as the first column of the matrix
// ( a1 1 ) ( a2 1 ) ( a3 1 ) ...
// ( 1 0 ) ( 1 0 ) ( 1 0 )
// Instead of keeping the sequence of continued fraction terms,
// we just keep the last partial product of these matrices.
std::pair<int, int> result;
long m[2][2];
long maxden = maxDenom;
long ai;
double x = val;
double startx = x;
/* initialize matrix */
m[0][0] = m[1][1] = 1;
m[0][1] = m[1][0] = 0;
/* loop finding terms until denom gets too big */
while (m[1][0] * ( ai = (long)x ) + m[1][1] <= maxden) {
long t;
t = m[0][0] * ai + m[0][1];
m[0][1] = m[0][0];
m[0][0] = t;
t = m[1][0] * ai + m[1][1];
m[1][1] = m[1][0];
m[1][0] = t;
if(x == (double) ai)
break; // AF: division by zero
x = 1/(x - (double) ai);
if(x > (double) std::numeric_limits<int>::max())
break; // AF: representation failure
}
/* now remaining x is between 0 and 1/ai */
/* approx as either 0 or 1/m where m is max that will fit in maxden */
/* first try zero */
double error1 = startx - ((double) m[0][0] / (double) m[1][0]);
int n1 = m[0][0];
int d1 = m[1][0];
/* now try other possibility */
ai = (maxden - m[1][1]) / m[1][0];
m[0][0] = m[0][0] * ai + m[0][1];
m[1][0] = m[1][0] * ai + m[1][1];
double error2 = startx - ((double) m[0][0] / (double) m[1][0]);
int n2 = m[0][0];
int d2 = m[1][0];
if (std::fabs(error1) <= std::fabs(error2)) {
result.first = n1;
result.second = d1;
} else {
result.first = n2;
result.second = d2;
}
return result;
}
// Interval marking functions
// ==========================

View File

@@ -58,6 +58,9 @@
#define SVG_NS_URI "http://www.w3.org/2000/svg"
#define FREECAD_SVG_NS_URI "https://www.freecad.org/wiki/index.php?title=Svg_Namespace"
#define FREECAD_ATTR_EDITABLE "freecad:editable"
#define FREECAD_ATTR_AUTOFILL "freecad:autofill"
//some shapes are being passed in where edges that should be connected are in fact
//separated by more than 2*Precision::Confusion (expected tolerance for 2 TopoDS_Vertex)
//this value is used in EdgeWalker, DrawProjectSplit and DrawUtil and needs to be in sync in
@@ -217,6 +220,7 @@ public:
static void angleNormalize(double& fi);
static double angleComposition(double fi, double delta);
static double angleDifference(double fi1, double fi2, bool reflex = false);
static std::pair<int, int> nearestFraction(double val, int maxDenom = 999);
// Interval marking functions
static unsigned int intervalMerge(std::vector<std::pair<double, bool>>& marking,

View File

@@ -125,7 +125,7 @@ std::vector<std::string> DrawViewSymbol::getEditableFields()
// has "freecad:editable" attribute
query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
"//text[@freecad:editable]/tspan"),
"//text[@" FREECAD_ATTR_EDITABLE "]/tspan"),
[&editables](QDomElement& tspan) -> bool {
QString editableValue = tspan.firstChild().nodeValue();
editables.emplace_back(editableValue.toStdString());
@@ -154,7 +154,7 @@ void DrawViewSymbol::updateFieldsInSymbol()
// has "freecad:editable" attribute
query.processItems(QString::fromUtf8("declare default element namespace \"" SVG_NS_URI "\"; "
"declare namespace freecad=\"" FREECAD_SVG_NS_URI "\"; "
"//text[@freecad:editable]/tspan"),
"//text[@" FREECAD_ATTR_EDITABLE "]/tspan"),
[&symbolDocument, &editText, &count](QDomElement& tspanElement) -> bool {
if (count >= editText.size()) {

View File

@@ -27,6 +27,7 @@
#include <QDomDocument>
#endif
#include "DrawUtil.h"
#include "XMLQuery.h"
@@ -51,7 +52,7 @@ static bool processElements(const QDomElement& element, const QString& queryStr,
for(int i = 0; i < editable.count(); i++) {
QDomNode node = editable.item(i);
QDomElement element = node.toElement();
if (element.hasAttribute(QString(QLatin1String("freecad:editable")))) {
if (element.hasAttribute(QString(QLatin1String(FREECAD_ATTR_EDITABLE)))) {
if (find_tspan) {
element = element.firstChildElement();
}