Mercurial > clickerconvert
changeset 23:927794e3cc52
Add HTML output and some pdf support
PDF does not look well as the CSS used for HTML is not supported.
author | Andre Heinecke <andre.heinecke@intevation.de> |
---|---|
date | Mon, 11 Apr 2016 11:00:40 +0200 |
parents | e8320104979d |
children | 1470cb8a00a8 |
files | src/CMakeLists.txt src/cconvert_options.h src/constants.h src/converter.cpp src/converter.h src/l10n/main_de_DE.ts src/main.cpp |
diffstat | 7 files changed, 130 insertions(+), 43 deletions(-) [+] |
line wrap: on
line diff
--- a/src/CMakeLists.txt Mon Apr 11 10:59:08 2016 +0200 +++ b/src/CMakeLists.txt Mon Apr 11 11:00:40 2016 +0200 @@ -57,8 +57,8 @@ endif() target_link_libraries(${PROJECT_NAME} - Qt5::Core - Qt5::Gui + Qt5::Widgets + Qt5::PrintSupport ${PROJECT_NAME}_libqtxslx ${EXTRA_STATIC_LIBS} )
--- a/src/cconvert_options.h Mon Apr 11 10:59:08 2016 +0200 +++ b/src/cconvert_options.h Mon Apr 11 11:00:40 2016 +0200 @@ -17,9 +17,10 @@ { QList<QCommandLineOption> options; - options /*<< QCommandLineOption(QStringList() << QStringLiteral("pdf") - << QStringLiteral("p"), - QObject::tr("Output as pdf (default xlsx)")) */ + options << QCommandLineOption(QStringList() << QStringLiteral("format") + << QStringLiteral("f"), + QObject::tr("Output format (default xlsx)."), + QStringLiteral("xlsx pdf html")) << QCommandLineOption(QStringList() << QStringLiteral("output") << QStringLiteral("o"), QObject::tr("write output to file (default stdout)"),
--- a/src/constants.h Mon Apr 11 10:59:08 2016 +0200 +++ b/src/constants.h Mon Apr 11 11:00:40 2016 +0200 @@ -47,7 +47,7 @@ #define COLUMN_CNT 3 /** * @brief The width of the columns in characters. */ -#define COLUMN_WIDTHS { 40, 22, 27 } +#define COLUMN_WIDTHS { 40, 20, 26 } /** * @brief Regular expression to define a question. @@ -79,4 +79,6 @@ #define DEFAULT_TITLE QStringLiteral("Clicker-Fragen und Antworten") +#define HTML_WIDTH 960 + #endif // CONSTANTS_H
--- a/src/converter.cpp Mon Apr 11 10:59:08 2016 +0200 +++ b/src/converter.cpp Mon Apr 11 11:00:40 2016 +0200 @@ -10,6 +10,8 @@ #include <QDebug> #include <QRegularExpression> #include <QRegularExpressionMatch> +#include <QTextDocument> +#include <QPrinter> #include "xlsxdocument.h" #include "xlsxconditionalformatting.h" @@ -32,31 +34,38 @@ mTitleFmt.setFontBold(true); mTitleFmt.setVerticalAlignment(Format::AlignTop); + mTitleStyle = QStringLiteral("style='vertical-align: top;font-weight: bold;font-size:18; text-decoration: underline;'"); + mQuestionFmt.setFontSize(11); mQuestionFmt.setFontName("Calibri"); mQuestionFmt.setFontBold(true); mQuestionFmt.setTopBorderStyle(Format::BorderThin); mQuestionFmt.setBottomBorderStyle(Format::BorderThin); mQuestionFmt.setTextWarp(true); + mQuestionStyle = QStringLiteral("style='border-bottom: 1pt solid black;border-top: 1pt solid black;font-weight: bold;font-size:11;'"); mAnswerChoiceFmt.setFontSize(11); mAnswerChoiceFmt.setFontName("Calibri"); - mAnswerChoiceFmt.setHorizontalAlignment(Format::AlignRight); + mAnswerChoiceFmt.setHorizontalAlignment(Format::AlignLeft); mAnswerChoiceFmt.setTextWarp(true); + mAnswerChoiceStyle= QStringLiteral("style='text-align: left;font-size:11;'"); mChoiceTextFmt = mAnswerChoiceFmt; mChoiceTextFmt.setVerticalAlignment(Format::AlignVCenter); + mChoiceTextStyle= QStringLiteral("style='text-align: right;vertical-algin: center; font-size:11;'"); mChoiceVotesFmt = mChoiceTextFmt; mChoiceVotesFmt.setFontSize(10); - mAnswerChoiceFmt.setHorizontalAlignment(Format::AlignLeft); + mChoiceVotesStyle = QStringLiteral("style='text-align: left;vertical-algin: center; font-size:10;'"); mFreeTextFmt = mQuestionFmt; mFreeTextFmt.setFontBold(false); + mFreeTextStyle = QStringLiteral("style='border-bottom: 1pt solid black;border-top: 1pt solid black;font-size:11;'"); mAnswerTextFmt = mQuestionFmt; mAnswerTextFmt.setVerticalAlignment(Format::AlignVCenter); mAnswerTextFmt.setHorizontalAlignment(Format::AlignLeft); + mAnswerTextStyle = QStringLiteral("style='text-align: left;font-weight: bold;vertical-algin: center; border-bottom: 1pt solid black;border-top: 1pt solid black;font-size:11;'"); } void Converter::run() @@ -96,6 +105,9 @@ void Converter::convertToXSLX(QTextStream& instream, QFile &output) { Document xlsx; + QTextDocument doc; + QString htmlString; + QTextStream html (&htmlString); ConditionalFormatting bars; @@ -104,22 +116,33 @@ const double colWidth[] = COLUMN_WIDTHS; double sum = 0; + for (int i = 1; i <= COLUMN_CNT; i++) { xlsx.setColumnWidth(i, colWidth[i-1]); sum += colWidth[i-1]; } + double xlsx2htmlFactor = HTML_WIDTH / sum; + int col1Width = colWidth[0] * xlsx2htmlFactor; + int col2Width = colWidth[1] * xlsx2htmlFactor; + int col3Width = colWidth[2] * xlsx2htmlFactor; + /* For the merged cell wordwrap trick. */ xlsx.setColumnWidth(26, sum + 1); xlsx.setColumnHidden(26, true); + html << "<html><meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">" + "<body><table border=\"0\" style='width:\"" << HTML_WIDTH << "px\";border-collapse:collapse'>"; + int row = 1; - if (!mTitle.isEmpty()) { - // Set the title of the Questionaire - xlsx.write(row++, 1, mTitle, mTitleFmt); - } else { - xlsx.write(row++, 1, DEFAULT_TITLE, mTitleFmt); - } + html << "<tr><th width=\"" << col1Width << "\"</th>"; + html << "<th width=\"" << col2Width << "\"</th>"; + html << "<th width=\"" << col3Width << "\"</th>"; + html << "<tr><td colspan='3' " << mTitleStyle << "/>"; + const QString title = mTitle.isEmpty() ? DEFAULT_TITLE : mTitle; + // Set the title of the Questionaire + xlsx.write(row++, 1, title, mTitleFmt); + html << title.toHtmlEscaped() << "</td></tr>"; xlsx.mergeCells("A1:C1"); xlsx.setRowHeight(1, TITLE_ROW_HEIGHT); @@ -146,11 +169,15 @@ xlsx.write(row, 2, QString(" "), mQuestionFmt); xlsx.write(row, 3, QString(" "), mQuestionFmt); xlsx.write(row++, 1, question, mQuestionFmt); + html << "<tr><td " << mQuestionStyle << ">" << question.toHtmlEscaped() << "</td>" + "<td " << mQuestionStyle << "/>" + "<td " << mQuestionStyle << "/></tr>"; if (answerLine == QStringLiteral(CHOICE_IDENTIFIER)) { QRegularExpressionMatch choiceMatch = choiceEx.match(input, cursor); xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT); xlsx.write(row++, 1, "Answer", mAnswerChoiceFmt); + html << "<tr><td " << mAnswerChoiceStyle << ">" << "Answer" << "</td><td/><td/></tr>"; int firstChoiceRow = row; int lastChoiceRow = row; while (choiceMatch.hasMatch() && choiceMatch.capturedStart() == cursor + 1) { @@ -165,15 +192,20 @@ choiceName = " " + choiceName; } xlsx.write(row, 1, choiceName, mChoiceTextFmt); + html << "<tr><td " << mChoiceTextStyle << ">" << choiceName.toHtmlEscaped() << "</td>"; bool ok; double percent = choiceMatch.captured(3).toDouble(&ok); if (!ok) { mErrors << "Unparsable number in string: " + choiceMatch.captured(); } + html << QStringLiteral("<td style='background:linear-gradient(to right," + "#ff9933, #ff9933 %1%, #ffffff %1%)'>").arg(percent); + html << "</td>"; xlsx.write(row, 2, percent); - xlsx.write(row, 3, QString("%1% | %2 Number of votes"). - arg(choiceMatch.captured(3)).arg(choiceMatch.captured(2)), - mChoiceVotesFmt); + const QString numVotesString = QString("%1% | %2 Number of votes"). + arg(choiceMatch.captured(3)).arg(choiceMatch.captured(2)); + html << "<td " << mChoiceVotesStyle << ">" << numVotesString.toHtmlEscaped() << "</td></tr>"; + xlsx.write(row, 3, numVotesString, mChoiceVotesFmt); xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT); /* As long as we can match a choice which is either before the next question or before the end of the document */ @@ -187,6 +219,11 @@ QRegularExpressionMatch textMatch = freetxtEx.match(input, cursor); xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT); xlsx.write(row++, 1, "Answer", mAnswerTextFmt); + html << "<tr><td " << mAnswerTextStyle << ">" << "Answer" << "</td><td/><td/></tr>"; + + /* To handle the workaround for quotes in answers we store + * the number of rows and only afterwards create the html rows. */ + int firstFreeRow = row; while (textMatch.hasMatch()) { if (textMatch.capturedStart() != cursor + 1) { /* The format allows unescaped quotes in the text. @@ -239,13 +276,20 @@ xlsx.write(QString("Z%1").arg(row), text, mFreeTextFmt); xlsx.write(row, 1, text, mFreeTextFmt); row++; + textMatch = freetxtEx.match(input, cursor); } + for (int i = firstFreeRow; i < row; i++) { + html << "<tr><td colspan='3' " << mFreeTextStyle << ">"; + html << xlsx.read(i, 1).toString().toHtmlEscaped(); + html << "</td></tr>"; + } } /* Insert Empty row. */ xlsx.setRowHeight(row++, CHOICE_ROW_HEIGHT); match = questionEx.match(input, cursor); cursor = match.capturedEnd(); + html << QStringLiteral("<tr style='height: %1px'/>").arg(CHOICE_ROW_HEIGHT); } xlsx.addConditionalFormatting(bars); @@ -253,8 +297,26 @@ mErrors << tr("Failed to parse input document."); } - if (!xlsx.saveAs(&output)) { + if (mFmt == Format_XLSX && !xlsx.saveAs(&output)) { mErrors << tr("Saving the XLSX document failed."); return; } + + html << "</table></body></html>"; + + if (mFmt == Format_HTML) { + QTextStream outstream(&output); + outstream << htmlString; + return; + } + + if (mFmt == Format_PDF) { + output.close(); + QPrinter printer(QPrinter::PrinterResolution); + printer.setOutputFormat(QPrinter::PdfFormat); + printer.setPaperSize(QPrinter::A4); + printer.setOutputFileName(output.fileName()); + doc.setHtml(htmlString); + doc.print(&printer); + } }
--- a/src/converter.h Mon Apr 11 10:59:08 2016 +0200 +++ b/src/converter.h Mon Apr 11 11:00:40 2016 +0200 @@ -27,7 +27,9 @@ /*! XLSX (default). */ Format_XLSX, /*! PDF */ - Format_PDF + Format_PDF, + /*! HTML */ + Format_HTML, }; /** @brief Base class of Convert operations. @@ -74,6 +76,13 @@ mFreeTextFmt, mChoiceTextFmt, mChoiceVotesFmt; + QString mTitleStyle, + mQuestionStyle, + mAnswerChoiceStyle, + mAnswerTextStyle, + mFreeTextStyle, + mChoiceTextStyle, + mChoiceVotesStyle; }; #endif // CONVERTER_H
--- a/src/l10n/main_de_DE.ts Mon Apr 11 10:59:08 2016 +0200 +++ b/src/l10n/main_de_DE.ts Mon Apr 11 11:00:40 2016 +0200 @@ -1,35 +1,35 @@ <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE TS> -<TS version="2.0" language="de_DE"> +<TS version="2.1" language="de_DE"> <context> <name>Converter</name> <message> - <location filename="../converter.cpp" line="68"/> + <location filename="../converter.cpp" line="78"/> <source>Failed to open standard input and no input file provided.</source> <translation>Öffnen des Eingabekanals Fehlgeschlagen und keine Eingabedatei übergeben.</translation> </message> <message> - <location filename="../converter.cpp" line="74"/> + <location filename="../converter.cpp" line="84"/> <source>Failed to open %1 for reading.</source> <translation>Die Datei "%1" konnte nicht gelesen werden.</translation> </message> <message> - <location filename="../converter.cpp" line="83"/> + <location filename="../converter.cpp" line="93"/> <source>Failed to open standard output and no output file provided.</source> <translation>Öffnen des Ausgabekanals Fehlgeschlagen und keine Ausgabedatei übergeben.</translation> </message> <message> - <location filename="../converter.cpp" line="89"/> + <location filename="../converter.cpp" line="99"/> <source>Failed to open %1 for writing.</source> <translation>Die Datei "%1" konnte nicht geöffnet werden.</translation> </message> <message> - <location filename="../converter.cpp" line="253"/> + <location filename="../converter.cpp" line="298"/> <source>Failed to parse input document.</source> <translation>Fehler bei der verarbeitung des Eingabedokuments.</translation> </message> <message> - <location filename="../converter.cpp" line="257"/> + <location filename="../converter.cpp" line="302"/> <source>Saving the XLSX document failed.</source> <translation>Das erstellen des XLSX Dokuments ist fehlgeschlagen.</translation> </message> @@ -37,37 +37,42 @@ <context> <name>QObject</name> <message> - <location filename="../cconvert_options.h" line="25"/> + <location filename="../cconvert_options.h" line="26"/> <source>write output to file (default stdout)</source> <translation>Ausgabe in Datei schreiben (standard stdout)</translation> </message> <message> - <location filename="../cconvert_options.h" line="41"/> + <location filename="../cconvert_options.h" line="42"/> <source>[file]</source> <translation>[Datei]</translation> </message> <message> - <location filename="../cconvert_options.h" line="29"/> + <location filename="../cconvert_options.h" line="30"/> <source>Set the title of the document.</source> <translation>Titel des Dokuments</translation> </message> <message> - <location filename="../cconvert_options.h" line="26"/> + <location filename="../cconvert_options.h" line="22"/> + <source>Output format (default xlsx).</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../cconvert_options.h" line="27"/> <source>file</source> <translation>Datei</translation> </message> <message> - <location filename="../cconvert_options.h" line="30"/> + <location filename="../cconvert_options.h" line="31"/> <source>"The Title"</source> <translation>"Der Titel"</translation> </message> <message> - <location filename="../cconvert_options.h" line="32"/> + <location filename="../cconvert_options.h" line="33"/> <source>Print debug output.</source> <translation>Debug ausgaben aktivieren</translation> </message> <message> - <location filename="../cconvert_options.h" line="40"/> + <location filename="../cconvert_options.h" line="41"/> <source>File to process (default stdin)</source> <translation>Eingabedatei (standard stdin)</translation> </message>
--- a/src/main.cpp Mon Apr 11 10:59:08 2016 +0200 +++ b/src/main.cpp Mon Apr 11 11:00:40 2016 +0200 @@ -9,7 +9,7 @@ /** @file Main entry point for the application. * * This file is the wrapper around the qt application. - * Does command line parsing and preparation of the QCoreApplication. + * Does command line parsing and preparation of the QApplication. */ #include "constants.h" @@ -17,7 +17,7 @@ #include "converter.h" #include "cconvert_options.h" -#include <QCoreApplication> +#include <QApplication> #include <QCommandLineParser> #include <QtPlugin> #include <QDebug> @@ -97,11 +97,11 @@ * @returns 0 on success an error code otherwise. */ int realMain(int argc, char **argv) { - /* QCoreApplication setup */ - QCoreApplication app (argc, argv); - QCoreApplication::setOrganizationName(QStringLiteral(APPNAME)); - QCoreApplication::setApplicationName(QStringLiteral(APPNAME)); - QCoreApplication::setApplicationVersion(QStringLiteral(VERSION)); + /* QApplication setup */ + QApplication app (argc, argv); + QApplication::setOrganizationName(QStringLiteral(APPNAME)); + QApplication::setApplicationName(QStringLiteral(APPNAME)); + QApplication::setApplicationVersion(QStringLiteral(VERSION)); // QSettings::setDefaultFormat(QSettings::IniFormat); /* Setup translations */ @@ -130,10 +130,18 @@ } ConvertFormat fmt = Format_XLSX; - /* Initialize the converter. - if (parser.isSet(QStringLiteral("pdf"))) { + /* Initialize the converter. */ + const QString format = parser.value("format").toLower(); + if (format == "xlsx" || format.isEmpty()) { + fmt = Format_XLSX; + } else if (format == "html") { + fmt = Format_HTML; + } else if (format == "pdf") { fmt = Format_PDF; - }*/ + } else { + qCritical() << "Format: " << parser.value("format") << "not supported"; + exit(1); + } QString infile; if (args.size()) { infile = args.first();