Panic in Bitmap::decode on zero-width EBDT glyphs (SimSun): "chunk size must be non-zero"
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 78/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Quiet
- Tech stack
- rust
- Domain
- computer-graphics
Research direction
Start in strike.rs at Bitmap::decode, especially the BitmapFormat::Alpha branches using chunks. Reproduce with the provided SimSun example through Render::render or Scaler::scale_bitmap, then verify zero-sized glyphs avoid a panic and fall back cleanly while non-empty bitmap decoding remains unchanged.
Written by the indexing model from the issue text.
Description
Summary
Bitmap::decode panics when decoding a bitmap glyph whose width is 0. Such glyphs are valid font data — e.g. blank glyphs in the embedded bitmap strikes (EBLC/EBDT) of CJK fonts like SimSun (simsun.ttc, strikes at ppem 12–17, ~28k glyphs per strike). Any caller using Source::Bitmap(..) / Scaler::scale_bitmap* on such a font will hit the panic while rendering ordinary text.
Environment
- swash 0.2.10 (crates.io)
- Windows 11 Pro (10.0.26200)
- Font:
C:\Windows\Fonts\simsun.ttc, collection index 0 (SimSun as shipped with Windows 11) - Render path:
Render::new(&[Source::Bitmap(StrikeWith::ExactSize)])(we enable this in a bevy_text patch to prefer embedded bitmap strikes)
Panic
thread 'Compute Task Pool (0)' panicked at ...\.cargo\registry\src\...\swash-0.2.10\src\strike.rs:439:36:
chunk size must be non-zero
strike.rs:439 is the 1-bit branch of BitmapFormat::Alpha in Bitmap::decode:
BitmapFormat::Alpha(bits) => match bits {
1 => {
let mut dst_idx = 0;
for row in src.chunks(((w * bits as usize) + 7) / 8) { // w == 0 => chunks(0) panics
The 2-bit and 4-bit branches below it compute the same row stride and panic identically.
Root cause
For w == 0 the row stride ((w * bits) + 7) / 8 evaluates to 0, and slice::chunks panics on zero chunk size by contract. decoded_size() is also 0 for such glyphs, so the earlier target.len() < size check does not filter them out.
Zero-width/zero-height bitmap entries are legitimate font data (blank glyphs); FreeType returns an empty bitmap for these rather than failing, so native apps render such fonts without issues.
Minimal reproduction
// Cargo.toml: swash = "0.2.10"
use swash::{
FontRef,
scale::{Render, ScaleContext, Source, StrikeWith},
zeno::Format,
};
fn main() {
let data = std::fs::read(r"C:\Windows\Fonts\simsun.ttc").unwrap();
let font = FontRef::from_index(&data, 0).unwrap();
let mut cx = ScaleContext::new();
let mut scaler = cx.builder(font).size(12.0).build();
let chmap = font.charmap();
for u in 0u32..=0x10FFFF {
let Some(ch) = char::from_u32(u) else { continue };
let gid = chmap.map(ch);
if gid == 0 { continue; }
// Panics on the first glyph whose EBDT entry has width 0:
let _ = Render::new(&[Source::Bitmap(StrikeWith::ExactSize)])
.format(Format::Alpha)
.render(&mut scaler, gid);
}
println!("no panic");
}
Rendering ordinary text (e.g. a chat message) through the bitmap source panics the same way; we hit this in a live app.
Suggested fix
Treat zero-sized bitmaps as a decode failure so callers can fall back to the next source (this is the one-line guard we run locally):
let w = self.width as usize;
let h = self.height as usize;
+ // Zero-sized bitmaps are valid font data (blank glyphs in some EBDT
+ // strikes, e.g. SimSun). Fail the decode so callers can fall back
+ // to another source instead of panicking on chunks(0).
+ if w == 0 || h == 0 {
+ return false;
+ }
let src = self.data;
With this guard, Render::render_into cleanly falls through to the next source in the chain (e.g. Source::Outline), and Scaler::scale_bitmap returns None — no behavior change for non-empty bitmaps. We verified by scanning SimSun's entire BMP charmap across all strikes (ppem 12–17, ~28,428 bitmap glyphs per strike): stock 0.2.10 panics, patched build renders everything with graceful fallback for the zero-sized entries.
Alternatively, if an empty 0×0 image is considered the more correct representation of a blank glyph, decode could succeed with an empty buffer — either way, panicking on valid font data should be avoided.
- Dominant language
- Rust
- Stars
- 872
- Forks
- 62
- PR merge metrics
- No merged PRs in 30d
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from dfrg/swash
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
Difficulty 3/5 1-2 days Newbie friendliness 50/100
-
Text looks blurry/unsharp/bad unless the font size is absurdly large [Moved upstream from bevy] Open
Difficulty 4/5 3-5 days Newbie friendliness 45/100
-
Difficulty 3/5 1-2 days Newbie friendliness 48/100
-
Difficulty 4/5 3-5 days Newbie friendliness 38/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Eynzof/Hermes-CN-Desktop#610 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
gitbutlerapp/gitbutler#15998 · 1 comment ·
-
bug triage:deciding
Difficulty 1/5 Under an hour Newbie friendliness 88/100
open-telemetry/otel-arrow#4132 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 84/100