Mercurial > clickerconvert
diff src/converter.cpp @ 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 | 0b66b10a287d |
children | e5c5ebfa4205 |
line wrap: on
line diff
--- 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); + } }