Add support for image sizes in markdowns

The discount markdown parser supports parsing explicit image sizes in markdown, but we ignore them and replace them with the actual image dimensions. With this patch, any specified image sizes are respected.
remotes/origin/work/aacid/more_expressive_signed_messages
Markus Brenneis 6 years ago committed by Albert Astals Cid
parent 511fc7acca
commit 89cf21b222
  1. 9
      autotests/CMakeLists.txt
  2. BIN
      autotests/data/1500x300.png
  3. BIN
      autotests/data/300x1500.png
  4. 77
      autotests/data/imageSizes.md
  5. 128
      autotests/markdowntest.cpp
  6. 55
      generators/markdown/converter.cpp
  7. 11
      generators/markdown/converter.h
  8. 14
      generators/markdown/generator_md.cpp
  9. 9
      generators/markdown/generator_md.h

@ -106,3 +106,12 @@ ecm_add_test(signatureformtest.cpp
TEST_NAME "signatureformtest" TEST_NAME "signatureformtest"
LINK_LIBRARIES Qt5::Test okularcore LINK_LIBRARIES Qt5::Test okularcore
) )
find_package(Discount "2")
if(discount_FOUND)
ecm_add_test(markdowntest.cpp ../generators/markdown/converter.cpp
TEST_NAME "markdowntest"
LINK_LIBRARIES Qt5::Test okularcore KF5::I18n discount::Lib
)
endif()

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

@ -0,0 +1,77 @@
# Test for specifying image sizes in markdown
(c)
## small image
* no explicit size:
![potato](potato.jpg)
* only width specified `=100x`:
![potato](potato.jpg =100x)
* only height specified `=x100`:
![potato](potato.jpg =x100)
* both specified `=100x100`:
![potato](potato.jpg =100x100)
* only width, using html `4200x`:
<img src="potato.jpg" alt="potato" width="4200"/>
* both specified, using html `4200x4200`:
<img src="potato.jpg" alt="potato" width="4200" height="4200"/>
## wide image
* no explicit size:
![1500x300](1500x300.png)
* only width specified `=100x`:
![1500x300](1500x300.png =100x)
* only height specified `=x100`:
![1500x300](1500x300.png =x100)
* both specified `=100x100`:
![1500x300](1500x300.png =100x100)
* only height specified, using html `x4200`:
<img src="1500x300.png" alt="1500x300" height="4200"/>
* both specified, using html `4200x4200`:
<img src="1500x300.png" alt="1500x300" width="4200" height="4200"/>
## tall image
* no explicit size:
![300x1500](300x1500.png)
* only width specified `=100x`:
![300x1500](300x1500.png =100x)
* only height specified `=x100`:
![300x1500](300x1500.png =x100)
* both specified `=100x100`:
![300x1500](300x1500.png =100x100)
* both specified, using html `4200x4200`:
<img src="300x1500.png" alt="300x1500" width="4200" height="4200"/>

@ -0,0 +1,128 @@
/***************************************************************************
* Copyright (C) 2020 by Markus Brenneis <support.gulp21+kde@gmail.com> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
***************************************************************************/
#include <QtTest>
#include "../settings_core.h"
#include "core/document.h"
#include "generators/markdown/converter.h"
#include <QMimeDatabase>
#include <QMimeType>
#include <QTextDocument>
class MarkdownTest : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void testFancyPantsEnabled();
void testFancyPantsDisabled();
void testImageSizes();
private:
void findImages(QTextFrame *parent, QVector<QTextImageFormat> &images);
void findImages(const QTextBlock &parent, QVector<QTextImageFormat> &images);
};
void MarkdownTest::initTestCase()
{
Okular::SettingsCore::instance(QStringLiteral("markdowntest"));
}
void MarkdownTest::testFancyPantsEnabled()
{
Markdown::Converter converter;
converter.setFancyPantsEnabled(true);
QTextDocument *document = converter.convert(QStringLiteral(KDESRCDIR "data/imageSizes.md"));
QTextFrame::iterator secondFrame = ++(document->rootFrame()->begin());
QVERIFY(secondFrame.currentBlock().text().startsWith(QStringLiteral("©")));
}
void MarkdownTest::testFancyPantsDisabled()
{
Markdown::Converter converter;
converter.setFancyPantsEnabled(false);
QTextDocument *document = converter.convert(QStringLiteral(KDESRCDIR "data/imageSizes.md"));
QTextFrame::iterator secondFrame = ++(document->rootFrame()->begin());
QVERIFY(secondFrame.currentBlock().text().startsWith(QStringLiteral("(c)")));
}
void MarkdownTest::testImageSizes()
{
Markdown::Converter converter;
QTextDocument *document = converter.convert(QStringLiteral(KDESRCDIR "data/imageSizes.md"));
QTextFrame *parent = document->rootFrame();
QVector<QTextImageFormat> images;
findImages(parent, images);
QCOMPARE(images.size(), 17);
qreal expectedSizes[][2] = {// width, height
// small image
{412, 349},
{100, 84.70873786407767},
{118.0515759312321, 100},
{100, 100},
{890, 753.9077669902913},
{890, 890},
// wide image
{890, 178},
{100, 20},
{500, 100},
{100, 100},
{890, 178},
{890, 890},
// tall image
{300, 1500},
{100, 500},
{20, 100},
{100, 100},
{890, 890}};
for (int i = 0; i < images.size(); i++) {
QCOMPARE(images[i].width(), expectedSizes[i][0]);
QCOMPARE(images[i].height(), expectedSizes[i][1]);
}
}
void MarkdownTest::findImages(QTextFrame *parent, QVector<QTextImageFormat> &images)
{
for (QTextFrame::iterator it = parent->begin(); !it.atEnd(); ++it) {
QTextFrame *textFrame = it.currentFrame();
const QTextBlock textBlock = it.currentBlock();
if (textFrame) {
findImages(textFrame, images);
} else if (textBlock.isValid()) {
findImages(textBlock, images);
}
}
}
void MarkdownTest::findImages(const QTextBlock &parent, QVector<QTextImageFormat> &images)
{
for (QTextBlock::iterator it = parent.begin(); !it.atEnd(); ++it) {
const QTextFragment textFragment = it.fragment();
if (textFragment.isValid()) {
const QTextCharFormat textCharFormat = textFragment.charFormat();
if (textCharFormat.isImageFormat()) {
images.append(textCharFormat.toImageFormat());
}
}
}
}
QTEST_MAIN(MarkdownTest)
#include "markdowntest.moc"

@ -9,8 +9,6 @@
#include "converter.h" #include "converter.h"
#include "generator_md.h"
#include <KLocalizedString> #include <KLocalizedString>
#include <QDir> #include <QDir>
@ -41,10 +39,16 @@ extern "C" {
#define MKD_AUTOLINK 0 #define MKD_AUTOLINK 0
#endif #endif
#define PAGE_WIDTH 980
#define PAGE_HEIGHT 1307
#define PAGE_MARGIN 45
#define CONTENT_WIDTH (PAGE_WIDTH - 2 * PAGE_MARGIN)
using namespace Markdown; using namespace Markdown;
Converter::Converter() Converter::Converter()
: m_markdownFile(nullptr) : m_markdownFile(nullptr)
, m_isFancyPantsEnabled(true)
{ {
} }
@ -96,7 +100,7 @@ QTextDocument *Converter::convertOpenFile()
MMIOT *markdownHandle = mkd_in(m_markdownFile, 0); MMIOT *markdownHandle = mkd_in(m_markdownFile, 0);
int flags = MKD_FENCEDCODE | MKD_GITHUBTAGS | MKD_AUTOLINK | MKD_TOC | MKD_IDANCHOR; int flags = MKD_FENCEDCODE | MKD_GITHUBTAGS | MKD_AUTOLINK | MKD_TOC | MKD_IDANCHOR;
if (!MarkdownGenerator::isFancyPantsEnabled()) if (!m_isFancyPantsEnabled)
flags |= MKD_NOPANTS; flags |= MKD_NOPANTS;
if (!mkd_compile(markdownHandle, flags)) { if (!mkd_compile(markdownHandle, flags)) {
emit error(i18n("Failed to compile the Markdown document."), -1); emit error(i18n("Failed to compile the Markdown document."), -1);
@ -109,14 +113,15 @@ QTextDocument *Converter::convertOpenFile()
const QString html = QString::fromUtf8(htmlDocument, size); const QString html = QString::fromUtf8(htmlDocument, size);
QTextDocument *textDocument = new QTextDocument; QTextDocument *textDocument = new QTextDocument;
textDocument->setPageSize(QSizeF(980, 1307)); textDocument->setPageSize(QSizeF(PAGE_WIDTH, PAGE_HEIGHT));
textDocument->setHtml(html); textDocument->setHtml(html);
textDocument->setDefaultFont(generator()->generalSettings()->font()); if (generator())
textDocument->setDefaultFont(generator()->generalSettings()->font());
mkd_cleanup(markdownHandle); mkd_cleanup(markdownHandle);
QTextFrameFormat frameFormat; QTextFrameFormat frameFormat;
frameFormat.setMargin(45); frameFormat.setMargin(PAGE_MARGIN);
QTextFrame *rootFrame = textDocument->rootFrame(); QTextFrame *rootFrame = textDocument->rootFrame();
rootFrame->setFrameFormat(frameFormat); rootFrame->setFrameFormat(frameFormat);
@ -189,16 +194,13 @@ void Converter::convertImages(const QTextBlock &parent, const QDir &dir, QTextDo
QTextImageFormat format; QTextImageFormat format;
const qreal specifiedHeight = textCharFormat.toImageFormat().height();
const qreal specifiedWidth = textCharFormat.toImageFormat().width();
format.setName(QDir::cleanPath(dir.absoluteFilePath(textCharFormat.toImageFormat().name()))); format.setName(QDir::cleanPath(dir.absoluteFilePath(textCharFormat.toImageFormat().name())));
const QImage img = QImage(format.name()); const QImage img = QImage(format.name());
if (img.width() > 890) { setImageSize(format, specifiedWidth, specifiedHeight, img.width(), img.height());
format.setWidth(890);
format.setHeight(img.height() * 890. / img.width());
} else {
format.setWidth(img.width());
format.setHeight(img.height());
}
QTextCursor cursor(textDocument); QTextCursor cursor(textDocument);
cursor.setPosition(textFragment.position(), QTextCursor::MoveAnchor); cursor.setPosition(textFragment.position(), QTextCursor::MoveAnchor);
@ -209,3 +211,30 @@ void Converter::convertImages(const QTextBlock &parent, const QDir &dir, QTextDo
} }
} }
} }
void Converter::setImageSize(QTextImageFormat &format, const qreal specifiedWidth, const qreal specifiedHeight, const qreal originalWidth, const qreal originalHeight)
{
qreal width = 0;
qreal height = 0;
const bool hasSpecifiedSize = specifiedHeight > 0 || specifiedWidth > 0;
if (hasSpecifiedSize) {
width = specifiedWidth;
height = specifiedHeight;
if (width == 0 && originalHeight > 0) {
width = originalWidth * height / originalHeight;
} else if (height == 0 && originalWidth > 0) {
height = originalHeight * width / originalWidth;
}
} else {
width = originalWidth;
height = originalHeight;
}
if (width > CONTENT_WIDTH) {
height = height * CONTENT_WIDTH / width;
width = CONTENT_WIDTH;
}
format.setWidth(width);
format.setHeight(height);
}

@ -32,6 +32,15 @@ public:
void convertAgain(); void convertAgain();
void setFancyPantsEnabled(bool b)
{
m_isFancyPantsEnabled = b;
}
bool isFancyPantsEnabled() const
{
return m_isFancyPantsEnabled;
}
QTextDocument *convertOpenFile(); QTextDocument *convertOpenFile();
private: private:
@ -39,9 +48,11 @@ private:
void extractLinks(const QTextBlock &parent, QHash<QString, QTextFragment> &internalLinks, QHash<QString, QTextBlock> &documentAnchors); void extractLinks(const QTextBlock &parent, QHash<QString, QTextFragment> &internalLinks, QHash<QString, QTextBlock> &documentAnchors);
void convertImages(QTextFrame *parent, const QDir &dir, QTextDocument *textDocument); void convertImages(QTextFrame *parent, const QDir &dir, QTextDocument *textDocument);
void convertImages(const QTextBlock &parent, const QDir &dir, QTextDocument *textDocument); void convertImages(const QTextBlock &parent, const QDir &dir, QTextDocument *textDocument);
void setImageSize(QTextImageFormat &format, const qreal specifiedWidth, const qreal specifiedHeight, const qreal originalWidth, const qreal originalHeight);
FILE *m_markdownFile; FILE *m_markdownFile;
QDir m_fileDir; QDir m_fileDir;
bool m_isFancyPantsEnabled;
}; };
} }

@ -20,27 +20,27 @@
OKULAR_EXPORT_PLUGIN(MarkdownGenerator, "libokularGenerator_md.json") OKULAR_EXPORT_PLUGIN(MarkdownGenerator, "libokularGenerator_md.json")
bool MarkdownGenerator::s_isFancyPantsEnabled = true;
bool MarkdownGenerator::s_wasFancyPantsEnabled = true;
MarkdownGenerator::MarkdownGenerator(QObject *parent, const QVariantList &args) MarkdownGenerator::MarkdownGenerator(QObject *parent, const QVariantList &args)
: Okular::TextDocumentGenerator(new Markdown::Converter, QStringLiteral("okular_markdown_generator_settings"), parent, args) : Okular::TextDocumentGenerator(new Markdown::Converter, QStringLiteral("okular_markdown_generator_settings"), parent, args)
{ {
Okular::TextDocumentSettings *mdSettings = generalSettings(); Okular::TextDocumentSettings *mdSettings = generalSettings();
mdSettings->addItemBool(QStringLiteral("SmartyPants"), s_isFancyPantsEnabled, true); mdSettings->addItemBool(QStringLiteral("SmartyPants"), m_isFancyPantsConfigEnabled, true);
mdSettings->load(); mdSettings->load();
s_wasFancyPantsEnabled = s_isFancyPantsEnabled; m_wasFancyPantsConfigEnabled = m_isFancyPantsConfigEnabled;
Markdown::Converter *c = static_cast<Markdown::Converter *>(converter());
c->setFancyPantsEnabled(m_isFancyPantsConfigEnabled);
} }
bool MarkdownGenerator::reparseConfig() bool MarkdownGenerator::reparseConfig()
{ {
const bool textDocumentGeneratorChangedConfig = Okular::TextDocumentGenerator::reparseConfig(); const bool textDocumentGeneratorChangedConfig = Okular::TextDocumentGenerator::reparseConfig();
if (s_wasFancyPantsEnabled != s_isFancyPantsEnabled) { if (m_wasFancyPantsConfigEnabled != m_isFancyPantsConfigEnabled) {
s_wasFancyPantsEnabled = s_isFancyPantsEnabled; m_wasFancyPantsConfigEnabled = m_isFancyPantsConfigEnabled;
Markdown::Converter *c = static_cast<Markdown::Converter *>(converter()); Markdown::Converter *c = static_cast<Markdown::Converter *>(converter());
c->setFancyPantsEnabled(m_isFancyPantsConfigEnabled);
c->convertAgain(); c->convertAgain();
setTextDocument(c->document()); setTextDocument(c->document());

@ -24,14 +24,9 @@ public:
bool reparseConfig() override; bool reparseConfig() override;
void addPages(KConfigDialog *dlg) override; void addPages(KConfigDialog *dlg) override;
static bool isFancyPantsEnabled()
{
return s_isFancyPantsEnabled;
}
private: private:
static bool s_isFancyPantsEnabled; bool m_isFancyPantsConfigEnabled = true;
static bool s_wasFancyPantsEnabled; bool m_wasFancyPantsConfigEnabled = true;
}; };
#endif #endif

Loading…
Cancel
Save