| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689 |
- #include "exporter.h"
- #include <QWidget>
- #include <QTemporaryDir>
- #include <notebook/notebook.h>
- #include <notebook/node.h>
- #include <buffer/buffer.h>
- #include <core/file.h>
- #include <utils/fileutils.h>
- #include <utils/utils.h>
- #include <utils/pathutils.h>
- #include <utils/processutils.h>
- #include <utils/contentmediautils.h>
- #include "webviewexporter.h"
- #include <core/exception.h>
- using namespace vnotex;
- Exporter::Exporter(QWidget *p_parent)
- : QObject(p_parent)
- {
- }
- QString Exporter::doExport(const ExportOption &p_option, Buffer *p_buffer)
- {
- m_askedToStop = false;
- QString outputFile;
- auto file = p_buffer->getFile();
- if (!file) {
- emit logRequested(tr("Skipped buffer (%1) without file base.").arg(p_buffer->getName()));
- return outputFile;
- }
- // Make sure output folder exists.
- if (!QDir().mkpath(p_option.m_outputDir)) {
- emit logRequested(tr("Failed to create output folder (%1).").arg(p_option.m_outputDir));
- return outputFile;
- }
- outputFile = doExport(p_option, p_option.m_outputDir, file.data());
- cleanUp();
- return outputFile;
- }
- static QString makeOutputFolder(const QString &p_outputDir, const QString &p_folderName)
- {
- const auto name = FileUtils::generateFileNameWithSequence(p_outputDir, p_folderName);
- const auto outputFolder = PathUtils::concatenateFilePath(p_outputDir, name);
- if (!QDir().mkpath(outputFolder)) {
- return QString();
- }
- return outputFolder;
- }
- QString Exporter::doExportMarkdown(const ExportOption &p_option, const QString &p_outputDir, const File *p_file)
- {
- QString outputFile;
- if (!p_file->getContentType().isMarkdown()) {
- emit logRequested(tr("Format %1 is not supported to export as Markdown.").arg(p_file->getContentType().m_displayName));
- return outputFile;
- }
- // Export it to a folder with the same name.
- const auto name = FileUtils::generateFileNameWithSequence(p_outputDir, p_file->getName(), "");
- const auto outputFolder = PathUtils::concatenateFilePath(p_outputDir, name);
- QDir outDir(outputFolder);
- if (!outDir.mkpath(outputFolder)) {
- emit logRequested(tr("Failed to create output folder under (%1).").arg(p_outputDir));
- return outputFile;
- }
- // Copy source file itself.
- const auto srcFilePath = p_file->getFilePath();
- auto destFilePath = outDir.filePath(p_file->getName());
- FileUtils::copyFile(srcFilePath, destFilePath, false);
- outputFile = destFilePath;
- ContentMediaUtils::copyMediaFiles(p_file, destFilePath);
- // Copy attachments if available.
- if (p_option.m_exportAttachments) {
- exportAttachments(p_file->getNode(), srcFilePath, outputFolder, destFilePath);
- }
- return outputFile;
- }
- void Exporter::exportAttachments(Node *p_node,
- const QString &p_srcFilePath,
- const QString &p_outputFolder,
- const QString &p_destFilePath)
- {
- if (!p_node) {
- return;
- }
- const auto &attachmentFolder = p_node->getAttachmentFolder();
- if (!attachmentFolder.isEmpty()) {
- auto relativePath = PathUtils::relativePath(PathUtils::parentDirPath(p_srcFilePath),
- p_node->fetchAttachmentFolderPath());
- auto destAttachmentFolderPath = QDir(p_outputFolder).filePath(relativePath);
- destAttachmentFolderPath = FileUtils::renameIfExistsCaseInsensitive(destAttachmentFolderPath);
- ContentMediaUtils::copyAttachment(p_node, nullptr, p_destFilePath, destAttachmentFolderPath);
- }
- }
- QString Exporter::doExport(const ExportOption &p_option, Node *p_note)
- {
- m_askedToStop = false;
- QString outputFile;
- auto file = p_note->getContentFile();
- // Make sure output folder exists.
- if (!QDir().mkpath(p_option.m_outputDir)) {
- emit logRequested(tr("Failed to create output folder (%1).").arg(p_option.m_outputDir));
- return outputFile;
- }
- outputFile = doExport(p_option, p_option.m_outputDir, file.data());
- cleanUp();
- return outputFile;
- }
- 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.
- 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()) {
- emit logRequested(tr("Failed to create output folder under (%1).").arg(p_option.m_outputDir));
- return QString();
- }
- // Export to HTML to a tmp dir first.
- QTemporaryDir tmpDir;
- if (!tmpDir.isValid()) {
- emit logRequested(tr("Failed to create temporary directory to hold HTML files."));
- return QString();
- }
- auto tmpOption(getExportOptionForIntermediateHtml(p_option, tmpDir.path()));
- QStringList htmlFiles;
- if (p_notebook) {
- htmlFiles = doExportNotebook(tmpOption, tmpDir.path(), p_notebook);
- } else {
- htmlFiles = doExport(tmpOption, tmpDir.path(), p_folder);
- }
- cleanUpWebViewExporter();
- if (htmlFiles.isEmpty()) {
- return QString();
- }
- if (checkAskedToStop()) {
- return QString();
- }
- auto fileName = FileUtils::generateFileNameWithSequence(outputFolder,
- tr("all_in_one_export"),
- "pdf");
- auto destFilePath = PathUtils::concatenateFilePath(outputFolder, fileName);
- if (getWebViewExporter(p_option)->htmlToPdfViaWkhtmltopdf(p_option.m_pdfOption, htmlFiles, destFilePath)) {
- emit logRequested(tr("Exported to (%1).").arg(destFilePath));
- return destFilePath;
- }
- return QString();
- }
- 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.
- 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()) {
- emit logRequested(tr("Failed to create output folder under (%1).").arg(p_option.m_outputDir));
- return QString();
- }
- 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 directory to hold HTML files."));
- return QString();
- }
- auto tmpOption(getExportOptionForIntermediateHtml(p_option, tmpDir.path()));
- QStringList htmlFiles;
- if (p_notebook) {
- htmlFiles = doExportNotebook(tmpOption, tmpDir.path(), p_notebook);
- } else {
- htmlFiles = doExport(tmpOption, tmpDir.path(), p_folder);
- }
- cleanUpWebViewExporter();
- if (htmlFiles.isEmpty()) {
- return QString();
- }
- if (checkAskedToStop()) {
- return QString();
- }
- 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,
- tr("all_in_one_export"),
- p_option.m_customOption->m_targetSuffix);
- auto destFilePath = PathUtils::concatenateFilePath(outputFolder, fileName);
- bool success = doExportCustom(p_option,
- inputFiles,
- resourcePaths,
- destFilePath);
- if (success) {
- emit logRequested(tr("Exported to (%1).").arg(destFilePath));
- return destFilePath;
- }
- return QString();
- }
- QStringList Exporter::doExportFolder(const ExportOption &p_option, Node *p_folder)
- {
- m_askedToStop = false;
- QStringList outputFiles;
- if (p_option.m_targetFormat == ExportFormat::PDF
- && 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()) {
- outputFiles << file;
- }
- } else {
- outputFiles = doExport(p_option, p_option.m_outputDir, p_folder);
- }
- cleanUp();
- return outputFiles;
- }
- QStringList Exporter::doExport(const ExportOption &p_option, const QString &p_outputDir, Node *p_folder)
- {
- Q_ASSERT(p_folder->isContainer());
- QStringList outputFiles;
- // Make path.
- const auto outputFolder = makeOutputFolder(p_outputDir, p_folder->getName());
- if (outputFolder.isEmpty()) {
- emit logRequested(tr("Failed to create output folder under (%1).").arg(p_outputDir));
- return outputFiles;
- }
- try {
- 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();
- emit progressUpdated(0, children.size());
- for (int i = 0; i < children.size(); ++i) {
- if (checkAskedToStop()) {
- break;
- }
- const auto &child = children[i];
- if (child->hasContent()) {
- auto outputFile = doExport(p_option, outputFolder, child->getContentFile().data());
- if (!outputFile.isEmpty()) {
- outputFiles << outputFile;
- }
- }
- if (p_option.m_recursive && child->isContainer() && child->getUse() == Node::Use::Normal) {
- outputFiles.append(doExport(p_option, outputFolder, child.data()));
- }
- emit progressUpdated(i + 1, children.size());
- }
- return outputFiles;
- }
- QString Exporter::doExport(const ExportOption &p_option, const QString &p_outputDir, const File *p_file)
- {
- QString outputFile;
- switch (p_option.m_targetFormat) {
- case ExportFormat::Markdown:
- outputFile = doExportMarkdown(p_option, p_outputDir, p_file);
- break;
- case ExportFormat::HTML:
- outputFile = doExportHtml(p_option, p_outputDir, p_file);
- break;
- case ExportFormat::PDF:
- outputFile = doExportPdf(p_option, p_outputDir, p_file);
- break;
- case ExportFormat::Custom:
- outputFile = doExportCustom(p_option, p_outputDir, p_file);
- break;
- default:
- emit logRequested(tr("Unknown target format %1.").arg(exportFormatString(p_option.m_targetFormat)));
- break;
- }
- if (!outputFile.isEmpty()) {
- emit logRequested(tr("File (%1) exported to (%2)").arg(p_file->getFilePath(), outputFile));
- } else {
- emit logRequested(tr("Failed to export file (%1)").arg(p_file->getFilePath()));
- }
- return outputFile;
- }
- QStringList Exporter::doExport(const ExportOption &p_option, Notebook *p_notebook)
- {
- m_askedToStop = false;
- QStringList outputFiles;
- if (p_option.m_targetFormat == ExportFormat::PDF
- && 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()) {
- outputFiles << file;
- }
- } else {
- outputFiles = doExportNotebook(p_option, p_option.m_outputDir, p_notebook);
- }
- cleanUp();
- return outputFiles;
- }
- QStringList Exporter::doExportNotebook(const ExportOption &p_option, const QString &p_outputDir, Notebook *p_notebook)
- {
- m_askedToStop = false;
- QStringList outputFiles;
- // Make path.
- const auto outputFolder = makeOutputFolder(p_outputDir, tr("notebook_%1").arg(p_notebook->getName()));
- if (outputFolder.isEmpty()) {
- emit logRequested(tr("Failed to create output folder under (%1).").arg(p_outputDir));
- return outputFiles;
- }
- auto rootNode = p_notebook->getRootNode();
- Q_ASSERT(rootNode->isLoaded());
- const auto &children = rootNode->getChildrenRef();
- emit progressUpdated(0, children.size());
- for (int i = 0; i < children.size(); ++i) {
- if (checkAskedToStop()) {
- break;
- }
- const auto &child = children[i];
- if (child->hasContent()) {
- auto outputFile = doExport(p_option, outputFolder, child->getContentFile().data());
- if (!outputFile.isEmpty()) {
- outputFiles << outputFile;
- }
- }
- if (child->isContainer() && child->getUse() == Node::Use::Normal) {
- outputFiles.append(doExport(p_option, outputFolder, child.data()));
- }
- emit progressUpdated(i + 1, children.size());
- }
- cleanUp();
- return outputFiles;
- }
- QString Exporter::doExportHtml(const ExportOption &p_option, const QString &p_outputDir, const File *p_file)
- {
- QString outputFile;
- if (!p_file->getContentType().isMarkdown()) {
- emit logRequested(tr("Format %1 is not supported to export as HTML.").arg(p_file->getContentType().m_displayName));
- return outputFile;
- }
- QString suffix = p_option.m_htmlOption.m_useMimeHtmlFormat ? QStringLiteral("mht") : QStringLiteral("html");
- auto fileName = FileUtils::generateFileNameWithSequence(p_outputDir,
- QFileInfo(p_file->getName()).completeBaseName(),
- suffix);
- auto destFilePath = PathUtils::concatenateFilePath(p_outputDir, fileName);
- bool success = getWebViewExporter(p_option)->doExport(p_option, p_file, destFilePath);
- if (success) {
- outputFile = destFilePath;
- // Copy attachments if available.
- if (p_option.m_exportAttachments) {
- exportAttachments(p_file->getNode(), p_file->getFilePath(), p_outputDir, destFilePath);
- }
- }
- return outputFile;
- }
- WebViewExporter *Exporter::getWebViewExporter(const ExportOption &p_option)
- {
- if (!m_webViewExporter) {
- m_webViewExporter = new WebViewExporter(static_cast<QWidget *>(parent()));
- connect(m_webViewExporter, &WebViewExporter::logRequested,
- this, &Exporter::logRequested);
- m_webViewExporter->prepare(p_option);
- }
- return m_webViewExporter;
- }
- void Exporter::cleanUpWebViewExporter()
- {
- if (m_webViewExporter) {
- m_webViewExporter->clear();
- delete m_webViewExporter;
- m_webViewExporter = nullptr;
- }
- }
- void Exporter::cleanUp()
- {
- cleanUpWebViewExporter();
- }
- void Exporter::stop()
- {
- m_askedToStop = true;
- if (m_webViewExporter) {
- m_webViewExporter->stop();
- }
- }
- bool Exporter::checkAskedToStop() const
- {
- if (m_askedToStop) {
- emit const_cast<Exporter *>(this)->logRequested(tr("Asked to stop. Aborting."));
- return true;
- }
- return false;
- }
- QString Exporter::doExportPdf(const ExportOption &p_option, const QString &p_outputDir, const File *p_file)
- {
- QString outputFile;
- if (!p_file->getContentType().isMarkdown()) {
- emit logRequested(tr("Format %1 is not supported to export as PDF.").arg(p_file->getContentType().m_displayName));
- return outputFile;
- }
- auto fileName = FileUtils::generateFileNameWithSequence(p_outputDir,
- QFileInfo(p_file->getName()).completeBaseName(),
- "pdf");
- auto destFilePath = PathUtils::concatenateFilePath(p_outputDir, fileName);
- bool success = getWebViewExporter(p_option)->doExport(p_option, p_file, destFilePath);
- if (success) {
- outputFile = destFilePath;
- // Copy attachments if available.
- if (p_option.m_exportAttachments) {
- exportAttachments(p_file->getNode(), p_file->getFilePath(), p_outputDir, destFilePath);
- }
- }
- 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 directory 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 tmpOption(p_option);
- tmpOption.m_exportAttachments = false;
- tmpOption.m_targetFormat = ExportFormat::HTML;
- tmpOption.m_transformSvgToPngEnabled = true;
- tmpOption.m_removeCodeToolBarEnabled = true;
- tmpOption.m_htmlOption.m_embedStyles = true;
- tmpOption.m_htmlOption.m_completePage = true;
- tmpOption.m_htmlOption.m_embedImages = false;
- tmpOption.m_htmlOption.m_useMimeHtmlFormat = false;
- tmpOption.m_htmlOption.m_addOutlinePanel = 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;
- 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 QStringLiteral("\"%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();
- }
- }
|