Skip to content

Commit

Permalink
much better quality image size handling
Browse files Browse the repository at this point in the history
  • Loading branch information
martinrotter committed Dec 5, 2023
1 parent 6fc0bdd commit b8f2295
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 41 deletions.
2 changes: 2 additions & 0 deletions docs/source/features/filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
1 change: 0 additions & 1 deletion resources/scripts/github-actions/build-windows.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ $os = $args[0]
$use_webengine = $args[1]
$use_qt5 = $args[2]


if ($use_webengine -eq "ON") {
$not_use_webengine = "OFF"
}
Expand Down
2 changes: 1 addition & 1 deletion src/librssguard/gui/settings/settingsfeedsmessages.ui
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
40 changes: 10 additions & 30 deletions src/librssguard/gui/webviewers/qtextbrowser/textbrowserviewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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};");
Expand Down
3 changes: 2 additions & 1 deletion src/librssguard/gui/webviewers/webengine/webengineviewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/librssguard/gui/webviewers/webviewer.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ struct ContextMenuData {
QUrl m_mediaUrl;
};

#define ACCEPTABLE_IMAGE_PERCENTUAL_WIDTH 0.97

// Interface for web/article viewers.
class WebViewer {
public:
Expand Down
21 changes: 14 additions & 7 deletions src/librssguard/miscellaneous/skinfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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));
}
}
}
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/librssguard/miscellaneous/skinfactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
105 changes: 105 additions & 0 deletions src/librssguard/network-web/webfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
1 change: 1 addition & 0 deletions src/librssguard/network-web/webfactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit b8f2295

Please sign in to comment.