diff --git a/src/grantleebuilder/markupdirector.cpp b/src/grantleebuilder/markupdirector.cpp index da6a61d..fae6bb6 100644 --- a/src/grantleebuilder/markupdirector.cpp +++ b/src/grantleebuilder/markupdirector.cpp @@ -1,199 +1,198 @@ /* Copyright (c) 2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "markupdirector.h" - +#include using namespace KPIMTextEdit; MarkupDirector::MarkupDirector(Grantlee::AbstractMarkupBuilder *builder) : Grantlee::MarkupDirector(builder) { } MarkupDirector::~MarkupDirector() { } QTextFrame::iterator MarkupDirector::processBlockContents(QTextFrame::iterator frameIt, const QTextBlock &block) { //Same code as grantlee but interprete margin auto blockFormat = block.blockFormat(); auto blockAlignment = blockFormat.alignment(); // TODO: decide when to use

etc. if (blockFormat.hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth)) { m_builder->insertHorizontalRule(); if (!frameIt.atEnd()) return ++frameIt; return frameIt; } auto it = block.begin(); // The beginning is the end. This is an empty block. Insert a newline and // move // on. if (it.atEnd()) { m_builder->addNewline(); if (!frameIt.atEnd()) return ++frameIt; return frameIt; } // Don't have p tags inside li tags. if (!block.textList()) { //Laurent : we need this margin as it's necessary to show blockquote // Don't instruct builders to use margins. The rich text widget doesn't // have // an action for them yet, // So users can't edit them. See bug // http://bugs.kde.org/show_bug.cgi?id=160600 m_builder->beginParagraph( blockAlignment, blockFormat.topMargin(), blockFormat.bottomMargin(), blockFormat.leftMargin(), blockFormat.rightMargin() ); } while (!it.atEnd()) { it = processFragment(it, it.fragment(), block.document()); } - // Don't have p tags inside li tags. if (!block.textList()) { m_builder->endParagraph(); } if (!frameIt.atEnd()) return ++frameIt; return frameIt; } QTextBlock::iterator MarkupDirector::processFragment(QTextBlock::iterator it, const QTextFragment &fragment, QTextDocument const *doc) { //Same code as Grantlee + a fix ! // Q_D( MarkupDirector ); auto charFormat = fragment.charFormat(); if (charFormat.objectType() >= QTextFormat::UserObject) { processCustomFragment(fragment, doc); if (!it.atEnd()) return ++it; return it; } auto textObject = doc->objectForFormat(charFormat); if (textObject) return processCharTextObject(it, fragment, textObject); if (fragment.text().at(0).category() == QChar::Separator_Line) { m_builder->addNewline(); if (!it.atEnd()) return ++it; return it; } // The order of closing and opening tags can determine whether generated // html // is valid or not. // When processing a document with formatting which appears as // 'Some // formatted text', // the correct generated output will contain 'Some // formatted text'. // However, processing text which appears as 'Some formatted // text' might be incorrectly rendered // as 'Some formatted text' if tags which start at // the same fragment are // opened out of order. Here, tags are not nested properly, and the html // would // not be valid or render correctly by unforgiving parsers (like QTextEdit). // One solution is to make the order of opening tags dynamic. In the above // case, the em tag would // be opened before the strong tag 'Some formatted // text'. That would // require knowledge of which tag is going to close first. That might be // possible by examining // the 'next' QTextFragment while processing one. // // The other option is to do pessimistic closing of tags. // In the above case, this means that if a fragment has two or more formats // applied (bold and italic here), // and one of them is closed, then all tags should be closed first. They // will // of course be reopened // if necessary while processing the next fragment. // The above case would be rendered as 'Some // formatted text'. // // The first option is taken here, as the redundant opening and closing tags // in the second option // didn't appeal. // See testDoubleStartDifferentFinish, // testDoubleStartDifferentFinishReverseOrder processOpeningElements(it); // If a sequence such as '

' is imported into a document with // setHtml, LineSeparator // characters are inserted. Here I make sure to put them back. auto sl = fragment.text().split(QChar(QChar::LineSeparator)); QStringListIterator i(sl); auto paraClosed = false; while (i.hasNext()) { m_builder->appendLiteralText(i.next()); if (i.hasNext()) { if (i.peekNext().isEmpty()) { if (!paraClosed) { m_builder->endParagraph(); paraClosed = true; } m_builder->addNewline(); } else if (paraClosed) { m_builder->beginParagraph(/* blockAlignment */); paraClosed = false; } else { //Bug fixing : missing end line here m_builder->addNewline(); } } } if (!it.atEnd()) ++it; processClosingElements(it); return it; } diff --git a/src/grantleebuilder/plaintextmarkupbuilder.cpp b/src/grantleebuilder/plaintextmarkupbuilder.cpp index 5ab67db..67ff3f6 100644 --- a/src/grantleebuilder/plaintextmarkupbuilder.cpp +++ b/src/grantleebuilder/plaintextmarkupbuilder.cpp @@ -1,496 +1,497 @@ /* Copyright (c) 2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "plaintextmarkupbuilder.h" #include namespace KPIMTextEdit { class PlainTextMarkupBuilderPrivate { public: PlainTextMarkupBuilderPrivate(PlainTextMarkupBuilder *b) : q_ptr(b) {} /** Get a letter string to represent a number. The numbers 1-26 are represented by a-z, and 27-52 by aa-az, 53-79 by ba-bz etc. @param The number to convert @return The letter string representation of the number. */ QString getLetterString(int itemNumber); QString getRomanString(int itemNumber); /** Gets a block of references in the body of the text. This is an ordered list of links and images in the text. */ QString getReferences(); QStringList m_urls; QList currentListItemStyles; QList currentListItemNumbers; QString activeLink; QString m_text; QString m_quoteprefix; PlainTextMarkupBuilder *q_ptr; Q_DECLARE_PUBLIC(PlainTextMarkupBuilder) }; } using namespace KPIMTextEdit; PlainTextMarkupBuilder::PlainTextMarkupBuilder() : d_ptr(new PlainTextMarkupBuilderPrivate(this)) { } QString PlainTextMarkupBuilderPrivate::getRomanString(int item) { QString result; // Code based to gui/text/qtextlist.cpp if (item < 5000) { QString romanNumeral; // works for up to 4999 items auto romanSymbols = QStringLiteral("iiivixxxlxcccdcmmmm"); int c[] = {1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000}; auto n = item; for (auto i = 12; i >= 0; n %= c[i], i--) { auto q = n / c[i]; if (q > 0) { auto startDigit = i + (i + 3) / 4; int numDigits; if (i % 4) { // c[i] == 4|5|9|40|50|90|400|500|900 if ((i - 2) % 4) { // c[i] == 4|9|40|90|400|900 => with subtraction (IV, // IX, XL, XC, // ...) numDigits = 2; } else { // c[i] == 5|50|500 (V, L, D) numDigits = 1; } } else { // c[i] == 1|10|100|1000 (I, II, III, X, XX, ...) numDigits = q; } romanNumeral.append(romanSymbols.mid(startDigit, numDigits)); } } result = romanNumeral; } else { result = QStringLiteral("?"); } return result; } QString PlainTextMarkupBuilderPrivate::getLetterString(int itemNumber) { QString letterString; while (true) { // Create the letter string by prepending one char at a time. // The itemNumber is converted to a number in the base 36 (number of // letters // in the // alphabet plus 10) after being increased by 10 (to pass out the digits // 0 // to 9). letterString.prepend(QStringLiteral("%1").arg( (itemNumber % LETTERSINALPHABET) + DIGITSOFFSET, 0, // no padding while building this string. LETTERSINALPHABET + DIGITSOFFSET)); if ((itemNumber >= LETTERSINALPHABET)) { itemNumber = itemNumber / LETTERSINALPHABET; itemNumber--; } else { break; } } return letterString; } QString PlainTextMarkupBuilderPrivate::getReferences() { QString refs; if (!m_urls.isEmpty()) { refs.append(QStringLiteral("\n--------\n")); auto index = 1; while (!m_urls.isEmpty()) { refs.append( QStringLiteral("[%1] %2\n").arg(index++).arg(m_urls.takeFirst())); } } return refs; } PlainTextMarkupBuilder::~PlainTextMarkupBuilder() { delete d_ptr; } void PlainTextMarkupBuilder::setQuotePrefix(const QString &prefix) { Q_D(PlainTextMarkupBuilder); d->m_quoteprefix = prefix; } void PlainTextMarkupBuilder::beginStrong() { Q_D(PlainTextMarkupBuilder); d->m_text.append(QLatin1Char('*')); } void PlainTextMarkupBuilder::endStrong() { Q_D(PlainTextMarkupBuilder); d->m_text.append(QLatin1Char('*')); } void PlainTextMarkupBuilder::beginEmph() { Q_D(PlainTextMarkupBuilder); d->m_text.append(QLatin1Char('/')); } void PlainTextMarkupBuilder::endEmph() { Q_D(PlainTextMarkupBuilder); d->m_text.append(QLatin1Char('/')); } void PlainTextMarkupBuilder::beginUnderline() { Q_D(PlainTextMarkupBuilder); d->m_text.append(QLatin1Char('_')); } void PlainTextMarkupBuilder::endUnderline() { Q_D(PlainTextMarkupBuilder); d->m_text.append(QLatin1Char('_')); } void PlainTextMarkupBuilder::beginStrikeout() { Q_D(PlainTextMarkupBuilder); d->m_text.append(QLatin1Char('-')); } void PlainTextMarkupBuilder::endStrikeout() { Q_D(PlainTextMarkupBuilder); d->m_text.append(QLatin1Char('-')); } void PlainTextMarkupBuilder::beginAnchor(const QString &href, const QString &name) { Q_D(PlainTextMarkupBuilder); Q_UNUSED(name); if (!d->m_urls.contains(href)) { d->m_urls.append(href); } d->activeLink = href; } void PlainTextMarkupBuilder::endAnchor() { Q_D(PlainTextMarkupBuilder); d->m_text.append( QStringLiteral("[%1]").arg(d->m_urls.indexOf(d->activeLink) + 1)); } void PlainTextMarkupBuilder::endParagraph() { Q_D(PlainTextMarkupBuilder); d->m_text.append(QLatin1Char('\n')); } void PlainTextMarkupBuilder::addNewline() { Q_D(PlainTextMarkupBuilder); d->m_text.append(QLatin1Char('\n')); } void PlainTextMarkupBuilder::insertHorizontalRule(int width) { Q_UNUSED(width) Q_D(PlainTextMarkupBuilder); d->m_text.append(QStringLiteral("--------------------\n")); } int PlainTextMarkupBuilder::addReference(const QString &reference) { Q_D(PlainTextMarkupBuilder); if (!d->m_urls.contains(reference)) d->m_urls.append(reference); return d->m_urls.indexOf(reference) + 1; } void PlainTextMarkupBuilder::insertImage(const QString &src, qreal width, qreal height) { Q_D(PlainTextMarkupBuilder); Q_UNUSED(width) Q_UNUSED(height) auto ref = addReference(src); d->m_text.append(QStringLiteral("[%1]").arg(ref)); } void PlainTextMarkupBuilder::beginList(QTextListFormat::Style style) { Q_D(PlainTextMarkupBuilder); d->currentListItemStyles.append(style); d->currentListItemNumbers.append(0); } void PlainTextMarkupBuilder::endList() { Q_D(PlainTextMarkupBuilder); if (!d->currentListItemNumbers.isEmpty()) { d->currentListItemStyles.removeLast(); d->currentListItemNumbers.removeLast(); } } void PlainTextMarkupBuilder::beginListItem() { Q_D(PlainTextMarkupBuilder); for (auto i = 0; i < d->currentListItemNumbers.size(); i++) { d->m_text.append(QStringLiteral(" ")); } auto itemNumber = d->currentListItemNumbers.last(); switch (d->currentListItemStyles.last()) { case QTextListFormat::ListDisc: d->m_text.append(QStringLiteral(" * ")); break; case QTextListFormat::ListCircle: d->m_text.append(QStringLiteral(" o ")); break; case QTextListFormat::ListSquare: d->m_text.append(QStringLiteral(" - ")); break; case QTextListFormat::ListDecimal: d->m_text.append(QStringLiteral(" %1. ").arg(itemNumber + 1)); break; case QTextListFormat::ListLowerAlpha: d->m_text.append( QStringLiteral(" %1. ").arg(d->getLetterString(itemNumber))); break; case QTextListFormat::ListUpperAlpha: d->m_text.append( QStringLiteral(" %1. ").arg(d->getLetterString(itemNumber).toUpper())); break; case QTextListFormat::ListLowerRoman: d->m_text.append( QStringLiteral(" %1. ").arg(d->getRomanString(itemNumber + 1))); break; case QTextListFormat::ListUpperRoman: d->m_text.append(QStringLiteral(" %1. ").arg( d->getRomanString(itemNumber + 1).toUpper())); break; default: break; } } void PlainTextMarkupBuilder::endListItem() { Q_D(PlainTextMarkupBuilder); d->currentListItemNumbers.last() = d->currentListItemNumbers.last() + 1; d->m_text.append(QLatin1Char('\n')); } void PlainTextMarkupBuilder::beginSuperscript() { Q_D(PlainTextMarkupBuilder); d->m_text.append(QStringLiteral("^{")); } void PlainTextMarkupBuilder::endSuperscript() { Q_D(PlainTextMarkupBuilder); d->m_text.append(QLatin1Char('}')); } void PlainTextMarkupBuilder::beginSubscript() { Q_D(PlainTextMarkupBuilder); d->m_text.append(QStringLiteral("_{")); } void PlainTextMarkupBuilder::endSubscript() { Q_D(PlainTextMarkupBuilder); d->m_text.append(QLatin1Char('}')); } void PlainTextMarkupBuilder::appendLiteralText(const QString &text) { Q_D(PlainTextMarkupBuilder); d->m_text.append(text); } void PlainTextMarkupBuilder::appendRawText(const QString &text) { Q_D(PlainTextMarkupBuilder); d->m_text.append(text); } QString PlainTextMarkupBuilder::getResult() { Q_D(PlainTextMarkupBuilder); auto ret = d->m_text; ret.append(d->getReferences()); d->m_text.clear(); return ret; } void PlainTextMarkupBuilder::beginParagraph(Qt::Alignment a, qreal top, qreal bottom, qreal left, qreal right) { Q_UNUSED(a); Q_D(PlainTextMarkupBuilder); if (isQuoteBlock(top, bottom, left, right)) { d->m_text.append(d->m_quoteprefix); } } bool PlainTextMarkupBuilder::isQuoteBlock(qreal top, qreal bottom, qreal left, qreal right) const { - return (top == 12) && (bottom == 12) && (left == 40) && (right == 40); + //qDebug() << " bool PlainTextMarkupBuilder::isQuoteBlock(qreal top, qreal bottom, qreal left, qreal right) const" << " top " << top << " bottom " << bottom << " left " << left << " righ " << right; + return /*(top == 12) && (bottom == 12) &&*/ (left == 40) && (right == 40); /* case Html_blockquote: margin[QTextHtmlParser::MarginTop] = 12; margin[QTextHtmlParser::MarginBottom] = 12; margin[QTextHtmlParser::MarginLeft] = 40; margin[QTextHtmlParser::MarginRight] = 40; break; */ } void PlainTextMarkupBuilder::beginBackground(const QBrush &brush) { Q_UNUSED(brush); } void PlainTextMarkupBuilder::beginFontFamily(const QString &family) { Q_UNUSED(family); } void PlainTextMarkupBuilder::beginFontPointSize(int size) { Q_UNUSED(size); } void PlainTextMarkupBuilder::beginForeground(const QBrush &brush) { Q_UNUSED(brush); } void PlainTextMarkupBuilder::beginHeader(int level) { Q_UNUSED(level); } void PlainTextMarkupBuilder::beginTable(qreal cellpadding, qreal cellspacing, const QString &width) { Q_UNUSED(cellpadding); Q_UNUSED(cellspacing); Q_UNUSED(width); } void PlainTextMarkupBuilder::beginTableCell(const QString &width, int colSpan, int rowSpan) { Q_UNUSED(width); Q_UNUSED(colSpan); Q_UNUSED(rowSpan); } void PlainTextMarkupBuilder::beginTableHeaderCell(const QString &width, int colSpan, int rowSpan) { Q_UNUSED(width); Q_UNUSED(colSpan); Q_UNUSED(rowSpan); } void PlainTextMarkupBuilder::beginTableRow() { } void PlainTextMarkupBuilder::endBackground() { } void PlainTextMarkupBuilder::endFontFamily() { } void PlainTextMarkupBuilder::endFontPointSize() { } void PlainTextMarkupBuilder::endForeground() { } void PlainTextMarkupBuilder::endHeader(int level) { Q_UNUSED(level) } void PlainTextMarkupBuilder::endTable() { } void PlainTextMarkupBuilder::endTableCell() { } void PlainTextMarkupBuilder::endTableHeaderCell() { } void PlainTextMarkupBuilder::endTableRow() { }