Skip to content

Commit 2b8f66c

Browse files
authored
og_image: Replace byte-based slicing with character-based for Unicode safety (#11526)
1 parent 9cc2009 commit 2b8f66c

File tree

4 files changed

+46
-2
lines changed

4 files changed

+46
-2
lines changed

crates/crates_io_og_image/src/lib.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,4 +861,32 @@ mod tests {
861861
insta::assert_binary_snapshot!("404-avatar.png", image_data);
862862
}
863863
}
864+
865+
#[tokio::test]
866+
async fn test_generate_og_image_unicode_truncation() {
867+
let _guard = init_tracing();
868+
869+
// Test case that reproduces the Unicode truncation bug from issue #11524
870+
// Uses the exact description from "adder-codec-rs" crate which contains
871+
// multibyte Unicode characters (Δ) that cause string slicing to fail
872+
static AUTHORS: &[OgImageAuthorData<'_>] = &[author("adder-codec-rs-author")];
873+
874+
let data = OgImageData {
875+
name: "adder-codec-rs",
876+
version: "1.0.0",
877+
description: Some(
878+
"Encoder/transcoder/decoder for raw and compressed ADΔER (Address, Decimation, Δt Event Representation) streams. Includes a transcoder for casting either framed or event video into an ADΔER representation in a manner which preserves the temporal resolution of the source. This is a very long description that should trigger text truncation to test the Unicode character boundary issue when the text is too long to fit in the available space. Adding even more text with Unicode characters like ADΔER and Δt to ensure we hit the problematic slice operation at character boundaries.",
879+
),
880+
license: Some("MIT"),
881+
tags: &["codec", "adder", "event-representation"],
882+
authors: AUTHORS,
883+
lines_of_code: Some(5000),
884+
crate_size: 128000,
885+
releases: 3,
886+
};
887+
888+
if let Some(image_data) = generate_image(data).await {
889+
insta::assert_binary_snapshot!("unicode-truncation.png", image_data);
890+
}
891+
}
864892
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
source: crates/crates_io_og_image/src/lib.rs
3+
expression: image_data
4+
extension: png
5+
snapshot_kind: binary
6+
---

crates/crates_io_og_image/template/og-image.typ

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,12 @@
5050
return text
5151
} else {
5252
while measure(width: size.width, text + "").height > maxHeight {
53-
text = text.slice(0, text.len() - 1).trim()
53+
// Use character-based slicing instead of byte-based to handle Unicode correctly
54+
let chars = text.clusters()
55+
if chars.len() == 0 {
56+
break
57+
}
58+
text = chars.slice(0, chars.len() - 1).join().trim()
5459
}
5560
return text + ""
5661
}
@@ -74,7 +79,12 @@
7479
return text
7580
} else {
7681
while measure(text + "").width > maxWidth {
77-
text = text.slice(0, text.len() - 1).trim()
82+
// Use character-based slicing instead of byte-based to handle Unicode correctly
83+
let chars = text.clusters()
84+
if chars.len() == 0 {
85+
break
86+
}
87+
text = chars.slice(0, chars.len() - 1).join().trim()
7888
}
7989
return text + ""
8090
}

0 commit comments

Comments
 (0)