Gốc rễ lỗi font ô sửa: field IPC lệch tên camelCase/snake_case + kéo-thả mở file
Build Windows portable / build (push) Canceled after 0s

Debug trên app thật (WebView2 remote debugging + CDP) với file thật tìm ra
nguyên nhân gốc chuỗi lỗi font: DTO Tauri serialize camelCase (fontBold,
fontFamily, fontSize) nhưng code edit-mode đọc snake_case (font_bold...) →
mọi thuộc tính font trong UI luôn undefined (không đậm, sai family, cỡ mặc
định 12). Đổi hết sang camelCase nhất quán với app.

- Engine: fontmatch::style_from_font_bytes parse sfnt thủ công — OS/2
  usWeightClass/fsSelection, fallback head.macStyle (LibreOffice subset vứt
  OS/2 nhưng head luôn còn; kiểm bằng qpdf trích FontFile2 file thật). 2
  unit test sfnt tự dựng.
- Kéo-thả .pdf từ Explorer vào cửa sổ → mở file (tauri://drag-drop).
- Ẩn khung dòng overlay khi ô sửa mở (nền trong suốt lộ khung gây nhiễu).
- Ignore /*.pdf ở root (file test thật của user không được commit).

Kiểm chứng E2E không cần rebuild: inject hàm mới vào app build-12 đang chạy
qua CDP, mở ô sửa trên file thật + chụp màn hình — tiêu đề 2 dòng bold đúng
cỡ/vị trí nền trong suốt; khối bullet 8 dòng sạch PUA, bullet gốc nguyên
trên trang. Test Node trên dữ liệu run thật từ engine: 6/6 PASS.
Chi tiết: docs/12-phase4-summary.md (Vá vòng 5).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-08 00:50:22 +07:00
co-authored by Claude Fable 5
parent de1ba2cc63
commit 68883b9e75
5 changed files with 173 additions and 34 deletions
+6 -11
View File
@@ -169,19 +169,14 @@ fn text_object_style(t: &PdfPageTextObject) -> (String, bool, bool) {
let mut bold = weight_bold || name_bold;
let mut italic = angle_italic || name_italic;
// Fallback: nhiều file (Word/Chrome xuất subset "ABCDEF+Xyz") không khai
// weight trong descriptor và tên không chứa "Bold"/"Italic" → đọc thẳng
// OS/2 usWeightClass + cờ italic từ bytes font nhúng.
// Fallback: nhiều file không khai weight trong descriptor và tên không
// chứa "Bold"/"Italic" → đọc thẳng từ bytes font nhúng (OS/2 usWeightClass
// / fsSelection, kể cả khi subset đã vứt OS/2 thì còn head.macStyle).
if (!bold || !italic) && t.font().is_embedded().unwrap_or(false) {
if let Ok(bytes) = t.font().data() {
let n = ttf_parser::fonts_in_collection(&bytes).unwrap_or(1).max(1);
for i in 0..n {
if let Ok(face) = ttf_parser::Face::parse(&bytes, i) {
bold = bold || face.weight().to_number() >= 600;
italic = italic || face.is_italic() || face.is_oblique();
break;
}
}
let (data_bold, data_italic) = fontmatch::style_from_font_bytes(&bytes);
bold = bold || data_bold;
italic = italic || data_italic;
}
}
(family, bold, italic)