exporter.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. #include "exporter.h"
  2. #include <QWidget>
  3. #include <QTemporaryDir>
  4. #include <notebook/notebook.h>
  5. #include <notebook/node.h>
  6. #include <buffer/buffer.h>
  7. #include <core/file.h>
  8. #include <utils/fileutils.h>
  9. #include <utils/utils.h>
  10. #include <utils/pathutils.h>
  11. #include <utils/processutils.h>
  12. #include <utils/contentmediautils.h>
  13. #include "webviewexporter.h"
  14. #include <core/exception.h>
  15. using namespace vnotex;
  16. Exporter::Exporter(QWidget *p_parent)
  17. : QObject(p_parent)
  18. {
  19. }
  20. QString Exporter::doExport(const ExportOption &p_option, Buffer *p_buffer)
  21. {
  22. m_askedToStop = false;
  23. QString outputFile;
  24. auto file = p_buffer->getFile();
  25. if (!file) {
  26. emit logRequested(tr("Skipped buffer (%1) without file base.").arg(p_buffer->getName()));
  27. return outputFile;
  28. }
  29. // Make sure output folder exists.
  30. if (!QDir().mkpath(p_option.m_outputDir)) {
  31. emit logRequested(tr("Failed to create output folder (%1).").arg(p_option.m_outputDir));
  32. return outputFile;
  33. }
  34. outputFile = doExport(p_option, p_option.m_outputDir, file.data());
  35. cleanUp();
  36. return outputFile;
  37. }
  38. static QString makeOutputFolder(const QString &p_outputDir, const QString &p_folderName)
  39. {
  40. const auto name = FileUtils::generateFileNameWithSequence(p_outputDir, p_folderName);
  41. const auto outputFolder = PathUtils::concatenateFilePath(p_outputDir, name);
  42. if (!QDir().mkpath(outputFolder)) {
  43. return QString();
  44. }
  45. return outputFolder;
  46. }
  47. QString Exporter::doExportMarkdown(const ExportOption &p_option, const QString &p_outputDir, const File *p_file)
  48. {
  49. QString outputFile;
  50. if (!p_file->getContentType().isMarkdown()) {
  51. emit logRequested(tr("Format %1 is not supported to export as Markdown.").arg(p_file->getContentType().m_displayName));
  52. return outputFile;
  53. }
  54. // Export it to a folder with the same name.
  55. const auto name = FileUtils::generateFileNameWithSequence(p_outputDir, p_file->getName(), "");
  56. const auto outputFolder = PathUtils::concatenateFilePath(p_outputDir, name);
  57. QDir outDir(outputFolder);
  58. if (!outDir.mkpath(outputFolder)) {
  59. emit logRequested(tr("Failed to create output folder under (%1).").arg(p_outputDir));
  60. return outputFile;
  61. }
  62. // Copy source file itself.
  63. const auto srcFilePath = p_file->getFilePath();
  64. auto destFilePath = outDir.filePath(p_file->getName());
  65. FileUtils::copyFile(srcFilePath, destFilePath, false);
  66. outputFile = destFilePath;
  67. ContentMediaUtils::copyMediaFiles(p_file, destFilePath);
  68. // Copy attachments if available.
  69. if (p_option.m_exportAttachments) {
  70. exportAttachments(p_file->getNode(), srcFilePath, outputFolder, destFilePath);
  71. }
  72. return outputFile;
  73. }
  74. void Exporter::exportAttachments(Node *p_node,
  75. const QString &p_srcFilePath,
  76. const QString &p_outputFolder,
  77. const QString &p_destFilePath)
  78. {
  79. if (!p_node) {
  80. return;
  81. }
  82. const auto &attachmentFolder = p_node->getAttachmentFolder();
  83. if (!attachmentFolder.isEmpty()) {
  84. auto relativePath = PathUtils::relativePath(PathUtils::parentDirPath(p_srcFilePath),
  85. p_node->fetchAttachmentFolderPath());
  86. auto destAttachmentFolderPath = QDir(p_outputFolder).filePath(relativePath);
  87. destAttachmentFolderPath = FileUtils::renameIfExistsCaseInsensitive(destAttachmentFolderPath);
  88. ContentMediaUtils::copyAttachment(p_node, nullptr, p_destFilePath, destAttachmentFolderPath);
  89. }
  90. }
  91. QString Exporter::doExport(const ExportOption &p_option, Node *p_note)
  92. {
  93. m_askedToStop = false;
  94. QString outputFile;
  95. auto file = p_note->getContentFile();
  96. // Make sure output folder exists.
  97. if (!QDir().mkpath(p_option.m_outputDir)) {
  98. emit logRequested(tr("Failed to create output folder (%1).").arg(p_option.m_outputDir));
  99. return outputFile;
  100. }
  101. outputFile = doExport(p_option, p_option.m_outputDir, file.data());
  102. cleanUp();
  103. return outputFile;
  104. }
  105. QString Exporter::doExportPdfAllInOne(const ExportOption &p_option, Notebook *p_notebook, Node *p_folder)
  106. {
  107. Q_ASSERT((p_notebook || p_folder) && !(p_notebook && p_folder));
  108. // Make path.
  109. const auto name = p_notebook ? tr("notebook_%1").arg(p_notebook->getName()) : p_folder->getName();
  110. const auto outputFolder = makeOutputFolder(p_option.m_outputDir, name);
  111. if (outputFolder.isEmpty()) {
  112. emit logRequested(tr("Failed to create output folder under (%1).").arg(p_option.m_outputDir));
  113. return QString();
  114. }
  115. // Export to HTML to a tmp dir first.
  116. QTemporaryDir tmpDir;
  117. if (!tmpDir.isValid()) {
  118. emit logRequested(tr("Failed to create temporary directory to hold HTML files."));
  119. return QString();
  120. }
  121. auto tmpOption(getExportOptionForIntermediateHtml(p_option, tmpDir.path()));
  122. QStringList htmlFiles;
  123. if (p_notebook) {
  124. htmlFiles = doExportNotebook(tmpOption, tmpDir.path(), p_notebook);
  125. } else {
  126. htmlFiles = doExport(tmpOption, tmpDir.path(), p_folder);
  127. }
  128. cleanUpWebViewExporter();
  129. if (htmlFiles.isEmpty()) {
  130. return QString();
  131. }
  132. if (checkAskedToStop()) {
  133. return QString();
  134. }
  135. auto fileName = FileUtils::generateFileNameWithSequence(outputFolder,
  136. tr("all_in_one_export"),
  137. "pdf");
  138. auto destFilePath = PathUtils::concatenateFilePath(outputFolder, fileName);
  139. if (getWebViewExporter(p_option)->htmlToPdfViaWkhtmltopdf(p_option.m_pdfOption, htmlFiles, destFilePath)) {
  140. emit logRequested(tr("Exported to (%1).").arg(destFilePath));
  141. return destFilePath;
  142. }
  143. return QString();
  144. }
  145. QString Exporter::doExportCustomAllInOne(const ExportOption &p_option, Notebook *p_notebook, Node *p_folder)
  146. {
  147. Q_ASSERT((p_notebook || p_folder) && !(p_notebook && p_folder));
  148. // Make path.
  149. const auto name = p_notebook ? tr("notebook_%1").arg(p_notebook->getName()) : p_folder->getName();
  150. const auto outputFolder = makeOutputFolder(p_option.m_outputDir, name);
  151. if (outputFolder.isEmpty()) {
  152. emit logRequested(tr("Failed to create output folder under (%1).").arg(p_option.m_outputDir));
  153. return QString();
  154. }
  155. QStringList inputFiles;
  156. QStringList resourcePaths;
  157. QTemporaryDir tmpDir;
  158. if (p_option.m_customOption->m_useHtmlInput) {
  159. // Export to HTML to a tmp dir first.
  160. if (!tmpDir.isValid()) {
  161. emit logRequested(tr("Failed to create temporary directory to hold HTML files."));
  162. return QString();
  163. }
  164. auto tmpOption(getExportOptionForIntermediateHtml(p_option, tmpDir.path()));
  165. QStringList htmlFiles;
  166. if (p_notebook) {
  167. htmlFiles = doExportNotebook(tmpOption, tmpDir.path(), p_notebook);
  168. } else {
  169. htmlFiles = doExport(tmpOption, tmpDir.path(), p_folder);
  170. }
  171. cleanUpWebViewExporter();
  172. if (htmlFiles.isEmpty()) {
  173. return QString();
  174. }
  175. if (checkAskedToStop()) {
  176. return QString();
  177. }
  178. inputFiles = htmlFiles;
  179. for (const auto &file : htmlFiles) {
  180. resourcePaths << PathUtils::parentDirPath(file);
  181. }
  182. } else {
  183. // Collect source files.
  184. if (p_notebook) {
  185. collectFiles(p_notebook->collectFiles(), inputFiles, resourcePaths);
  186. } else {
  187. collectFiles(p_folder->collectFiles(), inputFiles, resourcePaths);
  188. }
  189. if (checkAskedToStop()) {
  190. return QString();
  191. }
  192. }
  193. if (inputFiles.isEmpty()) {
  194. return QString();
  195. }
  196. auto fileName = FileUtils::generateFileNameWithSequence(outputFolder,
  197. tr("all_in_one_export"),
  198. p_option.m_customOption->m_targetSuffix);
  199. auto destFilePath = PathUtils::concatenateFilePath(outputFolder, fileName);
  200. bool success = doExportCustom(p_option,
  201. inputFiles,
  202. resourcePaths,
  203. destFilePath);
  204. if (success) {
  205. emit logRequested(tr("Exported to (%1).").arg(destFilePath));
  206. return destFilePath;
  207. }
  208. return QString();
  209. }
  210. QStringList Exporter::doExportFolder(const ExportOption &p_option, Node *p_folder)
  211. {
  212. m_askedToStop = false;
  213. QStringList outputFiles;
  214. if (p_option.m_targetFormat == ExportFormat::PDF
  215. && p_option.m_pdfOption.m_useWkhtmltopdf
  216. && p_option.m_pdfOption.m_allInOne) {
  217. auto file = doExportPdfAllInOne(p_option, nullptr, p_folder);
  218. if (!file.isEmpty()) {
  219. outputFiles << file;
  220. }
  221. } else if (p_option.m_targetFormat == ExportFormat::Custom
  222. && p_option.m_customOption->m_allInOne) {
  223. auto file = doExportCustomAllInOne(p_option, nullptr, p_folder);
  224. if (!file.isEmpty()) {
  225. outputFiles << file;
  226. }
  227. } else {
  228. outputFiles = doExport(p_option, p_option.m_outputDir, p_folder);
  229. }
  230. cleanUp();
  231. return outputFiles;
  232. }
  233. QStringList Exporter::doExport(const ExportOption &p_option, const QString &p_outputDir, Node *p_folder)
  234. {
  235. Q_ASSERT(p_folder->isContainer());
  236. QStringList outputFiles;
  237. // Make path.
  238. const auto outputFolder = makeOutputFolder(p_outputDir, p_folder->getName());
  239. if (outputFolder.isEmpty()) {
  240. emit logRequested(tr("Failed to create output folder under (%1).").arg(p_outputDir));
  241. return outputFiles;
  242. }
  243. try {
  244. p_folder->load();
  245. } catch (Exception &p_e) {
  246. QString msg = tr("Failed to load node (%1) (%2).").arg(p_folder->fetchPath(), p_e.what());
  247. qWarning() << msg;
  248. emit logRequested(msg);
  249. return outputFiles;
  250. }
  251. const auto &children = p_folder->getChildrenRef();
  252. emit progressUpdated(0, children.size());
  253. for (int i = 0; i < children.size(); ++i) {
  254. if (checkAskedToStop()) {
  255. break;
  256. }
  257. const auto &child = children[i];
  258. if (child->hasContent()) {
  259. auto outputFile = doExport(p_option, outputFolder, child->getContentFile().data());
  260. if (!outputFile.isEmpty()) {
  261. outputFiles << outputFile;
  262. }
  263. }
  264. if (p_option.m_recursive && child->isContainer() && child->getUse() == Node::Use::Normal) {
  265. outputFiles.append(doExport(p_option, outputFolder, child.data()));
  266. }
  267. emit progressUpdated(i + 1, children.size());
  268. }
  269. return outputFiles;
  270. }
  271. QString Exporter::doExport(const ExportOption &p_option, const QString &p_outputDir, const File *p_file)
  272. {
  273. QString outputFile;
  274. switch (p_option.m_targetFormat) {
  275. case ExportFormat::Markdown:
  276. outputFile = doExportMarkdown(p_option, p_outputDir, p_file);
  277. break;
  278. case ExportFormat::HTML:
  279. outputFile = doExportHtml(p_option, p_outputDir, p_file);
  280. break;
  281. case ExportFormat::PDF:
  282. outputFile = doExportPdf(p_option, p_outputDir, p_file);
  283. break;
  284. case ExportFormat::Custom:
  285. outputFile = doExportCustom(p_option, p_outputDir, p_file);
  286. break;
  287. default:
  288. emit logRequested(tr("Unknown target format %1.").arg(exportFormatString(p_option.m_targetFormat)));
  289. break;
  290. }
  291. if (!outputFile.isEmpty()) {
  292. emit logRequested(tr("File (%1) exported to (%2)").arg(p_file->getFilePath(), outputFile));
  293. } else {
  294. emit logRequested(tr("Failed to export file (%1)").arg(p_file->getFilePath()));
  295. }
  296. return outputFile;
  297. }
  298. QStringList Exporter::doExport(const ExportOption &p_option, Notebook *p_notebook)
  299. {
  300. m_askedToStop = false;
  301. QStringList outputFiles;
  302. if (p_option.m_targetFormat == ExportFormat::PDF
  303. && p_option.m_pdfOption.m_useWkhtmltopdf
  304. && p_option.m_pdfOption.m_allInOne) {
  305. auto file = doExportPdfAllInOne(p_option, p_notebook, nullptr);
  306. if (!file.isEmpty()) {
  307. outputFiles << file;
  308. }
  309. } else if (p_option.m_targetFormat == ExportFormat::Custom
  310. && p_option.m_customOption->m_allInOne) {
  311. auto file = doExportCustomAllInOne(p_option, p_notebook, nullptr);
  312. if (!file.isEmpty()) {
  313. outputFiles << file;
  314. }
  315. } else {
  316. outputFiles = doExportNotebook(p_option, p_option.m_outputDir, p_notebook);
  317. }
  318. cleanUp();
  319. return outputFiles;
  320. }
  321. QStringList Exporter::doExportNotebook(const ExportOption &p_option, const QString &p_outputDir, Notebook *p_notebook)
  322. {
  323. m_askedToStop = false;
  324. QStringList outputFiles;
  325. // Make path.
  326. const auto outputFolder = makeOutputFolder(p_outputDir, tr("notebook_%1").arg(p_notebook->getName()));
  327. if (outputFolder.isEmpty()) {
  328. emit logRequested(tr("Failed to create output folder under (%1).").arg(p_outputDir));
  329. return outputFiles;
  330. }
  331. auto rootNode = p_notebook->getRootNode();
  332. Q_ASSERT(rootNode->isLoaded());
  333. const auto &children = rootNode->getChildrenRef();
  334. emit progressUpdated(0, children.size());
  335. for (int i = 0; i < children.size(); ++i) {
  336. if (checkAskedToStop()) {
  337. break;
  338. }
  339. const auto &child = children[i];
  340. if (child->hasContent()) {
  341. auto outputFile = doExport(p_option, outputFolder, child->getContentFile().data());
  342. if (!outputFile.isEmpty()) {
  343. outputFiles << outputFile;
  344. }
  345. }
  346. if (child->isContainer() && child->getUse() == Node::Use::Normal) {
  347. outputFiles.append(doExport(p_option, outputFolder, child.data()));
  348. }
  349. emit progressUpdated(i + 1, children.size());
  350. }
  351. cleanUp();
  352. return outputFiles;
  353. }
  354. QString Exporter::doExportHtml(const ExportOption &p_option, const QString &p_outputDir, const File *p_file)
  355. {
  356. QString outputFile;
  357. if (!p_file->getContentType().isMarkdown()) {
  358. emit logRequested(tr("Format %1 is not supported to export as HTML.").arg(p_file->getContentType().m_displayName));
  359. return outputFile;
  360. }
  361. QString suffix = p_option.m_htmlOption.m_useMimeHtmlFormat ? QStringLiteral("mht") : QStringLiteral("html");
  362. auto fileName = FileUtils::generateFileNameWithSequence(p_outputDir,
  363. QFileInfo(p_file->getName()).completeBaseName(),
  364. suffix);
  365. auto destFilePath = PathUtils::concatenateFilePath(p_outputDir, fileName);
  366. bool success = getWebViewExporter(p_option)->doExport(p_option, p_file, destFilePath);
  367. if (success) {
  368. outputFile = destFilePath;
  369. // Copy attachments if available.
  370. if (p_option.m_exportAttachments) {
  371. exportAttachments(p_file->getNode(), p_file->getFilePath(), p_outputDir, destFilePath);
  372. }
  373. }
  374. return outputFile;
  375. }
  376. WebViewExporter *Exporter::getWebViewExporter(const ExportOption &p_option)
  377. {
  378. if (!m_webViewExporter) {
  379. m_webViewExporter = new WebViewExporter(static_cast<QWidget *>(parent()));
  380. connect(m_webViewExporter, &WebViewExporter::logRequested,
  381. this, &Exporter::logRequested);
  382. m_webViewExporter->prepare(p_option);
  383. }
  384. return m_webViewExporter;
  385. }
  386. void Exporter::cleanUpWebViewExporter()
  387. {
  388. if (m_webViewExporter) {
  389. m_webViewExporter->clear();
  390. delete m_webViewExporter;
  391. m_webViewExporter = nullptr;
  392. }
  393. }
  394. void Exporter::cleanUp()
  395. {
  396. cleanUpWebViewExporter();
  397. }
  398. void Exporter::stop()
  399. {
  400. m_askedToStop = true;
  401. if (m_webViewExporter) {
  402. m_webViewExporter->stop();
  403. }
  404. }
  405. bool Exporter::checkAskedToStop() const
  406. {
  407. if (m_askedToStop) {
  408. emit const_cast<Exporter *>(this)->logRequested(tr("Asked to stop. Aborting."));
  409. return true;
  410. }
  411. return false;
  412. }
  413. QString Exporter::doExportPdf(const ExportOption &p_option, const QString &p_outputDir, const File *p_file)
  414. {
  415. QString outputFile;
  416. if (!p_file->getContentType().isMarkdown()) {
  417. emit logRequested(tr("Format %1 is not supported to export as PDF.").arg(p_file->getContentType().m_displayName));
  418. return outputFile;
  419. }
  420. auto fileName = FileUtils::generateFileNameWithSequence(p_outputDir,
  421. QFileInfo(p_file->getName()).completeBaseName(),
  422. "pdf");
  423. auto destFilePath = PathUtils::concatenateFilePath(p_outputDir, fileName);
  424. bool success = getWebViewExporter(p_option)->doExport(p_option, p_file, destFilePath);
  425. if (success) {
  426. outputFile = destFilePath;
  427. // Copy attachments if available.
  428. if (p_option.m_exportAttachments) {
  429. exportAttachments(p_file->getNode(), p_file->getFilePath(), p_outputDir, destFilePath);
  430. }
  431. }
  432. return outputFile;
  433. }
  434. QString Exporter::doExportCustom(const ExportOption &p_option, const QString &p_outputDir, const File *p_file)
  435. {
  436. Q_ASSERT(p_option.m_customOption);
  437. QStringList inputFiles;
  438. QStringList resourcePaths;
  439. QTemporaryDir tmpDir;
  440. if (p_option.m_customOption->m_useHtmlInput) {
  441. // Export to HTML to a tmp dir first.
  442. if (!tmpDir.isValid()) {
  443. emit logRequested(tr("Failed to create temporary directory to hold HTML files."));
  444. return QString();
  445. }
  446. auto tmpOption(getExportOptionForIntermediateHtml(p_option, tmpDir.path()));
  447. auto htmlFile = doExport(tmpOption, tmpDir.path(), p_file);
  448. if (htmlFile.isEmpty()) {
  449. return QString();
  450. }
  451. if (checkAskedToStop()) {
  452. return QString();
  453. }
  454. cleanUpWebViewExporter();
  455. inputFiles << htmlFile;
  456. resourcePaths << PathUtils::parentDirPath(htmlFile);
  457. } else {
  458. inputFiles << p_file->getContentPath();
  459. resourcePaths << p_file->getResourcePath();
  460. }
  461. auto fileName = FileUtils::generateFileNameWithSequence(p_outputDir,
  462. QFileInfo(p_file->getName()).completeBaseName(),
  463. p_option.m_customOption->m_targetSuffix);
  464. auto destFilePath = PathUtils::concatenateFilePath(p_outputDir, fileName);
  465. bool success = doExportCustom(p_option,
  466. inputFiles,
  467. resourcePaths,
  468. destFilePath);
  469. if (success) {
  470. // Copy attachments if available.
  471. if (p_option.m_exportAttachments) {
  472. exportAttachments(p_file->getNode(), p_file->getFilePath(), p_outputDir, destFilePath);
  473. }
  474. return destFilePath;
  475. }
  476. return QString();
  477. }
  478. ExportOption Exporter::getExportOptionForIntermediateHtml(const ExportOption &p_option, const QString &p_outputDir)
  479. {
  480. ExportOption tmpOption(p_option);
  481. tmpOption.m_exportAttachments = false;
  482. tmpOption.m_targetFormat = ExportFormat::HTML;
  483. tmpOption.m_transformSvgToPngEnabled = true;
  484. tmpOption.m_removeCodeToolBarEnabled = true;
  485. tmpOption.m_htmlOption.m_embedStyles = true;
  486. tmpOption.m_htmlOption.m_completePage = true;
  487. tmpOption.m_htmlOption.m_embedImages = false;
  488. tmpOption.m_htmlOption.m_useMimeHtmlFormat = false;
  489. tmpOption.m_htmlOption.m_addOutlinePanel = false;
  490. tmpOption.m_htmlOption.m_scrollable = false;
  491. if (p_option.m_targetFormat == ExportFormat::Custom && p_option.m_customOption->m_targetPageScrollable) {
  492. tmpOption.m_htmlOption.m_scrollable = true;
  493. }
  494. tmpOption.m_outputDir = p_outputDir;
  495. return tmpOption;
  496. }
  497. bool Exporter::doExportCustom(const ExportOption &p_option,
  498. const QStringList &p_files,
  499. const QStringList &p_resourcePaths,
  500. const QString &p_filePath)
  501. {
  502. const auto cmd = evaluateCommand(p_option,
  503. p_files,
  504. p_resourcePaths,
  505. p_filePath);
  506. emit logRequested(tr("Custom command: %1").arg(cmd));
  507. qDebug() << "custom export" << cmd;
  508. auto state = ProcessUtils::start(cmd,
  509. [this](const QString &msg) {
  510. emit logRequested(msg);
  511. },
  512. m_askedToStop);
  513. return state == ProcessUtils::Succeeded;
  514. }
  515. QString Exporter::evaluateCommand(const ExportOption &p_option,
  516. const QStringList &p_files,
  517. const QStringList &p_resourcePaths,
  518. const QString &p_filePath)
  519. {
  520. auto cmd(p_option.m_customOption->m_command);
  521. QString inputs;
  522. for (int i = 0; i < p_files.size(); ++i) {
  523. if (i > 0) {
  524. inputs += " ";
  525. }
  526. inputs += getQuotedPath(p_files[i]);
  527. }
  528. QString resourcePath;
  529. for (int i = 0; i < p_resourcePaths.size(); ++i) {
  530. bool duplicated = false;
  531. for (int j = 0; j < i; ++j) {
  532. if (p_resourcePaths[j] == p_resourcePaths[i]) {
  533. // Deduplicate.
  534. duplicated = true;
  535. break;
  536. }
  537. }
  538. if (duplicated) {
  539. continue;
  540. }
  541. if (i > 0) {
  542. resourcePath += p_option.m_customOption->m_resourcePathSeparator;
  543. }
  544. resourcePath += getQuotedPath(p_resourcePaths[i]);
  545. }
  546. cmd.replace("%1", inputs);
  547. cmd.replace("%2", resourcePath);
  548. cmd.replace("%3", getQuotedPath(p_option.m_renderingStyleFile));
  549. cmd.replace("%4", getQuotedPath(p_option.m_syntaxHighlightStyleFile));
  550. cmd.replace("%5", getQuotedPath(p_filePath));
  551. return cmd;
  552. }
  553. QString Exporter::getQuotedPath(const QString &p_path)
  554. {
  555. return QStringLiteral("\"%1\"").arg(QDir::toNativeSeparators(p_path));
  556. }
  557. void Exporter::collectFiles(const QList<QSharedPointer<File>> &p_files, QStringList &p_inputFiles, QStringList &p_resourcePaths)
  558. {
  559. for (const auto &file : p_files) {
  560. p_inputFiles << file->getContentPath();
  561. p_resourcePaths << file->getResourcePath();
  562. }
  563. }