# HG changeset patch # User Andre Heinecke # Date 1475670203 -7200 # Node ID 1e6e7699f0b8767dcbb155d01ee60782978c9678 # Parent 92139cc601213fbc2ddf7038d5a0aa80e2772bd4 Add replacements.ini to configure text replacements diff -r 92139cc60121 -r 1e6e7699f0b8 packaging/win-createpackage.sh.in --- a/packaging/win-createpackage.sh.in Tue Oct 04 17:12:28 2016 +0200 +++ b/packaging/win-createpackage.sh.in Wed Oct 05 14:23:23 2016 +0200 @@ -11,6 +11,8 @@ echo File \"@CMAKE_BINARY_DIR@/src/@PROJECT_NAME@.exe\" >> @CMAKE_CURRENT_BINARY_DIR@/filelist_in.nsh echo Delete \"$INSTDIR\\@PROJECT_NAME@.exe\" >> @CMAKE_CURRENT_BINARY_DIR@/filelist_un.nsh +echo File \"@CMAKE_SOURCE_DIR@/src/replacements.ini\" >> @CMAKE_CURRENT_BINARY_DIR@/filelist_in.nsh +echo Delete \"$INSTDIR\\replacements.ini\" >> @CMAKE_CURRENT_BINARY_DIR@/filelist_un.nsh echo File \"@CMAKE_SOURCE_DIR@/LICENSE.txt\" >> @CMAKE_CURRENT_BINARY_DIR@/filelist_in.nsh echo Delete \"$INSTDIR\\LICENSE.txt\" >> @CMAKE_CURRENT_BINARY_DIR@/filelist_un.nsh diff -r 92139cc60121 -r 1e6e7699f0b8 src/CMakeLists.txt --- a/src/CMakeLists.txt Tue Oct 04 17:12:28 2016 +0200 +++ b/src/CMakeLists.txt Wed Oct 05 14:23:23 2016 +0200 @@ -20,6 +20,7 @@ icons/icon.rc filenamerequester.cpp mainwindow.cpp + replacements.ini ) find_package(Qt5LinguistTools) @@ -119,6 +120,7 @@ endif(WIN32) install(TARGETS ${PROJECT_NAME} DESTINATION bin BUNDLE DESTINATION .) +install(FILES replacements.ini DESTINATION share/apps/${PROJECT_NAME} COMPONENT config) if(APPLE) install(CODE " diff -r 92139cc60121 -r 1e6e7699f0b8 src/constants.h --- a/src/constants.h Tue Oct 04 17:12:28 2016 +0200 +++ b/src/constants.h Wed Oct 05 14:23:23 2016 +0200 @@ -75,15 +75,6 @@ * @brief The pattern used to match a free text answer. */ #define FREETXT_PATTERN "\"([^\"]*)\"?", QRegularExpression::MultilineOption -/** - * @brief the pattern to unsecape images. */ -#define IMAGE_PATTERN "##(.*)##", QRegularExpression::MultilineOption - - -/** - * @brief the pattern to unsecape latex. */ -#define LATEX_PATTERN "\\$\\$(.*)\\$\\$", QRegularExpression::MultilineOption - #define TITLE_ROW_HEIGHT 30 #define CHOICE_ROW_HEIGHT 30 @@ -114,5 +105,7 @@ #define PRETTY_NAME "EduExportConverter" +#define CONFIG_FILE_NAME "replacements.ini" + #endif // CONSTANTS_H diff -r 92139cc60121 -r 1e6e7699f0b8 src/converter.cpp --- a/src/converter.cpp Tue Oct 04 17:12:28 2016 +0200 +++ b/src/converter.cpp Wed Oct 05 14:23:23 2016 +0200 @@ -14,6 +14,10 @@ #include #include #include +#include +#include +#include +#include #include "xlsxdocument.h" #include "xlsxconditionalformatting.h" @@ -135,22 +139,80 @@ return; } -static void unescapeRegex(QString &str, const QRegularExpression &exp) +static const QMap loadExpressionsFromFile(const QString &path, QStringList &errors) { - QRegularExpressionMatch match = exp.match(str); - while (match.hasMatch()) { - str.replace(match.capturedStart(), match.capturedLength(), match.captured(1)); - match = exp.match(str); + QFile file(path); + QMap ret; + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + errors << QObject::tr("Failed to open replacement file:") + "\n\"" + path + "\""; + return ret; } + + while (!file.atEnd()) { + const auto line = file.readLine(); + auto strline = QString::fromUtf8(line).trimmed(); + if (strline.startsWith(QStringLiteral(";")) || strline.isEmpty()) { + continue; + } + + auto split = strline.split("="); + if (split.count() != 2) { + errors << QObject::tr("Invalid replacement line:") + "\n\"" + strline + "\""; + continue; + } + + auto exp = new QRegularExpression (split[0], QRegularExpression::MultilineOption); + if (!exp->isValid()) { + errors << QObject::tr("Invalid regular expression:") + "\n\"" + split[0] + "\"" + + "\n" + QObject::tr("Error: ") + exp->errorString(); + continue; + } + const auto rep = split[1].replace("\"", ""); + ret.insert(exp, rep); + qDebug() << "Loaded replacement: " << *exp << " replacement:" << rep << ""; + } + /* Special one that does not fit into the ini format well. */ + ret.insert(new QRegularExpression("^="), " ="); + return ret; } -static void unescapeString(QString &str) +static const QMap loadExpressions(QStringList &errors) { - static const QRegularExpression imgEx(IMAGE_PATTERN); - static const QRegularExpression texEx(LATEX_PATTERN); + QMap regexs; + /* Look for file next to our place */ + auto ourDir = QDir(QCoreApplication::applicationDirPath()); + const auto filename = QStringLiteral(CONFIG_FILE_NAME); + if (ourDir.exists(filename)) { + regexs = loadExpressionsFromFile(ourDir.filePath(filename), errors); + return regexs; + } - unescapeRegex(str, imgEx); - unescapeRegex(str, texEx); + /* Look in ../share/apps/PROJECT_NAME */ + ourDir.cd(QStringLiteral("../share/apps/" APPNAME).toLower()); + if (ourDir.exists(filename)) { + regexs = loadExpressionsFromFile(ourDir.filePath(filename), errors); + } else { + qDebug() << "Failed to find regular expressions."; + } + return regexs; +} + +static const QStringList sanitizeInput(QString &str) +{ + QStringList errors; + str.replace("\r\n", "\n"); + str.replace("\n\r", "\n"); + const auto expressions = loadExpressions(errors); + for (const auto regex: expressions.keys()) { + str.replace(*regex, expressions.value(regex)); + delete regex; + } + return errors; +} + +static void xlsEscape(QString &str) +{ if (str.startsWith("=")) { str = " " + str; } @@ -203,9 +265,7 @@ QRegularExpression freetxtEx(FREETXT_PATTERN); QRegularExpression firstQuestionEx(FIRST_QUESTION_PATTERN); - input.replace("\r\n", "\n"); - input.replace("\n\r", "\n"); - input.replace("#NAME?\n", ""); + mErrors += sanitizeInput(input); QRegularExpressionMatch match = firstQuestionEx.match(input); bool foundSomething = false; @@ -229,6 +289,7 @@ const QString answerLine = match.captured(2).trimmed(); xlsx.write(row, 2, QString(" "), mQuestionFmt); xlsx.write(row, 3, QString(" "), mQuestionFmt); + xlsEscape(question); xlsx.write(row++, 1, question, mQuestionFmt); html << mQuestionStyle.arg(question.toHtmlEscaped()); @@ -248,10 +309,7 @@ /* Write the values */ QString choiceName = choiceMatch.captured(1).trimmed(); - if (choiceName.startsWith("=")) { - choiceName = " " + choiceName; - } - unescapeString(choiceName); + xlsEscape(choiceName); xlsx.write(row, 1, choiceName, mChoiceTextFmt); html << mChoiceTextStyle.arg(choiceName.toHtmlEscaped()); qDebug() << "Captured for choice: " << choiceMatch.captured(0); @@ -292,10 +350,10 @@ QString choice = choiceMatch.captured(1); cursor = choiceMatch.capturedEnd(); /* Alternative answer that is just a list of strings */ - unescapeString(choice); qDebug() << "Captured unfilled choice: " << choice; html << mChoiceTextStyle.arg(choice.toHtmlEscaped()); makeBar(html, 0, doc); + xlsEscape(choice); xlsx.write(row, 1, choice); xlsx.write(row, 2, QVariant()); const QString numVotesString = QStringLiteral("Keine eingegangenen Antworten"); @@ -345,14 +403,15 @@ const QString unquoted = input.mid(cursor, unquotedLen); qDebug() << "Found inner quoted string: " << unquoted; /* Now combine */ - const QString combined = QString("%1\"%2\"%3").arg(lastRow). - arg(unquoted). - arg(textMatch.captured(1).trimmed()); + QString combined = QString("%1\"%2\"%3").arg(lastRow). + arg(unquoted). + arg(textMatch.captured(1).trimmed()); qDebug() << "Last row: " << lastRow; qDebug() << "Next Question is at: " << nextQuestion.capturedStart(); qDebug() << "Text match is: " << textMatch.captured(1).trimmed(); qDebug() << "cursor is at: " << cursor; qDebug() << "text match starts at: " << textMatch.capturedStart(); + xlsEscape(combined); xlsx.write(row - 1, 26, combined, mFreeTextFmt); xlsx.write(row - 1, 1, combined, mFreeTextFmt); cursor = textMatch.capturedEnd(); @@ -374,6 +433,7 @@ http://excel.tips.net/T003207_Automatic_Row_Height_For_Merged_Cells_with_Text_Wrap.html */ /* Write the values */ + xlsEscape(text); xlsx.write(QString("Z%1").arg(row), text, mFreeTextFmt); xlsx.write(row, 1, text, mFreeTextFmt); row++; diff -r 92139cc60121 -r 1e6e7699f0b8 src/replacements.ini --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/replacements.ini Wed Oct 05 14:23:23 2016 +0200 @@ -0,0 +1,31 @@ +; Replacements for EduExportConvert +; +; This file describes replacements done by EduExportConvert +; +; The Syntax is: +; +; = +; +; Any input that matches the Expression will be replaced +; by the right hand side of the equation. +; +; The Regular Expression syntax is perl compatible. +; Please refer to: +; http://perldoc.perl.org/perlretut.html +; For documentation about the syntax. +; +; Groups can be referenced through \1 \2 etc. see examples below. +; +; Lines starting with ; are ignored +; +; This file should be UTF-8 encoded. + +; Excel artifiacts ? +#NAME\?= +; Remove latex escape characters. +\$\$(.*?)\$\$=" \1 " +; Remove image escape characters. +##(.*?)##=" \1 " +; Image qualifiers +\\hfill= +\\filbreak=