From 7b3859cef1ba6eee6d29da4ea126877e19cb7d7c Mon Sep 17 00:00:00 2001 From: Paul Pan Date: Fri, 26 Apr 2024 16:29:57 +0800 Subject: [PATCH 1/4] Use `wbindtextdomain` on Windows --- src/core/control/XournalMain.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/control/XournalMain.cpp b/src/core/control/XournalMain.cpp index 553704e5e218..a59e696194b3 100644 --- a/src/core/control/XournalMain.cpp +++ b/src/core/control/XournalMain.cpp @@ -85,7 +85,13 @@ void initCAndCoutLocales() { void initLocalisation() { #ifdef ENABLE_NLS fs::path localeDir = Util::getGettextFilepath(Util::getLocalePath()); + +#ifdef _WIN32 + wbindtextdomain(GETTEXT_PACKAGE, localeDir.wstring().c_str()); +#else bindtextdomain(GETTEXT_PACKAGE, localeDir.u8string().c_str()); +#endif + textdomain(GETTEXT_PACKAGE); #ifdef _WIN32 From 83926f375d32dd5860ca660d8573dd3666f2e0fa Mon Sep 17 00:00:00 2001 From: Paul Pan Date: Sat, 27 Apr 2024 00:28:50 +0800 Subject: [PATCH 2/4] Fix `createSaveFilename` encoding --- src/core/model/Document.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/core/model/Document.cpp b/src/core/model/Document.cpp index ea3c484788d0..765aa08a8666 100644 --- a/src/core/model/Document.cpp +++ b/src/core/model/Document.cpp @@ -1,6 +1,7 @@ #include "Document.h" #include +#include // for codecvt_utf8_utf16 #include // for size_t, localtime, strf... #include #include @@ -133,8 +134,9 @@ auto Document::createSaveFolder(fs::path lastSavePath) -> fs::path { return lastSavePath; } -auto Document::createSaveFilename(DocumentType type, const std::string& defaultSaveName, const std::string& defaultPdfName) -> fs::path { - constexpr static std::string_view forbiddenChars = {"\\/:*?\"<>|"}; +auto Document::createSaveFilename(DocumentType type, const std::string& defaultSaveName, + const std::string& defaultPdfName) -> fs::path { + constexpr static std::wstring_view forbiddenChars = {L"\\/:*?\"<>|"}; std::string wildcardString; if (type != Document::PDF) { if (!filepath.empty()) { @@ -154,13 +156,16 @@ auto Document::createSaveFilename(DocumentType type, const std::string& defaultS wildcardString = SaveNameUtils::parseFilenameFromWildcardString(defaultPdfName, this->filepath.filename()); } - const char* format = wildcardString.empty() ? defaultSaveName.c_str() : wildcardString.c_str(); + std::wstring_convert> converter; + + auto format_str = wildcardString.empty() ? defaultSaveName : wildcardString; + auto format = converter.from_bytes(format_str); // Todo (cpp20): use - std::ostringstream ss; + std::wostringstream ss; ss.imbue(std::locale()); time_t curtime = time(nullptr); - ss << std::put_time(localtime(&curtime), format); + ss << std::put_time(localtime(&curtime), format.c_str()); auto filename = ss.str(); // Todo (cpp20): use for (auto& c: filename) { From e6cb6c1795aff8c82c692e7dfe5d5c1d6a292316 Mon Sep 17 00:00:00 2001 From: Paul Pan Date: Tue, 30 Apr 2024 18:51:12 +0800 Subject: [PATCH 3/4] Ensure that filenames in `xopp` file are encoded with UTF-8 --- src/core/control/xojfile/LoadHandler.cpp | 31 ++++++++++++------------ src/core/control/xojfile/SaveHandler.cpp | 4 +-- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/core/control/xojfile/LoadHandler.cpp b/src/core/control/xojfile/LoadHandler.cpp index c58bb23ff8d4..24d658f2c101 100644 --- a/src/core/control/xojfile/LoadHandler.cpp +++ b/src/core/control/xojfile/LoadHandler.cpp @@ -46,9 +46,9 @@ using std::string; } namespace { - constexpr size_t MAX_VERSION_LENGTH = 50; - constexpr size_t MAX_MIMETYPE_LENGTH = 25; -} +constexpr size_t MAX_VERSION_LENGTH = 50; +constexpr size_t MAX_MIMETYPE_LENGTH = 25; +} // namespace LoadHandler::LoadHandler(): attachedPdfMissing(false), @@ -316,7 +316,7 @@ void LoadHandler::parseContents() { double width = LoadHandlerHelper::getAttribDouble("width", this); double height = LoadHandlerHelper::getAttribDouble("height", this); - this->page = std::make_unique(width, height, /*suppressLayer*/true); + this->page = std::make_unique(width, height, /*suppressLayer*/ true); pages.push_back(this->page); } else if (strcmp(elementName, "audio") == 0) { @@ -373,7 +373,7 @@ void LoadHandler::parseBgSolid() { void LoadHandler::parseBgPixmap() { const char* domain = LoadHandlerHelper::getAttrib("domain", false, this); - const fs::path filepath(LoadHandlerHelper::getAttrib("filename", false, this)); + const fs::path filepath = fs::u8path(LoadHandlerHelper::getAttrib("filename", false, this)); // in case of a cloned background image, filename is a string representation of the page number from which the image // is cloned @@ -392,7 +392,8 @@ void LoadHandler::parseBgPixmap() { img.loadFile(fileToLoad, &error); if (error) { - error("%s", FC(_F("Could not read image: {1}. Error message: {2}") % fileToLoad.string() % error->message)); + error("%s", + FC(_F("Could not read image: {1}. Error message: {2}") % fileToLoad.u8string() % error->message)); g_error_free(error); } @@ -427,8 +428,8 @@ void LoadHandler::parseBgPixmap() { this->page->setBackgroundImage(img); } else if (!strcmp(domain, "clone")) { gchar* endptr = nullptr; - auto const& filename = filepath.string(); - size_t nr = static_cast(g_ascii_strtoull(filename.c_str(), &endptr, 10)); + auto const& filename = filepath.u8string(); + auto nr = static_cast(g_ascii_strtoull(filename.c_str(), &endptr, 10)); if (endptr == filename.c_str()) { error("%s", FC(_F("Could not read page number for cloned background image: {1}.") % filepath.string())); } @@ -583,11 +584,11 @@ void LoadHandler::parseStroke() { const char* fn = LoadHandlerHelper::getAttrib("fn", true, this); if (fn != nullptr && strlen(fn) > 0) { if (this->isGzFile) { - stroke->setAudioFilename(fn); + stroke->setAudioFilename(fs::u8path(fn)); } else { auto tempFile = getTempFileForPath(fn); if (!tempFile.empty()) { - stroke->setAudioFilename(tempFile.string()); + stroke->setAudioFilename(tempFile); } } } @@ -676,11 +677,11 @@ void LoadHandler::parseText() { const char* fn = LoadHandlerHelper::getAttrib("fn", true, this); if (fn != nullptr && strlen(fn) > 0) { if (this->isGzFile) { - text->setAudioFilename(fn); + text->setAudioFilename(fs::u8path(fn)); } else { auto tempFile = getTempFileForPath(fn); if (!tempFile.empty()) { - text->setAudioFilename(tempFile.string()); + text->setAudioFilename(tempFile); } } } @@ -1045,7 +1046,7 @@ void LoadHandler::parserText(GMarkupParseContext* context, const gchar* text, gs handler->stroke->setPressure(handler->pressureBuffer); } } else { - g_warning("%s", FC(_F("xoj-File: {1}") % handler->filepath.string().c_str())); + g_warning("%s", FC(_F("xoj-File: {1}") % handler->filepath.u8string())); g_warning("%s", FC(_F("Wrong number of pressure values, got {1}, expected {2}") % handler->pressureBuffer.size() % (handler->stroke->getPointCount() - 1))); } @@ -1161,7 +1162,7 @@ auto LoadHandler::readZipAttachment(fs::path const& filename) -> std::optional fs::path { return string(static_cast(tmpFilename)); } - error("%s", FC(_F("Requested temporary file was not found for attachment {1}") % filename.string())); + error("%s", FC(_F("Requested temporary file was not found for attachment {1}") % filename.u8string())); return ""; } diff --git a/src/core/control/xojfile/SaveHandler.cpp b/src/core/control/xojfile/SaveHandler.cpp index 6c8d15bd11d3..78553e346be9 100644 --- a/src/core/control/xojfile/SaveHandler.cpp +++ b/src/core/control/xojfile/SaveHandler.cpp @@ -254,7 +254,7 @@ void SaveHandler::visitPage(XmlNode* root, PageRef p, Document* doc, int id) { } } else { background->setAttrib("domain", "absolute"); - background->setAttrib("filename", doc->getPdfFilepath().string()); + background->setAttrib("filename", doc->getPdfFilepath().u8string()); } } background->setAttrib("pageno", p->getPdfPageNr() + 1); @@ -279,7 +279,7 @@ void SaveHandler::visitPage(XmlNode* root, PageRef p, Document* doc, int id) { p->getBackgroundImage().setCloneId(id); } else { background->setAttrib("domain", "absolute"); - background->setAttrib("filename", p->getBackgroundImage().getFilepath().string()); + background->setAttrib("filename", p->getBackgroundImage().getFilepath().u8string()); p->getBackgroundImage().setCloneId(id); } } else { From 4159ff783980be25f223abd96763621d6fe3e82e Mon Sep 17 00:00:00 2001 From: Paul Pan Date: Tue, 30 Apr 2024 19:29:53 +0800 Subject: [PATCH 4/4] Add test for non-western characters --- "test/files/cjk/\346\265\213\350\257\225.pdf" | Bin 0 -> 16346 bytes .../\346\265\213\350\257\225.unzipped.xopp" | 17 ++++++++++ .../files/cjk/\346\265\213\350\257\225.xopp" | Bin 0 -> 2036 bytes test/unit_tests/control/LoadHandlerTest.cpp | 29 ++++++++++++++++++ 4 files changed, 46 insertions(+) create mode 100644 "test/files/cjk/\346\265\213\350\257\225.pdf" create mode 100644 "test/files/cjk/\346\265\213\350\257\225.unzipped.xopp" create mode 100644 "test/files/cjk/\346\265\213\350\257\225.xopp" diff --git "a/test/files/cjk/\346\265\213\350\257\225.pdf" "b/test/files/cjk/\346\265\213\350\257\225.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..024d1d34b7b051e9ece975e25f8cfe467d3c81b7 GIT binary patch literal 16346 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 + +Xournal++ document - see https://xournalpp.github.io/ +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 + + + +Test +测试 +テスト + + + + + + + diff --git "a/test/files/cjk/\346\265\213\350\257\225.xopp" "b/test/files/cjk/\346\265\213\350\257\225.xopp" new file mode 100644 index 0000000000000000000000000000000000000000..b78f930591181b33dc2a0f780edf756e6f38cfc4 GIT binary patch literal 2036 zcmV(y8J6Km52>DL>nv*v#NU&K-ixH0>D5g3+FjgVm;K@1ziMVO)4RjDdI%b%(%`~?v;riv z7a8x~PqVzLmWI_b8rtS+xNA<6QGolAr4E7`GS1VIbaBWzfUCG0Gz@ED2m$VFx-X11 zM@bFglfjL2yxwl}uzGE)n-uHfr*th9p$^h{p5N|I$DnQLrfJwqC5?#yWy+8NQ;w75{x3R!)7cu%Z7>$Vb-{Xiqs?4xIgp}pL4 zEz5vOKw*lPD|RJuvi00VrB&7(ExS9a&ZvPUXj>IKIY$_UnH6?IGRZ~AusO#7DR&Ub zFkyvRNV&Bxou;49nsD~~$}L@=K!G4esmNwWq<`X*U-%M&(Tj}w;|q#W)yd z3;Th((%I1SOHn`-iV(;$NQ?i;jfiMAhE)t%6?&Zc*6aFiCCTV^4s8$Z zqY@75P-#bYg#mCTVgrxm7Lrqw_wA;=H?Nb;ubxo4PwS!a4|0CIyfv1PdEzeTI|{VX z2)2uW*%z$iQw|304JX0c&x&~zBiRH-Vx-^+B`9zP9yfxx>5;D0b-C(UG4g>~e2@3W zpVWvTs7H=%{d2XXUL&ciofnbPmB=jjk})S6N!96Uj9KF+wLLE~)<5haE^#9(%12-} zt)TBXUdP&1f0h%kn!n2C;i0KE=PUQB<4qCKwgD$Zm_+(kycAZs4oIEf)O%U?Cj#3P z3)W4TifwjF7gdVkS|m1yY-@hs2aAF=b&B{e)6YBCgt=lr1nrBwgyG!xAdB04D6?#~ zG^RjA=mzP7*6`0}ja@Tq#DV*;xBztQqv~J*J1;GK@1O z@elDaDj{m46Dm|?>)AmcQqS_?WGTI!bDS+t?DBlE*{$=_D=f`ELb2N5cPDQ~PnE@! zM#L%@RkEb@%>3snRi}+TO?@XOuM%ICPS02sKX2sK8qSDZk$%SUDJe4aB6cm7^*nCE z1RGQZGW)XOxw-30cHcsJWL2$4nCpG5;>wn~@y?y36BVI*^esmMZrskk(lkc^wR-O* z?A}`^vO^ZuDtW$?#AWKs9FRif6h6n)3E0lGC{fc31{15S(7gqe8?-XFXhy*p$?s@0 zIanJ!ZO#nPi|O9Anp+7)5_|lj(HGQpu&oSv9MG?@VyvP{%(+M}3^0MncJ?K`nnI~s zpEjeWvFu^2$MNye-0Cc|!2AJQgqD;xuzHI079U5pwxFwwk zF%6=m%#0!4MHKv;NjxY{V6%18F6W-EZX_;C`uVKiD};iP7mHY|Kc9^e6x+NG;(U1O z0SJjjB#*ER2~S)}vzuwxC-wxQAV&n}Hc4zaHZdf-qZ6;!6=ipz?E#3BD(5L7=lRv; zI6rD3McGfdYzU4Q#@9mf(#=+aQMbG2dZLjaA#R0Q{IGAsLxSUgh+Fb@5(#iI|+fj zele0LTn5j6?O@kt#z`CPhQA}-y;2Elh8Ii%x8@}G{jE1=Q@VzdYu?xhWNj80BPFh( z>*K_p0UWQVQGed{^iT^}z$yZDADVI+qpxzT_Nv|C4gC9G z|MZW){Q2#R=R3b{cA<9!cUtMaqW)c~MeBHon|dEd_v&GnfZ4WpeII}QctmmR4Z{&^ zZ@u;w_C``C1V0@QK~uhK!QVh%N*o_Q8PPEA;%&$M0xJGSp)h*-LOmgG5DdZC&q2S= z{}*C07getPdfFilepath().filename().u8string().c_str(), u8"测试.pdf"); + + EXPECT_EQ((size_t)2, doc->getPageCount()); + const auto page = doc->getPage(0); + + EXPECT_EQ((size_t)1, page->getLayerCount()); + const auto* layer = (*page->getLayers())[0]; + + const auto& elements = layer->getElements(); + ASSERT_EQ((size_t)3, layer->getElements().size()); + + auto check_element = [&](int i, const char* answer) { + EXPECT_EQ(ELEMENT_TEXT, elements[i]->getType()); + auto* text = dynamic_cast(elements[i]); + ASSERT_NE(text, nullptr); + EXPECT_STREQ(text->getText().c_str(), answer); + }; + + check_element(0, u8"Test"); + check_element(1, u8"测试"); + check_element(2, u8"テスト"); +}