Commit 105d6e32 authored by Laurent Montel's avatar Laurent Montel 😁
Browse files

We need a specific texthtmlbuilder for fixing bug as 419448

parent 0f5fd3aa
cmake_minimum_required(VERSION 3.5)
set(PIM_VERSION "5.13.90")
set(PIM_VERSION "5.13.91")
project(KPimTextEdit VERSION ${PIM_VERSION})
......
......@@ -43,6 +43,7 @@ set(kpimtextedit_texttospeech_SRCS
set(kpimtextedit_grantlee_builder_SRCS
grantleebuilder/plaintextmarkupbuilder.cpp
grantleebuilder/markupdirector.cpp
grantleebuilder/texthtmlbuilder.cpp
)
set(kpimtextedit_emoticon_builder_SRCS
......@@ -129,6 +130,13 @@ ecm_generate_headers(KPimTextEdit_CamelCaseemoticon_HEADERS
RELATIVE emoticon
)
ecm_generate_headers(KPimTextEdit_CamelCasegrantlee_HEADERS
HEADER_NAMES
PlainTextMarkupBuilder
PREFIX KPIMTextEdit
REQUIRED_HEADERS kpimtextedit_HEADERS
RELATIVE grantleebuilder
)
ecm_generate_headers(PimCommon_CamelCasetextrichtexteditor_HEADERS
HEADER_NAMES
......@@ -199,6 +207,7 @@ install(FILES
${PimCommon_plaintexteditor_HEADERS}
${KPimTextEdit_composerng_HEADERS}
${KPimTextEdit_emoticon_HEADERS}
${KPimTextEdit_grantlee_HEADERS}
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KPIMTextEdit/kpimtextedit
COMPONENT Devel
)
......@@ -211,6 +220,7 @@ install(FILES
${PimCommon_CamelCasetexteditor_commonwidget_HEADERS}
${KPimTextEdit_Camelcasecomposerng_HEADERS}
${KPimTextEdit_CamelCaseemoticon_HEADERS}
${KPimTextEdit_CamelCasegrantlee_HEADERS}
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KPIMTextEdit/KPIMTextEdit/
COMPONENT Devel
)
......
/*
Copyright (C) 2020 Laurent Montel <montel@kde.org>
based on code from 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.
*/
#include "texthtmlbuilder.h"
#include <QList>
#include <QTextDocument>
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()
: AbstractMarkupBuilder(), d_ptr(new TextHTMLBuilderPrivate(this))
{
}
TextHTMLBuilder::~TextHTMLBuilder() { delete d_ptr; }
void TextHTMLBuilder::beginStrong()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("<strong>"));
}
void TextHTMLBuilder::endStrong()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</strong>"));
}
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)
{
Q_D(TextHTMLBuilder);
d->m_text.append(
QStringLiteral("<span style=\"color:%1;\">").arg(brush.color().name()));
}
void TextHTMLBuilder::endForeground()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</span>"));
}
void TextHTMLBuilder::beginBackground(const QBrush &brush)
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("<span style=\"background-color:%1;\">")
.arg(brush.color().name()));
}
void TextHTMLBuilder::endBackground()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</span>"));
}
void TextHTMLBuilder::beginAnchor(const QString &href, const QString &name)
{
Q_D(TextHTMLBuilder);
if (!href.isEmpty()) {
if (!name.isEmpty()) {
d->m_text.append(
QStringLiteral("<a href=\"%1\" name=\"%2\">").arg(href, name));
} else {
d->m_text.append(QStringLiteral("<a href=\"%1\">").arg(href));
}
} else {
if (!name.isEmpty()) {
d->m_text.append(QStringLiteral("<a name=\"%1\">").arg(name));
}
}
}
void TextHTMLBuilder::endAnchor()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</a>"));
}
void TextHTMLBuilder::beginFontFamily(const QString &family)
{
Q_D(TextHTMLBuilder);
d->m_text.append(
QStringLiteral("<span style=\"font-family:%1;\">").arg(family));
}
void TextHTMLBuilder::endFontFamily()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</span>"));
}
void TextHTMLBuilder::beginFontPointSize(int size)
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("<span style=\"font-size:%1pt;\">")
.arg(QString::number(size)));
}
void TextHTMLBuilder::endFontPointSize()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</span>"));
}
void TextHTMLBuilder::beginParagraph(Qt::Alignment al, qreal topMargin,
qreal bottomMargin, qreal leftMargin,
qreal rightMargin)
{
Q_D(TextHTMLBuilder);
// Don't put paragraph tags inside li tags. Qt bug reported.
// if (currentListItemStyles.size() != 0)
// {
QString styleString;
if (topMargin != 0) {
styleString.append(QStringLiteral("margin-top:%1;").arg(topMargin));
}
if (bottomMargin != 0) {
styleString.append(QStringLiteral("margin-bottom:%1;").arg(bottomMargin));
}
if (leftMargin != 0) {
styleString.append(QStringLiteral("margin-left:%1;").arg(leftMargin));
}
if (rightMargin != 0) {
styleString.append(QStringLiteral("margin-right:%1;").arg(rightMargin));
}
// Using == doesn't work here.
// Using bitwise comparison because an alignment can contain a vertical and
// a
// horizontal part.
if (al & Qt::AlignRight) {
d->m_text.append(QStringLiteral("<p align=\"right\" "));
} else if (al & Qt::AlignHCenter) {
d->m_text.append(QStringLiteral("<p align=\"center\" "));
} else if (al & Qt::AlignJustify) {
d->m_text.append(QStringLiteral("<p align=\"justify\" "));
} else if (al & Qt::AlignLeft) {
d->m_text.append(QStringLiteral("<p"));
} else {
d->m_text.append(QStringLiteral("<p"));
}
if (!styleString.isEmpty()) {
d->m_text.append(QStringLiteral(" \"") + styleString + QLatin1Char('"'));
}
d->m_text.append(QLatin1Char('>'));
// }
}
void TextHTMLBuilder::beginHeader(int level)
{
Q_D(TextHTMLBuilder);
switch (level) {
case 1:
d->m_text.append(QStringLiteral("<h1>"));
break;
case 2:
d->m_text.append(QStringLiteral("<h2>"));
break;
case 3:
d->m_text.append(QStringLiteral("<h3>"));
break;
case 4:
d->m_text.append(QStringLiteral("<h4>"));
break;
case 5:
d->m_text.append(QStringLiteral("<h5>"));
break;
case 6:
d->m_text.append(QStringLiteral("<h6>"));
break;
default:
break;
}
}
void TextHTMLBuilder::endHeader(int level)
{
Q_D(TextHTMLBuilder);
switch (level) {
case 1:
d->m_text.append(QStringLiteral("</h1>"));
break;
case 2:
d->m_text.append(QStringLiteral("</h2>"));
break;
case 3:
d->m_text.append(QStringLiteral("</h3>"));
break;
case 4:
d->m_text.append(QStringLiteral("</h4>"));
break;
case 5:
d->m_text.append(QStringLiteral("</h5>"));
break;
case 6:
d->m_text.append(QStringLiteral("</h6>"));
break;
default:
break;
}
}
void TextHTMLBuilder::endParagraph()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</p>\n"));
}
void TextHTMLBuilder::addNewline()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("<p>&nbsp;"));
}
void TextHTMLBuilder::insertHorizontalRule(int width)
{
Q_D(TextHTMLBuilder);
if (width != -1) {
d->m_text.append(QStringLiteral("<hr width=\"%1\" />\n").arg(width));
}
d->m_text.append(QStringLiteral("<hr />\n"));
}
void TextHTMLBuilder::insertImage(const QString &src, qreal width, qreal height)
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("<img src=\"%1\" ").arg(src));
if (width != 0)
d->m_text.append(QStringLiteral("width=\"%2\" ").arg(width));
if (height != 0)
d->m_text.append(QStringLiteral("height=\"%2\" ").arg(height));
d->m_text.append(QStringLiteral("/>"));
}
void TextHTMLBuilder::beginList(QTextListFormat::Style type)
{
Q_D(TextHTMLBuilder);
d->currentListItemStyles.append(type);
switch (type) {
case QTextListFormat::ListDisc:
d->m_text.append(QStringLiteral("<ul type=\"disc\">\n"));
break;
case QTextListFormat::ListCircle:
d->m_text.append(QStringLiteral("\n<ul type=\"circle\">\n"));
break;
case QTextListFormat::ListSquare:
d->m_text.append(QStringLiteral("\n<ul type=\"square\">\n"));
break;
case QTextListFormat::ListDecimal:
d->m_text.append(QStringLiteral("\n<ol type=\"1\">\n"));
break;
case QTextListFormat::ListLowerAlpha:
d->m_text.append(QStringLiteral("\n<ol type=\"a\">\n"));
break;
case QTextListFormat::ListUpperAlpha:
d->m_text.append(QStringLiteral("\n<ol type=\"A\">\n"));
break;
case QTextListFormat::ListLowerRoman:
d->m_text.append(QStringLiteral("\n<ol type=\"i\">\n"));
break;
case QTextListFormat::ListUpperRoman:
d->m_text.append(QStringLiteral("\n<ol type=\"I\">\n"));
break;
default:
break;
}
}
void TextHTMLBuilder::endList()
{
Q_D(TextHTMLBuilder);
switch (d->currentListItemStyles.last()) {
case QTextListFormat::ListDisc:
case QTextListFormat::ListCircle:
case QTextListFormat::ListSquare:
d->m_text.append(QStringLiteral("</ul>\n"));
break;
case QTextListFormat::ListDecimal:
case QTextListFormat::ListLowerAlpha:
case QTextListFormat::ListUpperAlpha:
case QTextListFormat::ListLowerRoman:
case QTextListFormat::ListUpperRoman:
d->m_text.append(QStringLiteral("</ol>\n"));
break;
default:
break;
}
d->currentListItemStyles.removeLast();
}
void TextHTMLBuilder::beginListItem()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("<li>"));
}
void TextHTMLBuilder::endListItem()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</li>\n"));
}
void TextHTMLBuilder::beginSuperscript()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("<sup>"));
}
void TextHTMLBuilder::endSuperscript()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</sup>"));
}
void TextHTMLBuilder::beginSubscript()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("<sub>"));
}
void TextHTMLBuilder::endSubscript()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</sub>"));
}
void TextHTMLBuilder::beginTable(qreal cellpadding, qreal cellspacing,
const QString &width)
{
Q_D(TextHTMLBuilder);
d->m_text.append(
QStringLiteral("<table cellpadding=\"%1\" cellspacing=\"%2\" "
"width=\"%3\" border=\"1\">")
.arg(cellpadding)
.arg(cellspacing)
.arg(width));
}
void TextHTMLBuilder::beginTableRow()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("<tr>"));
}
void TextHTMLBuilder::beginTableHeaderCell(const QString &width, int colspan,
int rowspan)
{
Q_D(TextHTMLBuilder);
d->m_text.append(
QStringLiteral("<th width=\"%1\" colspan=\"%2\" rowspan=\"%3\">")
.arg(width)
.arg(colspan)
.arg(rowspan));
}
void TextHTMLBuilder::beginTableCell(const QString &width, int colspan,
int rowspan)
{
Q_D(TextHTMLBuilder);
d->m_text.append(
QStringLiteral("<td width=\"%1\" colspan=\"%2\" rowspan=\"%3\">")
.arg(width)
.arg(colspan)
.arg(rowspan));
}
void TextHTMLBuilder::endTable()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</table>"));
}
void TextHTMLBuilder::endTableRow()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</tr>"));
}
void TextHTMLBuilder::endTableHeaderCell()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</th>"));
}
void TextHTMLBuilder::endTableCell()
{
Q_D(TextHTMLBuilder);
d->m_text.append(QStringLiteral("</td>"));
}
void TextHTMLBuilder::appendLiteralText(const QString &text)
{
Q_D(TextHTMLBuilder);
d->m_text.append(text.toHtmlEscaped());
}
void TextHTMLBuilder::appendRawText(const QString &text)
{
Q_D(TextHTMLBuilder);
d->m_text.append(text);
}
QString TextHTMLBuilder::getResult()
{
Q_D(TextHTMLBuilder);
auto ret = d->m_text;
d->m_text.clear();
return ret;
}
/*
Copyright (C) 2020 Laurent Montel <montel@kde.org>
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 TEXTHTMLBUILDER_H
#define TEXTHTMLBUILDER_H
#include "kpimtextedit_export.h"
#include <grantlee/texthtmlbuilder.h>
#include <grantlee/abstractmarkupbuilder.h>
namespace KPIMTextEdit
{
class TextHTMLBuilderPrivate;
/// @headerfile texthtmlbuilder.h grantlee/texthtmlbuilder.h
/**
@brief The TextHTMLBuilder creates a clean html markup output.
This class creates html output which is as minimal as possible and restricted
to the rich text features supported in %Qt.
(https://doc.qt.io/qt-5/richtext-html-subset.html)
The output contains only the body content, not the head element or other
metadata.
eg:
@code
<p>
This is some <strong>formatted content</strong> in a paragraph.
</p>
@endcode
instead of the content produced by %Qt:
@code
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"
"http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><meta
http-equiv="Content-Type" content="text/html; charset=UTF-8" /><style
type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body style=" font-family:'Sans Serif'; font-size:10pt;
font-weight:400; font-style:normal;">
<p style=" margin-top:12px; margin-bottom:12px; margin-left:0px;
margin-right:0px; -qt-block-indent:0; text-indent:0px;">This is some <span
style=" font-weight:600;">formatted content</span> in a paragraph.
</p></body></html>
@endcode
Such tags should be created separately. For example:
@code
auto b = new TextHTMLBuilder();
auto md = new MarkupDirector(b);
md->constructContent();
QString cleanHtml(
"<head>\n<title>%1</title>\n</head>\n<body>%2</body>\n</html>")
.arg(document.metaInformation(QTextDocument::DocumentTitle))
.arg(b->getOutput());
file.write(cleanHtml);
@endcode
Font formatting information on elements is represented by individual span
elements.
eg:
@code
<span style"color:blue;">
<span style="background-color:red;">
Blue text on red background
</span>
</span>
@endcode
instead of