From aa6abe6e814a9066c1ae4fd84eecefa92f47708c Mon Sep 17 00:00:00 2001 From: ispyisail Date: Wed, 7 Oct 2026 10:04:49 +1300 Subject: [PATCH] Export a symbol's part data as hidden attributes of its DXF block (#1339) With symbols as blocks, every non-empty information field of a symbol (label, manufacturer, reference, supplier, quantity...) is written as a hidden attribute of its INSERT, tagged with the field's name. A CAD program can then list the parts from the drawing, as with AutoCAD's attribute extraction; hidden, they change nothing on screen, in any reader. A field already written as a visible attribute (with --dxf-attributes) is not repeated. The label formula is left out: it is how the label is made, not part data. Co-Authored-By: Claude Opus 5.5 --- sources/dxfexport.cpp | 44 ++++++++++++++++++++++++++++ tests/qttest/fixtures/dxf_blocks.qet | 1 + tests/qttest/tst_dxfblocks.cpp | 44 ++++++++++++++++++++++------ 3 files changed, 80 insertions(+), 9 deletions(-) diff --git a/sources/dxfexport.cpp b/sources/dxfexport.cpp index db46ffd59..397c6d30d 100644 --- a/sources/dxfexport.cpp +++ b/sources/dxfexport.cpp @@ -35,6 +35,7 @@ #include "qetgraphicsitem/independenttextitem.h" #include "qetgraphicsitem/qetshapeitem.h" #include "qetgraphicsitem/terminal.h" +#include "qetinformation.h" #include "textlines.h" #include @@ -436,6 +437,49 @@ void DxfExport::write(Diagram *diagram, int width, int height, } attribute_texts << dti; } + + //The rest of what is known about a symbol (manufacturer, + //reference, supplier, quantity...) as hidden attributes, so a + //CAD program can list the parts from the drawing. A field + //already written as a visible attribute (dxf_attributes) is not + //repeated; the label formula is how the label is made, not + //part data. + for (Element *elmt : std::as_const(list_elements)) { + const QString block = block_names.value(elmt -> location().toString()); + if (block.isEmpty()) + continue; + const DiagramContext information = elmt -> elementInformations(); + const QPointF insert(elmt -> pos().x() * Createdxf::xScale, + Createdxf::sheetHeight - elmt -> pos().y() * Createdxf::yScale); + for (const QString &key : QETInformation::elementInfoKeys()) { + if (key == QETInformation::ELMT_FORMULA) + continue; + const QString value = information.value(key).toString(); + const QString tag = Createdxf::blockName(key); + if (value.isEmpty() || used_tags.value(elmt).contains(tag)) + continue; + used_tags[elmt] << tag; + + Createdxf::Attribute attribute; + attribute.tag = tag; + attribute.text = value; + attribute.x = insert.x(); + attribute.y = insert.y(); + attribute.height = 9 * Createdxf::yScale; + attribute.invisible = true; + attribute.layer = Layer::SymbolTexts; + attributes[elmt] << attribute; + + if (!defined_tags[block].contains(tag)) { + defined_tags[block] << tag; + Createdxf::Attribute definition = attribute; + definition.x = 0; + definition.y = Createdxf::sheetHeight; + definition.text.clear(); + attribute_definitions[block] << definition; + } + } + } } //A block is drawn as if its symbol sat unturned at the folio's diff --git a/tests/qttest/fixtures/dxf_blocks.qet b/tests/qttest/fixtures/dxf_blocks.qet index a1118d8e8..957427f80 100644 --- a/tests/qttest/fixtures/dxf_blocks.qet +++ b/tests/qttest/fixtures/dxf_blocks.qet @@ -31,6 +31,7 @@ K1 + Schneider diff --git a/tests/qttest/tst_dxfblocks.cpp b/tests/qttest/tst_dxfblocks.cpp index 5857f96c9..308088c9c 100644 --- a/tests/qttest/tst_dxfblocks.cpp +++ b/tests/qttest/tst_dxfblocks.cpp @@ -22,8 +22,8 @@ /** --export-dxf --dxf-blocks (issue #1339): a symbol placed twice is one BLOCK and two INSERTs, the second turned a quarter, and each symbol's - label, with --dxf-attributes, is an attribute of its INSERT; - without the switches there is neither. + information is a hidden attribute of it; with --dxf-attributes its + label is a visible one. Without the switches there is neither. */ class tst_dxfblocks : public QObject { @@ -88,13 +88,14 @@ private slots: const auto pairs = exportPairs({QStringLiteral("--dxf-attributes")}); QVERIFY2(!pairs.isEmpty(), "--export-dxf --dxf-attributes failed"); - QCOMPARE(values(pairs, QStringLiteral("ATTRIB"), QStringLiteral("2")), - (QStringList{QStringLiteral("LABEL"), QStringLiteral("LABEL")})); - QCOMPARE(values(pairs, QStringLiteral("ATTRIB"), QStringLiteral("1")), - (QStringList{QStringLiteral("K1"), QStringLiteral("K2")})); + QCOMPARE(values(pairs, QStringLiteral("ATTRIB"), QStringLiteral("2")) + .count(QStringLiteral("LABEL")), 2); + const QStringList texts = values(pairs, QStringLiteral("ATTRIB"), QStringLiteral("1")); + QVERIFY(texts.contains(QStringLiteral("K1"))); + QVERIFY(texts.contains(QStringLiteral("K2"))); //Defined once in the block, and no longer a loose text - QCOMPARE(values(pairs, QStringLiteral("ATTDEF"), QStringLiteral("2")), - QStringList{QStringLiteral("LABEL")}); + QCOMPARE(values(pairs, QStringLiteral("ATTDEF"), QStringLiteral("2")) + .count(QStringLiteral("LABEL")), 1); QVERIFY(!values(pairs, QStringLiteral("TEXT"), QStringLiteral("1")).contains(QStringLiteral("K1"))); } @@ -102,10 +103,35 @@ private slots: { const auto pairs = exportPairs({QStringLiteral("--dxf-blocks")}); QVERIFY2(!pairs.isEmpty(), "--export-dxf --dxf-blocks failed"); - QVERIFY(values(pairs, QStringLiteral("ATTRIB"), QStringLiteral("2")).isEmpty()); + //Only hidden attributes (70 = 1); the labels are texts + QVERIFY(!values(pairs, QStringLiteral("ATTRIB"), QStringLiteral("70")).contains(QStringLiteral("0"))); QVERIFY(values(pairs, QStringLiteral("TEXT"), QStringLiteral("1")).contains(QStringLiteral("K1"))); } + void dataAsHiddenAttributes() + { + //The label is a text, so it is part data too: K1's label and + //manufacturer, K2's label, all hidden (70 = 1) + auto pairs = exportPairs({QStringLiteral("--dxf-blocks")}); + QVERIFY2(!pairs.isEmpty(), "--export-dxf --dxf-blocks failed"); + QCOMPARE(values(pairs, QStringLiteral("ATTRIB"), QStringLiteral("2")), + (QStringList{QStringLiteral("LABEL"), QStringLiteral("MANUFACTURER"), + QStringLiteral("LABEL")})); + QCOMPARE(values(pairs, QStringLiteral("ATTRIB"), QStringLiteral("70")), + (QStringList{QStringLiteral("1"), QStringLiteral("1"), QStringLiteral("1")})); + QVERIFY(values(pairs, QStringLiteral("ATTRIB"), QStringLiteral("1")) + .contains(QStringLiteral("Schneider"))); + + //With the labels as attributes, the label is not repeated + pairs = exportPairs({QStringLiteral("--dxf-attributes")}); + QVERIFY2(!pairs.isEmpty(), "--export-dxf --dxf-attributes failed"); + QCOMPARE(values(pairs, QStringLiteral("ATTRIB"), QStringLiteral("2")), + (QStringList{QStringLiteral("LABEL"), QStringLiteral("MANUFACTURER"), + QStringLiteral("LABEL")})); + QCOMPARE(values(pairs, QStringLiteral("ATTRIB"), QStringLiteral("70")), + (QStringList{QStringLiteral("0"), QStringLiteral("1"), QStringLiteral("0")})); + } + void flatWithoutSwitch() { const auto pairs = exportPairs({});