diff --git a/generators/markdown/converter.cpp b/generators/markdown/converter.cpp index 042193a0d..fa8771ea1 100644 --- a/generators/markdown/converter.cpp +++ b/generators/markdown/converter.cpp @@ -1,191 +1,214 @@ /*************************************************************************** * Copyright (C) 2017 by Julian Wolff * * * * 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 "generator_md.h" #include #include #include #include #include #include #include #include "debug_md.h" extern "C" { #include } // 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() : m_markdownFile( nullptr ) { } Converter::~Converter() { if ( m_markdownFile ) { fclose( m_markdownFile ); } } QTextDocument* Converter::convert( const QString &fileName ) { m_markdownFile = fopen( fileName.toLocal8Bit(), "rb" ); if ( !m_markdownFile ) { emit error( i18n( "Failed to open the document" ), -1 ); return nullptr; } m_fileDir = QDir( fileName.left( fileName.lastIndexOf( '/' ) ) ); QTextDocument *doc = convertOpenFile(); - extractLinks( doc->rootFrame() ); + QHash internalLinks; + QHash documentAnchors; + extractLinks( doc->rootFrame(), internalLinks, documentAnchors ); + + for (auto linkIt = internalLinks.constBegin(); linkIt != internalLinks.constEnd(); ++linkIt) { + auto anchorIt = documentAnchors.constFind(linkIt.key()); + if (anchorIt != documentAnchors.constEnd()) { + const Okular::DocumentViewport viewport = calculateViewport(doc, anchorIt.value()); + Okular::GotoAction *action = new Okular::GotoAction(QString(), viewport); + emit addAction(action, linkIt.value().position(), linkIt.value().position()+linkIt.value().length()); + } else { + qDebug() << "Could not find destination for" << linkIt.key(); + } + } + return doc; } void Converter::convertAgain() { setDocument( convertOpenFile() ); } QTextDocument *Converter::convertOpenFile() { rewind( m_markdownFile ); MMIOT *markdownHandle = mkd_in( m_markdownFile, 0 ); - int flags = MKD_FENCEDCODE | MKD_GITHUBTAGS | MKD_AUTOLINK; + int flags = MKD_FENCEDCODE | MKD_GITHUBTAGS | MKD_AUTOLINK | MKD_TOC | MKD_IDANCHOR; if (!MarkdownGenerator::isFancyPantsEnabled()) flags |= MKD_NOPANTS; if ( !mkd_compile( markdownHandle, flags ) ) { emit error( i18n( "Failed to compile the Markdown document." ), -1 ); return nullptr; } char *htmlDocument; const int size = mkd_document( markdownHandle, &htmlDocument ); const QString html = QString::fromUtf8( htmlDocument, size ); QTextDocument *textDocument = new QTextDocument; textDocument->setPageSize( QSizeF( 980, 1307 ) ); textDocument->setHtml( html ); textDocument->setDefaultFont( generator()->generalSettings()->font() ); - + mkd_cleanup( markdownHandle ); QTextFrameFormat frameFormat; frameFormat.setMargin( 45 ); QTextFrame *rootFrame = textDocument->rootFrame(); rootFrame->setFrameFormat( frameFormat ); convertImages( rootFrame, m_fileDir, textDocument ); return textDocument; } -void Converter::extractLinks(QTextFrame * parent) +void Converter::extractLinks(QTextFrame * parent, QHash &internalLinks, QHash &documentAnchors) { for ( QTextFrame::iterator it = parent->begin(); !it.atEnd(); ++it ) { QTextFrame *textFrame = it.currentFrame(); const QTextBlock textBlock = it.currentBlock(); if ( textFrame ) { - extractLinks(textFrame); + extractLinks(textFrame, internalLinks, documentAnchors); } else if ( textBlock.isValid() ) { - extractLinks(textBlock); + extractLinks(textBlock, internalLinks, documentAnchors); } } } -void Converter::extractLinks(const QTextBlock & parent) +void Converter::extractLinks(const QTextBlock & parent, QHash &internalLinks, QHash &documentAnchors) { for ( QTextBlock::iterator it = parent.begin(); !it.atEnd(); ++it ) { const QTextFragment textFragment = it.fragment(); if ( textFragment.isValid() ) { const 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() ); - + const QString href = textCharFormat.anchorHref(); + if ( href.startsWith( '#' ) ) { // It's an internal link, store it and we'll resolve it at the end + internalLinks.insert(href.mid(1), textFragment); + } else { + Okular::BrowseAction *action = new Okular::BrowseAction( QUrl( textCharFormat.anchorHref() ) ); + emit addAction( action, textFragment.position(), textFragment.position()+textFragment.length() ); + } + + const QStringList anchorNames = textCharFormat.anchorNames(); + for (const QString &anchorName : anchorNames) { + documentAnchors.insert(anchorName, parent); + } } } } } void Converter::convertImages(QTextFrame * parent, const QDir &dir, QTextDocument *textDocument) { for ( QTextFrame::iterator it = parent->begin(); !it.atEnd(); ++it ) { QTextFrame *textFrame = it.currentFrame(); const QTextBlock textBlock = it.currentBlock(); if ( textFrame ) { convertImages(textFrame, dir, textDocument); } else if ( textBlock.isValid() ) { convertImages(textBlock, dir, textDocument); } } } void Converter::convertImages(const QTextBlock & parent, const QDir &dir, QTextDocument *textDocument) { 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() ) { //TODO: Show images from http URIs QTextImageFormat format; 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() ); } QTextCursor cursor( textDocument ); cursor.setPosition( textFragment.position(), QTextCursor::MoveAnchor ); cursor.setPosition( textFragment.position() + textFragment.length(), QTextCursor::KeepAnchor ); cursor.removeSelectedText(); cursor.insertImage( format ); } } } } diff --git a/generators/markdown/converter.h b/generators/markdown/converter.h index 695f5cea7..c68cecd37 100644 --- a/generators/markdown/converter.h +++ b/generators/markdown/converter.h @@ -1,48 +1,49 @@ /*************************************************************************** * Copyright (C) 2017 by Julian Wolff * * * * 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 #include +#include class QTextBlock; class QTextFrame; namespace Markdown { class Converter : public Okular::TextDocumentConverter { Q_OBJECT public: Converter(); ~Converter() override; QTextDocument *convert( const QString &fileName ) override; void convertAgain(); QTextDocument *convertOpenFile(); private: - void extractLinks(QTextFrame *parent); - void extractLinks(const QTextBlock& parent); + void extractLinks(QTextFrame *parent, QHash &internalLinks, QHash &documentAnchors); + 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); FILE *m_markdownFile; QDir m_fileDir; }; } #endif