From 20a3e4f636dde60015936954f02829d744c85a82 Mon Sep 17 00:00:00 2001 From: ispyisail Date: Sun, 27 Sep 2026 19:29:15 +1300 Subject: [PATCH 1/3] Put the DXF export on named layers (#1071) Everything in an exported DXF was on layer 0, and the file had no LAYER table: in a CAD program the border, title block, symbols, wires and texts could not be hidden, printed or recoloured separately. Each kind of content now has its own layer, declared in a LAYER table (white/black, continuous, so the file looks the same on opening): QET_BORDER, QET_TITLEBLOCK, QET_SYMBOLS, QET_SYMBOL_TEXTS, QET_TERMINALS, QET_WIRES, QET_WIRE_NUMBERS, QET_JUNCTIONS, QET_TEXTS, QET_XREFS, QET_SHAPES, QET_TABLES, QET_IMAGES Fixed, untranslated names, so layer filters and scripts work in any language. Createdxf gets a current layer beside its xScale/yScale; DxfExport sets it before each kind of content, and BorderTitleBlock switches to the title block layer itself. No DXF version change: layers exist in R10. Checked on the 24 example projects, 133 folios: with the layer values set back to 0 and the LAYER table removed, every file is byte-identical to before; no entity is on an undeclared layer or left on 0; ezdxf reads all of them with no audit errors. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG --- sources/bordertitleblock.cpp | 2 ++ sources/createdxf.cpp | 54 +++++++++++++++++++++++++++++------- sources/createdxf.h | 6 +++- sources/dxfexport.cpp | 20 ++++++++++++- sources/dxfexport.h | 29 +++++++++++++++++++ 5 files changed, 99 insertions(+), 12 deletions(-) diff --git a/sources/bordertitleblock.cpp b/sources/bordertitleblock.cpp index 7878d1a9f..f63877eb4 100644 --- a/sources/bordertitleblock.cpp +++ b/sources/bordertitleblock.cpp @@ -19,6 +19,7 @@ #include "bordercelllabels.h" #include "createdxf.h" +#include "dxfexport.h" #include "diagram.h" #include "diagramposition.h" #include "math.h" @@ -706,6 +707,7 @@ void BorderTitleBlock::drawDxf( // render the titleblock, using the TitleBlockTemplate object if (display_titleblock_) { + Createdxf::layer = DxfExport::Layer::TitleBlock; //qp -> translate(titleblock_rect_.topLeft()); QRectF rect = titleBlockRect(); m_titleblock_template_renderer -> renderDxf(rect, diff --git a/sources/createdxf.cpp b/sources/createdxf.cpp index 28fff363e..87cd2c91a 100644 --- a/sources/createdxf.cpp +++ b/sources/createdxf.cpp @@ -42,6 +42,7 @@ namespace { } } double Createdxf::yScale = 1; +QString Createdxf::layer = QStringLiteral("0"); Createdxf::Createdxf() { @@ -51,9 +52,12 @@ Createdxf::~Createdxf() { } -/* Header section of every DXF file.*/ -void Createdxf::dxfBegin (const QString& fileName) +/* Header section of every DXF file. + @param layers : the layers the entities will be written on, declared in + the LAYER table beside layer "0", which every DXF has. */ +void Createdxf::dxfBegin (const QString& fileName, const QStringList &layers) { + layer = QStringLiteral("0"); // Creation of an output stream object in text mode. // Header section of every dxf file. @@ -223,6 +227,35 @@ void Createdxf::dxfBegin (const QString& fileName) To_Dxf << 0.00 << "\r\n"; To_Dxf << 0 << "\r\n"; + To_Dxf << "ENDTAB" << "\r\n"; + + // Layers: "0" plus the ones the entities use, all white/black + // (colour 7) and continuous, so a file looks the same as before + // until the user changes a layer. + QStringList all_layers{QStringLiteral("0")}; + for (const QString &name : layers) { + if (!all_layers.contains(name)) + all_layers << name; + } + To_Dxf << 0 << "\r\n"; + To_Dxf << "TABLE" << "\r\n"; + To_Dxf << 2 << "\r\n"; + To_Dxf << "LAYER" << "\r\n"; + To_Dxf << 70 << "\r\n"; + To_Dxf << all_layers.size() << "\r\n"; + for (const QString &name : std::as_const(all_layers)) { + To_Dxf << 0 << "\r\n"; + To_Dxf << "LAYER" << "\r\n"; + To_Dxf << 2 << "\r\n"; + To_Dxf << name << "\r\n"; + To_Dxf << 70 << "\r\n"; + To_Dxf << 0 << "\r\n"; + To_Dxf << 62 << "\r\n"; + To_Dxf << 7 << "\r\n"; + To_Dxf << 6 << "\r\n"; + To_Dxf << "CONTINUOUS" << "\r\n"; + } + To_Dxf << 0 << "\r\n"; To_Dxf << "ENDTAB" << "\r\n"; To_Dxf << 0 << "\r\n"; To_Dxf << "ENDSEC" << "\r\n"; @@ -248,6 +281,7 @@ void Createdxf::dxfBegin (const QString& fileName) */ void Createdxf::dxfEnd(const QString& fileName) { + layer = QStringLiteral("0"); // Creation of an output stream object in text mode. if (!fileName.isEmpty()) { QFile file(fileName); @@ -298,7 +332,7 @@ void Createdxf::drawCircle( To_Dxf << 0 << "\r\n"; To_Dxf << "CIRCLE" << "\r\n"; To_Dxf << 8 << "\r\n"; - To_Dxf << 0 << "\r\n"; // Layer number (default layer in autocad) + To_Dxf << layer << "\r\n"; // Layer name To_Dxf << 62 << "\r\n"; To_Dxf << colour << "\r\n"; // Colour Code To_Dxf << 10 << "\r\n"; // XYZ is the Center point of circle @@ -346,7 +380,7 @@ void Createdxf::drawLine ( To_Dxf << 0 << "\r\n"; To_Dxf << "LINE" << "\r\n"; To_Dxf << 8 << "\r\n"; - To_Dxf << 0 << "\r\n"; // Layer number (default layer in autocad) + To_Dxf << layer << "\r\n"; // Layer name To_Dxf << 62 << "\r\n"; To_Dxf << colour << "\r\n"; // Colour Code To_Dxf << 10 << "\r\n"; @@ -671,7 +705,7 @@ void Createdxf::drawArc( To_Dxf << 0 << "\r\n"; To_Dxf << "ARC" << "\r\n"; To_Dxf << 8 << "\r\n"; - To_Dxf << 0 << "\r\n"; // Layer number (default layer in autocad) + To_Dxf << layer << "\r\n"; // Layer name To_Dxf << 62 << "\r\n"; To_Dxf << color << "\r\n"; // Colour Code To_Dxf << 10 << "\r\n"; // XYZ is the Center point of circle @@ -727,7 +761,7 @@ void Createdxf::drawText( To_Dxf << 0 << "\r\n"; To_Dxf << "TEXT" << "\r\n"; To_Dxf << 8 << "\r\n"; - To_Dxf << 0 << "\r\n"; // Layer number (default layer in autocad) + To_Dxf << layer << "\r\n"; // Layer name To_Dxf << 62 << "\r\n"; To_Dxf << colour << "\r\n"; // Colour Code To_Dxf << 10 << "\r\n"; // XYZ @@ -780,7 +814,7 @@ void Createdxf::drawTextAligned( To_Dxf << 0 << "\r\n"; To_Dxf << "TEXT" << "\r\n"; To_Dxf << 8 << "\r\n"; - To_Dxf << 0 << "\r\n"; // Layer number (default layer in autocad) + To_Dxf << layer << "\r\n"; // Layer name To_Dxf << 62 << "\r\n"; To_Dxf << colour << "\r\n"; // Colour Code To_Dxf << 10 << "\r\n"; // XYZ @@ -861,7 +895,7 @@ void Createdxf::drawPolyline(const QString &filepath, To_Dxf << 0 << "\r\n"; To_Dxf << "POLYLINE" << "\r\n"; To_Dxf << 8 << "\r\n"; - To_Dxf << 0 << "\r\n"; // Layer number (default layer in autocad) + To_Dxf << layer << "\r\n"; // Layer name To_Dxf << 62 << "\r\n"; To_Dxf << colorcode << "\r\n"; // Colour Code To_Dxf << 66 << "\r\n"; @@ -887,7 +921,7 @@ void Createdxf::drawPolyline(const QString &filepath, To_Dxf << 0 << "\r\n"; To_Dxf << "VERTEX" << "\r\n"; To_Dxf << 8 << "\r\n"; - To_Dxf << 0 << "\r\n"; // Layer number (default layer in autocad) + To_Dxf << layer << "\r\n"; // Layer name To_Dxf << 70 << "\r\n"; To_Dxf << 32 << "\r\n"; To_Dxf << 10 << "\r\n"; @@ -901,7 +935,7 @@ void Createdxf::drawPolyline(const QString &filepath, To_Dxf << 0 << "\r\n"; To_Dxf << "SEQEND" << "\r\n"; To_Dxf << 8 << "\r\n"; - To_Dxf << 0 << "\r\n"; // Layer number (default layer in autocad) + To_Dxf << layer << "\r\n"; // Layer name file.close(); } diff --git a/sources/createdxf.h b/sources/createdxf.h index b2ef93e7a..5fb042e82 100644 --- a/sources/createdxf.h +++ b/sources/createdxf.h @@ -18,6 +18,7 @@ #ifndef CREATEDXF_H #define CREATEDXF_H #include +#include #include #include @@ -30,7 +31,7 @@ class Createdxf public: Createdxf(); ~Createdxf(); - static void dxfBegin (const QString&); + static void dxfBegin (const QString&, const QStringList &layers = QStringList()); static void dxfEnd(const QString&); // you can add more functions to create more drawings. static void drawCircle( @@ -159,6 +160,9 @@ class Createdxf static const double sheetHeight; static double xScale; static double yScale; + /// Layer every entity is written on, "0" unless set; the + /// layers themselves are declared by dxfBegin() (discussion #1071). + static QString layer; }; #endif // CREATEDXF_H diff --git a/sources/dxfexport.cpp b/sources/dxfexport.cpp index 7d3d8818e..0ac1182b8 100644 --- a/sources/dxfexport.cpp +++ b/sources/dxfexport.cpp @@ -81,8 +81,12 @@ void DxfExport::write(Diagram *diagram, int width, int height, Createdxf::xScale = Createdxf::sheetWidth / double(width); Createdxf::yScale = Createdxf::sheetHeight / double(height); - Createdxf::dxfBegin(file_path); + Createdxf::dxfBegin(file_path, Layer::all()); + //Each kind of content on its own layer (discussion #1071). The + //border's title block switches to Layer::TitleBlock itself, see + //BorderTitleBlock::drawDxf(). + Createdxf::layer = Layer::Border; //Add project elements (lines, rectangles, circles, texts) to dxf file if (properties.draw_border) { QRectF rect(Diagram::margin,Diagram::margin,width,height); @@ -154,9 +158,11 @@ void DxfExport::write(Diagram *diagram, int width, int height, } // Draw shapes + Createdxf::layer = Layer::Shapes; foreach (QetShapeItem *qsi, list_shapes) qsi->toDXF(file_path, qsi->pen()); // Draw tables + Createdxf::layer = Layer::Tables; foreach (QetGraphicsTableItem *gti, list_tables) { gti->toDXF(file_path); } @@ -171,6 +177,7 @@ void DxfExport::write(Diagram *diagram, int width, int height, ElementPictureFactory::primitives primitives = ElementPictureFactory::instance()->getPrimitives(elmt->location()); + Createdxf::layer = Layer::SymbolTexts; for(QGraphicsSimpleTextItem *text : primitives.m_texts) { qreal fontSize = text->font().pointSizeF(); @@ -200,6 +207,7 @@ void DxfExport::write(Diagram *diagram, int width, int height, } } + Createdxf::layer = Layer::Symbols; for (QLineF line : primitives.m_lines) { QTransform t = QTransform().translate(elem_pos_x,elem_pos_y).rotate(rotation_angle); @@ -249,6 +257,7 @@ void DxfExport::write(Diagram *diagram, int width, int height, Createdxf::drawArcEllipse(file_path, r, startAngle, spanAngle, hotspot, rotation_angle, 0); } if (properties.draw_terminals) { + Createdxf::layer = Layer::Terminals; // Draw terminals QList list_terminals = elmt->terminals(); QColor col("red"); @@ -272,8 +281,10 @@ void DxfExport::write(Diagram *diagram, int width, int height, } poly << cond->pos()+segment->secondPoint(); } + Createdxf::layer = Layer::Wires; Createdxf::drawPolyline(file_path,poly,0); //Draw conductor text item + Createdxf::layer = Layer::WireNumbers; ConductorTextItem *textItem = cond -> textItem(); if (textItem) { @@ -302,6 +313,7 @@ void DxfExport::write(Diagram *diagram, int width, int height, } // Draw the junctions + Createdxf::layer = Layer::Junctions; QList junctions_list = cond->junctions(); if (!junctions_list.isEmpty()) { foreach(QPointF point, junctions_list) { @@ -312,6 +324,9 @@ void DxfExport::write(Diagram *diagram, int width, int height, //Draw text items foreach(DiagramTextItem *dti, list_texts) { + //A free text, or a symbol's own text (its label and the like) + Createdxf::layer = qgraphicsitem_cast(dti) + ? Layer::Texts : Layer::SymbolTexts; qreal fontSize = dti -> font().pointSizeF(); if (fontSize < 0) fontSize = dti -> font().pixelSize(); @@ -344,6 +359,7 @@ void DxfExport::write(Diagram *diagram, int width, int height, } //Draw the slave cross-reference labels + Createdxf::layer = Layer::Xrefs; for (QGraphicsTextItem *xref : std::as_const(list_xref_texts)) { qreal fontSize = xref->font().pointSizeF(); @@ -382,6 +398,7 @@ void DxfExport::write(Diagram *diagram, int width, int height, //Draw the master-side cross-reference items (table/cross), replaying //their existing paint() unmodified through DxfPaintEngine instead of //hand-porting drawAsCross()/drawAsContacts()/drawAsPlcTable(). + Createdxf::layer = Layer::Xrefs; for (CrossRefItem *xref : std::as_const(list_master_xrefs)) { DxfPaintDevice dxf_device(file_path); @@ -401,6 +418,7 @@ void DxfExport::write(Diagram *diagram, int width, int height, //actually turns the drawPixmap() call inside paint() into a //placeholder outline, since this DXF dialect has no raster image //entity to draw instead. + Createdxf::layer = Layer::Images; for (DiagramImageItem *image : std::as_const(list_images)) { DxfPaintDevice dxf_device(file_path); diff --git a/sources/dxfexport.h b/sources/dxfexport.h index e1c98e5bb..b9f1c0b2d 100644 --- a/sources/dxfexport.h +++ b/sources/dxfexport.h @@ -21,6 +21,7 @@ #include #include #include +#include class Diagram; class ExportProperties; @@ -32,6 +33,34 @@ class ExportProperties; */ namespace DxfExport { + /// The layers of an exported folio (discussion #1071). Fixed and + /// untranslated, so a CAD user's layer filters and scripts work + /// whatever language QElectroTech runs in; prefixed so they sort + /// together and do not clash with the recipient's own layers. + namespace Layer + { + inline const QString Border = QStringLiteral("QET_BORDER"); + inline const QString TitleBlock = QStringLiteral("QET_TITLEBLOCK"); + inline const QString Symbols = QStringLiteral("QET_SYMBOLS"); + inline const QString SymbolTexts = QStringLiteral("QET_SYMBOL_TEXTS"); + inline const QString Terminals = QStringLiteral("QET_TERMINALS"); + inline const QString Wires = QStringLiteral("QET_WIRES"); + inline const QString WireNumbers = QStringLiteral("QET_WIRE_NUMBERS"); + inline const QString Junctions = QStringLiteral("QET_JUNCTIONS"); + inline const QString Texts = QStringLiteral("QET_TEXTS"); + inline const QString Xrefs = QStringLiteral("QET_XREFS"); + inline const QString Shapes = QStringLiteral("QET_SHAPES"); + inline const QString Tables = QStringLiteral("QET_TABLES"); + inline const QString Images = QStringLiteral("QET_IMAGES"); + + inline QStringList all() + { + return {Border, TitleBlock, Symbols, SymbolTexts, Terminals, + Wires, WireNumbers, Junctions, Texts, Xrefs, Shapes, + Tables, Images}; + } + } + QSize folioSize(Diagram *diagram, const ExportProperties &properties); void write(Diagram *diagram, int width, int height, const QString &path, const ExportProperties &properties); From 1d980b164f2757713981c532aab929918966d15d Mon Sep 17 00:00:00 2001 From: ispyisail Date: Sun, 27 Sep 2026 19:32:00 +1300 Subject: [PATCH 2/3] DXF export: colour BYLAYER where QElectroTech sets no colour (#1071) Most entities were written with colour 0, BYBLOCK. Outside a block that falls back to the default colour, so it shows black on white or white on black, and no layer colour could change it: recolouring QET_WIRES in a CAD program left the wires as they were. Black itself maps to 0 too (RGBcodeTable[0]). Colour 0 is now written as 256, BYLAYER. The layers are colour 7, so a file looks the same on opening, and recolouring a layer now recolours its contents. Real colours (free texts, terminal markers) are kept. Checked on the 24 example projects, 133 folios: the only change from the previous commit is 110,283 colour codes 0 -> 256, no entity is left on 0, and ezdxf reads all of them with no audit errors. Rendered with QET_WIRES set to red and QET_SYMBOLS to blue: before, 0 red and 0 blue pixels; after, the wires and symbols take the layer colours. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG --- sources/createdxf.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/sources/createdxf.cpp b/sources/createdxf.cpp index 87cd2c91a..5135274ee 100644 --- a/sources/createdxf.cpp +++ b/sources/createdxf.cpp @@ -40,6 +40,16 @@ namespace { text.replace(QLatin1Char('\n'), QLatin1Char(' ')); return text; } + + /// Colour code of an entity. 0 is BYBLOCK, which means nothing outside a + /// block and so fell back to the default colour; it is also what black + /// maps to (RGBcodeTable[0]). BYLAYER (256) looks the same, the layers + /// being colour 7, and lets a CAD user recolour a whole layer at once + /// (discussion #1071). Real colours are kept. + int entityColour(int colour) + { + return colour == 0 ? 256 : colour; + } } double Createdxf::yScale = 1; QString Createdxf::layer = QStringLiteral("0"); @@ -334,7 +344,7 @@ void Createdxf::drawCircle( To_Dxf << 8 << "\r\n"; To_Dxf << layer << "\r\n"; // Layer name To_Dxf << 62 << "\r\n"; - To_Dxf << colour << "\r\n"; // Colour Code + To_Dxf << entityColour(colour) << "\r\n"; // Colour Code To_Dxf << 10 << "\r\n"; // XYZ is the Center point of circle To_Dxf << x << "\r\n"; // X in UCS (User Coordinate System)coordinates To_Dxf << 20 << "\r\n"; @@ -382,7 +392,7 @@ void Createdxf::drawLine ( To_Dxf << 8 << "\r\n"; To_Dxf << layer << "\r\n"; // Layer name To_Dxf << 62 << "\r\n"; - To_Dxf << colour << "\r\n"; // Colour Code + To_Dxf << entityColour(colour) << "\r\n"; // Colour Code To_Dxf << 10 << "\r\n"; To_Dxf << x1 << "\r\n"; // X in UCS (User Coordinate System)coordinates To_Dxf << 20 << "\r\n"; @@ -707,7 +717,7 @@ void Createdxf::drawArc( To_Dxf << 8 << "\r\n"; To_Dxf << layer << "\r\n"; // Layer name To_Dxf << 62 << "\r\n"; - To_Dxf << color << "\r\n"; // Colour Code + To_Dxf << entityColour(color) << "\r\n"; // Colour Code To_Dxf << 10 << "\r\n"; // XYZ is the Center point of circle To_Dxf << x << "\r\n"; // X in UCS (User Coordinate System)coordinates To_Dxf << 20 << "\r\n"; @@ -763,7 +773,7 @@ void Createdxf::drawText( To_Dxf << 8 << "\r\n"; To_Dxf << layer << "\r\n"; // Layer name To_Dxf << 62 << "\r\n"; - To_Dxf << colour << "\r\n"; // Colour Code + To_Dxf << entityColour(colour) << "\r\n"; // Colour Code To_Dxf << 10 << "\r\n"; // XYZ To_Dxf << x << "\r\n"; // X in UCS (User Coordinate System)coordinates To_Dxf << 20 << "\r\n"; @@ -816,7 +826,7 @@ void Createdxf::drawTextAligned( To_Dxf << 8 << "\r\n"; To_Dxf << layer << "\r\n"; // Layer name To_Dxf << 62 << "\r\n"; - To_Dxf << colour << "\r\n"; // Colour Code + To_Dxf << entityColour(colour) << "\r\n"; // Colour Code To_Dxf << 10 << "\r\n"; // XYZ To_Dxf << x << "\r\n"; // X in UCS (User Coordinate System)coordinates To_Dxf << 20 << "\r\n"; @@ -897,7 +907,7 @@ void Createdxf::drawPolyline(const QString &filepath, To_Dxf << 8 << "\r\n"; To_Dxf << layer << "\r\n"; // Layer name To_Dxf << 62 << "\r\n"; - To_Dxf << colorcode << "\r\n"; // Colour Code + To_Dxf << entityColour(colorcode) << "\r\n"; // Colour Code To_Dxf << 66 << "\r\n"; To_Dxf << 1 << "\r\n"; To_Dxf << 70 << "\r\n"; From a39992357552812a951f12249e68ac67a8380990 Mon Sep 17 00:00:00 2001 From: ispyisail Date: Sun, 27 Sep 2026 20:51:37 +1300 Subject: [PATCH 3/3] DXF export: keep the wires' colours Every wire was written in the default colour, whatever its colour on the folio: 723 of the 3,190 wires in the example projects (23 %, in 10 projects) lost theirs. The wire outline now takes the wire's colour, and its number the wire's text colour, mapped to the nearest DXF colour as the free texts already are. Black still comes out as BYLAYER. A two-colour wire gets its main colour, and dashed wires (46 in the examples) stay continuous: DXF line types are not written yet. Checked on the 24 example projects, 133 folios: exactly 723 wire outlines now carry a colour of their own, the only change from the previous commit; ezdxf reads all files with no audit errors. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01G2d2Zi8BfrYRPX88zhaoFG --- sources/dxfexport.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sources/dxfexport.cpp b/sources/dxfexport.cpp index 0ac1182b8..182634f38 100644 --- a/sources/dxfexport.cpp +++ b/sources/dxfexport.cpp @@ -282,7 +282,10 @@ void DxfExport::write(Diagram *diagram, int width, int height, poly << cond->pos()+segment->secondPoint(); } Createdxf::layer = Layer::Wires; - Createdxf::drawPolyline(file_path,poly,0); + //The wire's own colour, as on the folio (a two-colour wire gets + //its main one). Black comes out as BYLAYER, see Createdxf. + const ConductorProperties wire_properties = cond -> properties(); + Createdxf::drawPolyline(file_path, poly, Createdxf::dxfColor(wire_properties.color)); //Draw conductor text item Createdxf::layer = Layer::WireNumbers; ConductorTextItem *textItem = cond -> textItem(); @@ -306,7 +309,8 @@ void DxfExport::write(Diagram *diagram, int width, int height, qreal offset = fontSize * 1.6; foreach (QString line, lines) { if (line.size() > 0 && line != "_" ) - Createdxf::drawText(file_path, line, QPointF(x, y), fontSize, 360-angle, 0, 0.72 ); + Createdxf::drawText(file_path, line, QPointF(x, y), fontSize, 360-angle, + Createdxf::dxfColor(wire_properties.text_color), 0.72 ); x += offset * xdir; y -= offset * ydir; }