vexporter.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. #include "vexporter.h"
  2. #include <QDebug>
  3. #include <QWidget>
  4. #include <QWebChannel>
  5. #include "vconfigmanager.h"
  6. #include "vfile.h"
  7. #include "vwebview.h"
  8. #include "utils/vutils.h"
  9. #include "vpreviewpage.h"
  10. #include "vconstants.h"
  11. #include "vmarkdownconverter.h"
  12. #include "vdocument.h"
  13. extern VConfigManager *g_config;
  14. VExporter::VExporter(QWidget *p_parent)
  15. : QObject(p_parent),
  16. m_webViewer(NULL),
  17. m_state(ExportState::Idle)
  18. {
  19. }
  20. void VExporter::prepareExport(const ExportOption &p_opt)
  21. {
  22. m_htmlTemplate = VUtils::generateHtmlTemplate(p_opt.m_renderer,
  23. p_opt.m_renderBg,
  24. p_opt.m_renderStyle,
  25. p_opt.m_renderCodeBlockStyle,
  26. p_opt.m_format == ExportFormat::PDF);
  27. m_exportHtmlTemplate = VUtils::generateExportHtmlTemplate(p_opt.m_renderBg);
  28. m_pageLayout = *(p_opt.m_layout);
  29. }
  30. bool VExporter::exportPDF(VFile *p_file,
  31. const ExportOption &p_opt,
  32. const QString &p_outputFile,
  33. QString *p_errMsg)
  34. {
  35. return exportViaWebView(p_file, p_opt, p_outputFile, p_errMsg);
  36. }
  37. bool VExporter::exportHTML(VFile *p_file,
  38. const ExportOption &p_opt,
  39. const QString &p_outputFile,
  40. QString *p_errMsg)
  41. {
  42. return exportViaWebView(p_file, p_opt, p_outputFile, p_errMsg);
  43. }
  44. void VExporter::initWebViewer(VFile *p_file, const ExportOption &p_opt)
  45. {
  46. Q_ASSERT(!m_webViewer);
  47. m_webViewer = new VWebView(p_file, static_cast<QWidget *>(parent()));
  48. m_webViewer->hide();
  49. VPreviewPage *page = new VPreviewPage(m_webViewer);
  50. m_webViewer->setPage(page);
  51. connect(page, &VPreviewPage::loadFinished,
  52. this, &VExporter::handleLoadFinished);
  53. m_webDocument = new VDocument(p_file, m_webViewer);
  54. connect(m_webDocument, &VDocument::logicsFinished,
  55. this, &VExporter::handleLogicsFinished);
  56. QWebChannel *channel = new QWebChannel(m_webViewer);
  57. channel->registerObject(QStringLiteral("content"), m_webDocument);
  58. page->setWebChannel(channel);
  59. // Need to generate HTML using Hoedown.
  60. if (p_opt.m_renderer == MarkdownConverterType::Hoedown) {
  61. VMarkdownConverter mdConverter;
  62. QString toc;
  63. QString html = mdConverter.generateHtml(p_file->getContent(),
  64. g_config->getMarkdownExtensions(),
  65. toc);
  66. m_webDocument->setHtml(html);
  67. }
  68. m_webViewer->setHtml(m_htmlTemplate, p_file->getBaseUrl());
  69. }
  70. void VExporter::handleLogicsFinished()
  71. {
  72. Q_ASSERT(!(m_noteState & NoteState::WebLogicsReady));
  73. m_noteState = NoteState(m_noteState | NoteState::WebLogicsReady);
  74. }
  75. void VExporter::handleLoadFinished(bool p_ok)
  76. {
  77. Q_ASSERT(!(m_noteState & NoteState::WebLoadFinished));
  78. m_noteState = NoteState(m_noteState | NoteState::WebLoadFinished);
  79. if (!p_ok) {
  80. m_noteState = NoteState(m_noteState | NoteState::Failed);
  81. }
  82. }
  83. void VExporter::clearWebViewer()
  84. {
  85. // m_webDocument will be freeed by QObject.
  86. delete m_webViewer;
  87. m_webViewer = NULL;
  88. m_webDocument = NULL;
  89. }
  90. bool VExporter::exportToPDF(VWebView *p_webViewer,
  91. const QString &p_filePath,
  92. const QPageLayout &p_layout)
  93. {
  94. int pdfPrinted = 0;
  95. p_webViewer->page()->printToPdf([&, this](const QByteArray &p_result) {
  96. if (p_result.isEmpty() || this->m_state == ExportState::Cancelled) {
  97. pdfPrinted = -1;
  98. return;
  99. }
  100. V_ASSERT(!p_filePath.isEmpty());
  101. QFile file(p_filePath);
  102. if (!file.open(QFile::WriteOnly)) {
  103. pdfPrinted = -1;
  104. return;
  105. }
  106. file.write(p_result.data(), p_result.size());
  107. file.close();
  108. pdfPrinted = 1;
  109. }, p_layout);
  110. while (pdfPrinted == 0) {
  111. VUtils::sleepWait(100);
  112. if (m_state == ExportState::Cancelled) {
  113. break;
  114. }
  115. }
  116. return pdfPrinted == 1;
  117. }
  118. bool VExporter::exportViaWebView(VFile *p_file,
  119. const ExportOption &p_opt,
  120. const QString &p_outputFile,
  121. QString *p_errMsg)
  122. {
  123. Q_UNUSED(p_errMsg);
  124. bool ret = false;
  125. bool isOpened = p_file->isOpened();
  126. if (!isOpened && !p_file->open()) {
  127. goto exit;
  128. }
  129. Q_ASSERT(m_state == ExportState::Idle);
  130. m_state = ExportState::Busy;
  131. clearNoteState();
  132. initWebViewer(p_file, p_opt);
  133. while (!isNoteStateReady()) {
  134. VUtils::sleepWait(100);
  135. if (m_state == ExportState::Cancelled) {
  136. goto exit;
  137. }
  138. if (isNoteStateFailed()) {
  139. m_state = ExportState::Failed;
  140. goto exit;
  141. }
  142. }
  143. // Wait to ensure Web side is really ready.
  144. VUtils::sleepWait(200);
  145. if (m_state == ExportState::Cancelled) {
  146. goto exit;
  147. }
  148. {
  149. bool exportRet = false;
  150. switch (p_opt.m_format) {
  151. case ExportFormat::PDF:
  152. exportRet = exportToPDF(m_webViewer,
  153. p_outputFile,
  154. m_pageLayout);
  155. break;
  156. case ExportFormat::HTML:
  157. exportRet = exportToHTML(m_webViewer,
  158. m_webDocument,
  159. p_outputFile);
  160. break;
  161. default:
  162. break;
  163. }
  164. clearNoteState();
  165. if (!isOpened) {
  166. p_file->close();
  167. }
  168. if (exportRet) {
  169. m_state = ExportState::Successful;
  170. } else {
  171. m_state = ExportState::Failed;
  172. }
  173. }
  174. exit:
  175. clearWebViewer();
  176. if (m_state == ExportState::Successful) {
  177. ret = true;
  178. }
  179. m_state = ExportState::Idle;
  180. return ret;
  181. }
  182. bool VExporter::exportToHTML(VWebView *p_webViewer,
  183. VDocument *p_webDocument,
  184. const QString &p_filePath)
  185. {
  186. Q_UNUSED(p_webViewer);
  187. int htmlExported = 0;
  188. connect(p_webDocument, &VDocument::htmlContentFinished,
  189. this, [&, this](const QString &p_headContent, const QString &p_bodyContent) {
  190. if (p_bodyContent.isEmpty() || this->m_state == ExportState::Cancelled) {
  191. htmlExported = -1;
  192. return;
  193. }
  194. Q_ASSERT(!p_filePath.isEmpty());
  195. QFile file(p_filePath);
  196. if (!file.open(QFile::WriteOnly)) {
  197. htmlExported = -1;
  198. return;
  199. }
  200. QString html(m_exportHtmlTemplate);
  201. html.replace(HtmlHolder::c_headHolder, p_headContent);
  202. html.replace(HtmlHolder::c_bodyHolder, p_bodyContent);
  203. file.write(html.toUtf8());
  204. file.close();
  205. htmlExported = 1;
  206. });
  207. p_webDocument->getHtmlContentAsync();
  208. while (htmlExported == 0) {
  209. VUtils::sleepWait(100);
  210. if (m_state == ExportState::Cancelled) {
  211. break;
  212. }
  213. }
  214. return htmlExported == 1;
  215. }