Skip to content

Commit ded4b13

Browse files
Fix crash caused by glyph splitting (#929)
1 parent 881ed7b commit ded4b13

File tree

4 files changed

+33
-2
lines changed

4 files changed

+33
-2
lines changed

crates/resvg/tests/integration/render.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1502,6 +1502,7 @@ use crate::render;
15021502
#[test] fn text_text_filter_bbox() { assert_eq!(render("tests/text/text/filter-bbox"), 0); }
15031503
#[test] fn text_text_ligatures_handling_in_mixed_fonts_1() { assert_eq!(render("tests/text/text/ligatures-handling-in-mixed-fonts-1"), 0); }
15041504
#[test] fn text_text_ligatures_handling_in_mixed_fonts_2() { assert_eq!(render("tests/text/text/ligatures-handling-in-mixed-fonts-2"), 0); }
1505+
#[test] fn text_text_glyph_splitting() { assert_eq!(render("tests/text/text/glyph-splitting"), 0); }
15051506
#[test] fn text_text_mm_coordinates() { assert_eq!(render("tests/text/text/mm-coordinates"), 0); }
15061507
#[test] fn text_text_nested() { assert_eq!(render("tests/text/text/nested"), 0); }
15071508
#[test] fn text_text_no_coordinates() { assert_eq!(render("tests/text/text/no-coordinates"), 0); }
Loading
Lines changed: 17 additions & 0 deletions
Loading

crates/usvg/src/text/layout.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright 2022 the Resvg Authors
22
// SPDX-License-Identifier: Apache-2.0 OR MIT
33

4-
use std::collections::HashMap;
4+
use std::collections::{HashMap, HashSet};
55
use std::num::NonZeroU16;
66
use std::sync::Arc;
77

@@ -882,6 +882,11 @@ fn process_chunk(
882882
// but some can use `fi` (U+FB01) instead.
883883
// Meaning that during merging we have to overwrite not individual glyphs, but clusters.
884884

885+
// Glyph splitting assigns distinct glyphs to the same index in the original text, we need to
886+
// store previously used indices to make sure we do not re-use the same index while overwriting
887+
// span glyphs.
888+
let mut positions = HashSet::new();
889+
885890
let mut glyphs = Vec::new();
886891
for span in &chunk.spans {
887892
let font = match fonts_cache.get(&span.font) {
@@ -904,17 +909,25 @@ fn process_chunk(
904909
continue;
905910
}
906911

912+
positions.clear();
913+
907914
// Overwrite span's glyphs.
908915
let mut iter = tmp_glyphs.into_iter();
909916
while let Some(new_glyph) = iter.next() {
910917
if !span_contains(span, new_glyph.byte_idx) {
911918
continue;
912919
}
913920

914-
let Some(idx) = glyphs.iter().position(|g| g.byte_idx == new_glyph.byte_idx) else {
921+
let Some(idx) = glyphs
922+
.iter()
923+
.position(|g| g.byte_idx == new_glyph.byte_idx)
924+
.filter(|pos| !positions.contains(pos))
925+
else {
915926
continue;
916927
};
917928

929+
positions.insert(idx);
930+
918931
let prev_cluster_len = glyphs[idx].cluster_len;
919932
if prev_cluster_len < new_glyph.cluster_len {
920933
// If the new font represents the same cluster with fewer glyphs

0 commit comments

Comments
 (0)