diff --git a/sources/dxfexport.cpp b/sources/dxfexport.cpp index 5f3c266ff..6db86c5b4 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 "textlines.h" #include #include @@ -358,7 +359,8 @@ void DxfExport::write(Diagram *diagram, int width, int height, qreal y = dti->scenePos().y() - ydir * fontSize * 1.8 - xdir * fontSize * 0.9; - QStringList lines = dti -> toPlainText().split('\n'); + //As drawn: a text with a width is wrapped + QStringList lines = TextLines::layoutLines(dti -> document()); qreal offset = fontSize * 1.6; foreach (QString line, lines) { if (line.size() > 0 && line != "_" ) diff --git a/sources/textlines.h b/sources/textlines.h new file mode 100644 index 000000000..e9a15e130 --- /dev/null +++ b/sources/textlines.h @@ -0,0 +1,60 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#ifndef TEXTLINES_H +#define TEXTLINES_H + +#include +#include +#include +#include + +namespace TextLines +{ + /** + @return the lines of document as they are laid out: a paragraph + wrapped to the width of the text gives several lines. For the + exports that write a text line by line (DXF). + */ + inline QStringList layoutLines(const QTextDocument *document) + { + document->size(); //Lay the document out + + QStringList lines; + for (QTextBlock block = document->begin() ; block.isValid() ; block = block.next()) + { + const QString text = block.text(); + const QTextLayout *layout = block.layout(); + if (!layout || layout->lineCount() == 0) { + lines << text; + continue; + } + for (int i = 0 ; i < layout->lineCount() ; ++i) + { + const QTextLine line = layout->lineAt(i); + QString part = text.mid(line.textStart(), line.textLength()); + //The space the line was broken at, or a line separator + while (!part.isEmpty() && part.back().isSpace()) + part.chop(1); + lines << part; + } + } + return lines; + } +} + +#endif // TEXTLINES_H diff --git a/tests/qttest/CMakeLists.txt b/tests/qttest/CMakeLists.txt index 3b7e8c2b5..f5182993d 100644 --- a/tests/qttest/CMakeLists.txt +++ b/tests/qttest/CMakeLists.txt @@ -104,6 +104,14 @@ add_test(NAME tst_textgrid COMMAND tst_textgrid) target_include_directories(tst_textgrid PRIVATE ${QET_DIR}/sources) target_link_libraries(tst_textgrid PRIVATE Qt::Test) +# textlines.h is header-only: the lines of a text as they are laid out, +# for the exports that write a text line by line. +add_executable(tst_textlines tst_textlines.cpp) +add_test(NAME tst_textlines COMMAND tst_textlines) +set_tests_properties(tst_textlines PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen") +target_include_directories(tst_textlines PRIVATE ${QET_DIR}/sources) +target_link_libraries(tst_textlines PRIVATE Qt::Test Qt::Gui) + # elementviewgrid.h is header-only: the symbol editor's snap step at each # zoom (bugtracker #112), tested without an editor window. add_executable(tst_elementviewgrid tst_elementviewgrid.cpp) @@ -403,6 +411,16 @@ target_compile_definitions(tst_wiringlistexport PRIVATE "QET_TEST_BINARY_PATH=\"$\"" "QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"") +# --export-dxf writes a wrapped symbol text line by line, as it is drawn. +add_executable( + tst_dxfwrappedtext + tst_dxfwrappedtext.cpp) +add_test(NAME tst_dxfwrappedtext COMMAND tst_dxfwrappedtext) +add_dependencies(tst_dxfwrappedtext qelectrotech) +target_link_libraries(tst_dxfwrappedtext PRIVATE Qt::Test) +target_compile_definitions(tst_dxfwrappedtext PRIVATE + "QET_TEST_BINARY_PATH=\"$\"") + # --export-pdf puts an A3 folio on an A3 page whichever way it is turned: # QPageSize matches standard sheets upright only, so a wide folio is matched # upright and turned. fixtures/pdf_page_a3.qet has one folio each way. diff --git a/tests/qttest/fixtures/dxf_wrapped_text.qet b/tests/qttest/fixtures/dxf_wrapped_text.qet new file mode 100644 index 000000000..920aebc74 --- /dev/null +++ b/tests/qttest/fixtures/dxf_wrapped_text.qet @@ -0,0 +1,61 @@ + + + 10/2/26 + 02-10-2026 + 2026-10-02 + skeleton + /tmp/qet-mcp-new-k41dmnyo/skeleton.qet + 18:39 + + + + + + + + + + + + + + + + + + + + + + + + + + WrapAlpha WrapBeta WrapGamma WrapDelta WrapEpsilon + LineAlpha LineBeta LineGamma LineDelta LineEpsilon + + + + + + + + + Imported elements + + + + + + Wrapped symbol text + + + + WrapAlpha WrapBeta WrapGamma WrapDelta WrapEpsilon + LineAlpha LineBeta LineGamma LineDelta LineEpsilon + + + + + + diff --git a/tests/qttest/tst_dxfwrappedtext.cpp b/tests/qttest/tst_dxfwrappedtext.cpp new file mode 100644 index 000000000..857c32084 --- /dev/null +++ b/tests/qttest/tst_dxfwrappedtext.cpp @@ -0,0 +1,60 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#include +#include +#include + +/** + --export-dxf writes a symbol text wrapped to its width line by line, + as it is drawn, rather than as one long line. A text without a width + is still written on one line. +*/ +class tst_dxfwrappedtext : public QObject +{ + Q_OBJECT + +private slots: + void wrappedLines() + { + const QString project = QFINDTESTDATA("fixtures/dxf_wrapped_text.qet"); + QVERIFY2(!project.isEmpty(), "fixture project not found"); + QTemporaryDir dir; + QVERIFY(dir.isValid()); + + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen")); + QProcess proc; + proc.setProcessEnvironment(env); + proc.start(QStringLiteral(QET_TEST_BINARY_PATH), {QStringLiteral("--export-dxf"), project, dir.path()}); + QVERIFY2(proc.waitForFinished(60000), "--export-dxf timed out"); + QCOMPARE(proc.exitCode(), 0); + + QFile file(dir.filePath(QStringLiteral("01_diagram.dxf"))); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); + const QStringList values = QString::fromUtf8(file.readAll()).split(QLatin1Char('\n')); + + //One text entity per word of the text 70 px wide... + for (const QString &word : {QStringLiteral("WrapAlpha"), QStringLiteral("WrapGamma"), QStringLiteral("WrapEpsilon")}) + QVERIFY2(values.contains(word), qPrintable(word)); + //...one for the whole text without a width + QVERIFY(values.contains(QStringLiteral("LineAlpha LineBeta LineGamma LineDelta LineEpsilon"))); + } +}; + +QTEST_APPLESS_MAIN(tst_dxfwrappedtext) +#include "tst_dxfwrappedtext.moc" diff --git a/tests/qttest/tst_textlines.cpp b/tests/qttest/tst_textlines.cpp new file mode 100644 index 000000000..fb987c694 --- /dev/null +++ b/tests/qttest/tst_textlines.cpp @@ -0,0 +1,54 @@ +/* + Copyright 2006-2026 The QElectroTech Team + This file is part of QElectroTech. + + QElectroTech is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + QElectroTech is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with QElectroTech. If not, see . +*/ +#include +#include +#include + +#include "textlines.h" + +class tst_textlines : public QObject +{ + Q_OBJECT + +private slots: + // A text without a width gives its own lines. + void unwrappedTextGivesItsLines() + { + QTextDocument document(QStringLiteral("Motor protection\nswitch Q12")); + QCOMPARE(TextLines::layoutLines(&document), + QStringList({QStringLiteral("Motor protection"), QStringLiteral("switch Q12")})); + } + + // A text wrapped to its width gives one line per drawn line, without + // the space the line was broken at. + void wrappedTextGivesTheDrawnLines() + { + QTextDocument document(QStringLiteral("Motor protection switch Q12")); + QTextOption option = document.defaultTextOption(); + option.setWrapMode(QTextOption::WordWrap); + document.setDefaultTextOption(option); + document.setTextWidth(1); + + QCOMPARE(TextLines::layoutLines(&document), + QStringList({QStringLiteral("Motor"), QStringLiteral("protection"), + QStringLiteral("switch"), QStringLiteral("Q12")})); + } +}; + +QTEST_MAIN(tst_textlines) +#include "tst_textlines.moc"