comparison src/converter.cpp @ 41:f6c53e896008 0.9

Merge
author Andre Heinecke <andre.heinecke@intevation.de>
date Fri, 15 Apr 2016 15:34:51 +0200
parents 41cd27a64365 5354cbda7188
children f9a0b2b6832e
comparison
equal deleted inserted replaced
40:7458f113314c 41:f6c53e896008
20 20
21 #include "constants.h" 21 #include "constants.h"
22 22
23 QTXLSX_USE_NAMESPACE 23 QTXLSX_USE_NAMESPACE
24 24
25 Converter::Converter(const QString &input, const QString &output, 25 Converter::Converter(const QString &input, const QStringList &outputs,
26 ConvertFormat fmt, const QString &title): 26 const QString &title):
27 QThread(Q_NULLPTR), 27 QThread(Q_NULLPTR),
28 mInput(input), 28 mInput(input),
29 mOutput(output), 29 mOutputs(outputs),
30 mFmt(fmt),
31 mTitle(title) 30 mTitle(title)
32 { 31 {
33 mTitleFmt.setFontUnderline(Format::FontUnderlineSingle); 32 mTitleFmt.setFontUnderline(Format::FontUnderlineSingle);
34 mTitleFmt.setFontSize(18); 33 mTitleFmt.setFontSize(18);
35 mTitleFmt.setFontName("Calibri"); 34 mTitleFmt.setFontName("Calibri");
93 return; 92 return;
94 } 93 }
95 } 94 }
96 QTextStream instream(&infile); 95 QTextStream instream(&infile);
97 96
98 QFile outfile; 97 QList<QFile*> outfiles;
99 if (mOutput.isEmpty()) { 98
100 if (!outfile.open(stdout, QIODevice::WriteOnly)) { 99 if (mOutputs.isEmpty()) {
100 QFile *outfile = new QFile();
101 if (!outfile->open(stdout, QIODevice::WriteOnly)) {
101 mErrors << tr("Failed to open standard output and no output file provided."); 102 mErrors << tr("Failed to open standard output and no output file provided.");
102 return; 103 return;
103 } 104 }
104 } else { 105 outfiles << outfile;
105 outfile.setFileName(mOutput); 106 }
106 if (!outfile.open(QIODevice::WriteOnly)) { 107 foreach (const QString &fileName, mOutputs) {
107 mErrors << tr("Failed to open %1 for writing.").arg(mOutput); 108 QFile *outfile = new QFile();
108 return; 109 outfile->setFileName(fileName);
109 } 110 if (!outfile->open(QIODevice::WriteOnly)) {
110 } 111 mErrors << tr("Failed to open %1 for writing.").arg(fileName);
111 convertToXSLX(instream, outfile); 112 return;
113 }
114 outfiles << outfile;
115 }
116 convertToXSLX(instream, outfiles);
112 } 117 }
113 118
114 static void makeBar(QTextStream &html, double percent, int width, QTextDocument &doc, bool forPDF) 119 static void makeBar(QTextStream &html, double percent, QTextDocument &doc)
115 { 120 {
116 static int barCnt; 121 QImage image(QSize(IMAGE_WIDTH, 25), QImage::Format_RGB32);
117 if (!forPDF) {
118 html << QStringLiteral("<td style='background:linear-gradient(to right,"
119 BAR_COLOR ", " BAR_COLOR " %1%, #ffffff %1%)'></td>").arg(percent);
120 return;
121 }
122 QImage image(QSize(width, 25), QImage::Format_RGB32);
123 QPainter painter(&image); 122 QPainter painter(&image);
124 QRect rect = image.rect(); 123 QRect rect = image.rect();
125 qDebug() << "Image of " << width; 124 if (percent) {
126 rect.setRight(rect.right() / (100 / percent)); 125 rect.setRight(rect.right() / (100. / percent));
127 painter.fillRect(rect, QColor(BAR_COLOR)); 126 painter.fillRect(rect, QColor(BAR_COLOR));
127 rect.setLeft(rect.right());
128 }
128 qDebug() << "Filled " << rect << " with color"; 129 qDebug() << "Filled " << rect << " with color";
129 rect.setLeft(rect.right()); 130 rect.setRight(IMAGE_WIDTH);
130 rect.setRight(width);
131 painter.fillRect(rect, Qt::white); 131 painter.fillRect(rect, Qt::white);
132 qDebug() << "Filled " << rect << " with white"; 132 qDebug() << "Filled " << rect << " with white";
133 doc.addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("internal://bar%1.png").arg(barCnt)), 133 doc.addResource(QTextDocument::ImageResource, QUrl(QStringLiteral("internal://bar%1.png").arg((int)percent)),
134 QVariant(image)); 134 QVariant(image));
135 html << QStringLiteral("<td style='vertical-align: middle'><img src=\"internal://bar%1.png\"/></td>").arg(barCnt++); 135 html << QStringLiteral("<td style='vertical-align: middle'><img src=\"internal://bar%1.png\"/></td>").arg((int)percent);
136 image.save(QStringLiteral("/tmp/foo.png"));
137 return; 136 return;
138 } 137 }
139 138
140 void Converter::convertToXSLX(QTextStream& instream, QFile &output) 139 void Converter::convertToXSLX(QTextStream& instream, QList<QFile *>outputs)
141 { 140 {
142 Document xlsx; 141 Document xlsx;
143 QTextDocument doc; 142 QTextDocument doc;
144 QString htmlString; 143 QString htmlString;
145 QTextStream html (&htmlString); 144 QTextStream html (&htmlString);
155 for (int i = 1; i <= COLUMN_CNT; i++) { 154 for (int i = 1; i <= COLUMN_CNT; i++) {
156 xlsx.setColumnWidth(i, colWidth[i-1]); 155 xlsx.setColumnWidth(i, colWidth[i-1]);
157 sum += colWidth[i-1]; 156 sum += colWidth[i-1];
158 } 157 }
159 158
160 double xlsx2htmlFactor = HTML_WIDTH / sum;
161 int col1Width = colWidth[0] * xlsx2htmlFactor;
162 int col2Width = colWidth[1] * xlsx2htmlFactor;
163 int col3Width = colWidth[2] * xlsx2htmlFactor;
164
165 /* For the merged cell wordwrap trick. */ 159 /* For the merged cell wordwrap trick. */
166 xlsx.setColumnWidth(26, sum + 1); 160 xlsx.setColumnWidth(26, sum + 1);
167 xlsx.setColumnHidden(26, true); 161 xlsx.setColumnHidden(26, true);
168 162
169 int row = 1; 163 int row = 1;
170 html << "<html><meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">" 164 html << "<html><meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">"
171 "<body><table border=\"0\" style='width:\"" << HTML_WIDTH << "px\";border-collapse:collapse'>"; 165 "<body><table border=\"0\" width:\"100%\">";
172 html << "<tr><th width=\"" << col1Width << "px\"</th>"; 166 html << QStringLiteral("<tr><th width=\"%1%\"></th>").arg(HTML_COL1_PERCENT);
173 html << "<th width=\"" << col2Width << "px\"</th>"; 167 html << QStringLiteral("<th width=\"%1%\"></th>").arg(HTML_COL2_PERCENT);
174 html << "<th width=\"" << col3Width << "px\"</th>"; 168 html << QStringLiteral("<th width=\"%1%\"></th>").arg(HTML_COL3_PERCENT);
175 169
176 const QString title = mTitle.isEmpty() ? DEFAULT_TITLE : mTitle; 170 const QString title = mTitle.isEmpty() ? DEFAULT_TITLE : mTitle;
177 // Set the title of the Questionaire 171 // Set the title of the Questionaire
178 xlsx.write(row++, 1, title, mTitleFmt); 172 xlsx.write(row++, 1, title, mTitleFmt);
179 html << mTitleStyle.arg(title.toHtmlEscaped()); 173 html << mTitleStyle.arg(title.toHtmlEscaped());
182 176
183 const QString input = instream.readAll(); 177 const QString input = instream.readAll();
184 178
185 QRegularExpression questionEx(QUESTION_PATTERN); 179 QRegularExpression questionEx(QUESTION_PATTERN);
186 QRegularExpression choiceEx(CHOICE_PATTERN); 180 QRegularExpression choiceEx(CHOICE_PATTERN);
187 QRegularExpression freetxtEx (FREETXT_PATTERN); 181 QRegularExpression choiceAltEx(CHOICE_UNFILLED_PATTERN);
182 QRegularExpression freetxtEx(FREETXT_PATTERN);
188 183
189 QRegularExpressionMatch match = questionEx.match(input); 184 QRegularExpressionMatch match = questionEx.match(input);
190 bool foundSomething = false; 185 bool foundSomething = false;
191 int cursor = match.capturedEnd(); 186 int cursor = match.capturedEnd();
192 while (match.hasMatch() && cursor != -1) { 187 while (match.hasMatch() && cursor != -1) {
204 xlsx.write(row, 3, QString(" "), mQuestionFmt); 199 xlsx.write(row, 3, QString(" "), mQuestionFmt);
205 xlsx.write(row++, 1, question, mQuestionFmt); 200 xlsx.write(row++, 1, question, mQuestionFmt);
206 html << mQuestionStyle.arg(question.toHtmlEscaped()); 201 html << mQuestionStyle.arg(question.toHtmlEscaped());
207 202
208 if (answerLine == QStringLiteral(CHOICE_IDENTIFIER)) { 203 if (answerLine == QStringLiteral(CHOICE_IDENTIFIER)) {
209 QRegularExpressionMatch choiceMatch = choiceEx.match(input, cursor);
210 xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT); 204 xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT);
211 xlsx.write(row++, 1, "Answer", mAnswerChoiceFmt); 205 xlsx.write(row++, 1, "Answer", mAnswerChoiceFmt);
212 html << mAnswerChoiceStyle; 206 html << mAnswerChoiceStyle;
213 int firstChoiceRow = row; 207 int firstChoiceRow = row;
214 int lastChoiceRow = row; 208 int lastChoiceRow = row;
209 repeat:
210 QRegularExpressionMatch choiceMatch = choiceEx.match(input, cursor);
215 while (choiceMatch.hasMatch() && choiceMatch.capturedStart() == cursor + 1) { 211 while (choiceMatch.hasMatch() && choiceMatch.capturedStart() == cursor + 1) {
216 /* We use the cursor here to keep track of the state. Only if an answer 212 /* We use the cursor here to keep track of the state. Only if an answer
217 follows immediately behind the last answer we treat it as valid as 213 follows immediately behind the last answer we treat it as valid as
218 otherwise we can't figure out when the next question begins. */ 214 otherwise we can't figure out when the next question begins. */
219 cursor = choiceMatch.capturedEnd(); 215 cursor = choiceMatch.capturedEnd();
223 if (choiceName.startsWith("=")) { 219 if (choiceName.startsWith("=")) {
224 choiceName = " " + choiceName; 220 choiceName = " " + choiceName;
225 } 221 }
226 xlsx.write(row, 1, choiceName, mChoiceTextFmt); 222 xlsx.write(row, 1, choiceName, mChoiceTextFmt);
227 html << mChoiceTextStyle.arg(choiceName.toHtmlEscaped()); 223 html << mChoiceTextStyle.arg(choiceName.toHtmlEscaped());
224 qDebug() << "Captured for choice: " << choiceMatch.captured(0);
228 bool ok; 225 bool ok;
229 double percent = choiceMatch.captured(3).toDouble(&ok); 226 const QString percentStr = choiceMatch.captured("percent");
227 double percent;
228 if (percentStr.isNull()) {
229 percent = 0;
230 ok = true;
231 } else {
232 percent = percentStr.toDouble(&ok);
233 }
230 if (!ok) { 234 if (!ok) {
231 mErrors << "Unparsable number in string: " + choiceMatch.captured(); 235 mErrors << "Unparsable number in string: " + choiceMatch.captured();
232 } 236 }
233 makeBar(html, percent, doc); 237 makeBar(html, percent, doc);
234 xlsx.write(row, 2, percent == 0 ? QVariant() : percent); 238 xlsx.write(row, 2, percent == 0 ? QVariant() : percent);
235 const QString numStr = choiceMatch.captured("num"); 239 const QString numStr = choiceMatch.captured("num");
236 const QString numVotesString = QString("%1% | %2 Number of votes"). 240 const QString numVotesString = QString("%1% | %2 Number of votes").
237 arg(choiceMatch.captured(3)).arg(choiceMatch.captured(2)); 241 arg(percentStr.isNull() ? QStringLiteral("0") : percentStr).
242 arg(numStr.isNull() ? QStringLiteral("0") : numStr);
238 html << mChoiceVotesStyle.arg(numVotesString.toHtmlEscaped()); 243 html << mChoiceVotesStyle.arg(numVotesString.toHtmlEscaped());
239 xlsx.write(row, 3, numVotesString, mChoiceVotesFmt); 244 xlsx.write(row, 3, numVotesString, mChoiceVotesFmt);
240 xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT); 245 xlsx.setRowHeight(row, CHOICE_ROW_HEIGHT);
241 /* As long as we can match a choice which is either before the next question 246 /* As long as we can match a choice which is either before the next question
242 or before the end of the document */ 247 or before the end of the document */
249 while (choiceMatch.hasMatch() && choiceMatch.capturedStart() <= cursor + 1) { 254 while (choiceMatch.hasMatch() && choiceMatch.capturedStart() <= cursor + 1) {
250 additionalFound = true; 255 additionalFound = true;
251 const QString choice = choiceMatch.captured(1); 256 const QString choice = choiceMatch.captured(1);
252 cursor = choiceMatch.capturedEnd(); 257 cursor = choiceMatch.capturedEnd();
253 /* Alternative answer that is just a list of strings */ 258 /* Alternative answer that is just a list of strings */
254 qDebug() << choiceAltEx.captureCount();
255 qDebug() << choiceMatch.captured(2);
256 qDebug() << choiceMatch.capturedTexts();
257 qDebug() << "Caputured unfilled choice: " << choice; 259 qDebug() << "Caputured unfilled choice: " << choice;
258 html << mChoiceTextStyle.arg(choice.toHtmlEscaped()); 260 html << mChoiceTextStyle.arg(choice.toHtmlEscaped());
259 makeBar(html, 0, doc); 261 makeBar(html, 0, doc);
260 xlsx.write(row, 2, QVariant()); 262 xlsx.write(row, 2, QVariant());
261 const QString numVotesString = QStringLiteral("Keine eingegangenen Antworten"); 263 const QString numVotesString = QStringLiteral("Keine eingegangenen Antworten");
354 356
355 if (!foundSomething) { 357 if (!foundSomething) {
356 mErrors << tr("Failed to parse input document."); 358 mErrors << tr("Failed to parse input document.");
357 } 359 }
358 360
359 if (mFmt == Format_XLSX && !xlsx.saveAs(&output)) {
360 mErrors << tr("Saving the XLSX document failed.");
361 return;
362 }
363
364 html << "</table></body></html>"; 361 html << "</table></body></html>";
365 362 doc.setHtml(htmlString);
366 if (mFmt == Format_HTML) { 363
367 QTextStream outstream(&output); 364 /* Fixup images for html */
368 outstream << htmlString; 365 QRegularExpression htmlRe = QRegularExpression("<td style='vertical-align: middle'><img src=\"internal://bar(\\d+).png\"/></td>");
369 return; 366 htmlString.replace(htmlRe, QStringLiteral("<td style='background:linear-gradient(to right,"
370 } 367 BAR_COLOR ", " BAR_COLOR " \\1%, #ffffff \\1%)'></td>"));
371 368
372 if (mFmt == Format_PDF) { 369 foreach (QFile *output, outputs) {
373 output.close(); 370 const QString fName = output->fileName().toLower();
374 QPrinter printer(QPrinter::PrinterResolution); 371 if (fName.endsWith(".html")) {
375 printer.setOutputFormat(QPrinter::PdfFormat); 372 QTextStream outstream(output);
376 printer.setPaperSize(QPrinter::A4); 373 outstream << htmlString;
377 printer.setOutputFileName(output.fileName()); 374 output->close();
378 doc.setHtml(htmlString); 375 } else if (fName.endsWith(".pdf")) {
379 /* 376 output->close();
380 QPageLayout layout = printer.pageLayout(); 377 QPrinter printer(QPrinter::PrinterResolution);
381 layout.setUnits(QPageLayout::Millimeter); 378 printer.setOutputFormat(QPrinter::PdfFormat);
382 layout.setMargins(QMarginsF(20, 20, 20, 20)); 379 printer.setPaperSize(QPrinter::A4);
383 printer.setPageLayout(layout); 380 printer.setOutputFileName(output->fileName());
384 doc.setPageSize(printer.pageRect().size()); 381 /*
385 */ 382 QPageLayout layout = printer.pageLayout();
386 doc.print(&printer); 383 layout.setUnits(QPageLayout::Millimeter);
384 layout.setMargins(QMarginsF(20, 20, 20, 20));
385 printer.setPageLayout(layout);
386 doc.setPageSize(printer.pageRect().size());
387 */
388 doc.print(&printer);
389 } else {
390 if (!xlsx.saveAs(output)) {
391 mErrors << tr("Saving the XLSX document failed.");
392 }
393 output->close();
394 }
387 } 395 }
388 } 396 }
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)