diff --git a/sources/textlines.h b/sources/textlines.h
index e9a15e130..8d3382382 100644
--- a/sources/textlines.h
+++ b/sources/textlines.h
@@ -29,6 +29,11 @@ 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).
+ A paragraph that is not wrapped gives exactly its line of
+ toPlainText().split('\n'), as these exports wrote before: trailing
+ spaces are kept, and non-breaking spaces become plain spaces. In a
+ wrapped paragraph, the lines lose their trailing spaces (the one a
+ line was broken at, and a line made only of spaces becomes empty).
*/
inline QStringList layoutLines(const QTextDocument *document)
{
@@ -39,18 +44,32 @@ namespace TextLines
{
const QString text = block.text();
const QTextLayout *layout = block.layout();
- if (!layout || layout->lineCount() == 0) {
- lines << text;
+ const int count = layout ? layout->lineCount() : 0;
+ if (count == 0) {
+ lines << QString(text).replace(QChar::Nbsp, QLatin1Char(' '));
continue;
}
- for (int i = 0 ; i < layout->lineCount() ; ++i)
+
+ //Wrapped: a line ends without a line break typed by the user
+ bool wrapped = false;
+ for (int i = 0 ; i < count - 1 ; ++i) {
+ const QTextLine line = layout->lineAt(i);
+ const int end = line.textStart() + line.textLength();
+ if (end == 0 || text.at(end - 1) != QChar::LineSeparator)
+ wrapped = true;
+ }
+
+ for (int i = 0 ; i < count ; ++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;
+ if (part.endsWith(QChar::LineSeparator))
+ part.chop(1); //A line break typed by the user (Shift+Enter)
+ if (wrapped) {
+ while (!part.isEmpty() && part.back().isSpace())
+ part.chop(1);
+ }
+ lines << part.replace(QChar::Nbsp, QLatin1Char(' '));
}
}
return lines;
diff --git a/tests/qttest/tst_textlines.cpp b/tests/qttest/tst_textlines.cpp
index fb987c694..e9bd25ec3 100644
--- a/tests/qttest/tst_textlines.cpp
+++ b/tests/qttest/tst_textlines.cpp
@@ -34,6 +34,32 @@ private slots:
QStringList({QStringLiteral("Motor protection"), QStringLiteral("switch Q12")}));
}
+ // A text without a width gives exactly the lines the DXF export wrote
+ // before, toPlainText().split('\n'): trailing spaces and tabs kept,
+ // non-breaking spaces as plain spaces,
as a line break.
+ void unwrappedTextIsUnchanged_data()
+ {
+ QTest::addColumn("html");
+ QTest::newRow("trailing spaces") << QStringLiteral("K1 \nswitch\t");
+ QTest::newRow("non-breaking spaces") << QStringLiteral("K1 24V DC
");
+ QTest::newRow("line break") << QStringLiteral("Wiper to 6
contacts
");
+ QTest::newRow("paragraphs") << QStringLiteral("relais voyant
preventa
");
+ QTest::newRow("empty") << QString();
+ }
+
+ void unwrappedTextIsUnchanged()
+ {
+ QFETCH(QString, html);
+ QTextDocument document;
+ if (Qt::mightBeRichText(html))
+ document.setHtml(html);
+ else
+ document.setPlainText(html);
+
+ QCOMPARE(TextLines::layoutLines(&document),
+ document.toPlainText().split(QLatin1Char('\n')));
+ }
+
// A text wrapped to its width gives one line per drawn line, without
// the space the line was broken at.
void wrappedTextGivesTheDrawnLines()
@@ -48,6 +74,41 @@ private slots:
QStringList({QStringLiteral("Motor"), QStringLiteral("protection"),
QStringLiteral("switch"), QStringLiteral("Q12")}));
}
+
+ // A wrapped paragraph has no trailing spaces left, even on its last
+ // line: tabs and spaces that wrap to a line of their own (as in the
+ // affuteuse_250h example) give an empty line, which is not written but
+ // still keeps the line spacing.
+ void wrappedTextHasNoTrailingSpaces()
+ {
+ QTextDocument document(QStringLiteral("\tMotor\t\t "));
+ QTextOption option = document.defaultTextOption();
+ option.setWrapMode(QTextOption::WordWrap);
+ document.setDefaultTextOption(option);
+ document.setTextWidth(80);
+
+ const QStringList lines = TextLines::layoutLines(&document);
+ QVERIFY(lines.size() > 1);
+ QVERIFY(lines.contains(QStringLiteral("Motor")));
+ for (const QString &line : lines)
+ QVERIFY2(line.isEmpty() || !line.back().isSpace(), qPrintable(line));
+ }
+
+ // Non-breaking spaces also become plain spaces in a wrapped text.
+ void wrappedTextHasPlainSpaces()
+ {
+ QTextDocument document;
+ document.setHtml(QStringLiteral("K1 24V and more words
"));
+ QTextOption option = document.defaultTextOption();
+ option.setWrapMode(QTextOption::WordWrap);
+ document.setDefaultTextOption(option);
+ document.setTextWidth(1);
+
+ const QStringList lines = TextLines::layoutLines(&document);
+ QCOMPARE(lines.first(), QStringLiteral("K1 24V"));
+ for (const QString &line : lines)
+ QVERIFY(!line.contains(QChar::Nbsp));
+ }
};
QTEST_MAIN(tst_textlines)