mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
export: handle HTML resources
This commit is contained in:
parent
b7e6301136
commit
fe7c446e5f
@ -208,8 +208,16 @@ QWidget *VExportDialog::setupHTMLAdvancedSettings()
|
||||
m_embedStyleCB->setToolTip(tr("Embed CSS styles in HTML file"));
|
||||
m_embedStyleCB->setChecked(true);
|
||||
|
||||
// Complete HTML.
|
||||
m_completeHTMLCB = new QCheckBox(tr("Complete page"), this);
|
||||
m_completeHTMLCB->setToolTip(tr("Export the whole web page along with pictures "
|
||||
"which may not keep the HTML link structure of "
|
||||
"the original page"));
|
||||
m_completeHTMLCB->setChecked(true);
|
||||
|
||||
QFormLayout *advLayout = new QFormLayout();
|
||||
advLayout->addRow(m_embedStyleCB);
|
||||
advLayout->addRow(m_completeHTMLCB);
|
||||
|
||||
advLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
@ -303,7 +311,8 @@ void VExportDialog::startExport()
|
||||
m_renderStyleCB->currentData().toString(),
|
||||
m_renderCodeBlockStyleCB->currentData().toString(),
|
||||
&m_pageLayout,
|
||||
m_embedStyleCB->isChecked());
|
||||
m_embedStyleCB->isChecked(),
|
||||
m_completeHTMLCB->isChecked());
|
||||
|
||||
s_lastExportFormat = opt.m_format;
|
||||
|
||||
|
@ -47,7 +47,8 @@ struct ExportOption
|
||||
const QString &p_renderStyle,
|
||||
const QString &p_renderCodeBlockStyle,
|
||||
QPageLayout *p_layout,
|
||||
bool p_embedCssStyle)
|
||||
bool p_embedCssStyle,
|
||||
bool p_completeHTML)
|
||||
: m_source(p_source),
|
||||
m_format(p_format),
|
||||
m_renderer(p_renderer),
|
||||
@ -55,7 +56,8 @@ struct ExportOption
|
||||
m_renderStyle(p_renderStyle),
|
||||
m_renderCodeBlockStyle(p_renderCodeBlockStyle),
|
||||
m_layout(p_layout),
|
||||
m_embedCssStyle(p_embedCssStyle)
|
||||
m_embedCssStyle(p_embedCssStyle),
|
||||
m_completeHTML(p_completeHTML)
|
||||
{
|
||||
}
|
||||
|
||||
@ -71,6 +73,7 @@ struct ExportOption
|
||||
QPageLayout *m_layout;
|
||||
|
||||
bool m_embedCssStyle;
|
||||
bool m_completeHTML;
|
||||
};
|
||||
|
||||
|
||||
@ -186,6 +189,8 @@ private:
|
||||
|
||||
QCheckBox *m_embedStyleCB;
|
||||
|
||||
QCheckBox *m_completeHTMLCB;;
|
||||
|
||||
VNotebook *m_notebook;
|
||||
|
||||
VDirectory *m_directory;
|
||||
|
@ -40,19 +40,56 @@ if (typeof VEnableImageCaption == 'undefined') {
|
||||
VEnableImageCaption = false;
|
||||
}
|
||||
|
||||
var getUrlScheme = function(url) {
|
||||
var idx = url.indexOf(':');
|
||||
if (idx > -1) {
|
||||
return url.substr(0, idx);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
var replaceCssUrl = function(baseUrl, match, p1, offset, str) {
|
||||
if (getUrlScheme(p1)) {
|
||||
return match;
|
||||
}
|
||||
|
||||
var url = baseUrl + '/' + p1;
|
||||
return "url(\"" + url + "\");";
|
||||
};
|
||||
|
||||
var translateCssUrlToAbsolute = function(baseUrl, css) {
|
||||
return css.replace(/\burl\(\"([^\"\)]+)\"\);/g, replaceCssUrl.bind(undefined, baseUrl));
|
||||
};
|
||||
|
||||
var styleContent = function() {
|
||||
var styles = "";
|
||||
for (var i = 0; i < document.styleSheets.length; ++i) {
|
||||
var styleSheet = document.styleSheets[i];
|
||||
if (styleSheet.cssRules) {
|
||||
var baseUrl = null;
|
||||
if (styleSheet.href) {
|
||||
var scheme = getUrlScheme(styleSheet.href);
|
||||
// We only translate local resources.
|
||||
if (scheme == 'file' || scheme == 'qrc') {
|
||||
baseUrl = styleSheet.href.substr(0, styleSheet.href.lastIndexOf('/'));
|
||||
}
|
||||
}
|
||||
|
||||
for (var j = 0; j < styleSheet.cssRules.length; ++j) {
|
||||
styles = styles + styleSheet.cssRules[j].cssText + "\n";
|
||||
var css = styleSheet.cssRules[j].cssText;
|
||||
if (baseUrl) {
|
||||
// Try to replace the url() with absolute path.
|
||||
css = translateCssUrlToAbsolute(baseUrl, css);
|
||||
}
|
||||
|
||||
styles = styles + css + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return styles;
|
||||
}
|
||||
};
|
||||
|
||||
var htmlContent = function() {
|
||||
content.htmlContentCB("", styleContent(), placeholder.innerHTML);
|
||||
|
@ -74,17 +74,32 @@ QString VUtils::readFileFromDisk(const QString &filePath)
|
||||
return fileText;
|
||||
}
|
||||
|
||||
bool VUtils::writeFileToDisk(const QString &filePath, const QString &text)
|
||||
bool VUtils::writeFileToDisk(const QString &p_filePath, const QString &p_text)
|
||||
{
|
||||
QFile file(filePath);
|
||||
QFile file(p_filePath);
|
||||
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
qWarning() << "fail to open file" << filePath << "to write";
|
||||
qWarning() << "fail to open file" << p_filePath << "to write";
|
||||
return false;
|
||||
}
|
||||
|
||||
QTextStream stream(&file);
|
||||
stream << text;
|
||||
stream << p_text;
|
||||
file.close();
|
||||
qDebug() << "write file content:" << filePath;
|
||||
qDebug() << "write file content:" << p_filePath;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VUtils::writeFileToDisk(const QString &p_filePath, const QByteArray &p_data)
|
||||
{
|
||||
QFile file(p_filePath);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
qWarning() << "fail to open file" << p_filePath << "to write";
|
||||
return false;
|
||||
}
|
||||
|
||||
file.write(p_data);
|
||||
file.close();
|
||||
qDebug() << "write file content:" << p_filePath;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,9 @@ class VUtils
|
||||
public:
|
||||
static QString readFileFromDisk(const QString &filePath);
|
||||
|
||||
static bool writeFileToDisk(const QString &filePath, const QString &text);
|
||||
static bool writeFileToDisk(const QString &p_filePath, const QString &p_text);
|
||||
|
||||
static bool writeFileToDisk(const QString &p_filePath, const QByteArray &p_data);
|
||||
|
||||
static bool writeJsonToDisk(const QString &p_filePath, const QJsonObject &p_json);
|
||||
|
||||
|
@ -3,9 +3,13 @@
|
||||
#include <QFileInfo>
|
||||
#include <QObject>
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QUrl>
|
||||
|
||||
#include "vpalette.h"
|
||||
#include "vconfigmanager.h"
|
||||
#include "utils/vutils.h"
|
||||
#include "vdownloader.h"
|
||||
|
||||
extern VPalette *g_palette;
|
||||
|
||||
@ -891,3 +895,32 @@ bool VWebUtils::fixXHtmlTags(QString &p_html)
|
||||
|
||||
return altered;
|
||||
}
|
||||
|
||||
QString VWebUtils::copyResource(const QUrl &p_url, const QString &p_folder) const
|
||||
{
|
||||
Q_ASSERT(!p_url.isRelative());
|
||||
|
||||
QDir dir(p_folder);
|
||||
if (!dir.exists()) {
|
||||
VUtils::makePath(p_folder);
|
||||
}
|
||||
|
||||
QString file = p_url.isLocalFile() ? p_url.toLocalFile() : p_url.toString();
|
||||
QString fileName = VUtils::fileNameFromPath(file);
|
||||
fileName = VUtils::getFileNameWithSequence(p_folder, fileName, true);
|
||||
QString targetFile = dir.absoluteFilePath(fileName);
|
||||
|
||||
bool succ = false;
|
||||
if (p_url.scheme() == "https" || p_url.scheme() == "http") {
|
||||
// Download it.
|
||||
QByteArray data = VDownloader::downloadSync(p_url);
|
||||
if (!data.isEmpty()) {
|
||||
succ = VUtils::writeFileToDisk(targetFile, data);
|
||||
}
|
||||
} else if (QFileInfo::exists(file)) {
|
||||
// Do a copy.
|
||||
succ = VUtils::copyFile(file, targetFile, false);
|
||||
}
|
||||
|
||||
return succ ? targetFile : QString();
|
||||
}
|
||||
|
@ -21,6 +21,10 @@ public:
|
||||
// Returns true if @p_html is modified.
|
||||
bool alterHtmlAsTarget(const QUrl &p_baseUrl, QString &p_html, const QString &p_target) const;
|
||||
|
||||
// Download or copy @p_url to @p_folder.
|
||||
// Return the target file path on success or empty string on failure.
|
||||
QString copyResource(const QUrl &p_url, const QString &p_folder) const;
|
||||
|
||||
private:
|
||||
struct CopyTargetAction
|
||||
{
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "vdownloader.h"
|
||||
|
||||
#include "utils/vutils.h"
|
||||
|
||||
VDownloader::VDownloader(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
@ -11,7 +13,6 @@ void VDownloader::handleDownloadFinished(QNetworkReply *reply)
|
||||
{
|
||||
data = reply->readAll();
|
||||
reply->deleteLater();
|
||||
qDebug() << "VDownloader receive" << reply->url().toString();
|
||||
emit downloadFinished(data, reply->url().toString());
|
||||
}
|
||||
|
||||
@ -23,5 +24,29 @@ void VDownloader::download(const QUrl &p_url)
|
||||
|
||||
QNetworkRequest request(p_url);
|
||||
webCtrl.get(request);
|
||||
qDebug() << "VDownloader get" << p_url.toString();
|
||||
}
|
||||
|
||||
QByteArray VDownloader::downloadSync(const QUrl &p_url)
|
||||
{
|
||||
QByteArray data;
|
||||
if (!p_url.isValid()) {
|
||||
return data;
|
||||
}
|
||||
|
||||
bool finished = false;
|
||||
QNetworkAccessManager nam;
|
||||
connect(&nam, &QNetworkAccessManager::finished,
|
||||
[&data, &finished](QNetworkReply *p_reply) {
|
||||
data = p_reply->readAll();
|
||||
p_reply->deleteLater();
|
||||
finished = true;
|
||||
});
|
||||
|
||||
nam.get(QNetworkRequest(p_url));
|
||||
|
||||
while (!finished) {
|
||||
VUtils::sleepWait(100);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
@ -15,6 +15,8 @@ public:
|
||||
explicit VDownloader(QObject *parent = 0);
|
||||
void download(const QUrl &p_url);
|
||||
|
||||
static QByteArray downloadSync(const QUrl &p_url);
|
||||
|
||||
signals:
|
||||
void downloadFinished(const QByteArray &data, const QString &url);
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <QDebug>
|
||||
#include <QWidget>
|
||||
#include <QWebChannel>
|
||||
#include <QRegExp>
|
||||
|
||||
#include "vconfigmanager.h"
|
||||
#include "vfile.h"
|
||||
@ -12,9 +13,12 @@
|
||||
#include "vconstants.h"
|
||||
#include "vmarkdownconverter.h"
|
||||
#include "vdocument.h"
|
||||
#include "utils/vwebutils.h"
|
||||
|
||||
extern VConfigManager *g_config;
|
||||
|
||||
extern VWebUtils *g_webUtils;
|
||||
|
||||
VExporter::VExporter(QWidget *p_parent)
|
||||
: QObject(p_parent),
|
||||
m_webViewer(NULL),
|
||||
@ -82,7 +86,8 @@ void VExporter::initWebViewer(VFile *p_file, const ExportOption &p_opt)
|
||||
m_webDocument->setHtml(html);
|
||||
}
|
||||
|
||||
m_webViewer->setHtml(m_htmlTemplate, p_file->getBaseUrl());
|
||||
m_baseUrl = p_file->getBaseUrl();
|
||||
m_webViewer->setHtml(m_htmlTemplate, m_baseUrl);
|
||||
}
|
||||
|
||||
void VExporter::handleLogicsFinished()
|
||||
@ -107,6 +112,7 @@ void VExporter::clearWebViewer()
|
||||
delete m_webViewer;
|
||||
m_webViewer = NULL;
|
||||
m_webDocument = NULL;
|
||||
m_baseUrl.clear();
|
||||
}
|
||||
|
||||
bool VExporter::exportToPDF(VWebView *p_webViewer,
|
||||
@ -122,16 +128,11 @@ bool VExporter::exportToPDF(VWebView *p_webViewer,
|
||||
|
||||
V_ASSERT(!p_filePath.isEmpty());
|
||||
|
||||
QFile file(p_filePath);
|
||||
|
||||
if (!file.open(QFile::WriteOnly)) {
|
||||
if (!VUtils::writeFileToDisk(p_filePath, p_result)) {
|
||||
pdfPrinted = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
file.write(p_result.data(), p_result.size());
|
||||
file.close();
|
||||
|
||||
pdfPrinted = 1;
|
||||
}, p_layout);
|
||||
|
||||
@ -201,6 +202,7 @@ bool VExporter::exportViaWebView(VFile *p_file,
|
||||
exportRet = exportToHTML(m_webViewer,
|
||||
m_webDocument,
|
||||
p_opt.m_embedCssStyle,
|
||||
p_opt.m_completeHTML,
|
||||
p_outputFile);
|
||||
break;
|
||||
|
||||
@ -237,6 +239,7 @@ exit:
|
||||
bool VExporter::exportToHTML(VWebView *p_webViewer,
|
||||
VDocument *p_webDocument,
|
||||
bool p_embedCssStyle,
|
||||
bool p_completeHTML,
|
||||
const QString &p_filePath)
|
||||
{
|
||||
Q_UNUSED(p_webViewer);
|
||||
@ -260,20 +263,40 @@ bool VExporter::exportToHTML(VWebView *p_webViewer,
|
||||
return;
|
||||
}
|
||||
|
||||
QString resFolder = QFileInfo(p_filePath).completeBaseName() + "_files";
|
||||
QString resFolderPath = QDir(VUtils::basePathFromPath(p_filePath)).filePath(resFolder);
|
||||
|
||||
qDebug() << "HTML files folder" << resFolderPath;
|
||||
|
||||
QString html(m_exportHtmlTemplate);
|
||||
if (!p_styleContent.isEmpty() && p_embedCssStyle) {
|
||||
html.replace(HtmlHolder::c_styleHolder, p_styleContent);
|
||||
QString content(p_styleContent);
|
||||
fixStyleResources(resFolderPath, content);
|
||||
html.replace(HtmlHolder::c_styleHolder, content);
|
||||
}
|
||||
|
||||
if (!p_headContent.isEmpty()) {
|
||||
html.replace(HtmlHolder::c_headHolder, p_headContent);
|
||||
}
|
||||
|
||||
if (p_completeHTML) {
|
||||
QString content(p_bodyContent);
|
||||
fixBodyResources(m_baseUrl, resFolderPath, content);
|
||||
html.replace(HtmlHolder::c_bodyHolder, content);
|
||||
} else {
|
||||
html.replace(HtmlHolder::c_bodyHolder, p_bodyContent);
|
||||
}
|
||||
|
||||
file.write(html.toUtf8());
|
||||
file.close();
|
||||
|
||||
// Delete empty resource folder.
|
||||
QDir dir(resFolderPath);
|
||||
if (dir.isEmpty()) {
|
||||
dir.cdUp();
|
||||
dir.rmdir(resFolder);
|
||||
}
|
||||
|
||||
htmlExported = 1;
|
||||
});
|
||||
|
||||
@ -289,3 +312,80 @@ bool VExporter::exportToHTML(VWebView *p_webViewer,
|
||||
|
||||
return htmlExported == 1;
|
||||
}
|
||||
|
||||
bool VExporter::fixStyleResources(const QString &p_folder,
|
||||
QString &p_html)
|
||||
{
|
||||
bool altered = false;
|
||||
QRegExp reg("\\burl\\(\"((file|qrc):[^\"\\)]+)\"\\);");
|
||||
|
||||
int pos = 0;
|
||||
while (pos < p_html.size()) {
|
||||
int idx = p_html.indexOf(reg, pos);
|
||||
if (idx == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
QString targetFile = g_webUtils->copyResource(QUrl(reg.cap(1)), p_folder);
|
||||
if (targetFile.isEmpty()) {
|
||||
pos = idx + reg.matchedLength();
|
||||
} else {
|
||||
// Replace the url string in html.
|
||||
QString newUrl = QString("url(\"%1\");").arg(getResourceRelativePath(targetFile));
|
||||
p_html.replace(idx, reg.matchedLength(), newUrl);
|
||||
pos = idx + newUrl.size();
|
||||
altered = true;
|
||||
}
|
||||
}
|
||||
|
||||
return altered;
|
||||
}
|
||||
|
||||
bool VExporter::fixBodyResources(const QUrl &p_baseUrl,
|
||||
const QString &p_folder,
|
||||
QString &p_html)
|
||||
{
|
||||
bool altered = false;
|
||||
if (p_baseUrl.isEmpty()) {
|
||||
return altered;
|
||||
}
|
||||
|
||||
QRegExp reg("<img ([^>]*)src=\"([^\"]+)\"([^>]*)>");
|
||||
|
||||
int pos = 0;
|
||||
while (pos < p_html.size()) {
|
||||
int idx = p_html.indexOf(reg, pos);
|
||||
if (idx == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (reg.cap(2).isEmpty()) {
|
||||
pos = idx + reg.matchedLength();
|
||||
continue;
|
||||
}
|
||||
|
||||
QUrl srcUrl(p_baseUrl.resolved(reg.cap(2)));
|
||||
QString targetFile = g_webUtils->copyResource(srcUrl, p_folder);
|
||||
if (targetFile.isEmpty()) {
|
||||
pos = idx + reg.matchedLength();
|
||||
} else {
|
||||
// Replace the url string in html.
|
||||
QString newUrl = QString("<img %1src=\"%2\"%3>").arg(reg.cap(1))
|
||||
.arg(getResourceRelativePath(targetFile))
|
||||
.arg(reg.cap(3));
|
||||
p_html.replace(idx, reg.matchedLength(), newUrl);
|
||||
pos = idx + newUrl.size();
|
||||
altered = true;
|
||||
}
|
||||
}
|
||||
|
||||
return altered;
|
||||
}
|
||||
|
||||
QString VExporter::getResourceRelativePath(const QString &p_file)
|
||||
{
|
||||
int idx = p_file.lastIndexOf('/');
|
||||
int idx2 = p_file.lastIndexOf('/', idx - 1);
|
||||
Q_ASSERT(idx > 0 && idx2 < idx);
|
||||
return "." + p_file.mid(idx2);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QPageLayout>
|
||||
#include <QUrl>
|
||||
|
||||
#include "dialog/vexportdialog.h"
|
||||
|
||||
@ -75,8 +76,22 @@ private:
|
||||
bool exportToHTML(VWebView *p_webViewer,
|
||||
VDocument *p_webDocument,
|
||||
bool p_embedCssStyle,
|
||||
bool p_completeHTML,
|
||||
const QString &p_filePath);
|
||||
|
||||
// Fix @p_html's resources like url("...") with "file" or "qrc" schema.
|
||||
// Copy the resource to @p_folder and fix the url string.
|
||||
static bool fixStyleResources(const QString &p_folder,
|
||||
QString &p_html);
|
||||
|
||||
// Fix @p_html's resources like <img>.
|
||||
// Copy the resource to @p_folder and fix the url string.
|
||||
static bool fixBodyResources(const QUrl &p_baseUrl,
|
||||
const QString &p_folder,
|
||||
QString &p_html);
|
||||
|
||||
static QString getResourceRelativePath(const QString &p_file);
|
||||
|
||||
QPageLayout m_pageLayout;
|
||||
|
||||
// Will be allocated and free for each conversion.
|
||||
@ -84,6 +99,9 @@ private:
|
||||
|
||||
VDocument *m_webDocument;
|
||||
|
||||
// Base URL of VWebView.
|
||||
QUrl m_baseUrl;
|
||||
|
||||
QString m_htmlTemplate;
|
||||
|
||||
// Template to hold the export HTML result.
|
||||
|
Loading…
x
Reference in New Issue
Block a user