Commit 5d409583 authored by Laurent Montel's avatar Laurent Montel 😁
Browse files

Start to fix HTML content - mangled generation

CCBUG: 421908
parent 50011691
cmake_minimum_required(VERSION 3.5)
set(PIM_VERSION "5.14.44")
set(PIM_VERSION "5.14.45")
project(KPimTextEdit VERSION ${PIM_VERSION})
......
......@@ -136,6 +136,7 @@ ecm_generate_headers(KPimTextEdit_CamelCasegrantlee_HEADERS
PlainTextMarkupBuilder
TextHTMLBuilder
MarkupDirector
AbstractMarkupBuilder
PREFIX KPIMTextEdit
REQUIRED_HEADERS kpimtextedit_HEADERS
RELATIVE grantleebuilder
......
/*
Copyright (c) 2020 Montel Laurent <montel@kde.org>
Copyright (c) 2008,2010 Stephen Kelly <steveire@gmail.com>
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.
*/
#ifndef GRANTLEE_ABSTRACTMARKUPBUILDER_H
#define GRANTLEE_ABSTRACTMARKUPBUILDER_H
#include "kpimtextedit_export.h"
#include <QtCore/QString>
#include <QtGui/QTextListFormat>
class QBrush;
namespace KPIMTextEdit {
class AbstractMarkupBuilderPrivate;
/// @headerfile abstractmarkupbuilder.h grantlee/abstractmarkupbuilder.h
/**
@brief Interface for creating marked-up text output.
The **%AbstractMarkupBuilder** is used by a MarkupDirector to create marked-up
output such as html or markdown.
See PlainTextMarkupBuilder and TextHTMLBuilder for example implementations.
This interface can be extended to handle custom format types in a
QTextDocument. @see @ref custom_qtextobject
@author Stephen Kelly <steveire@gmail.com>
*/
class KPIMTEXTEDIT_EXPORT AbstractMarkupBuilder
{
public:
/** Destructor */
virtual ~AbstractMarkupBuilder() {}
/** Begin a bold element in the markup */
virtual void beginStrong() = 0;
/** Close the bold element in the markup */
virtual void endStrong() = 0;
/** Begin an emphasised element in the markup */
virtual void beginEmph() = 0;
/** Close the emphasised element in the markup */
virtual void endEmph() = 0;
/** Begin an underlined element in the markup */
virtual void beginUnderline() = 0;
/** Close the underlined element in the markup */
virtual void endUnderline() = 0;
/** Begin a struck out element in the markup */
virtual void beginStrikeout() = 0;
/** Close the struck out element in the markup */
virtual void endStrikeout() = 0;
/**
Begin a decorarated foreground element in the markup (A text color)
using @p brush
*/
virtual void beginForeground(const QBrush &brush) = 0;
/** Close the decorarated foreground element in the markup */
virtual void endForeground() = 0;
/**
Begin a decorarated background element in the markup (A text background
color) using @p brush
*/
virtual void beginBackground(const QBrush &brush) = 0;
/** Close the decorarated background element in the markup */
virtual void endBackground() = 0;
/**
Begin a url anchor element in the markup
@param href The href of the anchor.
@param name The name of the anchor.
*/
virtual void beginAnchor(const QString &href = {}, const QString &name = {})
= 0;
/** Close the anchor element */
virtual void endAnchor() = 0;
/**
Begin a new font familiy element in the markup
@param family The name of the font family to begin.
*/
virtual void beginFontFamily(const QString &family) = 0;
/** End font family element */
virtual void endFontFamily() = 0;
/**
Begin a new font point size element in the markup
@param size The point size to begin.
*/
virtual void beginFontPointSize(int size) = 0;
/** End font point size element */
virtual void endFontPointSize() = 0;
/**
Begin a new paragraph in the markup
@param a The alignment of the new paragraph.
@param top The top margin of the new paragraph.
@param bottom The bottom margin of the new paragraph.
@param left The left margin of the new paragraph.
@param right The right margin of the new paragraph.
*/
virtual void beginParagraph(Qt::Alignment a = Qt::AlignLeft, qreal top = 0.0,
qreal bottom = 0.0, qreal left = 0.0,
qreal right = 0.0)
= 0;
/** Close the paragraph in the markup. */
virtual void endParagraph() = 0;
/** Add a newline to the markup. */
virtual void addNewline() = 0;
/**
Insert a horizontal rule into the markup.
@param width The width of the rule. Default is full width.
*/
virtual void insertHorizontalRule(int width = -1) = 0;
/**
Insert a new image element into the markup.
@param url The url of the image
@param width The width of the image
@param height The height of the image.
*/
virtual void insertImage(const QString &url, qreal width, qreal height) = 0;
/**
Begin a new list element in the markup.
A list element contains list items, and may contain other lists.
@param style The style of list to create.
*/
virtual void beginList(QTextListFormat::Style style) = 0;
/**
Close the list.
*/
virtual void endList() = 0;
/** Begin a new list item in the markup */
virtual void beginListItem() = 0;
/** End the list item */
virtual void endListItem() = 0;
/** Begin a superscript element */
virtual void beginSuperscript() = 0;
/** End superscript element */
virtual void endSuperscript() = 0;
/** Begin a subscript element */
virtual void beginSubscript() = 0;
/** End subscript element */
virtual void endSubscript() = 0;
/**
Begin a table element.
@param cellpadding The padding attribute for the table.
@param cellspacing The spacing attribute for the table.
@param width The width of the table. May be either an integer, or a
percentage value.
*/
virtual void beginTable(qreal cellpadding, qreal cellspacing,
const QString &width)
= 0;
/**
Begin a new table row
*/
virtual void beginTableRow() = 0;
/**
Begin a new table header cell.
@param width The width of the cell.
@param colSpan The column span of the cell.
@param rowSpan The row span of the cell.
*/
virtual void beginTableHeaderCell(const QString &width, int colSpan,
int rowSpan)
= 0;
/**
Begin a new table cell.
@param width The width of the cell.
@param colSpan The column span of the cell.
@param rowSpan The row span of the cell.
*/
virtual void beginTableCell(const QString &width, int colSpan, int rowSpan)
= 0;
/** End a table element */
virtual void endTable() = 0;
/** End a table row */
virtual void endTableRow() = 0;
/** End a table header cell */
virtual void endTableHeaderCell() = 0;
/** End a table cell */
virtual void endTableCell() = 0;
/**
Begin a level @p level header
@param level An integer between 1 and 6
*/
virtual void beginHeader(int level) = 0;
/**
End a level @p level header
@param level An integer between 1 and 6
*/
virtual void endHeader(int level) = 0;
/**
Append the plain text @p text to the markup
@param text The text to append.
*/
virtual void appendLiteralText(const QString &text) = 0;
/**
Append the raw text @p text to the markup. @p text is added unescaped
*/
virtual void appendRawText(const QString &text) = 0;
/**
Return the fully marked up result of the building process.
This may contain metadata etc, such as a head element in html.
@return The fully marked up text.
*/
virtual QString getResult() = 0;
virtual void addSingleBreakLine() = 0;
};
}
#endif
......@@ -20,6 +20,7 @@
#include "plaintextmarkupbuildertest.h"
#include <KPIMTextEdit/PlainTextMarkupBuilder>
#include <KPIMTextEdit/MarkupDirector>
#include <QRegularExpression>
#include <QTest>
#include <QTextDocument>
......@@ -58,7 +59,7 @@ void PlainTextMarkupBuilderTest::testPlainText()
doc->setHtml(text);
auto hb = new KPIMTextEdit::PlainTextMarkupBuilder();
auto md = new Grantlee::MarkupDirector(hb);
auto md = new KPIMTextEdit::MarkupDirector(hb);
md->processDocument(doc);
const auto result = hb->getResult();
......
......@@ -23,7 +23,6 @@
#include <QRegularExpression>
#include <QTest>
#include <QTextDocument>
#include <grantlee/markupdirector.h>
QTEST_MAIN(TextHTMLBuilderTest)
TextHTMLBuilderTest::TextHTMLBuilderTest(QObject *parent)
: QObject(parent)
......@@ -828,10 +827,29 @@ void TextHTMLBuilderTest::testTitle1()
auto regex = QRegularExpression(
QStringLiteral("^<p style=\"margin-top:0;margin-bottom:0;margin-left:0;margin-right:0;\"><span style=\"font-size:29pt;\"><strong>Foo</strong></span></p>\n$"));
qDebug() << " result : " << result;
//qDebug() << " result " << result;
//TODO implement header support now.
QVERIFY(regex.match(result).hasMatch());
delete md;
delete hb;
delete doc;
}
void TextHTMLBuilderTest::testBug421908()
{
auto doc = new QTextDocument();
doc->setHtml(QStringLiteral("<p><span style=\" color:#aaaaff;\">some colored text<br />some colored text</span></p>"));
auto hb = new KPIMTextEdit::TextHTMLBuilder();
auto md = new KPIMTextEdit::MarkupDirector(hb);
md->processDocument(doc);
auto result = hb->getResult();
auto regex = QRegularExpression(
QStringLiteral("^<p style=\"margin-top:12;margin-bottom:12;margin-left:0;margin-right:0;\"><span style=\"color:#aaaaff;\">some colored text<br />some colored text</span></p>\n$"));
QVERIFY(regex.match(result).hasMatch());
delete md;
delete hb;
delete doc;
}
......@@ -56,7 +56,9 @@ private Q_SLOTS:
void testNewlinesThroughQTextCursor();
void testInsertImage();
void testInsertImageWithSize();
void testTitle1();
void testBug421908();
};
#endif // TEXTHTMLBUILDERTEST_H
......@@ -21,7 +21,7 @@
#include "markupdirector.h"
#include "markupdirector_p.h"
#include <grantlee/abstractmarkupbuilder.h>
#include "abstractmarkupbuilder.h"
#include <QFlags>
#include <QMap>
......@@ -40,7 +40,7 @@
#include <QDebug>
using namespace KPIMTextEdit;
MarkupDirector::MarkupDirector(Grantlee::AbstractMarkupBuilder *builder)
MarkupDirector::MarkupDirector(KPIMTextEdit::AbstractMarkupBuilder *builder)
: d_ptr(new MarkupDirectorPrivate(this))
, m_builder(builder)
{
......@@ -217,8 +217,8 @@ MarkupDirector::processFragment(QTextBlock::iterator it, const QTextFragment &fr
m_builder->beginParagraph(/* blockAlignment */);
paraClosed = false;
} else {
//Bug fixing : missing end line here
m_builder->addNewline();
//Bug fixing : add missing single break line
m_builder->addSingleBreakLine();
}
}
}
......
......@@ -21,7 +21,7 @@
#ifndef MARKUPDIRECTOR_H
#define MARKUPDIRECTOR_H
#include <grantlee/abstractmarkupbuilder.h>
#include "abstractmarkupbuilder.h"
#include "kpimtextedit_export.h"
#include <QTextDocument>
#include <QTextFrame>
......@@ -93,7 +93,7 @@ public:
/**
Constructor
*/
MarkupDirector(Grantlee::AbstractMarkupBuilder *builder);
explicit MarkupDirector(KPIMTextEdit::AbstractMarkupBuilder *builder);
/**
Destructor
......@@ -292,7 +292,7 @@ protected:
The builder this MarkupDirector is operating on. This is available when
subclassing to customize behaviour.
*/
Grantlee::AbstractMarkupBuilder *m_builder;
KPIMTextEdit::AbstractMarkupBuilder *m_builder;
#ifndef Q_QDOC
private:
......
......@@ -36,30 +36,30 @@ namespace KPIMTextEdit
*/
class MarkupDirectorPrivate
{
MarkupDirectorPrivate(MarkupDirector *md) : q_ptr(md) {}
Q_DECLARE_PUBLIC(MarkupDirector)
MarkupDirector *const q_ptr;
QString m_openAnchorHref;
QString m_anchorHrefToOpen;
QString m_openAnchorName;
QBrush m_openForeground;
QBrush m_foregroundToOpen;
QBrush m_openBackground;
QBrush m_backgroundToOpen;
int m_openFontPointSize;
int m_fontPointSizeToOpen;
QString m_openFontFamily;
QString m_fontFamilyToOpen;
// An ordered list containing the order elements were opened in.
QList<int> m_openElements;
// Elements that have yet to be opened. Used while determine the order to
// open them.
QSet<int> m_elementsToOpen;
MarkupDirectorPrivate(MarkupDirector *md) : q_ptr(md) {}
Q_DECLARE_PUBLIC(MarkupDirector)
MarkupDirector *const q_ptr;
QString m_openAnchorHref;
QString m_anchorHrefToOpen;
QString m_openAnchorName;
QBrush m_openForeground;
QBrush m_foregroundToOpen;
QBrush m_openBackground;
QBrush m_backgroundToOpen;
int m_openFontPointSize;
int m_fontPointSizeToOpen;
QString m_openFontFamily;
QString m_fontFamilyToOpen;
// An ordered list containing the order elements were opened in.
QList<int> m_openElements;
// Elements that have yet to be opened. Used while determine the order to
// open them.
QSet<int> m_elementsToOpen;
};
}
......
......@@ -534,3 +534,9 @@ void PlainTextMarkupBuilder::endTableHeaderCell()
void PlainTextMarkupBuilder::endTableRow()
{
}
void PlainTextMarkupBuilder::addSingleBreakLine()
{
Q_D(PlainTextMarkupBuilder);
d->m_text.append(QLatin1Char('\n'));
}
......@@ -23,15 +23,16 @@
#define PLAINTEXTMARKUPBUILDER_H
#include "kpimtextedit_export.h"
#include <grantlee/abstractmarkupbuilder.h>
#include <grantlee/plaintextmarkupbuilder.h>
#include "abstractmarkupbuilder.h"
#define LETTERSINALPHABET 26
#define DIGITSOFFSET 10
namespace KPIMTextEdit
{
class PlainTextMarkupBuilderPrivate;
class KPIMTEXTEDIT_EXPORT PlainTextMarkupBuilder
: virtual public Grantlee::AbstractMarkupBuilder
: virtual public KPIMTextEdit::AbstractMarkupBuilder
{
public:
/** Construct a new PlainTextHTMLMarkupBuilder. */
......@@ -124,9 +125,11 @@ public:
void endTableCell() override;
void beginHeader(int level) override;
void endHeader(int level) override;
void addSingleBreakLine() override;
private:
PlainTextMarkupBuilderPrivate *const d_ptr;
Q_DECLARE_PRIVATE(PlainTextMarkupBuilder)
};
}
......
......@@ -24,49 +24,161 @@
#include <QTextDocument>
#include <QDebug>
namespace KPIMTextEdit
{
class TextHTMLBuilderPrivate
{
public:
TextHTMLBuilderPrivate(TextHTMLBuilder *b) : q_ptr(b) {}
QList<QTextListFormat::Style> currentListItemStyles;
QString m_text;
TextHTMLBuilder *q_ptr;
Q_DECLARE_PUBLIC(TextHTMLBuilder)
};
}
using namespace KPIMTextEdit;
TextHTMLBuilder::TextHTMLBuilder()
: Grantlee::TextHTMLBuilder()
: AbstractMarkupBuilder(), d_ptr(new TextHTMLBuilderPrivate(this))
{
}
TextHTMLBuilder::~TextHTMLBuilder()
TextHTMLBuilder::~TextHTMLBuilder() { delete d_ptr; }
void TextHTMLBuilder::beginStrong()
{
Q_D(TextHTMLBuilder);
;
d->m_text.append(QStringLiteral("<strong>"));
}
//Add &nbsp for avoiding to remove space in html
void TextHTMLBuilder::appendLiteralText(const QString &text)
void TextHTMLBuilder::endStrong()
{
const QString textEscaped = text.toHtmlEscaped();
QString textEscapedResult;
for (int i = 0, total = textEscaped.count(); i < total; ++i) {
const QChar c = textEscaped.at(i);
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</strong>"));
}
if (c == QLatin1Char(' ')) {
if (i == 0) {
textEscapedResult += QStringLiteral("&nbsp;");
} else {
if (i + 1 < textEscaped.count() && (textEscaped.at(i + 1) == QLatin1Char(' '))) {
textEscapedResult += QStringLiteral("&nbsp;");
} else {
textEscapedResult += c;
}
}
} else if (c == QLatin1Char('\t')) {
textEscapedResult += QStringLiteral("&nbsp;&nbsp;&nbsp; ");
void TextHTMLBuilder::beginEmph()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("<em>"));
}
void TextHTMLBuilder::endEmph()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</em>"));
}
void TextHTMLBuilder::beginUnderline()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("<u>"));
}
void TextHTMLBuilder::endUnderline()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</u>"));
}
void TextHTMLBuilder::beginStrikeout()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("<s>"));
}
void TextHTMLBuilder::endStrikeout()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</s>"));
}
void TextHTMLBuilder::beginForeground(const QBrush &brush)
{