diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index 7ce898dda..334b8eadf 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -106,3 +106,12 @@ ecm_add_test(signatureformtest.cpp TEST_NAME "signatureformtest" 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() diff --git a/autotests/data/1500x300.png b/autotests/data/1500x300.png new file mode 100644 index 000000000..d6e26b995 Binary files /dev/null and b/autotests/data/1500x300.png differ diff --git a/autotests/data/300x1500.png b/autotests/data/300x1500.png new file mode 100644 index 000000000..e10cda14f Binary files /dev/null and b/autotests/data/300x1500.png differ diff --git a/autotests/data/imageSizes.md b/autotests/data/imageSizes.md new file mode 100644 index 000000000..abda7fad2 --- /dev/null +++ b/autotests/data/imageSizes.md @@ -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`: + +potato + +* both specified, using html `4200x4200`: + +potato + +## 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`: + +1500x300 + +* both specified, using html `4200x4200`: + +1500x300 + +## 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`: + +300x1500 diff --git a/autotests/markdowntest.cpp b/autotests/markdowntest.cpp new file mode 100644 index 000000000..57128a981 --- /dev/null +++ b/autotests/markdowntest.cpp @@ -0,0 +1,128 @@ +/*************************************************************************** + * Copyright (C) 2020 by Markus Brenneis * + * * + * 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 + +#include "../settings_core.h" +#include "core/document.h" +#include "generators/markdown/converter.h" +#include +#include +#include + +class MarkdownTest : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + + void testFancyPantsEnabled(); + void testFancyPantsDisabled(); + void testImageSizes(); + +private: + void findImages(QTextFrame *parent, QVector &images); + void findImages(const QTextBlock &parent, QVector &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 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 &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 &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" diff --git a/generators/markdown/converter.cpp b/generators/markdown/converter.cpp index 715842ee0..18a1a9ea7 100644 --- a/generators/markdown/converter.cpp +++ b/generators/markdown/converter.cpp @@ -9,8 +9,6 @@ #include "converter.h" -#include "generator_md.h" - #include #include @@ -41,10 +39,16 @@ extern "C" { #define MKD_AUTOLINK 0 #endif +#define PAGE_WIDTH 980 +#define PAGE_HEIGHT 1307 +#define PAGE_MARGIN 45 +#define CONTENT_WIDTH (PAGE_WIDTH - 2 * PAGE_MARGIN) + using namespace Markdown; Converter::Converter() : m_markdownFile(nullptr) + , m_isFancyPantsEnabled(true) { } @@ -96,7 +100,7 @@ QTextDocument *Converter::convertOpenFile() MMIOT *markdownHandle = mkd_in(m_markdownFile, 0); int flags = MKD_FENCEDCODE | MKD_GITHUBTAGS | MKD_AUTOLINK | MKD_TOC | MKD_IDANCHOR; - if (!MarkdownGenerator::isFancyPantsEnabled()) + if (!m_isFancyPantsEnabled) flags |= MKD_NOPANTS; if (!mkd_compile(markdownHandle, flags)) { emit error(i18n("Failed to compile the Markdown document."), -1); @@ -109,14 +113,15 @@ QTextDocument *Converter::convertOpenFile() const QString html = QString::fromUtf8(htmlDocument, size); QTextDocument *textDocument = new QTextDocument; - textDocument->setPageSize(QSizeF(980, 1307)); + textDocument->setPageSize(QSizeF(PAGE_WIDTH, PAGE_HEIGHT)); textDocument->setHtml(html); - textDocument->setDefaultFont(generator()->generalSettings()->font()); + if (generator()) + textDocument->setDefaultFont(generator()->generalSettings()->font()); mkd_cleanup(markdownHandle); QTextFrameFormat frameFormat; - frameFormat.setMargin(45); + frameFormat.setMargin(PAGE_MARGIN); QTextFrame *rootFrame = textDocument->rootFrame(); rootFrame->setFrameFormat(frameFormat); @@ -189,16 +194,13 @@ void Converter::convertImages(const QTextBlock &parent, const QDir &dir, QTextDo QTextImageFormat format; + const qreal specifiedHeight = textCharFormat.toImageFormat().height(); + const qreal specifiedWidth = textCharFormat.toImageFormat().width(); + format.setName(QDir::cleanPath(dir.absoluteFilePath(textCharFormat.toImageFormat().name()))); const QImage img = QImage(format.name()); - if (img.width() > 890) { - format.setWidth(890); - format.setHeight(img.height() * 890. / img.width()); - } else { - format.setWidth(img.width()); - format.setHeight(img.height()); - } + setImageSize(format, specifiedWidth, specifiedHeight, img.width(), img.height()); QTextCursor cursor(textDocument); 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); +} diff --git a/generators/markdown/converter.h b/generators/markdown/converter.h index 6bfb21279..a4e1e2dd2 100644 --- a/generators/markdown/converter.h +++ b/generators/markdown/converter.h @@ -32,6 +32,15 @@ public: void convertAgain(); + void setFancyPantsEnabled(bool b) + { + m_isFancyPantsEnabled = b; + } + bool isFancyPantsEnabled() const + { + return m_isFancyPantsEnabled; + } + QTextDocument *convertOpenFile(); private: @@ -39,9 +48,11 @@ private: void extractLinks(const QTextBlock &parent, QHash &internalLinks, QHash &documentAnchors); void convertImages(QTextFrame *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; QDir m_fileDir; + bool m_isFancyPantsEnabled; }; } diff --git a/generators/markdown/generator_md.cpp b/generators/markdown/generator_md.cpp index e7dce4e6c..0431bbe15 100644 --- a/generators/markdown/generator_md.cpp +++ b/generators/markdown/generator_md.cpp @@ -20,27 +20,27 @@ OKULAR_EXPORT_PLUGIN(MarkdownGenerator, "libokularGenerator_md.json") -bool MarkdownGenerator::s_isFancyPantsEnabled = true; -bool MarkdownGenerator::s_wasFancyPantsEnabled = true; - MarkdownGenerator::MarkdownGenerator(QObject *parent, const QVariantList &args) : Okular::TextDocumentGenerator(new Markdown::Converter, QStringLiteral("okular_markdown_generator_settings"), parent, args) { Okular::TextDocumentSettings *mdSettings = generalSettings(); - mdSettings->addItemBool(QStringLiteral("SmartyPants"), s_isFancyPantsEnabled, true); + mdSettings->addItemBool(QStringLiteral("SmartyPants"), m_isFancyPantsConfigEnabled, true); mdSettings->load(); - s_wasFancyPantsEnabled = s_isFancyPantsEnabled; + m_wasFancyPantsConfigEnabled = m_isFancyPantsConfigEnabled; + Markdown::Converter *c = static_cast(converter()); + c->setFancyPantsEnabled(m_isFancyPantsConfigEnabled); } bool MarkdownGenerator::reparseConfig() { const bool textDocumentGeneratorChangedConfig = Okular::TextDocumentGenerator::reparseConfig(); - if (s_wasFancyPantsEnabled != s_isFancyPantsEnabled) { - s_wasFancyPantsEnabled = s_isFancyPantsEnabled; + if (m_wasFancyPantsConfigEnabled != m_isFancyPantsConfigEnabled) { + m_wasFancyPantsConfigEnabled = m_isFancyPantsConfigEnabled; Markdown::Converter *c = static_cast(converter()); + c->setFancyPantsEnabled(m_isFancyPantsConfigEnabled); c->convertAgain(); setTextDocument(c->document()); diff --git a/generators/markdown/generator_md.h b/generators/markdown/generator_md.h index 4f64cbc6f..e9a1361a1 100644 --- a/generators/markdown/generator_md.h +++ b/generators/markdown/generator_md.h @@ -24,14 +24,9 @@ public: bool reparseConfig() override; void addPages(KConfigDialog *dlg) override; - static bool isFancyPantsEnabled() - { - return s_isFancyPantsEnabled; - } - private: - static bool s_isFancyPantsEnabled; - static bool s_wasFancyPantsEnabled; + bool m_isFancyPantsConfigEnabled = true; + bool m_wasFancyPantsConfigEnabled = true; }; #endif