much better quality image size handling

This commit is contained in:
Martin Rotter 2023-12-05 09:22:12 +01:00
parent 6fc0bdd988
commit b8f2295bc9
10 changed files with 138 additions and 41 deletions

View File

@ -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}

View File

@ -2,7 +2,6 @@ $os = $args[0]
$use_webengine = $args[1]
$use_qt5 = $args[2]
if ($use_webengine -eq "ON") {
$not_use_webengine = "OFF"
}

View File

@ -358,7 +358,7 @@
<item>
<widget class="QLabel" name="m_lblHeightImageAttachments">
<property name="text">
<string>Limit height of pictures</string>
<string>Limit height of all pictures</string>
</property>
<property name="buddy">
<cstring>m_spinHeightImageAttachments</cstring>

View File

@ -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;
img = img.scaledToWidth(acceptable_width);
tmr.start();
img = img.scaledToWidth(acceptable_width, Qt::TransformationMode::SmoothTransformation);
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<Message>& 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("&#x1F[0-9A-F]{3};");

View File

@ -50,7 +50,8 @@ WebEnginePage* WebEngineViewer::page() const {
}
void WebEngineViewer::loadMessages(const QList<Message>& 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;

View File

@ -26,6 +26,8 @@ struct ContextMenuData {
QUrl m_mediaUrl;
};
#define ACCEPTABLE_IMAGE_PERCENTUAL_WIDTH 0.97
// Interface for web/article viewers.
class WebViewer {
public:

View File

@ -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 <QDir>
@ -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<Message>& messages, RootItem* root) const {
PreparedHtml SkinFactory::generateHtmlOfArticles(const QList<Message>& 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<Message>& 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<Message>& 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,

View File

@ -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<Message>& messages, RootItem* root) const;
PreparedHtml generateHtmlOfArticles(const QList<Message>& 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;

View File

@ -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("<img ([^>]+)>"));
static QRegularExpression exp_image_attrs(QSL("(\\w+)=\"([^\"]+)\""));
// Replace too big pictures. What it exactly does:
// - find all <img> 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("<img");
// QString full = exp_match.captured();
// auto aa = exp_match.capturedLength();
QString img_tag_inner_text = exp_match.captured(1);
// We found image, now we parse its attributes and process them.
QRegularExpressionMatchIterator attrs_match_iter = exp_image_attrs.globalMatch(img_tag_inner_text);
QMap<QString, QString> 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());

View File

@ -38,6 +38,7 @@ class WebFactory : public QObject {
// ∀ = &forall; (entity name), &#8704; (base-10 entity), &#x2200; (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;