mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
MdEditor: support downloading images to local when Parse&Paste
This commit is contained in:
parent
a67584f8bb
commit
5cc8d6c8f1
@ -296,6 +296,9 @@ vim_leader_key=" "
|
|||||||
; Enable tab highlight
|
; Enable tab highlight
|
||||||
enable_tab_highlight=false
|
enable_tab_highlight=false
|
||||||
|
|
||||||
|
; Download images in parsed HTML text
|
||||||
|
parse_paste_local_image=true
|
||||||
|
|
||||||
[export]
|
[export]
|
||||||
; Path of the wkhtmltopdf tool
|
; Path of the wkhtmltopdf tool
|
||||||
wkhtmltopdf=wkhtmltopdf
|
wkhtmltopdf=wkhtmltopdf
|
||||||
|
@ -42,10 +42,12 @@ extern VConfigManager *g_config;
|
|||||||
|
|
||||||
QVector<QPair<QString, QString>> VUtils::s_availableLanguages;
|
QVector<QPair<QString, QString>> VUtils::s_availableLanguages;
|
||||||
|
|
||||||
const QString VUtils::c_imageLinkRegExp = QString("\\!\\[([^\\]]*)\\]\\(\\s*([^\\)\"'\\s]+)\\s*"
|
const QString VUtils::c_imageLinkRegExp = QString("\\!\\[([^\\]]*)\\]"
|
||||||
"((\"[^\"\\)\\n]*\")|('[^'\\)\\n]*'))?\\s*"
|
"\\(\\s*"
|
||||||
"(=(\\d*)x(\\d*))?\\s*"
|
"([^\\)\"'\\s]+)"
|
||||||
"\\)");
|
"(\\s*(\"[^\"\\)\\n]*\")|('[^'\\)\\n]*'))?"
|
||||||
|
"(\\s*=(\\d*)x(\\d*))?"
|
||||||
|
"\\s*\\)");
|
||||||
|
|
||||||
const QString VUtils::c_imageTitleRegExp = QString("[^\\[\\]]*");
|
const QString VUtils::c_imageTitleRegExp = QString("[^\\[\\]]*");
|
||||||
|
|
||||||
|
@ -133,6 +133,9 @@ public:
|
|||||||
static QVector<ImageLink> fetchImagesFromMarkdownFile(VFile *p_file,
|
static QVector<ImageLink> fetchImagesFromMarkdownFile(VFile *p_file,
|
||||||
ImageLink::ImageLinkType p_type = ImageLink::All);
|
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.
|
// Return the absolute path of @p_url according to @p_basePath.
|
||||||
static QString linkUrlToPath(const QString &p_basePath, const QString &p_url);
|
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;
|
// 3. Image Optional Title with double quotes or quotes;
|
||||||
// 4. Unused;
|
// 4. Unused;
|
||||||
// 5. Unused;
|
// 5. Unused;
|
||||||
// 6. Unused;
|
// 6. Width and height text;
|
||||||
// 7. Width;
|
// 7. Width;
|
||||||
// 8. Height;
|
// 8. Height;
|
||||||
static const QString c_imageLinkRegExp;
|
static const QString c_imageLinkRegExp;
|
||||||
@ -440,9 +443,6 @@ private:
|
|||||||
|
|
||||||
static void initAvailableLanguage();
|
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
|
// Delete file/directory specified by @p_path by moving it to the recycle bin
|
||||||
// folder @p_recycleBinFolderPath.
|
// folder @p_recycleBinFolderPath.
|
||||||
static bool deleteFile(const QString &p_recycleBinFolderPath,
|
static bool deleteFile(const QString &p_recycleBinFolderPath,
|
||||||
|
@ -356,6 +356,7 @@ void VConfigManager::initEditorConfigs()
|
|||||||
m_enableTabHighlight = getConfigFromSettings("editor",
|
m_enableTabHighlight = getConfigFromSettings("editor",
|
||||||
"enable_tab_highlight").toBool();
|
"enable_tab_highlight").toBool();
|
||||||
|
|
||||||
|
m_parsePasteLocalImage = getConfigFromSettings("editor", "parse_paste_local_image").toBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VConfigManager::initSettings()
|
void VConfigManager::initSettings()
|
||||||
|
@ -575,6 +575,8 @@ public:
|
|||||||
|
|
||||||
QList<int> getKeyboardLayoutMappingKeys() const;
|
QList<int> getKeyboardLayoutMappingKeys() const;
|
||||||
|
|
||||||
|
bool getParsePasteLocalImage() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Look up a config from user and default settings.
|
// Look up a config from user and default settings.
|
||||||
QVariant getConfigFromSettings(const QString §ion, const QString &key) const;
|
QVariant getConfigFromSettings(const QString §ion, const QString &key) const;
|
||||||
@ -1027,6 +1029,9 @@ private:
|
|||||||
// Whether insert new note in front.
|
// Whether insert new note in front.
|
||||||
bool m_insertNewNoteInFront;
|
bool m_insertNewNoteInFront;
|
||||||
|
|
||||||
|
// Whether download image from parse and paste.
|
||||||
|
bool m_parsePasteLocalImage;
|
||||||
|
|
||||||
// The name of the config file in each directory.
|
// The name of the config file in each directory.
|
||||||
static const QString c_dirConfigFile;
|
static const QString c_dirConfigFile;
|
||||||
|
|
||||||
@ -2684,4 +2689,9 @@ inline QList<int> VConfigManager::getKeyboardLayoutMappingKeys() const
|
|||||||
|
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool VConfigManager::getParsePasteLocalImage() const
|
||||||
|
{
|
||||||
|
return m_parsePasteLocalImage;
|
||||||
|
}
|
||||||
#endif // VCONFIGMANAGER_H
|
#endif // VCONFIGMANAGER_H
|
||||||
|
@ -101,20 +101,44 @@ void VMdEditOperations::insertImageFromQImage(const QString &title,
|
|||||||
mdEditor->imageInserted(filePath, url);
|
mdEditor->imageInserted(filePath, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VMdEditOperations::insertImageFromPath(const QString &title, const QString &path,
|
void VMdEditOperations::insertImageFromPath(const QString &p_title,
|
||||||
const QString &folderInLink, const QString &oriImagePath)
|
const QString &p_folderPath,
|
||||||
|
const QString &p_folderInLink,
|
||||||
|
const QString &p_srcImagePath)
|
||||||
{
|
{
|
||||||
QString fileName = VUtils::generateImageFileName(path, title, QFileInfo(oriImagePath).suffix());
|
insertImageFromPath(p_title,
|
||||||
QString filePath = QDir(path).filePath(fileName);
|
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());
|
V_ASSERT(!QFile(filePath).exists());
|
||||||
|
|
||||||
QString errStr;
|
QString errStr;
|
||||||
bool ret = VUtils::makePath(path);
|
bool ret = VUtils::makePath(p_folderPath);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
errStr = tr("Fail to create image folder <span style=\"%1\">%2</span>.")
|
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 {
|
} else {
|
||||||
ret = QFile::copy(oriImagePath, filePath);
|
ret = QFile::copy(p_srcImagePath, filePath);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
errStr = tr("Fail to copy image <span style=\"%1\">%2</span>.")
|
errStr = tr("Fail to copy image <span style=\"%1\">%2</span>.")
|
||||||
.arg(g_config->c_dataTextStyle).arg(filePath);
|
.arg(g_config->c_dataTextStyle).arg(filePath);
|
||||||
@ -122,8 +146,10 @@ void VMdEditOperations::insertImageFromPath(const QString &title, const QString
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
|
VUtils::showMessage(QMessageBox::Warning,
|
||||||
tr("Fail to insert image <span style=\"%1\">%2</span>.").arg(g_config->c_dataTextStyle).arg(title),
|
tr("Warning"),
|
||||||
|
tr("Fail to insert image <span style=\"%1\">%2</span>.")
|
||||||
|
.arg(g_config->c_dataTextStyle).arg(p_title),
|
||||||
errStr,
|
errStr,
|
||||||
QMessageBox::Ok,
|
QMessageBox::Ok,
|
||||||
QMessageBox::Ok,
|
QMessageBox::Ok,
|
||||||
@ -131,15 +157,19 @@ void VMdEditOperations::insertImageFromPath(const QString &title, const QString
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString url = QString("%1/%2").arg(folderInLink).arg(fileName);
|
p_urlInLink = QString("%1/%2").arg(p_folderInLink).arg(fileName);
|
||||||
QString md = QString("").arg(title).arg(url);
|
p_destImagePath = filePath;
|
||||||
insertText(md);
|
|
||||||
|
|
||||||
qDebug() << "insert image" << title << filePath;
|
if (p_insertText) {
|
||||||
|
QString md = QString("").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);
|
Q_ASSERT(mdEditor);
|
||||||
mdEditor->imageInserted(filePath, url);
|
mdEditor->imageInserted(filePath, p_urlInLink);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl)
|
bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl)
|
||||||
@ -177,7 +207,7 @@ bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl)
|
|||||||
VDownloader *downloader = new VDownloader(&dialog);
|
VDownloader *downloader = new VDownloader(&dialog);
|
||||||
connect(downloader, &VDownloader::downloadFinished,
|
connect(downloader, &VDownloader::downloadFinished,
|
||||||
&dialog, &VInsertImageDialog::imageDownloaded);
|
&dialog, &VInsertImageDialog::imageDownloaded);
|
||||||
downloader->download(imageUrl.toString());
|
downloader->download(imageUrl);
|
||||||
}
|
}
|
||||||
if (dialog.exec() == QDialog::Accepted) {
|
if (dialog.exec() == QDialog::Accepted) {
|
||||||
if (isLocal) {
|
if (isLocal) {
|
||||||
|
@ -34,11 +34,22 @@ public:
|
|||||||
|
|
||||||
bool insertImageLink(const QString &p_linkText, const QString &p_linkUrl);
|
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:
|
private:
|
||||||
// Insert image from @oriImagePath as @path.
|
// Insert image from @p_srcImagePath as to @p_folderPath.
|
||||||
// @folderInLink: the folder part in the image link.
|
// @p_folderInLink: the folder part in the image link.
|
||||||
void insertImageFromPath(const QString &title, const QString &path,
|
void insertImageFromPath(const QString &p_title,
|
||||||
const QString &folderInLink, const QString &oriImagePath);
|
const QString &p_folderPath,
|
||||||
|
const QString &p_folderInLink,
|
||||||
|
const QString &p_srcImagePath);
|
||||||
|
|
||||||
// @title: title of the inserted image;
|
// @title: title of the inserted image;
|
||||||
// @path: the image folder path to insert the image in;
|
// @path: the image folder path to insert the image in;
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
#include <QScopedPointer>
|
#include <QScopedPointer>
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QMimeDatabase>
|
#include <QMimeDatabase>
|
||||||
|
#include <QTemporaryFile>
|
||||||
|
#include <QProgressDialog>
|
||||||
|
|
||||||
#include "vdocument.h"
|
#include "vdocument.h"
|
||||||
#include "utils/veditutils.h"
|
#include "utils/veditutils.h"
|
||||||
@ -29,6 +31,7 @@
|
|||||||
#include "vplantumlhelper.h"
|
#include "vplantumlhelper.h"
|
||||||
#include "vgraphvizhelper.h"
|
#include "vgraphvizhelper.h"
|
||||||
#include "vmdtab.h"
|
#include "vmdtab.h"
|
||||||
|
#include "vdownloader.h"
|
||||||
|
|
||||||
extern VWebUtils *g_webUtils;
|
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);
|
Q_UNUSED(p_id);
|
||||||
if (m_copyTimeStamp == p_timeStamp && !p_text.isEmpty()) {
|
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"));
|
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());
|
Q_ASSERT(p_source->hasText());
|
||||||
return false;
|
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 ® = 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("")
|
||||||
|
.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());
|
||||||
|
}
|
||||||
|
@ -315,6 +315,8 @@ private:
|
|||||||
|
|
||||||
bool processTextFromMimeData(const QMimeData *p_source);
|
bool processTextFromMimeData(const QMimeData *p_source);
|
||||||
|
|
||||||
|
void replaceTextWithLocalImages(QString &p_text);
|
||||||
|
|
||||||
PegMarkdownHighlighter *m_pegHighlighter;
|
PegMarkdownHighlighter *m_pegHighlighter;
|
||||||
|
|
||||||
VCodeBlockHighlightHelper *m_cbHighlighter;
|
VCodeBlockHighlightHelper *m_cbHighlighter;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user