This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-lang.git


The following commit(s) were added to refs/heads/master by this push:
     new ae9c787a6 Keep WordUtils.wrap from splitting a surrogate pair (#1731)
ae9c787a6 is described below

commit ae9c787a68a7a97aead7d2d7e02e78aed56922c9
Author: alhuda <[email protected]>
AuthorDate: Fri Jun 26 00:48:57 2026 +0530

    Keep WordUtils.wrap from splitting a surrogate pair (#1731)
---
 src/main/java/org/apache/commons/lang3/text/WordUtils.java     | 10 +++++++---
 src/test/java/org/apache/commons/lang3/text/WordUtilsTest.java |  8 ++++++++
 2 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/src/main/java/org/apache/commons/lang3/text/WordUtils.java 
b/src/main/java/org/apache/commons/lang3/text/WordUtils.java
index 81039bf69..d711db3f5 100644
--- a/src/main/java/org/apache/commons/lang3/text/WordUtils.java
+++ b/src/main/java/org/apache/commons/lang3/text/WordUtils.java
@@ -704,10 +704,14 @@ public static String wrap(final String str, int 
wrapLength, String newLineStr, f
                 offset = endOfWrapAt;
             } else // really long word or URL
             if (wrapLongWords) {
-                // wrap really long word one line at a time
-                wrappedLine.append(str, offset, wrapLength + offset);
+                // wrap really long word one line at a time, but keep a 
surrogate pair whole
+                int wrapAt = wrapLength + offset;
+                if (Character.isHighSurrogate(str.charAt(wrapAt - 1)) && 
Character.isLowSurrogate(str.charAt(wrapAt))) {
+                    wrapAt++;
+                }
+                wrappedLine.append(str, offset, wrapAt);
                 wrappedLine.append(newLineStr);
-                offset += wrapLength;
+                offset = wrapAt;
             } else {
                 // do not wrap really long word, just extend beyond limit
                 matcher = patternToWrapOn.matcher(str.substring(offset + 
wrapLength));
diff --git a/src/test/java/org/apache/commons/lang3/text/WordUtilsTest.java 
b/src/test/java/org/apache/commons/lang3/text/WordUtilsTest.java
index ab34e7ea8..eed53b8ca 100644
--- a/src/test/java/org/apache/commons/lang3/text/WordUtilsTest.java
+++ b/src/test/java/org/apache/commons/lang3/text/WordUtilsTest.java
@@ -479,6 +479,14 @@ void testWrap_StringIntStringBoolean() {
         assertEquals(expected, WordUtils.wrap(input, 20, "\n", false));
         expected = "Click here,\nhttps://commons.apac\nhe.org, to jump to\nthe 
commons website";
         assertEquals(expected, WordUtils.wrap(input, 20, "\n", true));
+
+        // a hard break for a long word must not split a surrogate pair across 
the new line
+        input = "a\uD83D\uDE00\uD83D\uDE00\uD83D\uDE00\uD83D\uDE00";
+        expected = "a\uD83D\uDE00\uD83D\uDE00\n\uD83D\uDE00\uD83D\uDE00";
+        assertEquals(expected, WordUtils.wrap(input, 4, "\n", true));
+        input = "\uD83D\uDE00\uD83D\uDE00\uD83D\uDE00";
+        expected = "\uD83D\uDE00\uD83D\uDE00\n\uD83D\uDE00";
+        assertEquals(expected, WordUtils.wrap(input, 3, "\n", true));
     }
 
     @ParameterizedTest

Reply via email to