MdEditor: support downloading images to local when Parse&Paste

This commit is contained in:
Le Tan 2018-09-30 23:00:58 +08:00
parent a67584f8bb
commit 5cc8d6c8f1
9 changed files with 196 additions and 29 deletions

View File

@ -296,6 +296,9 @@ vim_leader_key=" "
; Enable tab highlight
enable_tab_highlight=false
; Download images in parsed HTML text
parse_paste_local_image=true
[export]
; Path of the wkhtmltopdf tool
wkhtmltopdf=wkhtmltopdf

View File

@ -42,10 +42,12 @@ extern VConfigManager *g_config;
QVector<QPair<QString, QString>> VUtils::s_availableLanguages;
const QString VUtils::c_imageLinkRegExp = QString("\\!\\[([^\\]]*)\\]\\(\\s*([^\\)\"'\\s]+)\\s*"
"((\"[^\"\\)\\n]*\")|('[^'\\)\\n]*'))?\\s*"
"(=(\\d*)x(\\d*))?\\s*"
"\\)");
const QString VUtils::c_imageLinkRegExp = QString("\\!\\[([^\\]]*)\\]"
"\\(\\s*"
"([^\\)\"'\\s]+)"
"(\\s*(\"[^\"\\)\\n]*\")|('[^'\\)\\n]*'))?"
"(\\s*=(\\d*)x(\\d*))?"
"\\s*\\)");
const QString VUtils::c_imageTitleRegExp = QString("[^\\[\\]]*");

View File

@ -133,6 +133,9 @@ public:
static QVector<ImageLink> fetchImagesFromMarkdownFile(VFile *p_file,
ImageLink::ImageLinkType p_type = ImageLink::All);
// Use PegParser to parse @p_content to get all image link regions.
static QVector<VElementRegion> fetchImageRegionsUsingParser(const QString &p_content);
// Return the absolute path of @p_url according to @p_basePath.
static QString linkUrlToPath(const QString &p_basePath, const QString &p_url);
@ -389,7 +392,7 @@ public:
// 3. Image Optional Title with double quotes or quotes;
// 4. Unused;
// 5. Unused;
// 6. Unused;
// 6. Width and height text;
// 7. Width;
// 8. Height;
static const QString c_imageLinkRegExp;
@ -440,9 +443,6 @@ private:
static void initAvailableLanguage();
// Use PegParser to parse @p_content to get all image link regions.
static QVector<VElementRegion> fetchImageRegionsUsingParser(const QString &p_content);
// Delete file/directory specified by @p_path by moving it to the recycle bin
// folder @p_recycleBinFolderPath.
static bool deleteFile(const QString &p_recycleBinFolderPath,

View File

@ -356,6 +356,7 @@ void VConfigManager::initEditorConfigs()
m_enableTabHighlight = getConfigFromSettings("editor",
"enable_tab_highlight").toBool();
m_parsePasteLocalImage = getConfigFromSettings("editor", "parse_paste_local_image").toBool();
}
void VConfigManager::initSettings()

View File

@ -575,6 +575,8 @@ public:
QList<int> getKeyboardLayoutMappingKeys() const;
bool getParsePasteLocalImage() const;
private:
// Look up a config from user and default settings.
QVariant getConfigFromSettings(const QString &section, const QString &key) const;
@ -1027,6 +1029,9 @@ private:
// Whether insert new note in front.
bool m_insertNewNoteInFront;
// Whether download image from parse and paste.
bool m_parsePasteLocalImage;
// The name of the config file in each directory.
static const QString c_dirConfigFile;
@ -2684,4 +2689,9 @@ inline QList<int> VConfigManager::getKeyboardLayoutMappingKeys() const
return keys;
}
inline bool VConfigManager::getParsePasteLocalImage() const
{
return m_parsePasteLocalImage;
}
#endif // VCONFIGMANAGER_H

View File

@ -101,20 +101,44 @@ void VMdEditOperations::insertImageFromQImage(const QString &title,
mdEditor->imageInserted(filePath, url);
}
void VMdEditOperations::insertImageFromPath(const QString &title, const QString &path,
const QString &folderInLink, const QString &oriImagePath)
void VMdEditOperations::insertImageFromPath(const QString &p_title,
const QString &p_folderPath,
const QString &p_folderInLink,
const QString &p_srcImagePath)
{
QString fileName = VUtils::generateImageFileName(path, title, QFileInfo(oriImagePath).suffix());
QString filePath = QDir(path).filePath(fileName);
insertImageFromPath(p_title,
p_folderPath,
p_folderInLink,
p_srcImagePath,
true,
QString(),
QString());
}
void VMdEditOperations::insertImageFromPath(const QString &p_title,
const QString &p_folderPath,
const QString &p_folderInLink,
const QString &p_srcImagePath,
bool p_insertText,
QString &p_destImagePath,
QString &p_urlInLink)
{
p_destImagePath.clear();
p_urlInLink.clear();
QString fileName = VUtils::generateImageFileName(p_folderPath,
p_title,
QFileInfo(p_srcImagePath).suffix());
QString filePath = QDir(p_folderPath).filePath(fileName);
V_ASSERT(!QFile(filePath).exists());
QString errStr;
bool ret = VUtils::makePath(path);
bool ret = VUtils::makePath(p_folderPath);
if (!ret) {
errStr = tr("Fail to create image folder <span style=\"%1\">%2</span>.")
.arg(g_config->c_dataTextStyle).arg(path);
.arg(g_config->c_dataTextStyle).arg(p_folderPath);
} else {
ret = QFile::copy(oriImagePath, filePath);
ret = QFile::copy(p_srcImagePath, filePath);
if (!ret) {
errStr = tr("Fail to copy image <span style=\"%1\">%2</span>.")
.arg(g_config->c_dataTextStyle).arg(filePath);
@ -122,8 +146,10 @@ void VMdEditOperations::insertImageFromPath(const QString &title, const QString
}
if (!ret) {
VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
tr("Fail to insert image <span style=\"%1\">%2</span>.").arg(g_config->c_dataTextStyle).arg(title),
VUtils::showMessage(QMessageBox::Warning,
tr("Warning"),
tr("Fail to insert image <span style=\"%1\">%2</span>.")
.arg(g_config->c_dataTextStyle).arg(p_title),
errStr,
QMessageBox::Ok,
QMessageBox::Ok,
@ -131,15 +157,19 @@ void VMdEditOperations::insertImageFromPath(const QString &title, const QString
return;
}
QString url = QString("%1/%2").arg(folderInLink).arg(fileName);
QString md = QString("![%1](%2)").arg(title).arg(url);
insertText(md);
p_urlInLink = QString("%1/%2").arg(p_folderInLink).arg(fileName);
p_destImagePath = filePath;
qDebug() << "insert image" << title << filePath;
if (p_insertText) {
QString md = QString("![%1](%2)").arg(p_title).arg(p_urlInLink);
insertText(md);
}
VMdEditor *mdEditor = dynamic_cast<VMdEditor *>(m_editor);
qDebug() << "insert image" << p_title << filePath;
VMdEditor *mdEditor = static_cast<VMdEditor *>(m_editor);
Q_ASSERT(mdEditor);
mdEditor->imageInserted(filePath, url);
mdEditor->imageInserted(filePath, p_urlInLink);
}
bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl)
@ -177,7 +207,7 @@ bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl)
VDownloader *downloader = new VDownloader(&dialog);
connect(downloader, &VDownloader::downloadFinished,
&dialog, &VInsertImageDialog::imageDownloaded);
downloader->download(imageUrl.toString());
downloader->download(imageUrl);
}
if (dialog.exec() == QDialog::Accepted) {
if (isLocal) {

View File

@ -34,11 +34,22 @@ public:
bool insertImageLink(const QString &p_linkText, const QString &p_linkUrl);
// @p_urlInLink and @p_destImagePath will be empty on failure.
void insertImageFromPath(const QString &p_title,
const QString &p_folderPath,
const QString &p_folderInLink,
const QString &p_srcImagePath,
bool p_insertText,
QString &p_destImagePath,
QString &p_urlInLink);
private:
// Insert image from @oriImagePath as @path.
// @folderInLink: the folder part in the image link.
void insertImageFromPath(const QString &title, const QString &path,
const QString &folderInLink, const QString &oriImagePath);
// Insert image from @p_srcImagePath as to @p_folderPath.
// @p_folderInLink: the folder part in the image link.
void insertImageFromPath(const QString &p_title,
const QString &p_folderPath,
const QString &p_folderInLink,
const QString &p_srcImagePath);
// @title: title of the inserted image;
// @path: the image folder path to insert the image in;

View File

@ -6,6 +6,8 @@
#include <QScopedPointer>
#include <QClipboard>
#include <QMimeDatabase>
#include <QTemporaryFile>
#include <QProgressDialog>
#include "vdocument.h"
#include "utils/veditutils.h"
@ -29,6 +31,7 @@
#include "vplantumlhelper.h"
#include "vgraphvizhelper.h"
#include "vmdtab.h"
#include "vdownloader.h"
extern VWebUtils *g_webUtils;
@ -1169,7 +1172,15 @@ void VMdEditor::htmlToTextFinished(int p_id, int p_timeStamp, const QString &p_t
{
Q_UNUSED(p_id);
if (m_copyTimeStamp == p_timeStamp && !p_text.isEmpty()) {
m_editOps->insertText(p_text);
emit m_object->statusMessage(tr("Inserting parsed Markdown text"));
QString text(p_text);
if (g_config->getParsePasteLocalImage()) {
// May take long time.
replaceTextWithLocalImages(text);
}
m_editOps->insertText(text);
emit m_object->statusMessage(tr("Parsed Markdown text inserted"));
}
}
@ -1991,3 +2002,100 @@ bool VMdEditor::processTextFromMimeData(const QMimeData *p_source)
Q_ASSERT(p_source->hasText());
return false;
}
void VMdEditor::replaceTextWithLocalImages(QString &p_text)
{
QVector<VElementRegion> regs = VUtils::fetchImageRegionsUsingParser(p_text);
if (regs.isEmpty()) {
return;
}
// Sort it in ascending order.
std::sort(regs.begin(), regs.end());
QProgressDialog proDlg(tr("Fetching images to local..."),
tr("Abort"),
0,
regs.size(),
this);
proDlg.setWindowModality(Qt::WindowModal);
proDlg.setWindowTitle(tr("Fetching Images To Local"));
QRegExp regExp(VUtils::c_imageLinkRegExp);
for (int i = regs.size() - 1; i >= 0; --i) {
proDlg.setValue(regs.size() - 1 - i);
if (proDlg.wasCanceled()) {
break;
}
const VElementRegion &reg = regs[i];
QString linkText = p_text.mid(reg.m_startPos, reg.m_endPos - reg.m_startPos);
if (regExp.indexIn(linkText) == -1) {
continue;
}
QString imageTitle = regExp.cap(1).trimmed();
QString imageUrl = regExp.cap(2).trimmed();
proDlg.setLabelText(tr("Fetching image: %1").arg(imageUrl));
QString destImagePath, urlInLink;
// Only handle absolute file path or network path.
QString srcImagePath;
QFileInfo info(imageUrl);
// For network image.
QString suffix = info.suffix();
QScopedPointer<QTemporaryFile> tmpFile;
if (info.exists() && info.isAbsolute()) {
// Absolute local path.
srcImagePath = info.absoluteFilePath();
} else {
// Network path.
QByteArray data = VDownloader::downloadSync(QUrl(imageUrl));
if (!data.isEmpty()) {
QString xx = suffix.isEmpty() ? "XXXXXX" : "XXXXXX.";
tmpFile.reset(new QTemporaryFile(QDir::tempPath()
+ QDir::separator()
+ xx
+ suffix));
if (tmpFile->open() && tmpFile->write(data) > -1) {
srcImagePath = tmpFile->fileName();
}
}
}
if (srcImagePath.isEmpty()) {
continue;
}
// Insert image without inserting text.
auto ops = static_cast<VMdEditOperations *>(m_editOps);
ops->insertImageFromPath(imageTitle,
m_file->fetchImageFolderPath(),
m_file->getImageFolderInLink(),
srcImagePath,
false,
destImagePath,
urlInLink);
if (urlInLink.isEmpty()) {
continue;
}
// Replace URL in link.
QString newLink = QString("![%1](%2%3%4)")
.arg(imageTitle)
.arg(urlInLink)
.arg(regExp.cap(3))
.arg(regExp.cap(6));
p_text.replace(reg.m_startPos,
reg.m_endPos - reg.m_startPos,
newLink);
qDebug() << "replace link" << linkText << "to" << newLink;
}
proDlg.setValue(regs.size());
}

View File

@ -315,6 +315,8 @@ private:
bool processTextFromMimeData(const QMimeData *p_source);
void replaceTextWithLocalImages(QString &p_text);
PegMarkdownHighlighter *m_pegHighlighter;
VCodeBlockHighlightHelper *m_cbHighlighter;