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=false
|
||||
|
||||
; Download images in parsed HTML text
|
||||
parse_paste_local_image=true
|
||||
|
||||
[export]
|
||||
; Path of the wkhtmltopdf tool
|
||||
wkhtmltopdf=wkhtmltopdf
|
||||
|
@ -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("[^\\[\\]]*");
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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()
|
||||
|
@ -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 §ion, 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
|
||||
|
@ -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("").arg(title).arg(url);
|
||||
p_urlInLink = QString("%1/%2").arg(p_folderInLink).arg(fileName);
|
||||
p_destImagePath = filePath;
|
||||
|
||||
if (p_insertText) {
|
||||
QString md = QString("").arg(p_title).arg(p_urlInLink);
|
||||
insertText(md);
|
||||
}
|
||||
|
||||
qDebug() << "insert image" << title << filePath;
|
||||
qDebug() << "insert image" << p_title << filePath;
|
||||
|
||||
VMdEditor *mdEditor = dynamic_cast<VMdEditor *>(m_editor);
|
||||
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) {
|
||||
|
@ -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;
|
||||
|
@ -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 ® = 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);
|
||||
|
||||
void replaceTextWithLocalImages(QString &p_text);
|
||||
|
||||
PegMarkdownHighlighter *m_pegHighlighter;
|
||||
|
||||
VCodeBlockHighlightHelper *m_cbHighlighter;
|
||||
|
Loading…
x
Reference in New Issue
Block a user