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 }
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)