Summary: Adds support for Markdown documents BUG: 360603 Test Plan: Open a Markdown (.md) document in Okular Reviewers: #okular, aacid Reviewed By: #okular, aacid Subscribers: michaelweghorn, aacid Tags: #okular Differential Revision: https://phabricator.kde.org/D7382remotes/origin/Applications/17.12
parent
37de97a030
commit
4931385777
15 changed files with 452 additions and 0 deletions
@ -0,0 +1,36 @@ |
||||
# - Find Discount |
||||
# Find the discount markdown library. |
||||
# |
||||
# This module defines |
||||
# discount_FOUND - whether the discount library was found |
||||
# discount_LIBRARIES - the discount library |
||||
# discount_INCLUDE_DIR - the include path of the discount library |
||||
|
||||
# Copyright (c) 2017, Julian Wolff, <wolff@julianwolff.de> |
||||
# |
||||
# Redistribution and use is allowed according to the terms of the BSD license. |
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file. |
||||
|
||||
|
||||
if (discount_INCLUDE_DIR AND discount_LIBRARIES) |
||||
|
||||
# Already in cache |
||||
set (discount_FOUND TRUE) |
||||
|
||||
else (discount_INCLUDE_DIR AND discount_LIBRARIES) |
||||
|
||||
find_library (discount_LIBRARIES |
||||
NAMES markdown libmarkdown |
||||
) |
||||
|
||||
find_path (discount_INCLUDE_DIR |
||||
NAMES mkdio.h |
||||
) |
||||
|
||||
include (FindPackageHandleStandardArgs) |
||||
find_package_handle_standard_args (discount DEFAULT_MSG discount_LIBRARIES discount_INCLUDE_DIR) |
||||
|
||||
endif (discount_INCLUDE_DIR AND discount_LIBRARIES) |
||||
|
||||
mark_as_advanced(discount_INCLUDE_DIR discount_LIBRARIES) |
||||
|
||||
@ -0,0 +1,22 @@ |
||||
include_directories( |
||||
${CMAKE_CURRENT_SOURCE_DIR}/../.. |
||||
) |
||||
|
||||
|
||||
########### next target ############### |
||||
|
||||
set(okularGenerator_md_PART_SRCS |
||||
converter.cpp |
||||
generator_md.cpp |
||||
) |
||||
|
||||
|
||||
okular_add_generator(okularGenerator_md ${okularGenerator_md_PART_SRCS}) |
||||
|
||||
target_link_libraries(okularGenerator_md okularcore KF5::I18n KF5::KIOCore ${discount_LIBRARIES}) |
||||
|
||||
########### install files ############### |
||||
install( FILES okularMd.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) |
||||
install( PROGRAMS okularApplication_md.desktop org.kde.mobile.okular_md.desktop DESTINATION ${KDE_INSTALL_APPDIR} ) |
||||
install( FILES org.kde.okular-md.metainfo.xml DESTINATION ${KDE_INSTALL_METAINFODIR} ) |
||||
|
||||
@ -0,0 +1,2 @@ |
||||
#!/bin/sh |
||||
$XGETTEXT $(find . -name "*.cpp" -o -name "*.h") -o $podir/okular_markdown.pot |
||||
@ -0,0 +1,177 @@ |
||||
/***************************************************************************
|
||||
* Copyright (C) 2017 by Julian Wolff <wolff@julianwolff.de> * |
||||
* * |
||||
* 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 "converter.h" |
||||
|
||||
#include <KLocalizedString> |
||||
|
||||
#include <QTemporaryFile> |
||||
#include <QTextDocument> |
||||
#include <QTextStream> |
||||
#include <QTextFrame> |
||||
#include <QTextCursor> |
||||
|
||||
#include <core/action.h> |
||||
|
||||
#include "debug_md.h" |
||||
|
||||
extern "C" { |
||||
#include <mkdio.h> |
||||
} |
||||
|
||||
// older versions of discount might not have these flags.
|
||||
// defining them to 0 allows us to convert without them
|
||||
#ifndef MKD_FENCEDCODE |
||||
#define MKD_FENCEDCODE 0 |
||||
#endif |
||||
|
||||
#ifndef MKD_GITHUBTAGS |
||||
#define MKD_GITHUBTAGS 0 |
||||
#endif |
||||
|
||||
#ifndef MKD_AUTOLINK |
||||
#define MKD_AUTOLINK 0 |
||||
#endif |
||||
|
||||
using namespace Markdown; |
||||
|
||||
Converter::Converter() |
||||
: mTextDocument(nullptr) |
||||
{ |
||||
} |
||||
|
||||
Converter::~Converter() |
||||
{ |
||||
} |
||||
|
||||
QTextDocument* Converter::convert( const QString &fileName ) |
||||
{ |
||||
FILE *markdownFile = fopen( fileName.toLocal8Bit(), "rb" ); |
||||
mDir = QDir( fileName.left( fileName.lastIndexOf( '/' ) ) ); |
||||
|
||||
MMIOT *markdownHandle = mkd_in( markdownFile, 0 ); |
||||
|
||||
if ( !mkd_compile( markdownHandle, MKD_FENCEDCODE | MKD_GITHUBTAGS | MKD_AUTOLINK ) ) { |
||||
emit error( i18n( "Failed to compile markdown document!" ), -1 ); |
||||
return 0; |
||||
} |
||||
|
||||
char *htmlDocument; |
||||
int size = mkd_document( markdownHandle, &htmlDocument ); |
||||
|
||||
QString html = QString::fromUtf8( htmlDocument, size ); |
||||
|
||||
mTextDocument = new QTextDocument; |
||||
mTextDocument->setPageSize( QSizeF( 980, 1307 ) ); |
||||
mTextDocument->setHtml( html ); |
||||
|
||||
mkd_cleanup( markdownHandle ); |
||||
|
||||
QTextFrameFormat frameFormat; |
||||
frameFormat.setMargin( 45 ); |
||||
|
||||
QTextFrame *rootFrame = mTextDocument->rootFrame(); |
||||
rootFrame->setFrameFormat( frameFormat ); |
||||
|
||||
convertLinks(); |
||||
convertImages(); |
||||
|
||||
return mTextDocument; |
||||
} |
||||
|
||||
void Converter::convertLinks() |
||||
{ |
||||
convertLinks( mTextDocument->rootFrame() ); |
||||
} |
||||
|
||||
|
||||
void Converter::convertLinks(QTextFrame * parent) |
||||
{ |
||||
for ( QTextFrame::iterator it = parent->begin(); !it.atEnd(); ++it ) { |
||||
QTextFrame *textFrame = it.currentFrame(); |
||||
QTextBlock textBlock = it.currentBlock(); |
||||
|
||||
if ( textFrame ) { |
||||
convertLinks(textFrame); |
||||
} |
||||
else if ( textBlock.isValid() ) { |
||||
convertLinks(textBlock); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void Converter::convertLinks(QTextBlock & parent) |
||||
{ |
||||
for ( QTextBlock::iterator it = parent.begin(); !it.atEnd(); ++it ) { |
||||
QTextFragment textFragment = it.fragment(); |
||||
if ( textFragment.isValid() ) { |
||||
QTextCharFormat textCharFormat = textFragment.charFormat(); |
||||
if ( textCharFormat.isAnchor() ) { |
||||
Okular::BrowseAction *action = new Okular::BrowseAction( QUrl( textCharFormat.anchorHref() ) ); |
||||
emit addAction( action, textFragment.position(), textFragment.position()+textFragment.length() ); |
||||
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
void Converter::convertImages() |
||||
{ |
||||
convertImages( mTextDocument->rootFrame() ); |
||||
} |
||||
|
||||
void Converter::convertImages(QTextFrame * parent) |
||||
{ |
||||
for ( QTextFrame::iterator it = parent->begin(); !it.atEnd(); ++it ) { |
||||
QTextFrame *textFrame = it.currentFrame(); |
||||
QTextBlock textBlock = it.currentBlock(); |
||||
|
||||
if ( textFrame ) { |
||||
convertImages(textFrame); |
||||
} |
||||
else if ( textBlock.isValid() ) { |
||||
convertImages(textBlock); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void Converter::convertImages(QTextBlock & parent) |
||||
{ |
||||
for ( QTextBlock::iterator it = parent.begin(); !it.atEnd(); ++it ) { |
||||
QTextFragment textFragment = it.fragment(); |
||||
if ( textFragment.isValid() ) { |
||||
QTextCharFormat textCharFormat = textFragment.charFormat(); |
||||
if( textCharFormat.isImageFormat() ) { |
||||
|
||||
//TODO: Show images from http URIs
|
||||
|
||||
QTextImageFormat format; |
||||
|
||||
format.setName( QDir::cleanPath( mDir.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() ); |
||||
} |
||||
|
||||
QTextCursor cursor( mTextDocument ); |
||||
cursor.setPosition( textFragment.position(), QTextCursor::MoveAnchor ); |
||||
cursor.setPosition( textFragment.position() + textFragment.length(), QTextCursor::KeepAnchor ); |
||||
cursor.removeSelectedText(); |
||||
cursor.insertImage( format ); |
||||
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,47 @@ |
||||
/***************************************************************************
|
||||
* Copyright (C) 2017 by Julian Wolff <wolff@julianwolff.de> * |
||||
* * |
||||
* 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. * |
||||
***************************************************************************/ |
||||
|
||||
#ifndef MARKDOWN_CONVERTER_H |
||||
#define MARKDOWN_CONVERTER_H |
||||
|
||||
#include <QDir> |
||||
|
||||
#include <core/textdocumentgenerator.h> |
||||
|
||||
class QTextCursor; |
||||
class QTextFrame; |
||||
|
||||
namespace Markdown { |
||||
|
||||
class Converter : public Okular::TextDocumentConverter |
||||
{ |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
Converter(); |
||||
~Converter(); |
||||
|
||||
QTextDocument *convert( const QString &fileName ) override; |
||||
|
||||
private: |
||||
void convertLinks(); |
||||
void convertImages(); |
||||
|
||||
void convertLinks(QTextFrame *parent); |
||||
void convertLinks(QTextBlock& parent); |
||||
void convertImages(QTextFrame *parent); |
||||
void convertImages(QTextBlock& parent); |
||||
|
||||
QTextDocument *mTextDocument; |
||||
QDir mDir; |
||||
}; |
||||
|
||||
} |
||||
|
||||
#endif |
||||
@ -0,0 +1,19 @@ |
||||
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2006 by Luigi Toscano <luigi.toscano@tiscali.it> * |
||||
* Copyright (C) 2014 by Frederik Gladhorn <gladhorn@kde.org> * |
||||
* * |
||||
* 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. * |
||||
***************************************************************************/ |
||||
|
||||
#ifndef OKULAR_MD_DEBUG_P_H |
||||
#define OKULAR_MD_DEBUG_P_H |
||||
|
||||
#include <QtCore/qloggingcategory.h> |
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(OkularMdDebug) |
||||
|
||||
#endif |
||||
@ -0,0 +1,37 @@ |
||||
/***************************************************************************
|
||||
* Copyright (C) 2017 by Julian Wolff <wolff@julianwolff.de> * |
||||
* * |
||||
* 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 "generator_md.h" |
||||
|
||||
#include "converter.h" |
||||
#include "debug_md.h" |
||||
|
||||
#include <KAboutData> |
||||
#include <KLocalizedString> |
||||
#include <kconfigdialog.h> |
||||
|
||||
OKULAR_EXPORT_PLUGIN(MarkdownGenerator, "libokularGenerator_md.json") |
||||
|
||||
MarkdownGenerator::MarkdownGenerator( QObject *parent, const QVariantList &args ) |
||||
: Okular::TextDocumentGenerator( new Markdown::Converter, QStringLiteral("okular_markdown_generator_settings"), parent, args ) |
||||
{ |
||||
} |
||||
|
||||
void MarkdownGenerator::addPages( KConfigDialog* dlg ) |
||||
{ |
||||
Okular::TextDocumentSettingsWidget *widget = new Okular::TextDocumentSettingsWidget(); |
||||
|
||||
dlg->addPage( widget, generalSettings(), i18n("Markdown"), QStringLiteral("text-markdown"), i18n("Markdown Backend Configuration") ); |
||||
} |
||||
|
||||
|
||||
Q_LOGGING_CATEGORY(OkularMdDebug, "org.kde.okular.generators.md", QtWarningMsg) |
||||
|
||||
#include "generator_md.moc" |
||||
|
||||
@ -0,0 +1,27 @@ |
||||
/***************************************************************************
|
||||
* Copyright (C) 2017 by Julian Wolff <wolff@julianwolff.de> * |
||||
* * |
||||
* 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. * |
||||
***************************************************************************/ |
||||
|
||||
#ifndef _OKULAR_GENERATOR_MD_H_ |
||||
#define _OKULAR_GENERATOR_MD_H_ |
||||
|
||||
#include <core/textdocumentgenerator.h> |
||||
|
||||
class MarkdownGenerator : public Okular::TextDocumentGenerator |
||||
{ |
||||
Q_OBJECT |
||||
Q_INTERFACES( Okular::Generator ) |
||||
|
||||
public: |
||||
MarkdownGenerator( QObject *parent, const QVariantList &args ); |
||||
|
||||
// [INHERITED] reparse configuration
|
||||
void addPages( KConfigDialog* dlg ) override; |
||||
}; |
||||
|
||||
#endif |
||||
@ -0,0 +1,26 @@ |
||||
{ |
||||
"KPlugin": { |
||||
"Authors": [ |
||||
{ |
||||
"Email": "wolff@julianwolff.de", |
||||
"Name": "Julian Wolff", |
||||
"Name[x-test]": "xxJulian Wolffxx" |
||||
} |
||||
], |
||||
"Copyright": "© 2017 Julian Wolff", |
||||
"Id": "okular_markdown", |
||||
"License": "GPL", |
||||
"MimeTypes": [ |
||||
"text/markdown", |
||||
"text/x-markdown" |
||||
], |
||||
"Name": "Markdown Backend", |
||||
"ServiceTypes": [ |
||||
"okular/Generator" |
||||
], |
||||
"Version": "0.1.0" |
||||
}, |
||||
"X-KDE-Priority": 1, |
||||
"X-KDE-okularAPIVersion": 1, |
||||
"X-KDE-okularHasInternalSettings": true |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
[Desktop Entry] |
||||
MimeType=text/markdown; |
||||
Terminal=false |
||||
Name=Okular |
||||
GenericName=Document Viewer |
||||
Comment=Universal document viewer |
||||
Exec=okular %U |
||||
Icon=okular |
||||
Type=Application |
||||
InitialPreference=7 |
||||
Categories=Qt;KDE;Graphics;Viewer; |
||||
NoDisplay=true |
||||
X-KDE-Keywords=Markdown |
||||
@ -0,0 +1,7 @@ |
||||
[Desktop Entry] |
||||
Icon=okular |
||||
Name=Okular |
||||
X-KDE-ServiceTypes=KParts/ReadOnlyPart |
||||
X-KDE-Library=okularpart |
||||
Type=Service |
||||
MimeType=text/markdown; |
||||
@ -0,0 +1,14 @@ |
||||
[Desktop Entry] |
||||
MimeType=text/markdown; |
||||
Name=Reader |
||||
GenericName=Document viewer |
||||
Comment=Viewer for various types of documents |
||||
TryExec=kpackagelauncherqml -a org.kde.mobile.okular |
||||
Exec=kpackagelauncherqml -a org.kde.mobile.okular %u |
||||
Terminal=false |
||||
Icon=okular |
||||
Type=Application |
||||
Categories=Qt;KDE;Graphics;Office;Viewer; |
||||
InitialPreference=2 |
||||
NoDisplay=true |
||||
X-KDE-Keywords=Markdown |
||||
@ -0,0 +1,13 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<component type="addon"> |
||||
<id>org.kde.okular-md</id> |
||||
<extends>org.kde.okular.desktop</extends> |
||||
<metadata_license>CC0-1.0</metadata_license> |
||||
<project_license>GPL-2.0+ and GFDL-1.3</project_license> |
||||
<name>Markdown</name> |
||||
<summary>Adds support for reading Markdown documents</summary> |
||||
<mimetypes> |
||||
<mimetype>text/markdown</mimetype> |
||||
</mimetypes> |
||||
<url type="homepage">https://okular.kde.org</url> |
||||
</component> |
||||
Loading…
Reference in new issue