Mercurial > clickerconvert
comparison 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 |
comparison
equal
deleted
inserted
replaced
22:e8320104979d | 23:927794e3cc52 |
---|---|
8 | 8 |
9 #include "converter.h" | 9 #include "converter.h" |
10 #include <QDebug> | 10 #include <QDebug> |
11 #include <QRegularExpression> | 11 #include <QRegularExpression> |
12 #include <QRegularExpressionMatch> | 12 #include <QRegularExpressionMatch> |
13 #include <QTextDocument> | |
14 #include <QPrinter> | |
13 | 15 |
14 #include "xlsxdocument.h" | 16 #include "xlsxdocument.h" |
15 #include "xlsxconditionalformatting.h" | 17 #include "xlsxconditionalformatting.h" |
16 | 18 |
17 #include "constants.h" | 19 #include "constants.h" |
30 mTitleFmt.setFontSize(18); | 32 mTitleFmt.setFontSize(18); |
31 mTitleFmt.setFontName("Calibri"); | 33 mTitleFmt.setFontName("Calibri"); |
32 mTitleFmt.setFontBold(true); | 34 mTitleFmt.setFontBold(true); |
33 mTitleFmt.setVerticalAlignment(Format::AlignTop); | 35 mTitleFmt.setVerticalAlignment(Format::AlignTop); |
34 | 36 |
37 mTitleStyle = QStringLiteral("style='vertical-align: top;font-weight: bold;font-size:18; text-decoration: underline;'"); | |
38 | |
35 mQuestionFmt.setFontSize(11); | 39 mQuestionFmt.setFontSize(11); |
36 mQuestionFmt.setFontName("Calibri"); | 40 mQuestionFmt.setFontName("Calibri"); |
37 mQuestionFmt.setFontBold(true); | 41 mQuestionFmt.setFontBold(true); |
38 mQuestionFmt.setTopBorderStyle(Format::BorderThin); | 42 mQuestionFmt.setTopBorderStyle(Format::BorderThin); |
39 mQuestionFmt.setBottomBorderStyle(Format::BorderThin); | 43 mQuestionFmt.setBottomBorderStyle(Format::BorderThin); |
40 mQuestionFmt.setTextWarp(true); | 44 mQuestionFmt.setTextWarp(true); |
45 mQuestionStyle = QStringLiteral("style='border-bottom: 1pt solid black;border-top: 1pt solid black;font-weight: bold;font-size:11;'"); | |
41 | 46 |
42 mAnswerChoiceFmt.setFontSize(11); | 47 mAnswerChoiceFmt.setFontSize(11); |
43 mAnswerChoiceFmt.setFontName("Calibri"); | 48 mAnswerChoiceFmt.setFontName("Calibri"); |
44 mAnswerChoiceFmt.setHorizontalAlignment(Format::AlignRight); | 49 mAnswerChoiceFmt.setHorizontalAlignment(Format::AlignLeft); |
45 mAnswerChoiceFmt.setTextWarp(true); | 50 mAnswerChoiceFmt.setTextWarp(true); |
51 mAnswerChoiceStyle= QStringLiteral("style='text-align: left;font-size:11;'"); | |
46 | 52 |
47 mChoiceTextFmt = mAnswerChoiceFmt; | 53 mChoiceTextFmt = mAnswerChoiceFmt; |
48 mChoiceTextFmt.setVerticalAlignment(Format::AlignVCenter); | 54 mChoiceTextFmt.setVerticalAlignment(Format::AlignVCenter); |
55 mChoiceTextStyle= QStringLiteral("style='text-align: right;vertical-algin: center; font-size:11;'"); | |
49 | 56 |
50 mChoiceVotesFmt = mChoiceTextFmt; | 57 mChoiceVotesFmt = mChoiceTextFmt; |
51 mChoiceVotesFmt.setFontSize(10); | 58 mChoiceVotesFmt.setFontSize(10); |
52 mAnswerChoiceFmt.setHorizontalAlignment(Format::AlignLeft); | 59 mChoiceVotesStyle = QStringLiteral("style='text-align: left;vertical-algin: center; font-size:10;'"); |
53 | 60 |
54 mFreeTextFmt = mQuestionFmt; | 61 mFreeTextFmt = mQuestionFmt; |
55 mFreeTextFmt.setFontBold(false); | 62 mFreeTextFmt.setFontBold(false); |
63 mFreeTextStyle = QStringLiteral("style='border-bottom: 1pt solid black;border-top: 1pt solid black;font-size:11;'"); | |
56 | 64 |
57 mAnswerTextFmt = mQuestionFmt; | 65 mAnswerTextFmt = mQuestionFmt; |
58 mAnswerTextFmt.setVerticalAlignment(Format::AlignVCenter); | 66 mAnswerTextFmt.setVerticalAlignment(Format::AlignVCenter); |
59 mAnswerTextFmt.setHorizontalAlignment(Format::AlignLeft); | 67 mAnswerTextFmt.setHorizontalAlignment(Format::AlignLeft); |
68 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;'"); | |
60 } | 69 } |
61 | 70 |
62 void Converter::run() | 71 void Converter::run() |
63 { | 72 { |
64 QFile infile; | 73 QFile infile; |
94 } | 103 } |
95 | 104 |
96 void Converter::convertToXSLX(QTextStream& instream, QFile &output) | 105 void Converter::convertToXSLX(QTextStream& instream, QFile &output) |
97 { | 106 { |
98 Document xlsx; | 107 Document xlsx; |
108 QTextDocument doc; | |
109 QString htmlString; | |
110 QTextStream html (&htmlString); | |
99 | 111 |
100 ConditionalFormatting bars; | 112 ConditionalFormatting bars; |
101 | 113 |
102 bars.addDataBarRule(QColor(0xFF, 0x99, 0x33), ConditionalFormatting::VOT_Num, | 114 bars.addDataBarRule(QColor(0xFF, 0x99, 0x33), ConditionalFormatting::VOT_Num, |
103 "0", ConditionalFormatting::VOT_Num, "100", false); | 115 "0", ConditionalFormatting::VOT_Num, "100", false); |
104 | 116 |
105 const double colWidth[] = COLUMN_WIDTHS; | 117 const double colWidth[] = COLUMN_WIDTHS; |
106 double sum = 0; | 118 double sum = 0; |
119 | |
107 for (int i = 1; i <= COLUMN_CNT; i++) { | 120 for (int i = 1; i <= COLUMN_CNT; i++) { |
108 xlsx.setColumnWidth(i, colWidth[i-1]); | 121 xlsx.setColumnWidth(i, colWidth[i-1]); |
109 sum += colWidth[i-1]; | 122 sum += colWidth[i-1]; |
110 } | 123 } |
111 | 124 |
125 double xlsx2htmlFactor = HTML_WIDTH / sum; | |
126 int col1Width = colWidth[0] * xlsx2htmlFactor; | |
127 int col2Width = colWidth[1] * xlsx2htmlFactor; | |
128 int col3Width = colWidth[2] * xlsx2htmlFactor; | |
129 | |
112 /* For the merged cell wordwrap trick. */ | 130 /* For the merged cell wordwrap trick. */ |
113 xlsx.setColumnWidth(26, sum + 1); | 131 xlsx.setColumnWidth(26, sum + 1); |
114 xlsx.setColumnHidden(26, true); | 132 xlsx.setColumnHidden(26, true); |
115 | 133 |
134 html << "<html><meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">" | |
135 "<body><table border=\"0\" style='width:\"" << HTML_WIDTH << "px\";border-collapse:collapse'>"; | |
136 | |
116 int row = 1; | 137 int row = 1; |
117 if (!mTitle.isEmpty()) { | 138 html << "<tr><th width=\"" << col1Width << "\"</th>"; |
118 // Set the title of the Questionaire | 139 html << "<th width=\"" << col2Width << "\"</th>"; |
119 xlsx.write(row++, 1, mTitle, mTitleFmt); | 140 html << "<th width=\"" << col3Width << "\"</th>"; |
120 } else { | 141 html << "<tr><td colspan='3' " << mTitleStyle << "/>"; |
121 xlsx.write(row++, 1, DEFAULT_TITLE, mTitleFmt); | 142 const QString title = mTitle.isEmpty() ? DEFAULT_TITLE : mTitle; |
122 } | 143 // Set the title of the Questionaire |
144 xlsx.write(row++, 1, title, mTitleFmt); | |
145 html << title.toHtmlEscaped() << "</td></tr>"; | |
123 xlsx.mergeCells("A1:C1"); | 146 xlsx.mergeCells("A1:C1"); |
124 xlsx.setRowHeight(1, TITLE_ROW_HEIGHT); | 147 xlsx.setRowHeight(1, TITLE_ROW_HEIGHT); |
125 | 148 |
126 const QString input = instream.readAll(); | 149 const QString input = instream.readAll(); |
127 | 150 |
144 const QString question = match.captured(1).trimmed(); | 167 const QString question = match.captured(1).trimmed(); |
145 const QString answerLine = match.captured(2).trimmed(); | 168 const QString answerLine = match.captured(2).trimmed(); |
146 xlsx.write(row, 2, QString(" "), mQuestionFmt); | 169 xlsx.write(row, 2, QString(" "), mQuestionFmt); |
147 xlsx.write(row, 3, QString(" "), mQuestionFmt); | 170 xlsx.write(row, 3, QString(" "), mQuestionFmt); |
148 xlsx.write(row++, 1, question, mQuestionFmt); | 171 xlsx.write(row++, 1, question, mQuestionFmt); |
172 html << "<tr><td " << mQuestionStyle << ">" << question.toHtmlEscaped() << "</td>" | |
173 "<td " << mQuestionStyle << "/>" | |
174 "<td " << mQuestionStyle << "/></tr>"; | |
149 | 175 |
150 if (answerLine == QStringLiteral(CHOICE_IDENTIFIER)) { | 176 if (answerLine == QStringLiteral(CHOICE_IDENTIFIER)) { |
151 QRegularExpressionMatch choiceMatch = choiceEx.match(input, cursor); | 177 QRegularExpressionMatch choiceMatch = choiceEx.match(input, cursor); |
152 xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT); | 178 xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT); |
153 xlsx.write(row++, 1, "Answer", mAnswerChoiceFmt); | 179 xlsx.write(row++, 1, "Answer", mAnswerChoiceFmt); |
180 html << "<tr><td " << mAnswerChoiceStyle << ">" << "Answer" << "</td><td/><td/></tr>"; | |
154 int firstChoiceRow = row; | 181 int firstChoiceRow = row; |
155 int lastChoiceRow = row; | 182 int lastChoiceRow = row; |
156 while (choiceMatch.hasMatch() && choiceMatch.capturedStart() == cursor + 1) { | 183 while (choiceMatch.hasMatch() && choiceMatch.capturedStart() == cursor + 1) { |
157 /* We use the cursor here to keep track of the state. Only if an answer | 184 /* We use the cursor here to keep track of the state. Only if an answer |
158 follows immediately behind the last answer we treat it as valid as | 185 follows immediately behind the last answer we treat it as valid as |
163 QString choiceName = choiceMatch.captured(1).trimmed(); | 190 QString choiceName = choiceMatch.captured(1).trimmed(); |
164 if (choiceName.startsWith("=")) { | 191 if (choiceName.startsWith("=")) { |
165 choiceName = " " + choiceName; | 192 choiceName = " " + choiceName; |
166 } | 193 } |
167 xlsx.write(row, 1, choiceName, mChoiceTextFmt); | 194 xlsx.write(row, 1, choiceName, mChoiceTextFmt); |
195 html << "<tr><td " << mChoiceTextStyle << ">" << choiceName.toHtmlEscaped() << "</td>"; | |
168 bool ok; | 196 bool ok; |
169 double percent = choiceMatch.captured(3).toDouble(&ok); | 197 double percent = choiceMatch.captured(3).toDouble(&ok); |
170 if (!ok) { | 198 if (!ok) { |
171 mErrors << "Unparsable number in string: " + choiceMatch.captured(); | 199 mErrors << "Unparsable number in string: " + choiceMatch.captured(); |
172 } | 200 } |
201 html << QStringLiteral("<td style='background:linear-gradient(to right," | |
202 "#ff9933, #ff9933 %1%, #ffffff %1%)'>").arg(percent); | |
203 html << "</td>"; | |
173 xlsx.write(row, 2, percent); | 204 xlsx.write(row, 2, percent); |
174 xlsx.write(row, 3, QString("%1% | %2 Number of votes"). | 205 const QString numVotesString = QString("%1% | %2 Number of votes"). |
175 arg(choiceMatch.captured(3)).arg(choiceMatch.captured(2)), | 206 arg(choiceMatch.captured(3)).arg(choiceMatch.captured(2)); |
176 mChoiceVotesFmt); | 207 html << "<td " << mChoiceVotesStyle << ">" << numVotesString.toHtmlEscaped() << "</td></tr>"; |
208 xlsx.write(row, 3, numVotesString, mChoiceVotesFmt); | |
177 xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT); | 209 xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT); |
178 /* As long as we can match a choice which is either before the next question | 210 /* As long as we can match a choice which is either before the next question |
179 or before the end of the document */ | 211 or before the end of the document */ |
180 choiceMatch = choiceEx.match(input, cursor); | 212 choiceMatch = choiceEx.match(input, cursor); |
181 row++; | 213 row++; |
185 // xlsx.groupRows(firstChoiceRow - 2, lastChoiceRow - 1, false); | 217 // xlsx.groupRows(firstChoiceRow - 2, lastChoiceRow - 1, false); |
186 } else if (answerLine == QStringLiteral(TEXT_IDENTIFIER)) { | 218 } else if (answerLine == QStringLiteral(TEXT_IDENTIFIER)) { |
187 QRegularExpressionMatch textMatch = freetxtEx.match(input, cursor); | 219 QRegularExpressionMatch textMatch = freetxtEx.match(input, cursor); |
188 xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT); | 220 xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT); |
189 xlsx.write(row++, 1, "Answer", mAnswerTextFmt); | 221 xlsx.write(row++, 1, "Answer", mAnswerTextFmt); |
222 html << "<tr><td " << mAnswerTextStyle << ">" << "Answer" << "</td><td/><td/></tr>"; | |
223 | |
224 /* To handle the workaround for quotes in answers we store | |
225 * the number of rows and only afterwards create the html rows. */ | |
226 int firstFreeRow = row; | |
190 while (textMatch.hasMatch()) { | 227 while (textMatch.hasMatch()) { |
191 if (textMatch.capturedStart() != cursor + 1) { | 228 if (textMatch.capturedStart() != cursor + 1) { |
192 /* The format allows unescaped quotes in the text. | 229 /* The format allows unescaped quotes in the text. |
193 This makes a workaround neccessary. If we have | 230 This makes a workaround neccessary. If we have |
194 an Unquoted string between the next quoted string | 231 an Unquoted string between the next quoted string |
237 */ | 274 */ |
238 /* Write the values */ | 275 /* Write the values */ |
239 xlsx.write(QString("Z%1").arg(row), text, mFreeTextFmt); | 276 xlsx.write(QString("Z%1").arg(row), text, mFreeTextFmt); |
240 xlsx.write(row, 1, text, mFreeTextFmt); | 277 xlsx.write(row, 1, text, mFreeTextFmt); |
241 row++; | 278 row++; |
279 | |
242 textMatch = freetxtEx.match(input, cursor); | 280 textMatch = freetxtEx.match(input, cursor); |
281 } | |
282 for (int i = firstFreeRow; i < row; i++) { | |
283 html << "<tr><td colspan='3' " << mFreeTextStyle << ">"; | |
284 html << xlsx.read(i, 1).toString().toHtmlEscaped(); | |
285 html << "</td></tr>"; | |
243 } | 286 } |
244 } | 287 } |
245 /* Insert Empty row. */ | 288 /* Insert Empty row. */ |
246 xlsx.setRowHeight(row++, CHOICE_ROW_HEIGHT); | 289 xlsx.setRowHeight(row++, CHOICE_ROW_HEIGHT); |
247 match = questionEx.match(input, cursor); | 290 match = questionEx.match(input, cursor); |
248 cursor = match.capturedEnd(); | 291 cursor = match.capturedEnd(); |
292 html << QStringLiteral("<tr style='height: %1px'/>").arg(CHOICE_ROW_HEIGHT); | |
249 } | 293 } |
250 xlsx.addConditionalFormatting(bars); | 294 xlsx.addConditionalFormatting(bars); |
251 | 295 |
252 if (!foundSomething) { | 296 if (!foundSomething) { |
253 mErrors << tr("Failed to parse input document."); | 297 mErrors << tr("Failed to parse input document."); |
254 } | 298 } |
255 | 299 |
256 if (!xlsx.saveAs(&output)) { | 300 if (mFmt == Format_XLSX && !xlsx.saveAs(&output)) { |
257 mErrors << tr("Saving the XLSX document failed."); | 301 mErrors << tr("Saving the XLSX document failed."); |
258 return; | 302 return; |
259 } | 303 } |
304 | |
305 html << "</table></body></html>"; | |
306 | |
307 if (mFmt == Format_HTML) { | |
308 QTextStream outstream(&output); | |
309 outstream << htmlString; | |
310 return; | |
311 } | |
312 | |
313 if (mFmt == Format_PDF) { | |
314 output.close(); | |
315 QPrinter printer(QPrinter::PrinterResolution); | |
316 printer.setOutputFormat(QPrinter::PdfFormat); | |
317 printer.setPaperSize(QPrinter::A4); | |
318 printer.setOutputFileName(output.fileName()); | |
319 doc.setHtml(htmlString); | |
320 doc.print(&printer); | |
321 } | |
260 } | 322 } |