diff --git a/source/detail/unicode.cpp b/source/detail/unicode.cpp index f93c17a9..f1fa9b05 100644 --- a/source/detail/unicode.cpp +++ b/source/detail/unicode.cpp @@ -29,6 +29,7 @@ #pragma clang diagnostic pop #include +#include namespace xlnt { namespace detail { @@ -69,5 +70,16 @@ std::string latin1_to_utf8(const std::string &latin1) return utf8; } +size_t string_length(const std::string &utf8_string) +{ + auto end_it = utf8::find_invalid(utf8_string.begin(), utf8_string.end()); + if (end_it != utf8_string.end()) + { + throw xlnt::exception("Invalid UTF-8 encoding detected"); + } + + return utf8::distance(utf8_string.begin(), end_it); +} + } // namespace detail } // namespace xlnt diff --git a/source/detail/unicode.hpp b/source/detail/unicode.hpp index bd83df74..ef30dffd 100644 --- a/source/detail/unicode.hpp +++ b/source/detail/unicode.hpp @@ -29,6 +29,7 @@ namespace detail { std::u16string utf8_to_utf16(const std::string &utf8_string); std::string utf16_to_utf8(const std::u16string &utf16_string); std::string latin1_to_utf8(const std::string &latin1); +size_t string_length(const std::string &utf8_string); } // namespace detail } // namespace xlnt diff --git a/source/worksheet/worksheet.cpp b/source/worksheet/worksheet.cpp index 2d4c8881..59403528 100644 --- a/source/worksheet/worksheet.cpp +++ b/source/worksheet/worksheet.cpp @@ -49,6 +49,7 @@ #include #include #include +#include namespace { @@ -237,7 +238,7 @@ void worksheet::title(const std::string &title) return; } // excel limits worksheet titles to 31 characters - if (title.empty() || title.length() > 31) + if (title.empty() || detail::string_length(title) > 31) { throw invalid_sheet_title(title); } diff --git a/tests/worksheet/worksheet_test_suite.cpp b/tests/worksheet/worksheet_test_suite.cpp index e587c134..bc80bfe8 100644 --- a/tests/worksheet/worksheet_test_suite.cpp +++ b/tests/worksheet/worksheet_test_suite.cpp @@ -104,6 +104,7 @@ public: register_test(test_clear_cell); register_test(test_clear_row); register_test(test_set_title); + register_test(test_set_title_unicode); register_test(test_phonetics); register_test(test_insert_rows); register_test(test_insert_columns); @@ -1267,6 +1268,18 @@ public: xlnt_assert(ws2_title == ws2.title()); } + void test_set_title_unicode() + { + xlnt::workbook wb; + auto ws = wb.active_sheet(); + // the 31 char limit also applies to 4-byte characters + const std::string test_long_utf8_title("巧みな外交は戦争を避ける助けとなる。"); + xlnt_assert_throws_nothing(ws.title(test_long_utf8_title)); + const std::string invalid_unicode("\xe6\x97\xa5\xd1\x88\xfa"); + xlnt_assert_throws(ws.title(invalid_unicode), + xlnt::exception); + } + void test_phonetics() { xlnt::workbook wb; @@ -1570,4 +1583,4 @@ public: } } }; -static worksheet_test_suite x; \ No newline at end of file +static worksheet_test_suite x;