From 25c81c3e588d84677df7a3fac0abcfd5df5a7632 Mon Sep 17 00:00:00 2001 From: nuttyartist Date: Fri, 12 Apr 2024 18:00:45 +0300 Subject: [PATCH 1/3] Qt6 support, fix closing_tags by prepending rather than appending, added support for underline and fixedPitch (monospace) --- qbasichtmlexporter.cpp | 32 +++++++++++++++++++++++--------- qbasichtmlexporter.h | 1 + 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/qbasichtmlexporter.cpp b/qbasichtmlexporter.cpp index fe3f909..6f7807e 100644 --- a/qbasichtmlexporter.cpp +++ b/qbasichtmlexporter.cpp @@ -38,7 +38,7 @@ QString QBasicHtmlExporter::toHtml() emitFrame(doc->rootFrame()->begin()); // Remove newlines at beginning - html.remove(QRegExp("^[\r\n]+")); + html.remove(QRegularExpression("^[\r\n]+")); return html; } @@ -256,7 +256,7 @@ void QBasicHtmlExporter::emitFragment(const QTextFragment &fragment) const QTextCharFormat format = fragment.charFormat(); bool closeAnchor = false; if (format.isAnchor()) { - const QString name = format.anchorName(); + const QString name = format.anchorNames().first(); if (!name.isEmpty()) { html += QLatin1String("")); + html += txt.replace(QRegularExpression(forcedLineBreakRegExp), QLatin1String("
")); } for (int i = 0; i < closing_tags.length(); i++) html += closing_tags[i]; @@ -318,8 +318,8 @@ void QBasicHtmlExporter::emitAttribute(const char *attribute, const QString &val QStringList QBasicHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format) { QStringList closing_tags; - QRegExp heading_tag_regex("h\\d"); - bool isHeading = heading_tag_regex.exactMatch( getTagName(format) ); + QRegularExpression heading_tag_regex("h\\d"); + bool isHeading = heading_tag_regex.match( getTagName(format) ).hasMatch(); int fontWeight = format.fontWeight(); bool isBold = fontWeight > defaultCharFormat.fontWeight(); @@ -331,18 +331,18 @@ QStringList QBasicHtmlExporter::emitCharFormatStyle(const QTextCharFormat &forma } if (isBold && !isHeading) { html += QLatin1String(""); - closing_tags << ""; + closing_tags.prepend(""); } if (format.hasProperty(QTextFormat::FontItalic) && format.fontItalic() != defaultCharFormat.fontItalic()) { html += QLatin1String(""); - closing_tags << ""; + closing_tags.prepend(""); isHeading = false; } // UNDERLINE CODE - // QLatin1String decorationTag(" text-decoration:"); + // QLatin1String decorationTag(" text-decoration:"); // html += decorationTag; // bool hasDecoration = false; // bool atLeastOneDecorationSet = false; @@ -356,11 +356,25 @@ QStringList QBasicHtmlExporter::emitCharFormatStyle(const QTextCharFormat &forma // } // } + if (format.hasProperty(QTextFormat::FontUnderline) || format.fontUnderline()) { + if (format.fontUnderline()) { + html += QLatin1String(""); + closing_tags.prepend(""); + } + } + + if (format.hasProperty(QTextFormat::FontFixedPitch) || format.fontFixedPitch()) { + if (format.fontFixedPitch()) { + html += QLatin1String(""); + closing_tags.prepend(""); + } + } + if (format.hasProperty(QTextFormat::FontStrikeOut) || format.fontStrikeOut()) { if (format.fontStrikeOut()) { html += QLatin1String(""); - closing_tags << ""; + closing_tags.prepend(""); } } diff --git a/qbasichtmlexporter.h b/qbasichtmlexporter.h index 93487cf..e7108dc 100644 --- a/qbasichtmlexporter.h +++ b/qbasichtmlexporter.h @@ -2,6 +2,7 @@ #define QBASICHTML_H #include #include +#include class QBasicHtmlExporter { From 6e61b8cadc04f71aa853b68bd73b31dc5196f3c5 Mon Sep 17 00:00:00 2001 From: nuttyartist Date: Tue, 16 Apr 2024 13:47:52 +0300 Subject: [PATCH 2/3] Fix crash when anchorNames is empty. Better code tag detection. --- qbasichtmlexporter.cpp | 603 ++++++++++++++++++++--------------------- qbasichtmlexporter.h | 1 + 2 files changed, 302 insertions(+), 302 deletions(-) diff --git a/qbasichtmlexporter.cpp b/qbasichtmlexporter.cpp index 6f7807e..2f03bb6 100644 --- a/qbasichtmlexporter.cpp +++ b/qbasichtmlexporter.cpp @@ -11,372 +11,371 @@ static QTextFormat formatDifference(const QTextFormat &from, const QTextFormat &to) { - QTextFormat diff = to; - const QMap props = to.properties(); - for (QMap::ConstIterator it = props.begin(), end = props.end(); - it != end; ++it) - if (it.value() == from.property(it.key())) - diff.clearProperty(it.key()); - return diff; + QTextFormat diff = to; + const QMap props = to.properties(); + for (QMap::ConstIterator it = props.begin(), end = props.end(); + it != end; ++it) + if (it.value() == from.property(it.key())) + diff.clearProperty(it.key()); + return diff; } QBasicHtmlExporter::QBasicHtmlExporter(QTextDocument *_doc) { - doc = _doc; - const QFont defaultFont = doc->defaultFont(); - defaultCharFormat.setFont(defaultFont); - // don't export those for the default font since we cannot turn them off with CSS - defaultCharFormat.clearProperty(QTextFormat::FontUnderline); - defaultCharFormat.clearProperty(QTextFormat::FontOverline); - defaultCharFormat.clearProperty(QTextFormat::FontStrikeOut); - defaultCharFormat.clearProperty(QTextFormat::TextUnderlineStyle); + doc = _doc; + const QFont defaultFont = doc->defaultFont(); + defaultCharFormat.setFont(defaultFont); + // don't export those for the default font since we cannot turn them off with CSS + defaultCharFormat.clearProperty(QTextFormat::FontUnderline); + defaultCharFormat.clearProperty(QTextFormat::FontOverline); + defaultCharFormat.clearProperty(QTextFormat::FontStrikeOut); + defaultCharFormat.clearProperty(QTextFormat::TextUnderlineStyle); } QString QBasicHtmlExporter::toHtml() { - html = QLatin1String(""); - emitFrame(doc->rootFrame()->begin()); + html = QLatin1String(""); + emitFrame(doc->rootFrame()->begin()); - // Remove newlines at beginning + // Remove newlines at beginning html.remove(QRegularExpression("^[\r\n]+")); - return html; + return html; } QBasicHtmlExporter::Heading QBasicHtmlExporter::headingType(QString name) { - if ( name == "xx-large" ) return h1; - if ( name == "x-large" ) return h2; - if ( name == "large" ) return h3; - if ( name == "medium" ) return h4; - if ( name == "small" ) return h5; - return paragraph; + if ( name == "xx-large" ) return h1; + if ( name == "x-large" ) return h2; + if ( name == "large" ) return h3; + if ( name == "medium" ) return h4; + if ( name == "small" ) return h5; + return paragraph; } QString QBasicHtmlExporter::headingStr(QBasicHtmlExporter::Heading heading) { - switch (heading) { - case h1: - return "h1"; - case h2: - return "h2"; - case h3: - return "h3"; - case h4: - return "h4"; - case h5: - return "h5"; - default: - return "p"; - } + switch (heading) { + case h1: + return "h1"; + case h2: + return "h2"; + case h3: + return "h3"; + case h4: + return "h4"; + case h5: + return "h5"; + default: + return "p"; + } } QString QBasicHtmlExporter::getTagName(const QTextCharFormat &format) { - Heading cur_heading = paragraph; - - if (format.hasProperty(QTextFormat::FontSizeAdjustment)) { - static const char sizeNameData[] = - "small" "\0" - "medium" "\0" - "xx-large" ; - static const quint8 sizeNameOffsets[] = { - 0, // "small" - sizeof("small"), // "medium" - sizeof("small") + sizeof("medium") + 3, // "large" ) - sizeof("small") + sizeof("medium") + 1, // "x-large" )> compressed into "xx-large" - sizeof("small") + sizeof("medium"), // "xx-large" ) - }; - const char *name = nullptr; - const int idx = format.intProperty(QTextFormat::FontSizeAdjustment) + 1; - if (idx >= 0 && idx <= 4) { - name = sizeNameData + sizeNameOffsets[idx]; - } - if (name) { - cur_heading = headingType(name); - } - } - return headingStr( cur_heading ); + Heading cur_heading = paragraph; + + if (format.hasProperty(QTextFormat::FontSizeAdjustment)) { + static const char sizeNameData[] = + "small" "\0" + "medium" "\0" + "xx-large" ; + static const quint8 sizeNameOffsets[] = { + 0, // "small" + sizeof("small"), // "medium" + sizeof("small") + sizeof("medium") + 3, // "large" ) + sizeof("small") + sizeof("medium") + 1, // "x-large" )> compressed into "xx-large" + sizeof("small") + sizeof("medium"), // "xx-large" ) + }; + const char *name = nullptr; + const int idx = format.intProperty(QTextFormat::FontSizeAdjustment) + 1; + if (idx >= 0 && idx <= 4) { + name = sizeNameData + sizeNameOffsets[idx]; + } + if (name) { + cur_heading = headingType(name); + } + } + return headingStr( cur_heading ); } void QBasicHtmlExporter::emitFrame(const QTextFrame::Iterator &frameIt) { - if (!frameIt.atEnd()) { - QTextFrame::Iterator next = frameIt; - ++next; - if (next.atEnd() - && frameIt.currentFrame() == nullptr - && frameIt.parentFrame() != doc->rootFrame() - && frameIt.currentBlock().begin().atEnd()) - return; - } - for (QTextFrame::Iterator it = frameIt; - !it.atEnd(); ++it) { - if (QTextFrame *f = it.currentFrame()) { - if (QTextTable *table = qobject_cast(f)) { - emitTable(table); - } else { - emitTextFrame(f); - } - } else if (it.currentBlock().isValid()) { - emitBlock(it.currentBlock()); - } - } + if (!frameIt.atEnd()) { + QTextFrame::Iterator next = frameIt; + ++next; + if (next.atEnd() + && frameIt.currentFrame() == nullptr + && frameIt.parentFrame() != doc->rootFrame() + && frameIt.currentBlock().begin().atEnd()) + return; + } + for (QTextFrame::Iterator it = frameIt; + !it.atEnd(); ++it) { + if (QTextFrame *f = it.currentFrame()) { + if (QTextTable *table = qobject_cast(f)) { + emitTable(table); + } else { + emitTextFrame(f); + } + } else if (it.currentBlock().isValid()) { + emitBlock(it.currentBlock()); + } + } } void QBasicHtmlExporter::emitTable(const QTextTable *table) { - QTextTableFormat format = table->format(); - html += QLatin1String("\n"); - const int rows = table->rows(); - const int columns = table->columns(); - QVector columnWidths = format.columnWidthConstraints(); - if (columnWidths.isEmpty()) { - columnWidths.resize(columns); - columnWidths.fill(QTextLength()); - } - Q_ASSERT(columnWidths.count() == columns); - QVarLengthArray widthEmittedForColumn(columns); - for (int i = 0; i < columns; ++i) - widthEmittedForColumn[i] = false; - const int headerRowCount = qMin(format.headerRowCount(), rows); - if (headerRowCount > 0) - html += QLatin1String(""); - for (int row = 0; row < rows; ++row) { - html += QLatin1String("\n"); - for (int col = 0; col < columns; ++col) { - const QTextTableCell cell = table->cellAt(row, col); - // for col/rowspans - if (cell.row() != row) - continue; - if (cell.column() != col) - continue; - html += QLatin1String("\n"); - } - html += QLatin1String(""); - if (headerRowCount > 0 && row == headerRowCount - 1) - html += QLatin1String(""); - } - html += QLatin1String("
"); - emitFrame(cell.begin()); - html += QLatin1String("
"); + QTextTableFormat format = table->format(); + html += QLatin1String("\n"); + const int rows = table->rows(); + const int columns = table->columns(); + QVector columnWidths = format.columnWidthConstraints(); + if (columnWidths.isEmpty()) { + columnWidths.resize(columns); + columnWidths.fill(QTextLength()); + } + Q_ASSERT(columnWidths.count() == columns); + QVarLengthArray widthEmittedForColumn(columns); + for (int i = 0; i < columns; ++i) + widthEmittedForColumn[i] = false; + const int headerRowCount = qMin(format.headerRowCount(), rows); + if (headerRowCount > 0) + html += QLatin1String(""); + for (int row = 0; row < rows; ++row) { + html += QLatin1String("\n"); + for (int col = 0; col < columns; ++col) { + const QTextTableCell cell = table->cellAt(row, col); + // for col/rowspans + if (cell.row() != row) + continue; + if (cell.column() != col) + continue; + html += QLatin1String("\n"); + } + html += QLatin1String(""); + if (headerRowCount > 0 && row == headerRowCount - 1) + html += QLatin1String(""); + } + html += QLatin1String("
"); + emitFrame(cell.begin()); + html += QLatin1String("
"); } void QBasicHtmlExporter::emitTextFrame(const QTextFrame *f) { - html += QLatin1String("\n"); - QTextFrameFormat format = f->frameFormat(); - html += QLatin1String("\n\n"); - emitFrame(f->begin()); - html += QLatin1String("
"); + html += QLatin1String("\n"); + QTextFrameFormat format = f->frameFormat(); + html += QLatin1String("\n\n"); + emitFrame(f->begin()); + html += QLatin1String("
"); } void QBasicHtmlExporter::emitBlock(const QTextBlock &block) { - html += QLatin1Char('\n'); - // save and later restore, in case we 'change' the default format by - // emitting block char format information - QTextCharFormat oldDefaultCharFormat = defaultCharFormat; - QTextList *list = block.textList(); - bool numbered_list = false; - if (list) { - if (list->itemNumber(block) == 0) { // first item? emit