Add word_wrap().
authorCarl Hetherington <cth@carlh.net>
Sat, 25 Feb 2023 22:38:47 +0000 (23:38 +0100)
committerCarl Hetherington <cth@carlh.net>
Fri, 3 Mar 2023 00:17:41 +0000 (01:17 +0100)
src/lib/util.cc
src/lib/util.h
test/util_test.cc

index 6ed66c40f27d14f3cc0dfd6c556b4a6f3c5b0d60..20a96578144109bd613cbf2a98f6f5cb6df0ff18 100644 (file)
@@ -47,6 +47,7 @@
 #include "ratio.h"
 #include "rect.h"
 #include "render_text.h"
+#include "scope_guard.h"
 #include "string_text.h"
 #include "text_decoder.h"
 #include "util.h"
@@ -73,6 +74,7 @@ LIBDCP_ENABLE_WARNINGS
 #include <unicode/utypes.h>
 #include <unicode/unistr.h>
 #include <unicode/translit.h>
+#include <unicode/brkiter.h>
 #include <boost/algorithm/string.hpp>
 #include <boost/range/algorithm/replace_if.hpp>
 #include <boost/thread.hpp>
@@ -1079,3 +1081,38 @@ contains_assetmap(boost::filesystem::path dir)
        return boost::filesystem::is_regular_file(dir / "ASSETMAP") || boost::filesystem::is_regular_file(dir / "ASSETMAP.xml");
 }
 
+
+string
+word_wrap(string input, int columns)
+{
+       icu::Locale locale;
+       UErrorCode status = U_ZERO_ERROR;
+       auto iter = icu::BreakIterator::createLineInstance(locale, status);
+       ScopeGuard sg = [iter]() { delete iter; };
+       if (U_FAILURE(status)) {
+               return input;
+       }
+
+       auto input_icu = icu::UnicodeString::fromUTF8(icu::StringPiece(input));
+       iter->setText(input_icu);
+
+       int position = 0;
+       string output;
+       while (position < input_icu.length()) {
+               int end_of_line = iter->preceding(position + columns + 1);
+               icu::UnicodeString line;
+               if (end_of_line <= position) {
+                       /* There's no good line-break position; just break in the middle of a word */
+                       line = input_icu.tempSubString(position, columns);
+                       position += columns;
+               } else {
+                       line = input_icu.tempSubString(position, end_of_line - position);
+                       position = end_of_line;
+               }
+               line.toUTF8String(output);
+               output += "\n";
+       }
+
+       return output;
+}
+
index d53fc06e0138696778084dd9ff9b0cc3c0e0990e..4c8a2b1b7f4bba978c05c1a1f13ad9c41e6d66b3 100644 (file)
@@ -95,5 +95,6 @@ extern boost::filesystem::path default_font_file ();
 extern void start_of_thread (std::string name);
 extern std::string error_details(boost::system::error_code ec);
 extern bool contains_assetmap(boost::filesystem::path dir);
+extern std::string word_wrap(std::string input, int columns);
 
 #endif
index 872e6f885c03580efe104bc833d19622f4babba3..49d0b3bc297ab3458ddae7ab6691234a1508b636 100644 (file)
@@ -147,3 +147,11 @@ BOOST_AUTO_TEST_CASE (copy_in_bits_test)
                check_file ("build/test/random.dat", "build/test/random.dat2");
        }
 }
+
+
+BOOST_AUTO_TEST_CASE(word_wrap_test)
+{
+       BOOST_CHECK_EQUAL(word_wrap("hello world", 8), "hello \nworld\n");
+       BOOST_CHECK(word_wrap("hello this is a longer bit of text and it should be word-wrapped", 31) == string{"hello this is a longer bit of \ntext and it should be word-\nwrapped\n"});
+       BOOST_CHECK_EQUAL(word_wrap("hellocan'twrapthissadly", 5), "hello\ncan't\nwrapt\nhissa\ndly\n");
+}