mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
Export: support custom export
This commit is contained in:
parent
c9be7a7a7f
commit
8326d3c702
@ -24,7 +24,7 @@
|
|||||||
using namespace vnotex;
|
using namespace vnotex;
|
||||||
|
|
||||||
#ifndef QT_NO_DEBUG
|
#ifndef QT_NO_DEBUG
|
||||||
// #define VX_DEBUG_WEB
|
// #define VX_DEBUG_WEB
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const QString ConfigMgr::c_orgName = "VNote";
|
const QString ConfigMgr::c_orgName = "VNote";
|
||||||
|
@ -15,8 +15,6 @@ using namespace vnotex;
|
|||||||
|
|
||||||
HtmlTemplateHelper::Template HtmlTemplateHelper::s_markdownViewerTemplate;
|
HtmlTemplateHelper::Template HtmlTemplateHelper::s_markdownViewerTemplate;
|
||||||
|
|
||||||
static const QString c_globalStylesPlaceholder = "/* VX_GLOBAL_STYLES_PLACEHOLDER */";
|
|
||||||
|
|
||||||
QString WebGlobalOptions::toJavascriptObject() const
|
QString WebGlobalOptions::toJavascriptObject() const
|
||||||
{
|
{
|
||||||
return QStringLiteral("window.vxOptions = {\n")
|
return QStringLiteral("window.vxOptions = {\n")
|
||||||
@ -35,6 +33,7 @@ QString WebGlobalOptions::toJavascriptObject() const
|
|||||||
+ QString("bodyHeight: %1,\n").arg(m_bodyHeight)
|
+ QString("bodyHeight: %1,\n").arg(m_bodyHeight)
|
||||||
+ QString("transformSvgToPngEnabled: %1,\n").arg(Utils::boolToString(m_transformSvgToPngEnabled))
|
+ QString("transformSvgToPngEnabled: %1,\n").arg(Utils::boolToString(m_transformSvgToPngEnabled))
|
||||||
+ QString("mathJaxScale: %1,\n").arg(m_mathJaxScale)
|
+ QString("mathJaxScale: %1,\n").arg(m_mathJaxScale)
|
||||||
|
+ QString("removeCodeToolBarEnabled: %1,\n").arg(Utils::boolToString(m_removeCodeToolBarEnabled))
|
||||||
+ QString("sectionNumberBaseLevel: %1\n").arg(m_sectionNumberBaseLevel)
|
+ QString("sectionNumberBaseLevel: %1\n").arg(m_sectionNumberBaseLevel)
|
||||||
+ QStringLiteral("}");
|
+ QStringLiteral("}");
|
||||||
}
|
}
|
||||||
@ -59,7 +58,7 @@ static void fillGlobalStyles(QString &p_template, const WebResource &p_resource,
|
|||||||
styles += p_additionalStyles;
|
styles += p_additionalStyles;
|
||||||
|
|
||||||
if (!styles.isEmpty()) {
|
if (!styles.isEmpty()) {
|
||||||
p_template.replace(c_globalStylesPlaceholder, styles);
|
p_template.replace("/* VX_GLOBAL_STYLES_PLACEHOLDER */", styles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,22 +174,15 @@ void HtmlTemplateHelper::updateMarkdownViewerTemplate(const MarkdownEditorConfig
|
|||||||
|
|
||||||
s_markdownViewerTemplate.m_revision = p_config.revision();
|
s_markdownViewerTemplate.m_revision = p_config.revision();
|
||||||
|
|
||||||
|
Paras paras;
|
||||||
const auto &themeMgr = VNoteX::getInst().getThemeMgr();
|
const auto &themeMgr = VNoteX::getInst().getThemeMgr();
|
||||||
s_markdownViewerTemplate.m_template =
|
paras.m_webStyleSheetFile = themeMgr.getFile(Theme::File::WebStyleSheet);
|
||||||
generateMarkdownViewerTemplate(p_config,
|
paras.m_highlightStyleSheetFile = themeMgr.getFile(Theme::File::HighlightStyleSheet);
|
||||||
themeMgr.getFile(Theme::File::WebStyleSheet),
|
|
||||||
themeMgr.getFile(Theme::File::HighlightStyleSheet));
|
s_markdownViewerTemplate.m_template = generateMarkdownViewerTemplate(p_config, paras);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString HtmlTemplateHelper::generateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config,
|
QString HtmlTemplateHelper::generateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config, const Paras &p_paras)
|
||||||
const QString &p_webStyleSheetFile,
|
|
||||||
const QString &p_highlightStyleSheetFile,
|
|
||||||
bool p_useTransparentBg,
|
|
||||||
bool p_scrollable,
|
|
||||||
int p_bodyWidth,
|
|
||||||
int p_bodyHeight,
|
|
||||||
bool p_transformSvgToPng,
|
|
||||||
qreal p_mathJaxScale)
|
|
||||||
{
|
{
|
||||||
const auto &viewerResource = p_config.getViewerResource();
|
const auto &viewerResource = p_config.getViewerResource();
|
||||||
const auto templateFile = ConfigMgr::getInst().getUserOrAppFile(viewerResource.m_template);
|
const auto templateFile = ConfigMgr::getInst().getUserOrAppFile(viewerResource.m_template);
|
||||||
@ -198,7 +190,7 @@ QString HtmlTemplateHelper::generateMarkdownViewerTemplate(const MarkdownEditorC
|
|||||||
|
|
||||||
fillGlobalStyles(htmlTemplate, viewerResource, "");
|
fillGlobalStyles(htmlTemplate, viewerResource, "");
|
||||||
|
|
||||||
fillThemeStyles(htmlTemplate, p_webStyleSheetFile, p_highlightStyleSheetFile);
|
fillThemeStyles(htmlTemplate, p_paras.m_webStyleSheetFile, p_paras.m_highlightStyleSheetFile);
|
||||||
|
|
||||||
{
|
{
|
||||||
WebGlobalOptions opts;
|
WebGlobalOptions opts;
|
||||||
@ -212,12 +204,13 @@ QString HtmlTemplateHelper::generateMarkdownViewerTemplate(const MarkdownEditorC
|
|||||||
opts.m_autoBreakEnabled = p_config.getAutoBreakEnabled();
|
opts.m_autoBreakEnabled = p_config.getAutoBreakEnabled();
|
||||||
opts.m_linkifyEnabled = p_config.getLinkifyEnabled();
|
opts.m_linkifyEnabled = p_config.getLinkifyEnabled();
|
||||||
opts.m_indentFirstLineEnabled = p_config.getIndentFirstLineEnabled();
|
opts.m_indentFirstLineEnabled = p_config.getIndentFirstLineEnabled();
|
||||||
opts.m_transparentBackgroundEnabled = p_useTransparentBg;
|
opts.m_transparentBackgroundEnabled = p_paras.m_transparentBackgroundEnabled;
|
||||||
opts.m_scrollable = p_scrollable;
|
opts.m_scrollable = p_paras.m_scrollable;
|
||||||
opts.m_bodyWidth = p_bodyWidth;
|
opts.m_bodyWidth = p_paras.m_bodyWidth;
|
||||||
opts.m_bodyHeight = p_bodyHeight;
|
opts.m_bodyHeight = p_paras.m_bodyHeight;
|
||||||
opts.m_transformSvgToPngEnabled = p_transformSvgToPng;
|
opts.m_transformSvgToPngEnabled = p_paras.m_transformSvgToPngEnabled;
|
||||||
opts.m_mathJaxScale = p_mathJaxScale;
|
opts.m_mathJaxScale = p_paras.m_mathJaxScale;
|
||||||
|
opts.m_removeCodeToolBarEnabled = p_paras.m_removeCodeToolBarEnabled;
|
||||||
fillGlobalOptions(htmlTemplate, opts);
|
fillGlobalOptions(htmlTemplate, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,17 +228,36 @@ QString HtmlTemplateHelper::generateExportTemplate(const MarkdownEditorConfig &p
|
|||||||
|
|
||||||
fillGlobalStyles(htmlTemplate, exportResource, "");
|
fillGlobalStyles(htmlTemplate, exportResource, "");
|
||||||
|
|
||||||
// Outline panel.
|
fillOutlinePanel(htmlTemplate, exportResource, p_addOutlinePanel);
|
||||||
for (auto &ele : exportResource.m_resources) {
|
|
||||||
|
fillResourcesByContent(htmlTemplate, exportResource);
|
||||||
|
|
||||||
|
return htmlTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HtmlTemplateHelper::fillOutlinePanel(QString &p_template, WebResource &p_exportResource, bool p_addOutlinePanel)
|
||||||
|
{
|
||||||
|
for (auto &ele : p_exportResource.m_resources) {
|
||||||
if (ele.m_name == QStringLiteral("outline")) {
|
if (ele.m_name == QStringLiteral("outline")) {
|
||||||
ele.m_enabled = p_addOutlinePanel;
|
ele.m_enabled = p_addOutlinePanel;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fillResourcesByContent(htmlTemplate, exportResource);
|
// Remove static content to make the page clean.
|
||||||
|
if (!p_addOutlinePanel) {
|
||||||
|
int startIdx = p_template.indexOf("<!-- VX_OUTLINE_PANEL_START -->");
|
||||||
|
QString endMark("<!-- VX_OUTLINE_PANEL_END -->");
|
||||||
|
int endIdx = p_template.lastIndexOf(endMark);
|
||||||
|
Q_ASSERT(startIdx > -1 && endIdx > startIdx);
|
||||||
|
p_template.remove(startIdx, endIdx + endMark.size() - startIdx);
|
||||||
|
|
||||||
return htmlTemplate;
|
startIdx = p_template.indexOf("<!-- VX_OUTLINE_BUTTON_START -->");
|
||||||
|
endMark = "<!-- VX_OUTLINE_BUTTON_END -->";
|
||||||
|
endIdx = p_template.lastIndexOf(endMark);
|
||||||
|
Q_ASSERT(startIdx > -1 && endIdx > startIdx);
|
||||||
|
p_template.remove(startIdx, endIdx + endMark.size() - startIdx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HtmlTemplateHelper::fillTitle(QString &p_template, const QString &p_title)
|
void HtmlTemplateHelper::fillTitle(QString &p_template, const QString &p_title)
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
namespace vnotex
|
namespace vnotex
|
||||||
{
|
{
|
||||||
class MarkdownEditorConfig;
|
class MarkdownEditorConfig;
|
||||||
|
struct WebResource;
|
||||||
|
|
||||||
// Global options to be passed to Web side at the very beginning.
|
// Global options to be passed to Web side at the very beginning.
|
||||||
struct WebGlobalOptions
|
struct WebGlobalOptions
|
||||||
@ -48,6 +49,9 @@ namespace vnotex
|
|||||||
// wkhtmltopdf will make the MathJax formula too small.
|
// wkhtmltopdf will make the MathJax formula too small.
|
||||||
qreal m_mathJaxScale = -1;
|
qreal m_mathJaxScale = -1;
|
||||||
|
|
||||||
|
// Whether remove the tool bar of code blocks added by Prism.js.
|
||||||
|
bool m_removeCodeToolBarEnabled = false;
|
||||||
|
|
||||||
QString toJavascriptObject() const;
|
QString toJavascriptObject() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -55,20 +59,33 @@ namespace vnotex
|
|||||||
class HtmlTemplateHelper
|
class HtmlTemplateHelper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
struct Paras
|
||||||
|
{
|
||||||
|
QString m_webStyleSheetFile;
|
||||||
|
|
||||||
|
QString m_highlightStyleSheetFile;
|
||||||
|
|
||||||
|
bool m_transparentBackgroundEnabled = false;
|
||||||
|
|
||||||
|
bool m_scrollable = true;
|
||||||
|
|
||||||
|
int m_bodyWidth = -1;
|
||||||
|
|
||||||
|
int m_bodyHeight = -1;
|
||||||
|
|
||||||
|
bool m_transformSvgToPngEnabled = false;
|
||||||
|
|
||||||
|
qreal m_mathJaxScale = -1;
|
||||||
|
|
||||||
|
bool m_removeCodeToolBarEnabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
HtmlTemplateHelper() = delete;
|
HtmlTemplateHelper() = delete;
|
||||||
|
|
||||||
static const QString &getMarkdownViewerTemplate();
|
static const QString &getMarkdownViewerTemplate();
|
||||||
static void updateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config);
|
static void updateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config);
|
||||||
|
|
||||||
static QString generateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config,
|
static QString generateMarkdownViewerTemplate(const MarkdownEditorConfig &p_config, const Paras &p_paras);
|
||||||
const QString &p_webStyleSheetFile,
|
|
||||||
const QString &p_highlightStyleSheetFile,
|
|
||||||
bool p_useTransparentBg = false,
|
|
||||||
bool p_scrollable = true,
|
|
||||||
int p_bodyWidth = -1,
|
|
||||||
int p_bodyHeight = -1,
|
|
||||||
bool p_transformSvgToPng = false,
|
|
||||||
qreal p_mathJaxScale = -1);
|
|
||||||
|
|
||||||
static QString generateExportTemplate(const MarkdownEditorConfig &p_config,
|
static QString generateExportTemplate(const MarkdownEditorConfig &p_config,
|
||||||
bool p_addOutlinePanel);
|
bool p_addOutlinePanel);
|
||||||
@ -83,6 +100,8 @@ namespace vnotex
|
|||||||
|
|
||||||
static void fillBodyClassList(QString &p_template, const QString &p_classList);
|
static void fillBodyClassList(QString &p_template, const QString &p_classList);
|
||||||
|
|
||||||
|
static void fillOutlinePanel(QString &p_template, WebResource &p_exportResource, bool p_addOutlinePanel);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Template
|
struct Template
|
||||||
{
|
{
|
||||||
|
@ -413,3 +413,22 @@ bool Node::checkExists()
|
|||||||
}
|
}
|
||||||
return after;
|
return after;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<QSharedPointer<File>> Node::collectFiles()
|
||||||
|
{
|
||||||
|
QList<QSharedPointer<File>> files;
|
||||||
|
|
||||||
|
load();
|
||||||
|
|
||||||
|
if (hasContent()) {
|
||||||
|
files.append(getContentFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isContainer()) {
|
||||||
|
for (const auto &child : m_children) {
|
||||||
|
files.append(child->collectFiles());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
@ -179,6 +179,9 @@ namespace vnotex
|
|||||||
|
|
||||||
void sortChildren(const QVector<int> &p_beforeIdx, const QVector<int> &p_afterIdx);
|
void sortChildren(const QVector<int> &p_beforeIdx, const QVector<int> &p_afterIdx);
|
||||||
|
|
||||||
|
// Get content files recursively.
|
||||||
|
QList<QSharedPointer<File>> collectFiles();
|
||||||
|
|
||||||
static bool isAncestor(const Node *p_ancestor, const Node *p_child);
|
static bool isAncestor(const Node *p_ancestor, const Node *p_child);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -361,3 +361,20 @@ QJsonObject Notebook::getExtraConfig(const QString &p_key) const
|
|||||||
const auto &configs = getExtraConfigs();
|
const auto &configs = getExtraConfigs();
|
||||||
return configs.value(p_key).toObject();
|
return configs.value(p_key).toObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<QSharedPointer<File>> Notebook::collectFiles()
|
||||||
|
{
|
||||||
|
QList<QSharedPointer<File>> files;
|
||||||
|
|
||||||
|
auto rootNode = getRootNode();
|
||||||
|
|
||||||
|
const auto &children = rootNode->getChildrenRef();
|
||||||
|
for (const auto &child : children) {
|
||||||
|
if (child->getUse() != Node::Use::Normal) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
files.append(child->collectFiles());
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
@ -16,6 +16,7 @@ namespace vnotex
|
|||||||
class IVersionController;
|
class IVersionController;
|
||||||
class INotebookConfigMgr;
|
class INotebookConfigMgr;
|
||||||
struct NodeParameters;
|
struct NodeParameters;
|
||||||
|
class File;
|
||||||
|
|
||||||
// Base class of notebook.
|
// Base class of notebook.
|
||||||
class Notebook : public QObject
|
class Notebook : public QObject
|
||||||
@ -140,6 +141,9 @@ namespace vnotex
|
|||||||
QJsonObject getExtraConfig(const QString &p_key) const;
|
QJsonObject getExtraConfig(const QString &p_key) const;
|
||||||
virtual void setExtraConfig(const QString &p_key, const QJsonObject &p_obj) = 0;
|
virtual void setExtraConfig(const QString &p_key, const QJsonObject &p_obj) = 0;
|
||||||
|
|
||||||
|
// Get content files recursively.
|
||||||
|
QList<QSharedPointer<File>> collectFiles();
|
||||||
|
|
||||||
static const QString c_defaultAttachmentFolder;
|
static const QString c_defaultAttachmentFolder;
|
||||||
|
|
||||||
static const QString c_defaultImageFolder;
|
static const QString c_defaultImageFolder;
|
||||||
|
@ -48,6 +48,7 @@ QString VXNode::fetchAbsolutePath() const
|
|||||||
|
|
||||||
QSharedPointer<File> VXNode::getContentFile()
|
QSharedPointer<File> VXNode::getContentFile()
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(hasContent());
|
||||||
// We should not keep the shared ptr of VXNodeFile, or there is a cyclic ref.
|
// We should not keep the shared ptr of VXNodeFile, or there is a cyclic ref.
|
||||||
return QSharedPointer<VXNodeFile>::create(sharedFromThis().dynamicCast<VXNode>());
|
return QSharedPointer<VXNodeFile>::create(sharedFromThis().dynamicCast<VXNode>());
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ void SessionConfig::init()
|
|||||||
|
|
||||||
loadStateAndGeometry(sessionJobj);
|
loadStateAndGeometry(sessionJobj);
|
||||||
|
|
||||||
m_exportOption.fromJson(sessionJobj[QStringLiteral("export_option")].toObject());
|
loadExportOption(sessionJobj);
|
||||||
|
|
||||||
m_searchOption.fromJson(sessionJobj[QStringLiteral("search_option")].toObject());
|
m_searchOption.fromJson(sessionJobj[QStringLiteral("search_option")].toObject());
|
||||||
|
|
||||||
@ -206,7 +206,7 @@ QJsonObject SessionConfig::toJson() const
|
|||||||
obj[QStringLiteral("core")] = saveCore();
|
obj[QStringLiteral("core")] = saveCore();
|
||||||
obj[QStringLiteral("notebooks")] = saveNotebooks();
|
obj[QStringLiteral("notebooks")] = saveNotebooks();
|
||||||
obj[QStringLiteral("state_geometry")] = saveStateAndGeometry();
|
obj[QStringLiteral("state_geometry")] = saveStateAndGeometry();
|
||||||
obj[QStringLiteral("export_option")] = m_exportOption.toJson();
|
obj[QStringLiteral("export")] = saveExportOption();
|
||||||
obj[QStringLiteral("search_option")] = m_searchOption.toJson();
|
obj[QStringLiteral("search_option")] = m_searchOption.toJson();
|
||||||
writeByteArray(obj, QStringLiteral("viewarea_session"), m_viewAreaSession);
|
writeByteArray(obj, QStringLiteral("viewarea_session"), m_viewAreaSession);
|
||||||
writeByteArray(obj, QStringLiteral("notebook_explorer_session"), m_notebookExplorerSession);
|
writeByteArray(obj, QStringLiteral("notebook_explorer_session"), m_notebookExplorerSession);
|
||||||
@ -325,6 +325,16 @@ void SessionConfig::setExportOption(const ExportOption &p_option)
|
|||||||
updateConfig(m_exportOption, p_option, this);
|
updateConfig(m_exportOption, p_option, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QVector<ExportCustomOption> &SessionConfig::getCustomExportOptions() const
|
||||||
|
{
|
||||||
|
return m_customExportOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionConfig::setCustomExportOptions(const QVector<ExportCustomOption> &p_options)
|
||||||
|
{
|
||||||
|
updateConfig(m_customExportOptions, p_options, this);
|
||||||
|
}
|
||||||
|
|
||||||
const SearchOption &SessionConfig::getSearchOption() const
|
const SearchOption &SessionConfig::getSearchOption() const
|
||||||
{
|
{
|
||||||
return m_searchOption;
|
return m_searchOption;
|
||||||
@ -444,3 +454,31 @@ QJsonArray SessionConfig::saveHistory() const
|
|||||||
}
|
}
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SessionConfig::loadExportOption(const QJsonObject &p_session)
|
||||||
|
{
|
||||||
|
auto exportObj = p_session[QStringLiteral("export")].toObject();
|
||||||
|
|
||||||
|
m_exportOption.fromJson(exportObj[QStringLiteral("export_option")].toObject());
|
||||||
|
|
||||||
|
auto customArr = exportObj[QStringLiteral("custom_options")].toArray();
|
||||||
|
m_customExportOptions.resize(customArr.size());
|
||||||
|
for (int i = 0; i < customArr.size(); ++i) {
|
||||||
|
m_customExportOptions[i].fromJson(customArr[i].toObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject SessionConfig::saveExportOption() const
|
||||||
|
{
|
||||||
|
QJsonObject obj;
|
||||||
|
|
||||||
|
obj[QStringLiteral("export_option")] = m_exportOption.toJson();
|
||||||
|
|
||||||
|
QJsonArray customArr;
|
||||||
|
for (int i = 0; i < m_customExportOptions.size(); ++i) {
|
||||||
|
customArr.push_back(m_customExportOptions[i].toJson());
|
||||||
|
}
|
||||||
|
obj[QStringLiteral("custom_options")] = customArr;
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
@ -107,6 +107,9 @@ namespace vnotex
|
|||||||
const ExportOption &getExportOption() const;
|
const ExportOption &getExportOption() const;
|
||||||
void setExportOption(const ExportOption &p_option);
|
void setExportOption(const ExportOption &p_option);
|
||||||
|
|
||||||
|
const QVector<ExportCustomOption> &getCustomExportOptions() const;
|
||||||
|
void setCustomExportOptions(const QVector<ExportCustomOption> &p_options);
|
||||||
|
|
||||||
const SearchOption &getSearchOption() const;
|
const SearchOption &getSearchOption() const;
|
||||||
void setSearchOption(const SearchOption &p_option);
|
void setSearchOption(const SearchOption &p_option);
|
||||||
|
|
||||||
@ -151,6 +154,10 @@ namespace vnotex
|
|||||||
|
|
||||||
QJsonArray saveHistory() const;
|
QJsonArray saveHistory() const;
|
||||||
|
|
||||||
|
void loadExportOption(const QJsonObject &p_session);
|
||||||
|
|
||||||
|
QJsonObject saveExportOption() const;
|
||||||
|
|
||||||
QString m_newNotebookDefaultRootFolderPath;
|
QString m_newNotebookDefaultRootFolderPath;
|
||||||
|
|
||||||
// Use root folder to identify a notebook uniquely.
|
// Use root folder to identify a notebook uniquely.
|
||||||
@ -173,6 +180,8 @@ namespace vnotex
|
|||||||
|
|
||||||
ExportOption m_exportOption;
|
ExportOption m_exportOption;
|
||||||
|
|
||||||
|
QVector<ExportCustomOption> m_customExportOptions;
|
||||||
|
|
||||||
SearchOption m_searchOption;
|
SearchOption m_searchOption;
|
||||||
|
|
||||||
QByteArray m_viewAreaSession;
|
QByteArray m_viewAreaSession;
|
||||||
|
@ -86,10 +86,24 @@ class PrismRenderer extends VxWorker {
|
|||||||
p_containerNode.classList.add('line-numbers');
|
p_containerNode.classList.add('line-numbers');
|
||||||
|
|
||||||
Prism.highlightAllUnder(p_containerNode, false /* async or not */);
|
Prism.highlightAllUnder(p_containerNode, false /* async or not */);
|
||||||
|
|
||||||
|
// Remove the toolbar.
|
||||||
|
if (window.vxOptions.removeCodeToolBarEnabled) {
|
||||||
|
this.removeToolBar(p_containerNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.finishWork();
|
this.finishWork();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeToolBar(p_containerNode) {
|
||||||
|
// Static list.
|
||||||
|
let toolBarNodes = p_containerNode.querySelectorAll('div.code-toolbar > div.toolbar');
|
||||||
|
for (let i = 0; i < toolBarNodes.length; ++i) {
|
||||||
|
toolBarNodes[i].outerHTML = '';
|
||||||
|
delete toolBarNodes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.vnotex.registerWorker(new PrismRenderer());
|
window.vnotex.registerWorker(new PrismRenderer());
|
||||||
|
@ -27,19 +27,24 @@
|
|||||||
<body class="<!-- VX_BODY_CLASS_LIST_PLACEHOLDER -->">
|
<body class="<!-- VX_BODY_CLASS_LIST_PLACEHOLDER -->">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row flex-xl-nowrap">
|
<div class="row flex-xl-nowrap">
|
||||||
|
<!-- VX_OUTLINE_PANEL_START -->
|
||||||
<div id="outline-panel" style="display:none;" class="d-none d-md-block d-xl-block col-md-3 col-xl-2 bd-toc">
|
<div id="outline-panel" style="display:none;" class="d-none d-md-block d-xl-block col-md-3 col-xl-2 bd-toc">
|
||||||
<div id="outline-content" class="section-nav"></div>
|
<div id="outline-content" class="section-nav"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- VX_OUTLINE_PANEL_END -->
|
||||||
|
|
||||||
<div id="post-content" class="col-12 col-md-9 col-xl-10 py-md-3 pl-md-5 bd-content">
|
<div id="post-content" class="col-12 col-md-9 col-xl-10 py-md-3 pl-md-5 bd-content">
|
||||||
<!-- VX_CONTENT_PLACEHOLDER -->
|
<!-- VX_CONTENT_PLACEHOLDER -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- VX_OUTLINE_BUTTON_START -->
|
||||||
<div id="container-floating" style="display:none;" class="d-none d-md-block d-xl-block">
|
<div id="container-floating" style="display:none;" class="d-none d-md-block d-xl-block">
|
||||||
<div id="floating-button" onclick="toggleMore()">
|
<div id="floating-button" onclick="toggleMore()">
|
||||||
<p id="floating-more" class="more">></p>
|
<p id="floating-more" class="more">></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- VX_OUTLINE_BUTTON_END -->
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -83,6 +83,45 @@ bool ExportPdfOption::operator==(const ExportPdfOption &p_other) const
|
|||||||
&& m_wkhtmltopdfArgs == p_other.m_wkhtmltopdfArgs;
|
&& m_wkhtmltopdfArgs == p_other.m_wkhtmltopdfArgs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QJsonObject ExportCustomOption::toJson() const
|
||||||
|
{
|
||||||
|
QJsonObject obj;
|
||||||
|
obj["name"] = m_name;
|
||||||
|
obj["target_suffix"] = m_targetSuffix;
|
||||||
|
obj["command"] = m_command;
|
||||||
|
obj["use_html_input"] = m_useHtmlInput;
|
||||||
|
obj["all_in_one"] = m_allInOne;
|
||||||
|
obj["target_page_scrollable"] = m_targetPageScrollable;
|
||||||
|
obj["resource_path_separator"] = m_resourcePathSeparator;
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExportCustomOption::fromJson(const QJsonObject &p_obj)
|
||||||
|
{
|
||||||
|
if (p_obj.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_name = p_obj["name"].toString();
|
||||||
|
m_targetSuffix = p_obj["target_suffix"].toString();
|
||||||
|
m_command = p_obj["command"].toString();
|
||||||
|
m_useHtmlInput = p_obj["use_html_input"].toBool();
|
||||||
|
m_allInOne = p_obj["all_in_one"].toBool();
|
||||||
|
m_targetPageScrollable = p_obj["target_page_scrollable"].toBool();
|
||||||
|
m_resourcePathSeparator = p_obj["resource_path_separator"].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExportCustomOption::operator==(const ExportCustomOption &p_other) const
|
||||||
|
{
|
||||||
|
return m_name == p_other.m_name
|
||||||
|
&& m_useHtmlInput == p_other.m_useHtmlInput
|
||||||
|
&& m_targetSuffix == p_other.m_targetSuffix
|
||||||
|
&& m_command == p_other.m_command
|
||||||
|
&& m_allInOne == p_other.m_allInOne
|
||||||
|
&& m_targetPageScrollable == p_other.m_targetPageScrollable
|
||||||
|
&& m_resourcePathSeparator == p_other.m_resourcePathSeparator;
|
||||||
|
}
|
||||||
|
|
||||||
QJsonObject ExportOption::toJson() const
|
QJsonObject ExportOption::toJson() const
|
||||||
{
|
{
|
||||||
QJsonObject obj;
|
QJsonObject obj;
|
||||||
@ -93,6 +132,7 @@ QJsonObject ExportOption::toJson() const
|
|||||||
obj["export_attachments"] = m_exportAttachments;
|
obj["export_attachments"] = m_exportAttachments;
|
||||||
obj["html_option"] = m_htmlOption.toJson();
|
obj["html_option"] = m_htmlOption.toJson();
|
||||||
obj["pdf_option"] = m_pdfOption.toJson();
|
obj["pdf_option"] = m_pdfOption.toJson();
|
||||||
|
obj["custom_export"] = m_customExport;
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,6 +171,7 @@ void ExportOption::fromJson(const QJsonObject &p_obj)
|
|||||||
m_exportAttachments = p_obj["export_attachments"].toBool();
|
m_exportAttachments = p_obj["export_attachments"].toBool();
|
||||||
m_htmlOption.fromJson(p_obj["html_option"].toObject());
|
m_htmlOption.fromJson(p_obj["html_option"].toObject());
|
||||||
m_pdfOption.fromJson(p_obj["pdf_option"].toObject());
|
m_pdfOption.fromJson(p_obj["pdf_option"].toObject());
|
||||||
|
m_customExport = p_obj["custom_export"].toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExportOption::operator==(const ExportOption &p_other) const
|
bool ExportOption::operator==(const ExportOption &p_other) const
|
||||||
|
@ -69,6 +69,34 @@ namespace vnotex
|
|||||||
QString m_wkhtmltopdfArgs;
|
QString m_wkhtmltopdfArgs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ExportCustomOption
|
||||||
|
{
|
||||||
|
QJsonObject toJson() const;
|
||||||
|
void fromJson(const QJsonObject &p_obj);
|
||||||
|
|
||||||
|
bool operator==(const ExportCustomOption &p_other) const;
|
||||||
|
|
||||||
|
QString m_name;
|
||||||
|
|
||||||
|
QString m_targetSuffix;
|
||||||
|
|
||||||
|
QString m_command;
|
||||||
|
|
||||||
|
bool m_useHtmlInput = true;
|
||||||
|
|
||||||
|
bool m_allInOne = false;
|
||||||
|
|
||||||
|
// Whether the page of target format is scrollable.
|
||||||
|
bool m_targetPageScrollable = false;
|
||||||
|
|
||||||
|
// The default value here follows the rules of Pandoc.
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
QString m_resourcePathSeparator = ";";
|
||||||
|
#else
|
||||||
|
QString m_resourcePathSeparator = ":";
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
struct ExportOption
|
struct ExportOption
|
||||||
{
|
{
|
||||||
QJsonObject toJson() const;
|
QJsonObject toJson() const;
|
||||||
@ -95,6 +123,15 @@ namespace vnotex
|
|||||||
ExportHtmlOption m_htmlOption;
|
ExportHtmlOption m_htmlOption;
|
||||||
|
|
||||||
ExportPdfOption m_pdfOption;
|
ExportPdfOption m_pdfOption;
|
||||||
|
|
||||||
|
QString m_customExport;
|
||||||
|
|
||||||
|
// Following fields are used in runtime only.
|
||||||
|
ExportCustomOption *m_customOption = nullptr;
|
||||||
|
|
||||||
|
bool m_transformSvgToPngEnabled = false;
|
||||||
|
|
||||||
|
bool m_removeCodeToolBarEnabled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline QString exportFormatString(ExportFormat p_format)
|
inline QString exportFormatString(ExportFormat p_format)
|
||||||
|
@ -8,9 +8,12 @@
|
|||||||
#include <buffer/buffer.h>
|
#include <buffer/buffer.h>
|
||||||
#include <core/file.h>
|
#include <core/file.h>
|
||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
|
#include <utils/utils.h>
|
||||||
#include <utils/pathutils.h>
|
#include <utils/pathutils.h>
|
||||||
|
#include <utils/processutils.h>
|
||||||
#include <utils/contentmediautils.h>
|
#include <utils/contentmediautils.h>
|
||||||
#include "webviewexporter.h"
|
#include "webviewexporter.h"
|
||||||
|
#include <core/exception.h>
|
||||||
|
|
||||||
using namespace vnotex;
|
using namespace vnotex;
|
||||||
|
|
||||||
@ -125,10 +128,13 @@ QString Exporter::doExport(const ExportOption &p_option, Node *p_note)
|
|||||||
return outputFile;
|
return outputFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Exporter::doExportAllInOne(const ExportOption &p_option, Node *p_folder)
|
QString Exporter::doExportPdfAllInOne(const ExportOption &p_option, Notebook *p_notebook, Node *p_folder)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT((p_notebook || p_folder) && !(p_notebook && p_folder));
|
||||||
|
|
||||||
// Make path.
|
// Make path.
|
||||||
const auto outputFolder = makeOutputFolder(p_option.m_outputDir, p_folder->getName());
|
const auto name = p_notebook ? tr("notebook_%1").arg(p_notebook->getName()) : p_folder->getName();
|
||||||
|
const auto outputFolder = makeOutputFolder(p_option.m_outputDir, name);
|
||||||
if (outputFolder.isEmpty()) {
|
if (outputFolder.isEmpty()) {
|
||||||
emit logRequested(tr("Failed to create output folder under (%1).").arg(p_option.m_outputDir));
|
emit logRequested(tr("Failed to create output folder under (%1).").arg(p_option.m_outputDir));
|
||||||
return QString();
|
return QString();
|
||||||
@ -143,7 +149,15 @@ QString Exporter::doExportAllInOne(const ExportOption &p_option, Node *p_folder)
|
|||||||
|
|
||||||
auto tmpOption(getExportOptionForIntermediateHtml(p_option, tmpDir.path()));
|
auto tmpOption(getExportOptionForIntermediateHtml(p_option, tmpDir.path()));
|
||||||
|
|
||||||
auto htmlFiles = doExport(tmpOption, tmpDir.path(), p_folder);
|
QStringList htmlFiles;
|
||||||
|
if (p_notebook) {
|
||||||
|
htmlFiles = doExportNotebook(tmpOption, tmpDir.path(), p_notebook);
|
||||||
|
} else {
|
||||||
|
htmlFiles = doExport(tmpOption, tmpDir.path(), p_folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanUpWebViewExporter();
|
||||||
|
|
||||||
if (htmlFiles.isEmpty()) {
|
if (htmlFiles.isEmpty()) {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
@ -152,8 +166,6 @@ QString Exporter::doExportAllInOne(const ExportOption &p_option, Node *p_folder)
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanUpWebViewExporter();
|
|
||||||
|
|
||||||
auto fileName = FileUtils::generateFileNameWithSequence(outputFolder,
|
auto fileName = FileUtils::generateFileNameWithSequence(outputFolder,
|
||||||
tr("all_in_one_export"),
|
tr("all_in_one_export"),
|
||||||
"pdf");
|
"pdf");
|
||||||
@ -166,17 +178,24 @@ QString Exporter::doExportAllInOne(const ExportOption &p_option, Node *p_folder)
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Exporter::doExportAllInOne(const ExportOption &p_option, Notebook *p_notebook)
|
QString Exporter::doExportCustomAllInOne(const ExportOption &p_option, Notebook *p_notebook, Node *p_folder)
|
||||||
{
|
{
|
||||||
|
Q_ASSERT((p_notebook || p_folder) && !(p_notebook && p_folder));
|
||||||
|
|
||||||
// Make path.
|
// Make path.
|
||||||
const auto outputFolder = makeOutputFolder(p_option.m_outputDir, tr("notebook_%1").arg(p_notebook->getName()));
|
const auto name = p_notebook ? tr("notebook_%1").arg(p_notebook->getName()) : p_folder->getName();
|
||||||
|
const auto outputFolder = makeOutputFolder(p_option.m_outputDir, name);
|
||||||
if (outputFolder.isEmpty()) {
|
if (outputFolder.isEmpty()) {
|
||||||
emit logRequested(tr("Failed to create output folder under (%1).").arg(p_option.m_outputDir));
|
emit logRequested(tr("Failed to create output folder under (%1).").arg(p_option.m_outputDir));
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export to HTML to a tmp dir first.
|
QStringList inputFiles;
|
||||||
|
QStringList resourcePaths;
|
||||||
|
|
||||||
QTemporaryDir tmpDir;
|
QTemporaryDir tmpDir;
|
||||||
|
if (p_option.m_customOption->m_useHtmlInput) {
|
||||||
|
// Export to HTML to a tmp dir first.
|
||||||
if (!tmpDir.isValid()) {
|
if (!tmpDir.isValid()) {
|
||||||
emit logRequested(tr("Failed to create temporary dir to hold HTML files."));
|
emit logRequested(tr("Failed to create temporary dir to hold HTML files."));
|
||||||
return QString();
|
return QString();
|
||||||
@ -184,7 +203,15 @@ QString Exporter::doExportAllInOne(const ExportOption &p_option, Notebook *p_not
|
|||||||
|
|
||||||
auto tmpOption(getExportOptionForIntermediateHtml(p_option, tmpDir.path()));
|
auto tmpOption(getExportOptionForIntermediateHtml(p_option, tmpDir.path()));
|
||||||
|
|
||||||
auto htmlFiles = doExportNotebook(tmpOption, tmpDir.path(), p_notebook);
|
QStringList htmlFiles;
|
||||||
|
if (p_notebook) {
|
||||||
|
htmlFiles = doExportNotebook(tmpOption, tmpDir.path(), p_notebook);
|
||||||
|
} else {
|
||||||
|
htmlFiles = doExport(tmpOption, tmpDir.path(), p_folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanUpWebViewExporter();
|
||||||
|
|
||||||
if (htmlFiles.isEmpty()) {
|
if (htmlFiles.isEmpty()) {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
@ -193,13 +220,36 @@ QString Exporter::doExportAllInOne(const ExportOption &p_option, Notebook *p_not
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanUpWebViewExporter();
|
inputFiles = htmlFiles;
|
||||||
|
for (const auto &file : htmlFiles) {
|
||||||
|
resourcePaths << PathUtils::parentDirPath(file);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Collect source files.
|
||||||
|
if (p_notebook) {
|
||||||
|
collectFiles(p_notebook->collectFiles(), inputFiles, resourcePaths);
|
||||||
|
} else {
|
||||||
|
collectFiles(p_folder->collectFiles(), inputFiles, resourcePaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkAskedToStop()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputFiles.isEmpty()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
auto fileName = FileUtils::generateFileNameWithSequence(outputFolder,
|
auto fileName = FileUtils::generateFileNameWithSequence(outputFolder,
|
||||||
tr("all_in_one_export"),
|
tr("all_in_one_export"),
|
||||||
"pdf");
|
p_option.m_customOption->m_targetSuffix);
|
||||||
auto destFilePath = PathUtils::concatenateFilePath(outputFolder, fileName);
|
auto destFilePath = PathUtils::concatenateFilePath(outputFolder, fileName);
|
||||||
if (getWebViewExporter(p_option)->htmlToPdfViaWkhtmltopdf(p_option.m_pdfOption, htmlFiles, destFilePath)) {
|
bool success = doExportCustom(p_option,
|
||||||
|
inputFiles,
|
||||||
|
resourcePaths,
|
||||||
|
destFilePath);
|
||||||
|
if (success) {
|
||||||
emit logRequested(tr("Exported to (%1).").arg(destFilePath));
|
emit logRequested(tr("Exported to (%1).").arg(destFilePath));
|
||||||
return destFilePath;
|
return destFilePath;
|
||||||
}
|
}
|
||||||
@ -213,8 +263,16 @@ QStringList Exporter::doExportFolder(const ExportOption &p_option, Node *p_folde
|
|||||||
|
|
||||||
QStringList outputFiles;
|
QStringList outputFiles;
|
||||||
|
|
||||||
if (p_option.m_targetFormat == ExportFormat::PDF && p_option.m_pdfOption.m_useWkhtmltopdf && p_option.m_pdfOption.m_allInOne) {
|
if (p_option.m_targetFormat == ExportFormat::PDF
|
||||||
auto file = doExportAllInOne(p_option, p_folder);
|
&& p_option.m_pdfOption.m_useWkhtmltopdf
|
||||||
|
&& p_option.m_pdfOption.m_allInOne) {
|
||||||
|
auto file = doExportPdfAllInOne(p_option, nullptr, p_folder);
|
||||||
|
if (!file.isEmpty()) {
|
||||||
|
outputFiles << file;
|
||||||
|
}
|
||||||
|
} else if (p_option.m_targetFormat == ExportFormat::Custom
|
||||||
|
&& p_option.m_customOption->m_allInOne) {
|
||||||
|
auto file = doExportCustomAllInOne(p_option, nullptr, p_folder);
|
||||||
if (!file.isEmpty()) {
|
if (!file.isEmpty()) {
|
||||||
outputFiles << file;
|
outputFiles << file;
|
||||||
}
|
}
|
||||||
@ -240,7 +298,15 @@ QStringList Exporter::doExport(const ExportOption &p_option, const QString &p_ou
|
|||||||
return outputFiles;
|
return outputFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
p_folder->load();
|
p_folder->load();
|
||||||
|
} catch (Exception &p_e) {
|
||||||
|
QString msg = tr("Failed to load node (%1) (%2).").arg(p_folder->fetchPath(), p_e.what());
|
||||||
|
qWarning() << msg;
|
||||||
|
emit logRequested(msg);
|
||||||
|
return outputFiles;
|
||||||
|
}
|
||||||
|
|
||||||
const auto &children = p_folder->getChildrenRef();
|
const auto &children = p_folder->getChildrenRef();
|
||||||
emit progressUpdated(0, children.size());
|
emit progressUpdated(0, children.size());
|
||||||
for (int i = 0; i < children.size(); ++i) {
|
for (int i = 0; i < children.size(); ++i) {
|
||||||
@ -282,6 +348,10 @@ QString Exporter::doExport(const ExportOption &p_option, const QString &p_output
|
|||||||
outputFile = doExportPdf(p_option, p_outputDir, p_file);
|
outputFile = doExportPdf(p_option, p_outputDir, p_file);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ExportFormat::Custom:
|
||||||
|
outputFile = doExportCustom(p_option, p_outputDir, p_file);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
emit logRequested(tr("Unknown target format %1.").arg(exportFormatString(p_option.m_targetFormat)));
|
emit logRequested(tr("Unknown target format %1.").arg(exportFormatString(p_option.m_targetFormat)));
|
||||||
break;
|
break;
|
||||||
@ -302,8 +372,16 @@ QStringList Exporter::doExport(const ExportOption &p_option, Notebook *p_noteboo
|
|||||||
|
|
||||||
QStringList outputFiles;
|
QStringList outputFiles;
|
||||||
|
|
||||||
if (p_option.m_targetFormat == ExportFormat::PDF && p_option.m_pdfOption.m_useWkhtmltopdf && p_option.m_pdfOption.m_allInOne) {
|
if (p_option.m_targetFormat == ExportFormat::PDF
|
||||||
auto file = doExportAllInOne(p_option, p_notebook);
|
&& p_option.m_pdfOption.m_useWkhtmltopdf
|
||||||
|
&& p_option.m_pdfOption.m_allInOne) {
|
||||||
|
auto file = doExportPdfAllInOne(p_option, p_notebook, nullptr);
|
||||||
|
if (!file.isEmpty()) {
|
||||||
|
outputFiles << file;
|
||||||
|
}
|
||||||
|
} else if (p_option.m_targetFormat == ExportFormat::Custom
|
||||||
|
&& p_option.m_customOption->m_allInOne) {
|
||||||
|
auto file = doExportCustomAllInOne(p_option, p_notebook, nullptr);
|
||||||
if (!file.isEmpty()) {
|
if (!file.isEmpty()) {
|
||||||
outputFiles << file;
|
outputFiles << file;
|
||||||
}
|
}
|
||||||
@ -454,15 +532,158 @@ QString Exporter::doExportPdf(const ExportOption &p_option, const QString &p_out
|
|||||||
return outputFile;
|
return outputFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Exporter::doExportCustom(const ExportOption &p_option, const QString &p_outputDir, const File *p_file)
|
||||||
|
{
|
||||||
|
Q_ASSERT(p_option.m_customOption);
|
||||||
|
QStringList inputFiles;
|
||||||
|
QStringList resourcePaths;
|
||||||
|
|
||||||
|
QTemporaryDir tmpDir;
|
||||||
|
if (p_option.m_customOption->m_useHtmlInput) {
|
||||||
|
// Export to HTML to a tmp dir first.
|
||||||
|
if (!tmpDir.isValid()) {
|
||||||
|
emit logRequested(tr("Failed to create temporary dir to hold HTML files."));
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto tmpOption(getExportOptionForIntermediateHtml(p_option, tmpDir.path()));
|
||||||
|
auto htmlFile = doExport(tmpOption, tmpDir.path(), p_file);
|
||||||
|
if (htmlFile.isEmpty()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkAskedToStop()) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanUpWebViewExporter();
|
||||||
|
|
||||||
|
inputFiles << htmlFile;
|
||||||
|
resourcePaths << PathUtils::parentDirPath(htmlFile);
|
||||||
|
} else {
|
||||||
|
inputFiles << p_file->getContentPath();
|
||||||
|
resourcePaths << p_file->getResourcePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fileName = FileUtils::generateFileNameWithSequence(p_outputDir,
|
||||||
|
QFileInfo(p_file->getName()).completeBaseName(),
|
||||||
|
p_option.m_customOption->m_targetSuffix);
|
||||||
|
auto destFilePath = PathUtils::concatenateFilePath(p_outputDir, fileName);
|
||||||
|
|
||||||
|
bool success = doExportCustom(p_option,
|
||||||
|
inputFiles,
|
||||||
|
resourcePaths,
|
||||||
|
destFilePath);
|
||||||
|
if (success) {
|
||||||
|
// Copy attachments if available.
|
||||||
|
if (p_option.m_exportAttachments) {
|
||||||
|
exportAttachments(p_file->getNode(), p_file->getFilePath(), p_outputDir, destFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return destFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
ExportOption Exporter::getExportOptionForIntermediateHtml(const ExportOption &p_option, const QString &p_outputDir)
|
ExportOption Exporter::getExportOptionForIntermediateHtml(const ExportOption &p_option, const QString &p_outputDir)
|
||||||
{
|
{
|
||||||
ExportOption tmpOption(p_option);
|
ExportOption tmpOption(p_option);
|
||||||
|
tmpOption.m_exportAttachments = false;
|
||||||
tmpOption.m_targetFormat = ExportFormat::HTML;
|
tmpOption.m_targetFormat = ExportFormat::HTML;
|
||||||
|
tmpOption.m_transformSvgToPngEnabled = true;
|
||||||
|
tmpOption.m_removeCodeToolBarEnabled = true;
|
||||||
|
|
||||||
tmpOption.m_htmlOption.m_embedStyles = true;
|
tmpOption.m_htmlOption.m_embedStyles = true;
|
||||||
tmpOption.m_htmlOption.m_completePage = true;
|
tmpOption.m_htmlOption.m_completePage = true;
|
||||||
tmpOption.m_htmlOption.m_embedImages = false;
|
tmpOption.m_htmlOption.m_embedImages = false;
|
||||||
|
tmpOption.m_htmlOption.m_useMimeHtmlFormat = false;
|
||||||
|
tmpOption.m_htmlOption.m_addOutlinePanel = false;
|
||||||
tmpOption.m_htmlOption.m_scrollable = false;
|
tmpOption.m_htmlOption.m_scrollable = false;
|
||||||
|
if (p_option.m_targetFormat == ExportFormat::Custom && p_option.m_customOption->m_targetPageScrollable) {
|
||||||
|
tmpOption.m_htmlOption.m_scrollable = true;
|
||||||
|
}
|
||||||
tmpOption.m_outputDir = p_outputDir;
|
tmpOption.m_outputDir = p_outputDir;
|
||||||
|
|
||||||
return tmpOption;
|
return tmpOption;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Exporter::doExportCustom(const ExportOption &p_option,
|
||||||
|
const QStringList &p_files,
|
||||||
|
const QStringList &p_resourcePaths,
|
||||||
|
const QString &p_filePath)
|
||||||
|
{
|
||||||
|
const auto cmd = evaluateCommand(p_option,
|
||||||
|
p_files,
|
||||||
|
p_resourcePaths,
|
||||||
|
p_filePath);
|
||||||
|
|
||||||
|
emit logRequested(tr("Custom command: %1").arg(cmd));
|
||||||
|
qDebug() << "custom export" << cmd;
|
||||||
|
|
||||||
|
auto state = ProcessUtils::start(cmd,
|
||||||
|
[this](const QString &msg) {
|
||||||
|
emit logRequested(msg);
|
||||||
|
},
|
||||||
|
m_askedToStop);
|
||||||
|
|
||||||
|
return state == ProcessUtils::Succeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Exporter::evaluateCommand(const ExportOption &p_option,
|
||||||
|
const QStringList &p_files,
|
||||||
|
const QStringList &p_resourcePaths,
|
||||||
|
const QString &p_filePath)
|
||||||
|
{
|
||||||
|
auto cmd(p_option.m_customOption->m_command);
|
||||||
|
|
||||||
|
QString inputs;
|
||||||
|
for (int i = 0; i < p_files.size(); ++i) {
|
||||||
|
if (i > 0) {
|
||||||
|
inputs += " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs += getQuotedPath(p_files[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString resourcePath;
|
||||||
|
for (int i = 0; i < p_resourcePaths.size(); ++i) {
|
||||||
|
bool duplicated = false;
|
||||||
|
for (int j = 0; j < i; ++j) {
|
||||||
|
if (p_resourcePaths[j] == p_resourcePaths[i]) {
|
||||||
|
// Deduplicate.
|
||||||
|
duplicated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (duplicated) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i > 0) {
|
||||||
|
resourcePath += p_option.m_customOption->m_resourcePathSeparator;
|
||||||
|
}
|
||||||
|
|
||||||
|
resourcePath += getQuotedPath(p_resourcePaths[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.replace("%1", inputs);
|
||||||
|
cmd.replace("%2", resourcePath);
|
||||||
|
cmd.replace("%3", getQuotedPath(p_option.m_renderingStyleFile));
|
||||||
|
cmd.replace("%4", getQuotedPath(p_option.m_syntaxHighlightStyleFile));
|
||||||
|
cmd.replace("%5", getQuotedPath(p_filePath));
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Exporter::getQuotedPath(const QString &p_path)
|
||||||
|
{
|
||||||
|
return QString("\"%1\"").arg(QDir::toNativeSeparators(p_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Exporter::collectFiles(const QList<QSharedPointer<File>> &p_files, QStringList &p_inputFiles, QStringList &p_resourcePaths)
|
||||||
|
{
|
||||||
|
for (const auto &file : p_files) {
|
||||||
|
p_inputFiles << file->getContentPath();
|
||||||
|
p_resourcePaths << file->getResourcePath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -50,9 +50,17 @@ namespace vnotex
|
|||||||
|
|
||||||
QString doExportPdf(const ExportOption &p_option, const QString &p_outputDir, const File *p_file);
|
QString doExportPdf(const ExportOption &p_option, const QString &p_outputDir, const File *p_file);
|
||||||
|
|
||||||
QString doExportAllInOne(const ExportOption &p_option, Node *p_folder);
|
QString doExportCustom(const ExportOption &p_option, const QString &p_outputDir, const File *p_file);
|
||||||
|
|
||||||
QString doExportAllInOne(const ExportOption &p_option, Notebook *p_notebook);
|
bool doExportCustom(const ExportOption &p_option,
|
||||||
|
const QStringList &p_files,
|
||||||
|
const QStringList &p_resourcePaths,
|
||||||
|
const QString &p_filePath);
|
||||||
|
|
||||||
|
// Export @p_notebook or @p_folder. @p_folder will be considered only when @p_notebook is null.
|
||||||
|
QString doExportPdfAllInOne(const ExportOption &p_option, Notebook *p_notebook, Node *p_folder);
|
||||||
|
|
||||||
|
QString doExportCustomAllInOne(const ExportOption &p_option, Notebook *p_notebook, Node *p_folder);
|
||||||
|
|
||||||
void exportAttachments(Node *p_node,
|
void exportAttachments(Node *p_node,
|
||||||
const QString &p_srcFilePath,
|
const QString &p_srcFilePath,
|
||||||
@ -71,6 +79,15 @@ namespace vnotex
|
|||||||
|
|
||||||
static ExportOption getExportOptionForIntermediateHtml(const ExportOption &p_option, const QString &p_outputDir);
|
static ExportOption getExportOptionForIntermediateHtml(const ExportOption &p_option, const QString &p_outputDir);
|
||||||
|
|
||||||
|
static QString evaluateCommand(const ExportOption &p_option,
|
||||||
|
const QStringList &p_files,
|
||||||
|
const QStringList &p_resourcePaths,
|
||||||
|
const QString &p_filePath);
|
||||||
|
|
||||||
|
static QString getQuotedPath(const QString &p_path);
|
||||||
|
|
||||||
|
static void collectFiles(const QList<QSharedPointer<File>> &p_files, QStringList &p_inputFiles, QStringList &p_resourcePaths);
|
||||||
|
|
||||||
// Managed by QObject.
|
// Managed by QObject.
|
||||||
WebViewExporter *m_webViewExporter = nullptr;
|
WebViewExporter *m_webViewExporter = nullptr;
|
||||||
|
|
||||||
|
@ -248,6 +248,8 @@ QSize WebViewExporter::pageLayoutSize(const QPageLayout &p_layout) const
|
|||||||
void WebViewExporter::prepare(const ExportOption &p_option)
|
void WebViewExporter::prepare(const ExportOption &p_option)
|
||||||
{
|
{
|
||||||
Q_ASSERT(!m_viewer && !m_exportOngoing);
|
Q_ASSERT(!m_viewer && !m_exportOngoing);
|
||||||
|
Q_ASSERT(p_option.m_targetFormat == ExportFormat::PDF || p_option.m_targetFormat == ExportFormat::HTML);
|
||||||
|
|
||||||
{
|
{
|
||||||
// Adapter will be managed by MarkdownViewer.
|
// Adapter will be managed by MarkdownViewer.
|
||||||
auto adapter = new MarkdownViewerAdapter(this);
|
auto adapter = new MarkdownViewerAdapter(this);
|
||||||
@ -265,7 +267,8 @@ void WebViewExporter::prepare(const ExportOption &p_option)
|
|||||||
|
|
||||||
bool scrollable = true;
|
bool scrollable = true;
|
||||||
if (p_option.m_targetFormat == ExportFormat::PDF
|
if (p_option.m_targetFormat == ExportFormat::PDF
|
||||||
|| (p_option.m_targetFormat == ExportFormat::HTML && !p_option.m_htmlOption.m_scrollable)) {
|
|| (p_option.m_targetFormat == ExportFormat::HTML && !p_option.m_htmlOption.m_scrollable)
|
||||||
|
|| (p_option.m_targetFormat == ExportFormat::Custom && !p_option.m_customOption->m_targetPageScrollable)) {
|
||||||
scrollable = false;
|
scrollable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,16 +279,21 @@ void WebViewExporter::prepare(const ExportOption &p_option)
|
|||||||
useWkhtmltopdf = p_option.m_pdfOption.m_useWkhtmltopdf;
|
useWkhtmltopdf = p_option.m_pdfOption.m_useWkhtmltopdf;
|
||||||
pageBodySize = pageLayoutSize(*(p_option.m_pdfOption.m_layout));
|
pageBodySize = pageLayoutSize(*(p_option.m_pdfOption.m_layout));
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << "export page body size" << pageBodySize;
|
qDebug() << "export page body size" << pageBodySize;
|
||||||
m_htmlTemplate = HtmlTemplateHelper::generateMarkdownViewerTemplate(config,
|
|
||||||
p_option.m_renderingStyleFile,
|
HtmlTemplateHelper::Paras paras;
|
||||||
p_option.m_syntaxHighlightStyleFile,
|
paras.m_webStyleSheetFile = p_option.m_renderingStyleFile;
|
||||||
p_option.m_useTransparentBg,
|
paras.m_highlightStyleSheetFile = p_option.m_syntaxHighlightStyleFile;
|
||||||
scrollable,
|
paras.m_transparentBackgroundEnabled = p_option.m_useTransparentBg;
|
||||||
pageBodySize.width(),
|
paras.m_scrollable = scrollable;
|
||||||
pageBodySize.height(),
|
paras.m_bodyWidth = pageBodySize.width();
|
||||||
useWkhtmltopdf,
|
paras.m_bodyHeight = pageBodySize.height();
|
||||||
useWkhtmltopdf ? 2.5 : -1);
|
paras.m_transformSvgToPngEnabled = p_option.m_transformSvgToPngEnabled;
|
||||||
|
paras.m_mathJaxScale = useWkhtmltopdf ? 2.5 : -1;
|
||||||
|
paras.m_removeCodeToolBarEnabled = p_option.m_removeCodeToolBarEnabled;
|
||||||
|
|
||||||
|
m_htmlTemplate = HtmlTemplateHelper::generateMarkdownViewerTemplate(config, paras);
|
||||||
|
|
||||||
{
|
{
|
||||||
const bool addOutlinePanel = p_option.m_targetFormat == ExportFormat::HTML && p_option.m_htmlOption.m_addOutlinePanel;
|
const bool addOutlinePanel = p_option.m_targetFormat == ExportFormat::HTML && p_option.m_htmlOption.m_addOutlinePanel;
|
||||||
|
@ -128,16 +128,31 @@ ProcessUtils::State ProcessUtils::start(const QString &p_program,
|
|||||||
{
|
{
|
||||||
QProcess proc;
|
QProcess proc;
|
||||||
proc.start(p_program, p_args);
|
proc.start(p_program, p_args);
|
||||||
|
return handleProcess(&proc, p_logger, p_askedToStop);
|
||||||
|
}
|
||||||
|
|
||||||
if (!proc.waitForStarted()) {
|
ProcessUtils::State ProcessUtils::start(const QString &p_command,
|
||||||
|
const std::function<void(const QString &)> &p_logger,
|
||||||
|
const bool &p_askedToStop)
|
||||||
|
{
|
||||||
|
QProcess proc;
|
||||||
|
proc.start(p_command);
|
||||||
|
return handleProcess(&proc, p_logger, p_askedToStop);
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessUtils::State ProcessUtils::handleProcess(QProcess *p_process,
|
||||||
|
const std::function<void(const QString &)> &p_logger,
|
||||||
|
const bool &p_askedToStop)
|
||||||
|
{
|
||||||
|
if (!p_process->waitForStarted()) {
|
||||||
return State::FailedToStart;
|
return State::FailedToStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (proc.state() != QProcess::NotRunning) {
|
while (p_process->state() != QProcess::NotRunning) {
|
||||||
Utils::sleepWait(100);
|
Utils::sleepWait(100);
|
||||||
|
|
||||||
auto outBa = proc.readAllStandardOutput();
|
auto outBa = p_process->readAllStandardOutput();
|
||||||
auto errBa = proc.readAllStandardError();
|
auto errBa = p_process->readAllStandardError();
|
||||||
QString msg;
|
QString msg;
|
||||||
if (!outBa.isEmpty()) {
|
if (!outBa.isEmpty()) {
|
||||||
msg += QString::fromLocal8Bit(outBa);
|
msg += QString::fromLocal8Bit(outBa);
|
||||||
@ -154,7 +169,7 @@ ProcessUtils::State ProcessUtils::start(const QString &p_program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return proc.exitStatus() == QProcess::NormalExit ? State::Succeeded : State::Crashed;
|
return p_process->exitStatus() == QProcess::NormalExit ? State::Succeeded : State::Crashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessUtils::startDetached(const QString &p_command)
|
void ProcessUtils::startDetached(const QString &p_command)
|
||||||
|
@ -35,6 +35,10 @@ namespace vnotex
|
|||||||
const std::function<void(const QString &)> &p_logger,
|
const std::function<void(const QString &)> &p_logger,
|
||||||
const bool &p_askedToStop);
|
const bool &p_askedToStop);
|
||||||
|
|
||||||
|
static State start(const QString &p_command,
|
||||||
|
const std::function<void(const QString &)> &p_logger,
|
||||||
|
const bool &p_askedToStop);
|
||||||
|
|
||||||
static void startDetached(const QString &p_command);
|
static void startDetached(const QString &p_command);
|
||||||
|
|
||||||
// Copied from QProcess code.
|
// Copied from QProcess code.
|
||||||
@ -48,6 +52,10 @@ namespace vnotex
|
|||||||
int &p_exitCodeOnSuccess,
|
int &p_exitCodeOnSuccess,
|
||||||
QByteArray &p_stdOut,
|
QByteArray &p_stdOut,
|
||||||
QByteArray &p_stdErr);
|
QByteArray &p_stdErr);
|
||||||
|
|
||||||
|
static State handleProcess(QProcess *p_process,
|
||||||
|
const std::function<void(const QString &)> &p_logger,
|
||||||
|
const bool &p_askedToStop);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <QPrinter>
|
#include <QPrinter>
|
||||||
#include <QPageSetupDialog>
|
#include <QPageSetupDialog>
|
||||||
#include <QPageLayout>
|
#include <QPageLayout>
|
||||||
|
#include <QInputDialog>
|
||||||
|
|
||||||
#include <notebook/notebook.h>
|
#include <notebook/notebook.h>
|
||||||
#include <notebook/node.h>
|
#include <notebook/node.h>
|
||||||
@ -33,6 +34,7 @@
|
|||||||
#include <utils/clipboardutils.h>
|
#include <utils/clipboardutils.h>
|
||||||
#include <export/exporter.h>
|
#include <export/exporter.h>
|
||||||
#include <widgets/locationinputwithbrowsebutton.h>
|
#include <widgets/locationinputwithbrowsebutton.h>
|
||||||
|
#include <widgets/messageboxhelper.h>
|
||||||
|
|
||||||
using namespace vnotex;
|
using namespace vnotex;
|
||||||
|
|
||||||
@ -154,6 +156,10 @@ QGroupBox *ExportDialog::setupTargetGroup(QWidget *p_parent)
|
|||||||
settings = AdvancedSettings::PDF;
|
settings = AdvancedSettings::PDF;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case static_cast<int>(ExportFormat::Custom):
|
||||||
|
settings = AdvancedSettings::Custom;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -283,7 +289,9 @@ QString ExportDialog::getOutputDir() const
|
|||||||
void ExportDialog::initOptions()
|
void ExportDialog::initOptions()
|
||||||
{
|
{
|
||||||
// Read it from config.
|
// Read it from config.
|
||||||
m_option = ConfigMgr::getInst().getSessionConfig().getExportOption();
|
const auto &sessionConfig = ConfigMgr::getInst().getSessionConfig();
|
||||||
|
m_option = sessionConfig.getExportOption();
|
||||||
|
m_customOptions = sessionConfig.getCustomExportOptions();
|
||||||
|
|
||||||
const auto &theme = VNoteX::getInst().getThemeMgr().getCurrentTheme();
|
const auto &theme = VNoteX::getInst().getThemeMgr().getCurrentTheme();
|
||||||
m_option.m_renderingStyleFile = theme.getFile(Theme::File::WebStyleSheet);
|
m_option.m_renderingStyleFile = theme.getFile(Theme::File::WebStyleSheet);
|
||||||
@ -292,6 +300,10 @@ void ExportDialog::initOptions()
|
|||||||
if (m_option.m_outputDir.isEmpty()) {
|
if (m_option.m_outputDir.isEmpty()) {
|
||||||
m_option.m_outputDir = getDefaultOutputDir();
|
m_option.m_outputDir = getDefaultOutputDir();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (findCustomOption(m_option.m_customExport) == -1) {
|
||||||
|
m_option.m_customExport = m_customOptions.isEmpty() ? QString() : m_customOptions[0].m_name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportDialog::restoreFields(const ExportOption &p_option)
|
void ExportDialog::restoreFields(const ExportOption &p_option)
|
||||||
@ -351,6 +363,10 @@ void ExportDialog::saveFields(ExportOption &p_option)
|
|||||||
if (m_advancedSettings[AdvancedSettings::PDF]) {
|
if (m_advancedSettings[AdvancedSettings::PDF]) {
|
||||||
saveFields(p_option.m_pdfOption);
|
saveFields(p_option.m_pdfOption);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_advancedSettings[AdvancedSettings::Custom]) {
|
||||||
|
saveCustomFields(p_option);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportDialog::startExport()
|
void ExportDialog::startExport()
|
||||||
@ -417,6 +433,17 @@ int ExportDialog::doExport(ExportOption p_option)
|
|||||||
appendLog(tr("Please specify a valid wkhtmltopdf executable file (%1)").arg(wkExePath));
|
appendLog(tr("Please specify a valid wkhtmltopdf executable file (%1)").arg(wkExePath));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p_option.m_transformSvgToPngEnabled = true;
|
||||||
|
p_option.m_removeCodeToolBarEnabled = true;
|
||||||
|
} else if (p_option.m_targetFormat == ExportFormat::Custom) {
|
||||||
|
int optIdx = findCustomOption(p_option.m_customExport);
|
||||||
|
if (optIdx == -1) {
|
||||||
|
appendLog(tr("Please specify a valid scheme"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_option.m_customOption = &m_customOptions[optIdx];
|
||||||
}
|
}
|
||||||
|
|
||||||
int exportedFilesCount = 0;
|
int exportedFilesCount = 0;
|
||||||
@ -562,6 +589,10 @@ void ExportDialog::showAdvancedSettings(AdvancedSettings p_settings)
|
|||||||
widget = getPdfAdvancedSettings();
|
widget = getPdfAdvancedSettings();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case AdvancedSettings::Custom:
|
||||||
|
widget = getCustomAdvancedSettings();
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -640,7 +671,7 @@ QWidget *ExportDialog::getPdfAdvancedSettings()
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
m_allInOneCheckBox = WidgetsFactory::createCheckBox(tr("All In One"), widget);
|
m_allInOneCheckBox = WidgetsFactory::createCheckBox(tr("All-In-One"), widget);
|
||||||
m_allInOneCheckBox->setToolTip(tr("Export all source files into one file"));
|
m_allInOneCheckBox->setToolTip(tr("Export all source files into one file"));
|
||||||
connect(m_useWkhtmltopdfCheckBox, &QCheckBox::stateChanged,
|
connect(m_useWkhtmltopdfCheckBox, &QCheckBox::stateChanged,
|
||||||
this, [this](int p_state) {
|
this, [this](int p_state) {
|
||||||
@ -686,6 +717,103 @@ QWidget *ExportDialog::getPdfAdvancedSettings()
|
|||||||
return m_advancedSettings[AdvancedSettings::PDF];
|
return m_advancedSettings[AdvancedSettings::PDF];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QWidget *ExportDialog::getCustomAdvancedSettings()
|
||||||
|
{
|
||||||
|
if (!m_advancedSettings[AdvancedSettings::Custom]) {
|
||||||
|
QWidget *widget = new QWidget(m_advancedGroupBox);
|
||||||
|
auto layout = WidgetsFactory::createFormLayout(widget);
|
||||||
|
layout->setContentsMargins(0, 0, 0, 0);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto schemeLayout = new QHBoxLayout();
|
||||||
|
layout->addRow(tr("Scheme:"), schemeLayout);
|
||||||
|
|
||||||
|
m_customExportComboBox = WidgetsFactory::createComboBox(widget);
|
||||||
|
m_customExportComboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
||||||
|
schemeLayout->addWidget(m_customExportComboBox, 1);
|
||||||
|
|
||||||
|
auto addBtn = new QPushButton(tr("New"), widget);
|
||||||
|
addBtn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||||
|
connect(addBtn, &QPushButton::clicked,
|
||||||
|
this, &ExportDialog::addCustomExportScheme);
|
||||||
|
schemeLayout->addWidget(addBtn);
|
||||||
|
|
||||||
|
auto delBtn = new QPushButton(tr("Delete"), widget);
|
||||||
|
delBtn->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||||
|
connect(delBtn, &QPushButton::clicked,
|
||||||
|
this, &ExportDialog::removeCustomExportScheme);
|
||||||
|
schemeLayout->addWidget(delBtn);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
m_targetSuffixLineEdit = WidgetsFactory::createLineEdit(widget);
|
||||||
|
m_targetSuffixLineEdit->setToolTip(tr("Suffix of the target file like docs/pdf/epub"));
|
||||||
|
m_targetSuffixLineEdit->setEnabled(false);
|
||||||
|
layout->addRow(tr("Target file suffix:"), m_targetSuffixLineEdit);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
m_resourcePathSeparatorLineEdit = WidgetsFactory::createLineEdit(widget);
|
||||||
|
m_resourcePathSeparatorLineEdit->setToolTip(tr("Separator used to concatenate resource folder paths"));
|
||||||
|
m_resourcePathSeparatorLineEdit->setEnabled(false);
|
||||||
|
layout->addRow(tr("Resource path separator:"), m_resourcePathSeparatorLineEdit);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
m_useHtmlInputCheckBox = WidgetsFactory::createCheckBox(tr("Use HTML format as input"), widget);
|
||||||
|
m_useHtmlInputCheckBox->setToolTip(tr("Convert to HTMl format first as the input of the custom export command"));
|
||||||
|
m_useHtmlInputCheckBox->setEnabled(false);
|
||||||
|
layout->addRow(m_useHtmlInputCheckBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
m_allInOneCheckBox = WidgetsFactory::createCheckBox(tr("All-In-One"), widget);
|
||||||
|
m_allInOneCheckBox->setToolTip(tr("Export all source files into one file"));
|
||||||
|
m_allInOneCheckBox->setEnabled(false);
|
||||||
|
layout->addRow(m_allInOneCheckBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
m_targetPageScrollableCheckBox = WidgetsFactory::createCheckBox(tr("Target page scrollable"), widget);
|
||||||
|
m_targetPageScrollableCheckBox->setToolTip(tr("Whether the page of the target file is scrollable"));
|
||||||
|
m_targetPageScrollableCheckBox->setEnabled(false);
|
||||||
|
layout->addRow(m_targetPageScrollableCheckBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto usage = tr("%1: List of input files.\n"
|
||||||
|
"%2: List of paths to search for images and other resources.\n"
|
||||||
|
"%3: Path of rendering CSS style sheet.\n"
|
||||||
|
"%4: Path of syntax highlighting CSS style sheet.\n"
|
||||||
|
"%5: Path of output file.\n");
|
||||||
|
layout->addRow(tr("Command usage:"), new QLabel(usage, widget));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
m_commandTextEdit = WidgetsFactory::createPlainTextEdit(widget);
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
m_commandTextEdit->setPlaceholderText("pandoc.exe --resource-path=.;%2 --css=%3 --css=%4 -s -o %5 %1");
|
||||||
|
#else
|
||||||
|
m_commandTextEdit->setPlaceholderText("pandoc --resource-path=.:%2 --css=%3 --css=%4 -s -o %5 %1");
|
||||||
|
#endif
|
||||||
|
m_commandTextEdit->setMaximumHeight(m_commandTextEdit->minimumSizeHint().height());
|
||||||
|
m_commandTextEdit->setEnabled(false);
|
||||||
|
layout->addRow(tr("Command:"), m_commandTextEdit);
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(m_customExportComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
||||||
|
this, &ExportDialog::customExportCurrentSchemeChanged);
|
||||||
|
|
||||||
|
m_advancedGroupBox->layout()->addWidget(widget);
|
||||||
|
|
||||||
|
m_advancedSettings[AdvancedSettings::Custom] = widget;
|
||||||
|
|
||||||
|
restoreCustomFields(m_option);
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_advancedSettings[AdvancedSettings::Custom];
|
||||||
|
}
|
||||||
|
|
||||||
void ExportDialog::restoreFields(const ExportPdfOption &p_option)
|
void ExportDialog::restoreFields(const ExportPdfOption &p_option)
|
||||||
{
|
{
|
||||||
m_pageLayout = p_option.m_layout;
|
m_pageLayout = p_option.m_layout;
|
||||||
@ -708,6 +836,42 @@ void ExportDialog::saveFields(ExportPdfOption &p_option)
|
|||||||
p_option.m_wkhtmltopdfArgs = m_wkhtmltopdfArgsLineEdit->text();
|
p_option.m_wkhtmltopdfArgs = m_wkhtmltopdfArgsLineEdit->text();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExportDialog::restoreCustomFields(const ExportOption &p_option)
|
||||||
|
{
|
||||||
|
m_customExportComboBox->clear();
|
||||||
|
int curIndex = -1;
|
||||||
|
for (int i = 0; i < m_customOptions.size(); ++i) {
|
||||||
|
m_customExportComboBox->addItem(m_customOptions[i].m_name, m_customOptions[i].m_name);
|
||||||
|
if (m_customOptions[i].m_name == p_option.m_customExport) {
|
||||||
|
curIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_customExportComboBox->setCurrentIndex(curIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExportDialog::saveCustomFields(ExportOption &p_option)
|
||||||
|
{
|
||||||
|
p_option.m_customExport = m_customExportComboBox->currentData().toString();
|
||||||
|
|
||||||
|
int idx = findCustomOption(p_option.m_customExport);
|
||||||
|
if (idx > -1) {
|
||||||
|
auto &opt = m_customOptions[idx];
|
||||||
|
opt.m_targetSuffix = m_targetSuffixLineEdit->text();
|
||||||
|
opt.m_resourcePathSeparator = m_resourcePathSeparatorLineEdit->text();
|
||||||
|
opt.m_useHtmlInput = m_useHtmlInputCheckBox->isChecked();
|
||||||
|
opt.m_allInOne = m_allInOneCheckBox->isChecked();
|
||||||
|
opt.m_targetPageScrollable = m_targetPageScrollableCheckBox->isChecked();
|
||||||
|
|
||||||
|
opt.m_command = m_commandTextEdit->toPlainText().trimmed();
|
||||||
|
int lineIdx = opt.m_command.indexOf(QLatin1Char('\n'));
|
||||||
|
if (lineIdx > -1) {
|
||||||
|
opt.m_command = opt.m_command.left(lineIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigMgr::getInst().getSessionConfig().setCustomExportOptions(m_customOptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ExportDialog::updatePageLayoutButtonLabel()
|
void ExportDialog::updatePageLayoutButtonLabel()
|
||||||
{
|
{
|
||||||
Q_ASSERT(m_pageLayout);
|
Q_ASSERT(m_pageLayout);
|
||||||
@ -715,3 +879,107 @@ void ExportDialog::updatePageLayoutButtonLabel()
|
|||||||
QString("%1, %2").arg(m_pageLayout->pageSize().name(),
|
QString("%1, %2").arg(m_pageLayout->pageSize().name(),
|
||||||
m_pageLayout->orientation() == QPageLayout::Portrait ? tr("Portrait") : tr("Landscape")));
|
m_pageLayout->orientation() == QPageLayout::Portrait ? tr("Portrait") : tr("Landscape")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ExportDialog::findCustomOption(const QString &p_name) const
|
||||||
|
{
|
||||||
|
if (p_name.isEmpty()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < m_customOptions.size(); ++i) {
|
||||||
|
if (m_customOptions[i].m_name == p_name) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExportDialog::addCustomExportScheme()
|
||||||
|
{
|
||||||
|
QString name;
|
||||||
|
while (true) {
|
||||||
|
name = QInputDialog::getText(this, tr("New Custom Export Scheme"), tr("Scheme name:"));
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (findCustomOption(name) != -1) {
|
||||||
|
MessageBoxHelper::notify(MessageBoxHelper::Warning,
|
||||||
|
tr("Name conflicts with existing scheme."),
|
||||||
|
this);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on current scheme.
|
||||||
|
ExportCustomOption newOption;
|
||||||
|
|
||||||
|
{
|
||||||
|
int curIndex = findCustomOption(m_customExportComboBox->currentData().toString());
|
||||||
|
if (curIndex > -1) {
|
||||||
|
newOption = m_customOptions[curIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newOption.m_name = name;
|
||||||
|
m_customOptions.append(newOption);
|
||||||
|
ConfigMgr::getInst().getSessionConfig().setCustomExportOptions(m_customOptions);
|
||||||
|
|
||||||
|
// Add it to combo box.
|
||||||
|
m_customExportComboBox->addItem(name, name);
|
||||||
|
m_customExportComboBox->setCurrentIndex(m_customExportComboBox->findData(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExportDialog::removeCustomExportScheme()
|
||||||
|
{
|
||||||
|
auto name = m_customExportComboBox->currentData().toString();
|
||||||
|
if (name.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = MessageBoxHelper::questionOkCancel(MessageBoxHelper::Warning,
|
||||||
|
tr("Delete scheme (%1)?").arg(name),
|
||||||
|
QString(),
|
||||||
|
QString(),
|
||||||
|
this);
|
||||||
|
if (ret != QMessageBox::Ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int idx = findCustomOption(name);
|
||||||
|
Q_ASSERT(idx > -1);
|
||||||
|
m_customOptions.remove(idx);
|
||||||
|
ConfigMgr::getInst().getSessionConfig().setCustomExportOptions(m_customOptions);
|
||||||
|
|
||||||
|
m_customExportComboBox->removeItem(m_customExportComboBox->currentIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExportDialog::customExportCurrentSchemeChanged(int p_comboIdx)
|
||||||
|
{
|
||||||
|
const bool enabled = p_comboIdx >= 0;
|
||||||
|
m_targetSuffixLineEdit->setEnabled(enabled);
|
||||||
|
m_resourcePathSeparatorLineEdit->setEnabled(enabled);
|
||||||
|
m_useHtmlInputCheckBox->setEnabled(enabled);
|
||||||
|
m_allInOneCheckBox->setEnabled(enabled);
|
||||||
|
m_targetPageScrollableCheckBox->setEnabled(enabled);
|
||||||
|
m_commandTextEdit->setEnabled(enabled);
|
||||||
|
|
||||||
|
if (p_comboIdx < 0) {
|
||||||
|
m_option.m_customExport.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto name = m_customExportComboBox->currentData().toString();
|
||||||
|
m_option.m_customExport = name;
|
||||||
|
int curIndex = findCustomOption(name);
|
||||||
|
Q_ASSERT(curIndex > -1);
|
||||||
|
const auto &opt = m_customOptions[curIndex];
|
||||||
|
m_targetSuffixLineEdit->setText(opt.m_targetSuffix);
|
||||||
|
m_resourcePathSeparatorLineEdit->setText(opt.m_resourcePathSeparator);
|
||||||
|
m_useHtmlInputCheckBox->setChecked(opt.m_useHtmlInput);
|
||||||
|
m_allInOneCheckBox->setChecked(opt.m_allInOne);
|
||||||
|
m_targetPageScrollableCheckBox->setChecked(opt.m_targetPageScrollable);
|
||||||
|
m_commandTextEdit->setPlainText(opt.m_command);
|
||||||
|
}
|
||||||
|
@ -43,12 +43,15 @@ namespace vnotex
|
|||||||
|
|
||||||
void appendLog(const QString &p_log);
|
void appendLog(const QString &p_log);
|
||||||
|
|
||||||
|
void customExportCurrentSchemeChanged(int p_comboIdx);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum AdvancedSettings
|
enum AdvancedSettings
|
||||||
{
|
{
|
||||||
General,
|
General,
|
||||||
HTML,
|
HTML,
|
||||||
PDF,
|
PDF,
|
||||||
|
Custom,
|
||||||
Max
|
Max
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -66,6 +69,8 @@ namespace vnotex
|
|||||||
|
|
||||||
QWidget *getPdfAdvancedSettings();
|
QWidget *getPdfAdvancedSettings();
|
||||||
|
|
||||||
|
QWidget *getCustomAdvancedSettings();
|
||||||
|
|
||||||
void showAdvancedSettings(AdvancedSettings p_settings);
|
void showAdvancedSettings(AdvancedSettings p_settings);
|
||||||
|
|
||||||
void setupButtonBox();
|
void setupButtonBox();
|
||||||
@ -86,6 +91,10 @@ namespace vnotex
|
|||||||
|
|
||||||
void saveFields(ExportPdfOption &p_option);
|
void saveFields(ExportPdfOption &p_option);
|
||||||
|
|
||||||
|
void restoreCustomFields(const ExportOption &p_option);
|
||||||
|
|
||||||
|
void saveCustomFields(ExportOption &p_option);
|
||||||
|
|
||||||
void startExport();
|
void startExport();
|
||||||
|
|
||||||
void updateUIOnExport();
|
void updateUIOnExport();
|
||||||
@ -99,6 +108,12 @@ namespace vnotex
|
|||||||
|
|
||||||
void updatePageLayoutButtonLabel();
|
void updatePageLayoutButtonLabel();
|
||||||
|
|
||||||
|
int findCustomOption(const QString &p_name) const;
|
||||||
|
|
||||||
|
void addCustomExportScheme();
|
||||||
|
|
||||||
|
void removeCustomExportScheme();
|
||||||
|
|
||||||
// Managed by QObject.
|
// Managed by QObject.
|
||||||
Exporter *m_exporter = nullptr;
|
Exporter *m_exporter = nullptr;
|
||||||
|
|
||||||
@ -170,7 +185,24 @@ namespace vnotex
|
|||||||
|
|
||||||
QSharedPointer<QPageLayout> m_pageLayout;
|
QSharedPointer<QPageLayout> m_pageLayout;
|
||||||
|
|
||||||
|
// Custom settings.
|
||||||
|
QComboBox *m_customExportComboBox = nullptr;
|
||||||
|
|
||||||
|
QLineEdit *m_targetSuffixLineEdit = nullptr;
|
||||||
|
|
||||||
|
QLineEdit *m_resourcePathSeparatorLineEdit = nullptr;
|
||||||
|
|
||||||
|
QCheckBox *m_useHtmlInputCheckBox = nullptr;
|
||||||
|
|
||||||
|
QCheckBox *m_customAllInOneCheckBox = nullptr;
|
||||||
|
|
||||||
|
QCheckBox *m_targetPageScrollableCheckBox = nullptr;
|
||||||
|
|
||||||
|
QPlainTextEdit *m_commandTextEdit = nullptr;
|
||||||
|
|
||||||
ExportOption m_option;
|
ExportOption m_option;
|
||||||
|
|
||||||
|
QVector<ExportCustomOption> m_customOptions;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ void ImportFolderUtils::importFolderContentsByLegacyConfig(Notebook *p_notebook,
|
|||||||
try {
|
try {
|
||||||
config = LegacyNotebookUtils::getFolderConfig(rootDir.absolutePath());
|
config = LegacyNotebookUtils::getFolderConfig(rootDir.absolutePath());
|
||||||
} catch (Exception &p_e) {
|
} catch (Exception &p_e) {
|
||||||
Utils::appendMsg(p_errMsg, ImportFolderUtilsTranslate::tr("Failed to read folder config (%1).").arg(rootDir.absolutePath()));
|
Utils::appendMsg(p_errMsg, ImportFolderUtilsTranslate::tr("Failed to read folder config (%1) (%2).").arg(rootDir.absolutePath(), p_e.what()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,5 +109,6 @@ QPlainTextEdit *WidgetsFactory::createPlainTextConsole(QWidget *p_parent)
|
|||||||
QPlainTextEdit *WidgetsFactory::createPlainTextEdit(QWidget *p_parent)
|
QPlainTextEdit *WidgetsFactory::createPlainTextEdit(QWidget *p_parent)
|
||||||
{
|
{
|
||||||
auto edit = new QPlainTextEdit(p_parent);
|
auto edit = new QPlainTextEdit(p_parent);
|
||||||
|
edit->setProperty("ConsoleTextEdit", true);
|
||||||
return edit;
|
return edit;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user