diff --git a/docs/source/features/filters.md b/docs/source/features/filters.md index 00631e790..7cc0a04db 100644 --- a/docs/source/features/filters.md +++ b/docs/source/features/filters.md @@ -18,6 +18,8 @@ function filterMessage() { } The function should be fast and must return values which belong to enumeration [`FilteringAction`](#filteringaction-enum). +Supported set of built-in "standard library" adheres to [ECMA-262](https://ecma-international.org/publications-and-standards/standards/ecma-262). + Each article is accessible in your script via global variable named `msg` of type `MessageObject`, see [this file](https://github.com/martinrotter/rssguard/blob/master/src/librssguard/core/messageobject.h) for the declaration. Some properties are writeable, allowing you to change contents of the article before it is written to RSS Guard DB. You can mark article important, change its description, perhaps change author name or even assign some [label](labels) to it!!! ```{note} diff --git a/resources/scripts/github-actions/build-windows.ps1 b/resources/scripts/github-actions/build-windows.ps1 index a780cbfb2..9d7e102d4 100755 --- a/resources/scripts/github-actions/build-windows.ps1 +++ b/resources/scripts/github-actions/build-windows.ps1 @@ -2,7 +2,6 @@ $os = $args[0] $use_webengine = $args[1] $use_qt5 = $args[2] - if ($use_webengine -eq "ON") { $not_use_webengine = "OFF" } diff --git a/src/librssguard/gui/settings/settingsfeedsmessages.ui b/src/librssguard/gui/settings/settingsfeedsmessages.ui index fdec97655..396e58100 100644 --- a/src/librssguard/gui/settings/settingsfeedsmessages.ui +++ b/src/librssguard/gui/settings/settingsfeedsmessages.ui @@ -358,7 +358,7 @@ - Limit height of pictures + Limit height of all pictures m_spinHeightImageAttachments diff --git a/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp b/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp index 808f019b3..6ba9e644d 100644 --- a/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp +++ b/src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp @@ -78,13 +78,17 @@ QVariant TextBrowserViewer::loadOneResource(int type, const QUrl& name) { img = QImage::fromData(m_loadedResources.value(resolved_name)); } - int acceptable_width = int(width() * 0.9); + int acceptable_width = int(width() * ACCEPTABLE_IMAGE_PERCENTUAL_WIDTH); if (img.width() > acceptable_width) { - qWarningNN << LOGSEC_GUI << "Picture" << QUOTE_W_SPACE(name) - << "is too wide, down-scaling to prevent horizontal scrollbars."; + QElapsedTimer tmr; + + tmr.start(); + img = img.scaledToWidth(acceptable_width, Qt::TransformationMode::SmoothTransformation); - img = img.scaledToWidth(acceptable_width); + qWarningNN << LOGSEC_GUI << "Picture" << QUOTE_W_SPACE(name) + << "is too wide, down-scaling to prevent horizontal scrollbars. Scaling took" + << NONQUOTE_W_SPACE(tmr.elapsed()) << "miliseconds."; } return img; @@ -198,32 +202,8 @@ void TextBrowserViewer::loadMessages(const QList& messages, RootItem* r emit loadingStarted(); m_root = root; - auto html_messages = qApp->skins()->generateHtmlOfArticles(messages, root); - - static QRegularExpression exp_replace_wide_stuff(QSL("width=\"([^\"]+)\"")); - - // html_messages.m_html = html_messages.m_html.replace(exp_replace_wide_stuff, QSL("width=\"%1\"").arg(width() * - // 0.9)); - - // Replace too wide pictures. - QRegularExpressionMatch exp_match; - qsizetype match_offset = 0; - int acceptable_width = int(width() * 0.9); - - while ((exp_match = exp_replace_wide_stuff.match(html_messages.m_html, match_offset)).hasMatch()) { - int found_width = exp_match.captured(1).toInt(); - - if (found_width > acceptable_width) { - qWarningNN << LOGSEC_GUI << "Element" << QUOTE_W_SPACE(exp_match.captured()) - << "is too wide, setting smaller value to prevent horizontal scrollbars."; - - html_messages.m_html = html_messages.m_html.replace(exp_match.capturedStart(1), - exp_match.capturedLength(1), - QString::number(acceptable_width)); - } - - match_offset = exp_match.capturedEnd(); - } + auto html_messages = + qApp->skins()->generateHtmlOfArticles(messages, root, width() * ACCEPTABLE_IMAGE_PERCENTUAL_WIDTH); // Remove other characters which cannot be displayed properly. static QRegularExpression exp_symbols("[0-9A-F]{3};"); diff --git a/src/librssguard/gui/webviewers/webengine/webengineviewer.cpp b/src/librssguard/gui/webviewers/webengine/webengineviewer.cpp index 35ea2f59c..ae47b28f9 100644 --- a/src/librssguard/gui/webviewers/webengine/webengineviewer.cpp +++ b/src/librssguard/gui/webviewers/webengine/webengineviewer.cpp @@ -50,7 +50,8 @@ WebEnginePage* WebEngineViewer::page() const { } void WebEngineViewer::loadMessages(const QList& messages, RootItem* root) { - auto html_messages = qApp->skins()->generateHtmlOfArticles(messages, root); + auto html_messages = + qApp->skins()->generateHtmlOfArticles(messages, root, width() * ACCEPTABLE_IMAGE_PERCENTUAL_WIDTH); m_root = root; m_messageContents = html_messages.m_html; diff --git a/src/librssguard/gui/webviewers/webviewer.h b/src/librssguard/gui/webviewers/webviewer.h index 74a4795c0..553b400bd 100644 --- a/src/librssguard/gui/webviewers/webviewer.h +++ b/src/librssguard/gui/webviewers/webviewer.h @@ -26,6 +26,8 @@ struct ContextMenuData { QUrl m_mediaUrl; }; +#define ACCEPTABLE_IMAGE_PERCENTUAL_WIDTH 0.97 + // Interface for web/article viewers. class WebViewer { public: diff --git a/src/librssguard/miscellaneous/skinfactory.cpp b/src/librssguard/miscellaneous/skinfactory.cpp index 69637581d..2a4ac13a4 100644 --- a/src/librssguard/miscellaneous/skinfactory.cpp +++ b/src/librssguard/miscellaneous/skinfactory.cpp @@ -5,6 +5,7 @@ #include "miscellaneous/application.h" #include "miscellaneous/settings.h" #include "network-web/networkfactory.h" +#include "network-web/webfactory.h" #include "services/abstract/rootitem.h" #include @@ -228,11 +229,13 @@ PreparedHtml SkinFactory::prepareHtml(const QString& inner_html, const QUrl& bas return {currentSkin().m_layoutMarkupWrapper.arg(QString(), inner_html), base_url}; } -PreparedHtml SkinFactory::generateHtmlOfArticles(const QList& messages, RootItem* root) const { +PreparedHtml SkinFactory::generateHtmlOfArticles(const QList& messages, + RootItem* root, + int desired_width) const { Skin skin = currentSkin(); QString messages_layout; QString single_message_layout = skin.m_layoutMarkup; - const auto forced_img_size = + const int forced_img_height = qApp->settings()->value(GROUP(Messages), SETTING(Messages::MessageHeadImageHeight)).toInt(); auto* feed = root != nullptr @@ -261,7 +264,7 @@ PreparedHtml SkinFactory::generateHtmlOfArticles(const QList& messages, enclosure_images += skin.m_enclosureImageMarkup.arg(enclosure.m_url, enclosure.m_mimeType, - forced_img_size <= 0 ? QString() : QString::number(forced_img_size)); + forced_img_height <= 0 ? QString() : QString::number(forced_img_height)); } } } @@ -274,15 +277,19 @@ PreparedHtml SkinFactory::generateHtmlOfArticles(const QList& messages, : qApp->localization()->loadedLocale().toString(message.m_created.toLocalTime(), QLocale::FormatType::ShortFormat); + QString msg_contents = is_plain ? Qt::convertFromPlainText(message.m_contents, Qt::WhiteSpaceMode::WhiteSpaceNormal) + : message.m_contents; + + if (!is_plain) { + msg_contents = qApp->web()->limitSizeOfHtmlImages(msg_contents, desired_width, forced_img_height); + } + messages_layout.append(single_message_layout.arg(message.m_title, tr("Written by ") + (message.m_author.isEmpty() ? tr("unknown author") : message.m_author), message.m_url, - is_plain - ? Qt::convertFromPlainText(message.m_contents, - Qt::WhiteSpaceMode::WhiteSpaceNormal) - : message.m_contents, + msg_contents, msg_date, enclosures, enclosure_images, diff --git a/src/librssguard/miscellaneous/skinfactory.h b/src/librssguard/miscellaneous/skinfactory.h index 7eaec0b69..7a800e890 100644 --- a/src/librssguard/miscellaneous/skinfactory.h +++ b/src/librssguard/miscellaneous/skinfactory.h @@ -103,7 +103,7 @@ class RSSGUARD_DLLSPEC SkinFactory : public QObject { QString adBlockedPage(const QString& url, const QString& filter); PreparedHtml prepareHtml(const QString& inner_html, const QUrl& base_url); - PreparedHtml generateHtmlOfArticles(const QList& messages, RootItem* root) const; + PreparedHtml generateHtmlOfArticles(const QList& messages, RootItem* root, int desired_width) const; // Gets skin about a particular skin. Skin skinInfo(const QString& skin_name, bool lite, bool* ok = nullptr) const; diff --git a/src/librssguard/network-web/webfactory.cpp b/src/librssguard/network-web/webfactory.cpp index 4cc9a1c37..a9abcfe28 100644 --- a/src/librssguard/network-web/webfactory.cpp +++ b/src/librssguard/network-web/webfactory.cpp @@ -258,6 +258,111 @@ QString WebFactory::unescapeHtml(const QString& html) { return output; } +QString WebFactory::limitSizeOfHtmlImages(const QString& html, int desired_width, int desired_max_height) const { + static QRegularExpression exp_image_tag(QSL("]+)>")); + static QRegularExpression exp_image_attrs(QSL("(\\w+)=\"([^\"]+)\"")); + + // Replace too big pictures. What it exactly does: + // - find all tags and check for existence of height/width attributes: + // - both found -> keep aspect ratio and change to fit width if too big (or limit height if configured) + // - height found only -> limit height if configured + // - width found only -> change to fit width if too big + // - nothing found (image dimensions are taken directly from picture) -> limit height if configured, + QRegularExpressionMatch exp_match; + qsizetype match_offset = 0; + QString my_html = html; + QElapsedTimer tmr; + + IOFactory::writeFile("a.html", html.toUtf8()); + + tmr.start(); + + while ((exp_match = exp_image_tag.match(my_html, match_offset)).hasMatch()) { + QString img_reconstructed = QSL(" attrs; + + while (attrs_match_iter.hasNext()) { + QRegularExpressionMatch attrs_match = attrs_match_iter.next(); + + QString attr_name = attrs_match.captured(1); + QString attr_value = attrs_match.captured(2); + + attrs.insert(attr_name, attr_value); + } + + if (attrs.contains("height") && attrs.contains("width")) { + double ratio = attrs.value("width").toDouble() / attrs.value("height").toDouble(); + + if (desired_max_height > 0) { + // We limit height. + attrs.insert("height", QString::number(desired_max_height)); + attrs.insert("width", QString::number(int(ratio * desired_max_height))); + } + + // We fit width. + if (attrs.value("width").toInt() > desired_width) { + attrs.insert("width", QString::number(desired_width)); + attrs.insert("height", QString::number(int(desired_width / ratio))); + } + } + else if (attrs.contains("width")) { + // Only width. + if (attrs.value("width").toInt() > desired_width) { + attrs.insert("width", QString::number(desired_width)); + } + } + else { + // No dimensions given or just height. + if (desired_max_height > 0) { + attrs.insert("height", QString::number(desired_max_height)); + } + /* + else { + // We do not know image dimensions and size limitting is not there. + attrs.insert("width", QString::number(desired_width / 2)); + } + */ + } + + // Re-insert all attributes. + while (!attrs.isEmpty()) { + auto first_key = attrs.firstKey(); + auto first_value = attrs.first(); + + img_reconstructed += QSL(" %1=\"%2\"").arg(first_key, first_value); + + attrs.remove(first_key); + } + + img_reconstructed += QSL(">"); + + my_html = my_html.replace(exp_match.capturedStart(), exp_match.capturedLength(), img_reconstructed); + + /*if (found_width > desired_width) { + qWarningNN << LOGSEC_GUI << "Element" << QUOTE_W_SPACE(exp_match.captured()) + << "is too wide, setting smaller value to prevent horizontal scrollbars."; + + my_html = + my_html.replace(exp_match.capturedStart(1), exp_match.capturedLength(1), QString::number(desired_width)); + }*/ + + match_offset = exp_match.capturedStart() + img_reconstructed.size(); + } + + IOFactory::writeFile("b.html", my_html.toUtf8()); + + qDebugNN << LOGSEC_GUI << "HTML image resizing took" << NONQUOTE_W_SPACE(tmr.elapsed()) << "miliseconds."; + return my_html; +} + QString WebFactory::processFeedUriScheme(const QString& url) { if (url.startsWith(QSL(URI_SCHEME_FEED))) { return QSL(URI_SCHEME_HTTPS) + url.mid(QSL(URI_SCHEME_FEED).size()); diff --git a/src/librssguard/network-web/webfactory.h b/src/librssguard/network-web/webfactory.h index c68c0f24f..07549afad 100644 --- a/src/librssguard/network-web/webfactory.h +++ b/src/librssguard/network-web/webfactory.h @@ -38,6 +38,7 @@ class WebFactory : public QObject { // ∀ = ∀ (entity name), ∀ (base-10 entity), ∀ (base-16 entity) QString unescapeHtml(const QString& html); + QString limitSizeOfHtmlImages(const QString& html, int desired_width, int images_max_height) const; QString processFeedUriScheme(const QString& url); AdBlockManager* adBlock() const;