/*********************************************************************** * * Copyright (C) 2014-2019 wereturtle * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * ***********************************************************************/ #include #include #include #include "ExporterFactory.h" #include "CmarkGfmExporter.h" #include "CommandLineExporter.h" ExporterFactory* ExporterFactory::instance = NULL; ExporterFactory::~ExporterFactory() { // No need to delete Exporter instances, since this is a singleton class, // and all will be destroyed upon quitting the application. ; } ExporterFactory* ExporterFactory::getInstance() { if (NULL == instance) { instance = new ExporterFactory(); } return instance; } QList ExporterFactory::getFileExporters() { return fileExporters; } QList ExporterFactory::getHtmlExporters() { return htmlExporters; } Exporter* ExporterFactory::getExporterByName(const QString& name) { // Search in HTML exporter list first. foreach (Exporter* exporter, htmlExporters) { if (exporter->getName() == name) { // Found a match! return exporter; } } // If HTML exporter list does not contain an exporter // with the desired name, search in the file exporter // list next. // foreach (Exporter* exporter, fileExporters) { if (exporter->getName() == name) { // Found a match! return exporter; } } // No match found. return NULL; } ExporterFactory::ExporterFactory() { CommandLineExporter* exporter = NULL; bool pandocIsAvailable = isCommandAvailable("pandoc --version"); bool mmdIsAvailable = isCommandAvailable("multimarkdown --version"); bool cmarkIsAvailable = isCommandAvailable("cmark --version"); CmarkGfmExporter* cmarkGfmExporter = new CmarkGfmExporter(); fileExporters.append(cmarkGfmExporter); htmlExporters.append(cmarkGfmExporter); if (pandocIsAvailable) { int majorVersion = 0; int minorVersion = 0; // Check whether version of Pandoc can read CommonMark. QList versionNumber = extractVersionNumber("pandoc --version"); if (versionNumber.length() > 0) { majorVersion = versionNumber[0]; if (versionNumber.length() > 1) { minorVersion = versionNumber[1]; } } addPandocExporter("Pandoc", "markdown", majorVersion, minorVersion); if ( (majorVersion > 1) || ((1 == majorVersion) && (minorVersion >= 14)) ) { addPandocExporter("Pandoc CommonMark", "commonmark", majorVersion, minorVersion); } addPandocExporter("Pandoc GitHub-flavored Markdown", "markdown_github-hard_line_breaks", majorVersion, minorVersion); addPandocExporter("Pandoc PHP Markdown Extra", "markdown_phpextra", majorVersion, minorVersion); addPandocExporter("Pandoc MultiMarkdown", "markdown_mmd", majorVersion, minorVersion); addPandocExporter("Pandoc Strict", "markdown_strict", majorVersion, minorVersion); } if (mmdIsAvailable) { int majorVersion = 0; QList versionNumber = extractVersionNumber("multimarkdown --version"); if (versionNumber.length() > 0) { majorVersion = versionNumber[0]; } exporter = new CommandLineExporter("MultiMarkdown"); // Smart typography option (--smart) is only availabe in version 5 and below. // The option is was removed and enabled by default in version 6 and above. // if (majorVersion < 6) { exporter->setSmartTypographyOnArgument("--smart"); } exporter->setSmartTypographyOffArgument("--nosmart"); exporter->setHtmlRenderCommand(QString("multimarkdown %1 -t html") .arg(CommandLineExporter::SMART_TYPOGRAPHY_ARG)); exporter->addFileExportCommand ( ExportFormat::HTML, QString("multimarkdown %1 -t html -o %2") .arg(CommandLineExporter::SMART_TYPOGRAPHY_ARG) .arg(CommandLineExporter::OUTPUT_FILE_PATH_VAR) ); // Version 6 removed ODF option and replaced it with ODT and FODT. if (majorVersion >= 6) { exporter->addFileExportCommand ( ExportFormat::ODT, QString("multimarkdown %1 -t odt -o %2") .arg(CommandLineExporter::SMART_TYPOGRAPHY_ARG) .arg(CommandLineExporter::OUTPUT_FILE_PATH_VAR) ); exporter->addFileExportCommand ( ExportFormat::ODF, QString("multimarkdown %1 -t fodt -o %2") .arg(CommandLineExporter::SMART_TYPOGRAPHY_ARG) .arg(CommandLineExporter::OUTPUT_FILE_PATH_VAR) ); } else { exporter->addFileExportCommand ( ExportFormat::ODF, QString("multimarkdown %1 -t odf -o %2") .arg(CommandLineExporter::SMART_TYPOGRAPHY_ARG) .arg(CommandLineExporter::OUTPUT_FILE_PATH_VAR) ); } // Version 6 added EPUB 3 if (majorVersion >= 6) { exporter->addFileExportCommand ( ExportFormat::EPUBV3, QString("multimarkdown %1 -b -t epub -o %2") .arg(CommandLineExporter::SMART_TYPOGRAPHY_ARG) .arg(CommandLineExporter::OUTPUT_FILE_PATH_VAR) ); } exporter->addFileExportCommand ( ExportFormat::LATEX, QString("multimarkdown %1 -t latex -o %2") .arg(CommandLineExporter::SMART_TYPOGRAPHY_ARG) .arg(CommandLineExporter::OUTPUT_FILE_PATH_VAR) ); exporter->addFileExportCommand ( ExportFormat::MEMOIR, QString("multimarkdown %1 -t memoir -o %2") .arg(CommandLineExporter::SMART_TYPOGRAPHY_ARG) .arg(CommandLineExporter::OUTPUT_FILE_PATH_VAR) ); exporter->addFileExportCommand ( ExportFormat::LYX, QString("multimarkdown %1 -t lyx -o %2") .arg(CommandLineExporter::SMART_TYPOGRAPHY_ARG) .arg(CommandLineExporter::OUTPUT_FILE_PATH_VAR) ); fileExporters.append(exporter); htmlExporters.append(exporter); } if (cmarkIsAvailable) { exporter = new CommandLineExporter("cmark"); exporter->setSmartTypographyOnArgument("--smart"); exporter->setHtmlRenderCommand(QString("cmark -t html --smart %1") .arg(CommandLineExporter::SMART_TYPOGRAPHY_ARG)); exporter->addFileExportCommand ( ExportFormat::HTML, QString("cmark -t html %1") .arg(CommandLineExporter::SMART_TYPOGRAPHY_ARG) ); exporter->addFileExportCommand ( ExportFormat::LATEX, QString("cmark -t latex %1") .arg(CommandLineExporter::SMART_TYPOGRAPHY_ARG) ); exporter->addFileExportCommand ( ExportFormat::MANPAGE, QString("cmark -t man %1") .arg(CommandLineExporter::SMART_TYPOGRAPHY_ARG) ); fileExporters.append(exporter); htmlExporters.append(exporter); } } QList ExporterFactory::extractVersionNumber(const QString& command) const { QList versionNumber; QProcess process; process.start(command); if (!process.waitForStarted(500)) { return versionNumber; } else if (!process.waitForFinished(500)) { process.kill(); return versionNumber; } QString versionStr = QString::fromUtf8(process.readAllStandardOutput().data()); QRegularExpression versionRegex1("v(\\d+(\\.\\d+)*)"); QRegularExpression versionRegex2("(\\d+(\\.\\d+)*)"); QRegularExpressionMatch match; // Prefer searching for "v1.6.3" version format over "1.6.3" format int pos = versionStr.indexOf(versionRegex1, 0, &match); if ((pos >= 0) && match.hasMatch()) { versionStr = match.captured(1); } // Else try the "1.6.3" format without the "v" in front else { pos = versionStr.indexOf(versionRegex2, 0, &match); if ((pos >= 0) && match.hasMatch()) { versionStr = match.captured(1); } } QStringList numbers = versionStr.split('.', QString::SkipEmptyParts); foreach (QString num, numbers) { versionNumber.append(num.toInt()); } return versionNumber; } bool ExporterFactory::isCommandAvailable(const QString& testCommand) const { QProcess process; process.start(testCommand); if (!process.waitForStarted(500)) { return false; } else if (!process.waitForFinished(500)) { process.kill(); } return true; } void ExporterFactory::addPandocExporter ( const QString& name, const QString& inputFormat, int majorVersion, int minorVersion ) { Q_UNUSED(minorVersion) CommandLineExporter* exporter = new CommandLineExporter(name); if (majorVersion >= 2) { exporter->setSmartTypographyOnArgument("+smart"); exporter->setSmartTypographyOffArgument("-smart"); } else { exporter->setSmartTypographyOnArgument(" --smart"); } exporter->setHtmlRenderCommand ( QString("pandoc --mathml -f ") + inputFormat + CommandLineExporter::SMART_TYPOGRAPHY_ARG + " -t html" ); QString standardExportStr; if (majorVersion >= 2) { standardExportStr = QString("pandoc -f ") + inputFormat + CommandLineExporter::SMART_TYPOGRAPHY_ARG + " -t %1 --standalone --mathml --quiet -o " + CommandLineExporter::OUTPUT_FILE_PATH_VAR; } else { // older Pandoc releases don't know the option '--quiet' standardExportStr = QString("pandoc -f ") + inputFormat + CommandLineExporter::SMART_TYPOGRAPHY_ARG + " -t %1 --standalone --mathml -o " + CommandLineExporter::OUTPUT_FILE_PATH_VAR; } exporter->addFileExportCommand ( ExportFormat::HTML, standardExportStr.arg("html") ); exporter->addFileExportCommand ( ExportFormat::HTML5, standardExportStr.arg("html5") ); exporter->addFileExportCommand ( ExportFormat::ODT, standardExportStr.arg("odt") ); exporter->addFileExportCommand ( ExportFormat::ODF, standardExportStr.arg("opendocument") ); exporter->addFileExportCommand ( ExportFormat::RTF, standardExportStr.arg("rtf") ); exporter->addFileExportCommand ( ExportFormat::DOCX, standardExportStr.arg("docx") ); exporter->addFileExportCommand ( ExportFormat::PDF_LATEX, standardExportStr.arg("latex") + " -Vlinkcolor=blue -Vcitecolor=blue -Vurlcolor=blue -Vtoccolor=blue -Vmargin-left=1in -Vmargin-right=1in -Vmargin-top=1in -Vmargin-bottom=1in" ); exporter->addFileExportCommand ( ExportFormat::PDF_CONTEXT, standardExportStr.arg("context") + " --variable pagenumbering:location=footer --variable layout:header=0mm --variable layout:top=1in --variable layout:bottom=1in --variable layout:leftmargin=1in --variable layout:rightmargin=1in -Vlinkcolor=blue" ); // Note: Do not use --mathjax option with WKHTMLTOPDF export, as this will // cause pandoc to hang. // exporter->addFileExportCommand ( ExportFormat::PDF_WKHTML, standardExportStr.arg("html5") + " -Vmargin-left=1in -Vmargin-right=1in -Vmargin-top=1in -Vmargin-bottom=1in" ); exporter->addFileExportCommand ( ExportFormat::EPUBV2, standardExportStr.arg("epub") + " --toc --toc-depth 6" ); exporter->addFileExportCommand ( ExportFormat::EPUBV3, standardExportStr.arg("epub3") + " --toc --toc-depth 6" ); exporter->addFileExportCommand ( ExportFormat::FICTIONBOOK2, standardExportStr.arg("fb2") + " --toc --toc-depth 6" ); exporter->addFileExportCommand ( ExportFormat::LATEX, standardExportStr.arg("latex") ); exporter->addFileExportCommand ( ExportFormat::GROFFMAN, standardExportStr.arg("man") ); fileExporters.append(exporter); htmlExporters.append(exporter); }