diff --git a/win-linux/extras/update-daemon/UpdateDaemon.pro b/win-linux/extras/update-daemon/UpdateDaemon.pro index a9eead74f..8bf868242 100644 --- a/win-linux/extras/update-daemon/UpdateDaemon.pro +++ b/win-linux/extras/update-daemon/UpdateDaemon.pro @@ -4,7 +4,7 @@ include(common.pri) DEFINES += COPYRIGHT_YEAR=$${CURRENT_YEAR} DEFINES += APP_ICON_PATH=\"./icons/desktopeditors.ico\" -DEFINES += APP_LANG_PATH=\"./langs/langs.iss\" +DEFINES += APP_LANG_PATH=\"./langs/langs.bin\" ENV_URL_APPCAST_MAIN = $$(DESKTOP_URL_UPDATES_MAIN_CHANNEL) !isEmpty(ENV_URL_APPCAST_MAIN) { @@ -30,4 +30,4 @@ core_linux { QMAKE_EXTRA_COMPILERS += glib_resources } -OTHER_FILES += $$PWD/res/langs/langs.iss +OTHER_FILES += $$PWD/res/langs/langs.isl diff --git a/win-linux/extras/update-daemon/res/gresource.xml b/win-linux/extras/update-daemon/res/gresource.xml index 2d420e866..fa84b6574 100644 --- a/win-linux/extras/update-daemon/res/gresource.xml +++ b/win-linux/extras/update-daemon/res/gresource.xml @@ -1,7 +1,7 @@ - langs/langs.iss + langs/langs.bin diff --git a/win-linux/extras/update-daemon/res/langs/langs.bin b/win-linux/extras/update-daemon/res/langs/langs.bin new file mode 100644 index 000000000..7a741e5c4 Binary files /dev/null and b/win-linux/extras/update-daemon/res/langs/langs.bin differ diff --git a/win-linux/extras/update-daemon/res/langs/langs.iss b/win-linux/extras/update-daemon/res/langs/langs.isl similarity index 100% rename from win-linux/extras/update-daemon/res/langs/langs.iss rename to win-linux/extras/update-daemon/res/langs/langs.isl diff --git a/win-linux/extras/update-daemon/src/classes/csvcmanager.cpp b/win-linux/extras/update-daemon/src/classes/csvcmanager.cpp index ec42a4132..7c276daea 100644 --- a/win-linux/extras/update-daemon/src/classes/csvcmanager.cpp +++ b/win-linux/extras/update-daemon/src/classes/csvcmanager.cpp @@ -385,7 +385,7 @@ void CSvcManager::init() break; case MSG_SetLanguage: - Translator::setLanguage(params[1]); + Translator::instance().setLanguage(params[1]); break; default: diff --git a/win-linux/extras/update-daemon/src/classes/translator.cpp b/win-linux/extras/update-daemon/src/classes/translator.cpp index 83bafcdbc..3f0c4ef3b 100644 --- a/win-linux/extras/update-daemon/src/classes/translator.cpp +++ b/win-linux/extras/update-daemon/src/classes/translator.cpp @@ -1,39 +1,22 @@ #include "translator.h" #include +#include #ifdef _WIN32 # include "platform_win/resource.h" # include "platform_win/utils.h" # include +# include # include -# include -# define istalnum(c) std::iswalnum(c) -# define istalpha(c) std::iswalpha(c) # define tistringstream std::wistringstream #else # include "platform_linux/utils.h" # include "res/gresource.c" -# include -# define istalnum(c) std::isalnum(c) -# define istalpha(c) std::isalpha(c) +# include # define tistringstream std::istringstream + typedef uint16_t WORD; #endif -bool isSeparator(tchar c) -{ - return c == _T(' ') || c == _T('\t') || c == _T('\r') || c == _T('\n'); -} - -bool isValidStringIdCharacter(tchar c) -{ - return istalnum(c) || istalpha(c) || c == _T('_'); -} - -bool isValidLocaleCharacter(tchar c) -{ - return istalpha(c) || c == _T('_'); -} - tstring getPrimaryLang(const tstring &lang, bool withScript = false) { if (lang.empty()) { @@ -54,69 +37,223 @@ tstring getPrimaryLang(const tstring &lang, bool withScript = false) } #ifdef _WIN32 +static IStream* LoadResourceToStream(int resourceId) +{ + IStream *pStream = nullptr; + HMODULE hInst = GetModuleHandle(nullptr); + if (HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(resourceId), RT_RCDATA)) { + DWORD dataSize = SizeofResource(hInst, hRes); + if (dataSize > 0) { + if (HGLOBAL hResData = LoadResource(hInst, hRes)) { + if (LPVOID pData = LockResource(hResData)) { + if (HGLOBAL hGlobal = GlobalAlloc(GHND, dataSize)) { + if (LPVOID pBuffer = GlobalLock(hGlobal)) { + memcpy(pBuffer, pData, dataSize); + GlobalUnlock(hGlobal); + HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pStream); + if (FAILED(hr)) { + GlobalFree(hGlobal); + pStream = nullptr; + } + } else { + GlobalFree(hGlobal); + } + } + } + FreeResource(hResData); + } + } + } + return pStream; +} + wstring StrToWStr(const string &str) { std::wstring_convert> converter; return converter.from_bytes(str); } -#endif - -TranslationsMap Translator::translMap = TranslationsMap(); -tstring Translator::langName = _T("en"); -bool Translator::is_translations_valid = false; - -#ifdef _WIN32 -Translator::Translator(const tstring &lang, int resourceId) #else -Translator::Translator(const tstring &lang, const char *resourcePath) -#endif +static GInputStream* LoadResourceToStream(const char *resourcePath) { - langName = lang; - NS_Logger::WriteLog(_T("Current locale: ") + langName); - -#ifdef _WIN32 - HMODULE hInst = GetModuleHandle(NULL); - if (HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(resourceId), RT_RCDATA)) { - if (HGLOBAL hResData = LoadResource(hInst, hRes)) { - if (LPVOID pData = LockResource(hResData)) { - DWORD dataSize = SizeofResource(hInst, hRes); - if (dataSize > 0) { - string text((const char*)pData, dataSize); - translations = StrToWStr(text); - } else - NS_Logger::WriteLog(ADVANCED_ERROR_MESSAGE); - } else - NS_Logger::WriteLog(ADVANCED_ERROR_MESSAGE); - FreeResource(hResData); - } else - NS_Logger::WriteLog(ADVANCED_ERROR_MESSAGE); - } else - NS_Logger::WriteLog(ADVANCED_ERROR_MESSAGE); -#else + GInputStream *pStream = nullptr; if (GResource *res = gresource_get_resource()) { g_resources_register(res); if (GBytes *bytes = g_resource_lookup_data(res, resourcePath, G_RESOURCE_LOOKUP_FLAGS_NONE, NULL)) { gsize dataSize = 0; const char *pData = (const char*)g_bytes_get_data(bytes, &dataSize); if (dataSize > 0) { - string text(pData, dataSize); - translations = text; - } else - NS_Logger::WriteLog(ADVANCED_ERROR_MESSAGE); + pStream = g_memory_input_stream_new_from_data(pData, dataSize, NULL); + } g_bytes_unref(bytes); - } else - NS_Logger::WriteLog(ADVANCED_ERROR_MESSAGE); + } g_resource_unref(res); - } else - NS_Logger::WriteLog(ADVANCED_ERROR_MESSAGE); + } + return pStream; +} #endif - if (!translations.empty()) { - parseTranslations(); - if (!is_translations_valid) - NS_Logger::WriteLog(_T("Cannot parse translations, error in string: ") + error_substr + _T(" <---")); - } else - NS_Logger::WriteLog(_T("Error: translations is empty.")); +Translator::Translator() : + langName(_T("en")), + is_translations_valid(false) +{ + +} + +Translator& Translator::instance() +{ + static Translator inst; + return inst; +} + +#ifdef _WIN32 +void Translator::init(const tstring &lang, int resourceId) +#else +void Translator::init(const tstring &lang, const char *resourcePath) +#endif +{ + langName = lang; + std::replace(langName.begin(), langName.end(), '-', '_'); + NS_Logger::WriteLog(_T("Current locale: ") + langName); + + is_translations_valid = false; + const char ISL_MAGIC[] = "ISL"; +#ifdef _WIN32 + if (IStream *pStream = LoadResourceToStream(resourceId)) { + ULONG bytesRead = 0; + HRESULT hr = S_OK; + char magic[sizeof(ISL_MAGIC)] = { 0 }; + hr = pStream->Read(magic, sizeof(magic), &bytesRead); + if (FAILED(hr) || bytesRead != sizeof(magic) || strncmp(magic, ISL_MAGIC, sizeof(magic) - 1) != 0) { + pStream->Release(); + return; + } + WORD stringsMapSize = 0; + hr = pStream->Read(&stringsMapSize, sizeof(stringsMapSize), &bytesRead); + if (FAILED(hr) || bytesRead != sizeof(stringsMapSize)) { + pStream->Release(); + return; + } + for (WORD i = 0; i < stringsMapSize; i++) { + uint8_t stringIdLen = 0; + hr = pStream->Read(&stringIdLen, sizeof(stringIdLen), &bytesRead); + if (FAILED(hr) || bytesRead != sizeof(stringIdLen)) { + pStream->Release(); + return; + } + std::string stringId(stringIdLen, '\0'); + hr = pStream->Read(&stringId[0], stringIdLen, &bytesRead); + if (FAILED(hr) || bytesRead != stringIdLen) { + pStream->Release(); + return; + } + WORD localeMapSize = 0; + hr = pStream->Read(&localeMapSize, sizeof(localeMapSize), &bytesRead); + if (FAILED(hr) || bytesRead != sizeof(localeMapSize)) { + pStream->Release(); + return; + } + LocaleMap localeMap; + for (WORD j = 0; j < localeMapSize; j++) { + uint8_t localeLen = 0; + hr = pStream->Read(&localeLen, sizeof(localeLen), &bytesRead); + if (FAILED(hr) || bytesRead != sizeof(localeLen)) { + pStream->Release(); + return; + } + std::string localeName(localeLen, '\0'); + hr = pStream->Read(&localeName[0], localeLen, &bytesRead); + if (FAILED(hr) || bytesRead != localeLen) { + pStream->Release(); + return; + } + WORD translationLen = 0; + hr = pStream->Read(&translationLen, sizeof(translationLen), &bytesRead); + if (FAILED(hr) || bytesRead != sizeof(translationLen)) { + pStream->Release(); + return; + } + std::string translationString(translationLen, '\0'); + hr = pStream->Read(&translationString[0], translationLen, &bytesRead); + if (FAILED(hr) || bytesRead != translationLen) { + pStream->Release(); + return; + } + localeMap[StrToWStr(localeName)] = StrToWStr(translationString); + } + translMap[StrToWStr(stringId)] = localeMap; + } + pStream->Release(); + is_translations_valid = true; + } +#else + if (GInputStream *pStream = LoadResourceToStream(resourcePath)) { + gsize bytesRead = 0; + gboolean hr = true; + char magic[sizeof(ISL_MAGIC)] = { 0 }; + hr = g_input_stream_read_all(pStream, magic, sizeof(magic), &bytesRead, NULL, NULL); + if (!hr || bytesRead != sizeof(magic) || strncmp(magic, ISL_MAGIC, sizeof(magic) - 1) != 0) { + g_object_unref(pStream); + return; + } + WORD stringsMapSize = 0; + hr = g_input_stream_read_all(pStream, &stringsMapSize, sizeof(stringsMapSize), &bytesRead, NULL, NULL); + if (!hr || bytesRead != sizeof(stringsMapSize)) { + g_object_unref(pStream); + return; + } + for (WORD i = 0; i < stringsMapSize; i++) { + uint8_t stringIdLen = 0; + hr = g_input_stream_read_all(pStream, &stringIdLen, sizeof(stringIdLen), &bytesRead, NULL, NULL); + if (!hr || bytesRead != sizeof(stringIdLen)) { + g_object_unref(pStream); + return; + } + std::string stringId(stringIdLen, '\0'); + hr = g_input_stream_read_all(pStream, &stringId[0], stringIdLen, &bytesRead, NULL, NULL); + if (!hr || bytesRead != stringIdLen) { + g_object_unref(pStream); + return; + } + WORD localeMapSize = 0; + hr = g_input_stream_read_all(pStream, &localeMapSize, sizeof(localeMapSize), &bytesRead, NULL, NULL); + if (!hr || bytesRead != sizeof(localeMapSize)) { + g_object_unref(pStream); + return; + } + LocaleMap localeMap; + for (WORD j = 0; j < localeMapSize; j++) { + uint8_t localeLen = 0; + hr = g_input_stream_read_all(pStream, &localeLen, sizeof(localeLen), &bytesRead, NULL, NULL); + if (!hr || bytesRead != sizeof(localeLen)) { + g_object_unref(pStream); + return; + } + std::string localeName(localeLen, '\0'); + hr = g_input_stream_read_all(pStream, &localeName[0], localeLen, &bytesRead, NULL, NULL); + if (!hr || bytesRead != localeLen) { + g_object_unref(pStream); + return; + } + WORD translationLen = 0; + hr = g_input_stream_read_all(pStream, &translationLen, sizeof(translationLen), &bytesRead, NULL, NULL); + if (!hr || bytesRead != sizeof(translationLen)) { + g_object_unref(pStream); + return; + } + std::string translationString(translationLen, '\0'); + hr = g_input_stream_read_all(pStream, &translationString[0], translationLen, &bytesRead, NULL, NULL); + if (!hr || bytesRead != translationLen) { + g_object_unref(pStream); + return; + } + localeMap[localeName] = translationString; + } + translMap[stringId] = localeMap; + } + g_object_unref(pStream); + is_translations_valid = true; + } +#endif } Translator::~Translator() @@ -124,185 +261,32 @@ Translator::~Translator() } -tstring Translator::tr(const char *str) +tstring Translator::tr(const tchar *str) const { -#ifdef _WIN32 - tstring translatedStr = StrToWStr(str); -#else - tstring translatedStr = str; -#endif if (is_translations_valid) { - for (auto &strIdPair : translMap) { - //LocaleMap locMap = strIdPair.second; - // for (LocaleMap::const_iterator it = strIdPair.second.begin(); it != strIdPair.second.end(); ++it) { - //wcout << L"\n\n" << translatedStr << L"\n" << it->second; - if (strIdPair.first == translatedStr) { - if (strIdPair.second.find(langName) != strIdPair.second.end()) - return strIdPair.second[langName]; - else { - tstring primaryLangAndScript = getPrimaryLang(langName, true); - if (strIdPair.second.find(primaryLangAndScript) != strIdPair.second.end()) - return strIdPair.second[primaryLangAndScript]; - else { - tstring primaryLang = getPrimaryLang(langName); - if (strIdPair.second.find(primaryLang) != strIdPair.second.end()) - return strIdPair.second[primaryLang]; - } + auto it = translMap.find(str); + if (it != translMap.end()) { + const LocaleMap &lcmap = it->second; + auto lc_it = lcmap.find(langName); + if (lc_it == lcmap.end()) { + tstring primaryLangAndScript = getPrimaryLang(langName, true); + if ((lc_it = lcmap.find(primaryLangAndScript)) == lcmap.end()) { + tstring primaryLang = getPrimaryLang(langName); + if ((lc_it = lcmap.find(primaryLang)) == lcmap.end()) { + lc_it = lcmap.find(_T("en")); } - if (strIdPair.second.find(_T("en")) != strIdPair.second.end()) - return strIdPair.second[_T("en")]; - break; } - // } + } + if (lc_it != lcmap.end()) + return lc_it->second; } } - return translatedStr; + return str; } void Translator::setLanguage(const tstring &lang) { langName = lang; + std::replace(langName.begin(), langName.end(), '-', '_'); NS_Logger::WriteLog(_T("Current locale: ") + langName); } - -void Translator::parseTranslations() -{ - int token = TOKEN_BEGIN_DOCUMENT; - tstring stringId, currentLocale; - size_t pos = 0, len = translations.length(); - while (pos < len) { - size_t incr = 1; - tchar ch = translations.at(pos); - - switch (token) { - case TOKEN_BEGIN_DOCUMENT: - case TOKEN_END_VALUE: - if (!isSeparator(ch)) { - if (ch == _T(';')) { - // string is comment - size_t end = translations.find_first_of(_T('\n'), pos); - incr = (end == tstring::npos) ? len - pos : end - pos + 1; - } else { - size_t end; - for (end = pos; end < len; end++) { - tchar c = translations.at(end); - if (!isValidLocaleCharacter(c)) - break; - } - size_t locale_len = end - pos; - if (locale_len < 12 && locale_len != 0 && locale_len != 1 && locale_len != 4 && locale_len != 9) { - token = TOKEN_BEGIN_LOCALE; - continue; - } else { - // TOKEN_ERROR - error_substr = translations.substr(0, pos + 1); - return; - } - } - } - break; - - case TOKEN_BEGIN_STRING_ID: - if (!isSeparator(ch)) { - size_t end; - tchar c; - for (end = pos; end < len; end++) { - c = translations.at(end); - if (!isValidStringIdCharacter(c)) - break; - } - c = translations.at(end); - if (end < len && !isSeparator(c) && c != _T('=')) { - // TOKEN_ERROR - error_substr = translations.substr(0, end + 1); - return; - } - stringId = translations.substr(pos, end - pos); - if (!stringId.empty() && translMap.find(stringId) == translMap.end()) - translMap[stringId] = LocaleMap(); - - token = TOKEN_END_STRING_ID; - incr = end - pos; - } - break; - - case TOKEN_END_STRING_ID: - if (!isSeparator(ch)) { - if (ch == _T('=')) { - token = TOKEN_BEGIN_VALUE; - } else { - // TOKEN_ERROR - error_substr = translations.substr(0, pos + 1); - return; - } - } - break; - - case TOKEN_BEGIN_LOCALE: { - size_t end; - for (end = pos; end < len; end++) { - tchar c = translations.at(end); - if (!isValidLocaleCharacter(c)) - break; - } - size_t locale_len = end - pos; - currentLocale = translations.substr(pos, locale_len); - if (pos + locale_len == len) { - error_substr = translations.substr(0, pos + locale_len); - return; - } - token = TOKEN_END_LOCALE; - incr = locale_len; - break; - } - - case TOKEN_END_LOCALE: - if (!isSeparator(ch)) { - if (ch == _T('.')) { - token = TOKEN_BEGIN_STRING_ID; - } else { - // TOKEN_ERROR - error_substr = translations.substr(0, pos + 1); - return; - } - } - break; - - case TOKEN_BEGIN_VALUE: { - size_t end = translations.find_first_of(_T('\n'), pos); - tstring val; - if (end == tstring::npos) { - val = translations.substr(pos); - incr = len - pos; - } else { - val = translations.substr(pos, end - pos); - incr = end - pos; - } - - if (!val.empty() && val.back() == _T('\r')) - val.pop_back(); - - size_t p = val.find(_T("\\n")); - while (p != std::string::npos) { - val.replace(p, 2, _T("\\")); - val[p] = _T('\n'); - p = val.find(_T("\\n"), p + 1); - } - if (!currentLocale.empty() && translMap.find(stringId) != translMap.end()) - translMap[stringId][currentLocale] = val; - - token = TOKEN_END_VALUE; - break; - } - - default: - break; - } - pos += incr; - if (pos == len) - token = TOKEN_END_DOCUMENT; - } - - if (token == TOKEN_END_DOCUMENT) - is_translations_valid = true; -} diff --git a/win-linux/extras/update-daemon/src/classes/translator.h b/win-linux/extras/update-daemon/src/classes/translator.h index 28bb5f680..0d8a99564 100644 --- a/win-linux/extras/update-daemon/src/classes/translator.h +++ b/win-linux/extras/update-daemon/src/classes/translator.h @@ -13,7 +13,7 @@ # define tstring std::string #endif -#define _TR(str) Translator::tr(#str) +#define _TR(str) Translator::instance().tr(_T(#str)) using std::unordered_map; @@ -24,24 +24,25 @@ typedef unordered_map TranslationsMap; class Translator { public: -#ifdef _WIN32 - Translator(const tstring &lang, int resourceId); -#else - Translator(const tstring &lang, const char *resourcePath); -#endif - ~Translator(); + Translator(const Translator&) = delete; + Translator& operator=(const Translator&) = delete; + static Translator& instance(); - static tstring tr(const char*); - static void setLanguage(const tstring &lang); +#ifdef _WIN32 + void init(const tstring &lang, int resourceId); +#else + void init(const tstring &lang, const char *resourcePath); +#endif + tstring tr(const tchar*) const; + void setLanguage(const tstring &lang); private: - void parseTranslations(); + Translator(); + ~Translator(); - static TranslationsMap translMap; - tstring translations, - error_substr; - static tstring langName; - static bool is_translations_valid; + TranslationsMap translMap; + tstring langName; + bool is_translations_valid; enum TokenType { TOKEN_BEGIN_DOCUMENT = 0, diff --git a/win-linux/extras/update-daemon/src/platform_linux/main.cpp b/win-linux/extras/update-daemon/src/platform_linux/main.cpp index f6eeaeacb..b3bdab154 100644 --- a/win-linux/extras/update-daemon/src/platform_linux/main.cpp +++ b/win-linux/extras/update-daemon/src/platform_linux/main.cpp @@ -65,7 +65,7 @@ int main(int argc, char *argv[]) NS_Logger::WriteLog(gSvcVersion); } std::locale::global(std::locale("")); - Translator lang(NS_Utils::GetAppLanguage(), "/langs/langs.iss"); + Translator::instance().init(NS_Utils::GetAppLanguage(), "/langs/langs.bin"); CSocket socket(0, INSTANCE_SVC_PORT); if (!socket.isPrimaryInstance()) return 0; diff --git a/win-linux/extras/update-daemon/src/platform_win/main.cpp b/win-linux/extras/update-daemon/src/platform_win/main.cpp index 1a86236e0..87035e43f 100644 --- a/win-linux/extras/update-daemon/src/platform_win/main.cpp +++ b/win-linux/extras/update-daemon/src/platform_win/main.cpp @@ -105,7 +105,7 @@ int __cdecl _tmain (int argc, TCHAR *argv[]) NS_Logger::WriteLog(gSvcVersion); } std::locale::global(std::locale("")); - Translator lang(NS_Utils::GetAppLanguage().c_str(), IDT_TRANSLATIONS); + Translator::instance().init(NS_Utils::GetAppLanguage().c_str(), IDT_TRANSLATIONS); CSocket socket(0, INSTANCE_SVC_PORT); if (!socket.isPrimaryInstance()) return 0; @@ -147,7 +147,7 @@ int __cdecl _tmain (int argc, TCHAR *argv[]) } std::locale::global(std::locale("")); - Translator lang(NS_Utils::GetAppLanguage().c_str(), IDT_TRANSLATIONS); + Translator::instance().init(NS_Utils::GetAppLanguage().c_str(), IDT_TRANSLATIONS); SERVICE_TABLE_ENTRY DispatchTable[] = { {(LPTSTR)SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)SvcMain}, diff --git a/win-linux/src/cprintdata.cpp b/win-linux/src/cprintdata.cpp index f4600a6ea..6267a2532 100644 --- a/win-linux/src/cprintdata.cpp +++ b/win-linux/src/cprintdata.cpp @@ -188,10 +188,12 @@ public: PRINTER_INFO_4 *printers = reinterpret_cast(buf.data()); for (DWORD i = 0; i < ret; ++i) { bool duplex_supported = (DeviceCapabilities(printers[i].pPrinterName, NULL, DC_DUPLEX, NULL, NULL) == 1); + bool color_supported = (DeviceCapabilities(printers[i].pPrinterName, NULL, DC_COLORDEVICE, NULL, NULL) == 1); QJsonObject printerObject; printerObject["name"] = QString::fromWCharArray(printers[i].pPrinterName); printerObject["duplex_supported"] = duplex_supported; + printerObject["color_supported"] = color_supported; constexpr int PAPER_NAME_LENGTH = 64; bool paperNamesSuccess = false, paperSizeSuccess = false; @@ -243,9 +245,18 @@ public: QJsonObject printerObject; printerObject["name"] = QString::fromUtf8(dest->name); printerObject["duplex_supported"] = duplex_supported; + printerObject["color_supported"] = false; ppd_option_t *option = ppdFirstOption(ppdF); while (option) { + if (strcmp(option->keyword, "ColorModel") == 0) { + for (int j = 0; j < option->num_choices; j++) { + if (strcmp(option->choices[j].choice, "Gray") != 0) { + printerObject["color_supported"] = true; + break; + } + } + } else if (strcmp(option->keyword, "PageSize") == 0) { QJsonArray paperArray; for (int j = 0; j < option->num_choices; j++) {