Panic: debug_assert in SubroutineCollection::new on a CID-keyed font with 256 Font DICTs
まだ誰も着手していません。
評価
調査の方向性
まず subsetter-0.2.6/src/cff/subroutines.rs、特に SubroutineCollection::new と get_handler の経路を確認し、次に FDArray の数がどのように表現されているかを追ってください。提供された reproducer を実行するか、256 個の Font DICT を対象とする回帰テストを追加してください。有効な 256 個の Font DICT を持つフォントがデバッグビルドで panic を起こさなくなり、サポートされない数も panic なしで処理されれば完了です。
索引モデルが issue の本文から書いたものです。
説明
subset panics on a CID-keyed CFF font with 256 Font DICTs, in any build with debug assertions (debug builds, cargo test, fuzzing):
thread 'main' panicked at subsetter-0.2.6/src/cff/subroutines.rs:11:9:
assertion failed: subroutines.len() <= 255
SubroutineCollection::new asserts that there are at most 255 local subroutine lists, one per Font DICT. FDSelect gives each glyph's Font DICT as a Card8 (Technical Note #5176, FDSelect formats 0 and 3), so indices 0 to 255 address 256 Font DICTs, and a font with 256 is well formed. Without debug assertions subset returns Ok. This happens with 0.2.6 and with main (b0adcc1).
To reproduce with subsetter 0.2.6 (default-features = false). The font is built in code for this report:
/// A CFF INDEX of `items`.
fn index(items: &[Vec<u8>]) -> Vec<u8> {
if items.is_empty() {
return vec![0, 0];
}
let mut offsets = vec![1u32];
for item in items {
offsets.push(offsets.last().unwrap() + item.len() as u32);
}
let size = (1..=4u8).find(|&size| *offsets.last().unwrap() < 1 << (8 * size as u32)).unwrap();
let mut out = (items.len() as u16).to_be_bytes().to_vec();
out.push(size);
for offset in offsets {
out.extend(&offset.to_be_bytes()[4 - size as usize..]);
}
items.iter().for_each(|item| out.extend(item));
out
}
/// A DICT integer operand in five bytes.
fn int5(value: i32) -> Vec<u8> {
[&[29u8][..], &value.to_be_bytes()].concat()
}
/// A minimal OpenType font whose CFF table is CID-keyed: one glyph (.notdef, endchar)
/// and an FDArray of `count` Font DICTs, all pointing at one empty Private DICT. FDSelect
/// (format 0) puts the glyph in Font DICT 0.
fn font(count: usize) -> Vec<u8> {
let header = vec![1, 0, 4, 1];
let names = index(&[b"A".to_vec()]);
let strings = index(&[b"Adobe".to_vec(), b"Identity".to_vec()]);
let gsubrs = index(&[]);
let charstrings = index(&[vec![14]]);
let fdselect = vec![0, 0];
let top = |charstrings_at: i32, fdarray_at: i32, fdselect_at: i32| {
[
int5(391), int5(392), int5(0), vec![12, 30], // ROS Adobe-Identity-0
int5(charstrings_at), vec![17],
int5(fdarray_at), vec![12, 36],
int5(fdselect_at), vec![12, 37],
]
.concat()
};
let font_dict = |private_at: i32| [int5(0), int5(private_at), vec![18]].concat();
let top_len = index(&[top(0, 0, 0)]).len();
let fdarray_len = index(&vec![font_dict(0); count]).len();
let charstrings_at = header.len() + names.len() + top_len + strings.len() + gsubrs.len();
let fdselect_at = charstrings_at + charstrings.len();
let fdarray_at = fdselect_at + fdselect.len();
let private_at = fdarray_at + fdarray_len;
let top = index(&[top(charstrings_at as i32, fdarray_at as i32, fdselect_at as i32)]);
let fdarray = index(&vec![font_dict(private_at as i32); count]);
let cff = [header, names, top, strings, gsubrs, charstrings, fdselect, fdarray].concat();
let mut font = b"OTTO".to_vec();
for value in [1u16, 16, 0, 0] {
font.extend(value.to_be_bytes());
}
font.extend(b"CFF ");
for value in [0u32, 28, cff.len() as u32] {
font.extend(value.to_be_bytes());
}
font.extend(cff);
font
}
fn main() {
for count in [255, 256] {
let data = font(count);
let remapper = subsetter::GlyphRemapper::new();
println!("{count} Font DICTs: {:?}", subsetter::subset(&data, 0, &remapper).map(|font| font.len()));
}
}
cargo run prints 255 Font DICTs: Ok(156) and then panics as above; cargo run --release prints Ok(156) for both.
It was found while reviewing how officewright guards against subsetter panics; until it is fixed, our call to subset turns the panic into an error.
A possible fix: assert subroutines.len() <= 256, or, since get_handler takes a u8, return a CFFError for an FDArray of more than 256 Font DICTs instead of asserting.
- 主要言語
- Rust
- スター
- 61
- フォーク
- 16
- PR マージ指標
- 30日以内にマージされた PR はありません
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
typst/subsetter のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 83/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
-
head table bounding box is not recalculated for variable font instances対応中かも @khaledhosny が 16 日前に担当しました。 オープン
難易度 3/5 1〜2日 初心者へのやさしさ 56/100
-
難易度 5/5 1週間以上 初心者へのやさしさ 45/100
-
難易度 3/5 半日 初心者へのやさしさ 67/100
typst/subsetter の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
pierreaubert/gpui-toolkit#114 ·
メンテナーはふだん 1 日以内に返信
-
BibLaTeX `type` keys like `phdthesis`, `mathesis`, `techreport` are printed raw instead of localizedオープン
難易度 2/5 1〜3時間 初心者へのやさしさ 72/100
-
good first issue help wanted
難易度 2/5 1〜3時間 初心者へのやさしさ 80/100
メンテナーはふだん 1 日以内に返信
-
compiler
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
HerringtonDarkholme/hd-lang#1853 ·
メンテナーはふだん 1 日以内に返信