Sửa 3 lỗi edit inline vòng 2 (file thật Word-export): hết □ thay dấu cách, dòng không vỡ khi thêm ký tự, ô sửa WYSIWYG đúng cỡ từng dòng
Chẩn đoán □: PDFium FPDFText_LoadFont nạp LẠI font subset rồi SetText map U+0020 ra .notdef (hộp vuông) dù cmap font CÓ glyph space (đã probe cả 2 subtable) — lỗi PDFium với font nạp động, không phải do font. Engine (edit.rs): - Tầng 0 mới của thang font reflow: DÙNG LẠI chính font object gốc trong PDF (PdfFontToken của run neo, không nạp lại) — glyph + charcode map gốc xử lý dấu cách chuẩn, file không phình; chỉ chọn sau khi PROBE ghi/đọc-lại toàn bộ text mới thành công (tạo object nháp → so text → gỡ + mem::forget) - Ghi từng dòng xong ĐỌC LẠI NGAY tự kiểm; PDFium vẫn vứt ký tự → gỡ và ghi lại TỪNG TỪ tự đặt vị trí bằng advance hmtx (không bao giờ ghi glyph dấu cách) — lưới an toàn cuối cho font lạ - Dòng cứng (\n từ ô sửa) NỞ tới +35% bề rộng khối (chặn mép trang) trước khi bẻ lại — hộp text tự nở như Foxit; khối căn giữa nở đều quanh tâm - line_styles theo baseline gốc: dòng cứng thứ i giữ ĐÚNG CỠ dòng gốc thứ i (tiêu đề 22.5/20pt không bị ép cả khối về 1 cỡ) UI (main.js + styles.css): - Ô sửa cả đoạn đổi textarea → contenteditable plaintext-only: MỖI DÒNG GỐC = 1 div đúng font/cỡ/màu/đậm-nghiêng CỦA DÒNG ĐÓ, khối căn giữa hiện căn giữa (nở đều 2 phía quanh tâm), khung bù lệch dọc line-box → chữ trong ô TRÙNG vị trí chữ gốc khi mở; con trỏ đặt đúng điểm đúp (caretRangeFromPoint); Enter xuống dòng, Ctrl+Enter/blur áp dụng, Esc huỷ, dán luôn chữ thuần Kiểm chứng file thật: "HỢP"→"HỢPP" giữ đúng 2 dòng căn giữa cỡ 22.5/20, font BAAAAA+TimesNewRomanPS-BoldMT gốc, dấu cách nguyên vẹn, render khớp bản gốc. Test: +2 hồi quy (giữ cỡ từng dòng; dòng nở không bẻ) → edit 21/21, các suite annot/organize/redact/form xanh. Docs 12 (mục vá vòng 2), 13 (E39–E42). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HgnbPwSoRGNH7LHsDUmRpk
This commit is contained in:
@@ -872,6 +872,113 @@ fn reflow_preserves_centered_alignment() {
|
||||
assert_eq!(checked, 2, "phải kiểm được 2 dòng mới");
|
||||
}
|
||||
|
||||
/// Mỗi DÒNG CỨNG (theo `\n` từ ô sửa) giữ đúng CỠ CHỮ của dòng gốc cùng thứ
|
||||
/// tự — tiêu đề 22/16pt không bị ép cả khối về 1 cỡ.
|
||||
#[test]
|
||||
fn reflow_keeps_per_line_font_sizes() {
|
||||
let pdf = pdfium();
|
||||
let fx = tmp("ff_reflow_sz_fx.pdf");
|
||||
let out = tmp("ff_reflow_sz_out.pdf");
|
||||
let mk = |y: f32, s: &str, size: f32| EditOp::AddText {
|
||||
x: 80.0,
|
||||
y,
|
||||
text: s.into(),
|
||||
font_size: size,
|
||||
color: [0, 0, 0, 255],
|
||||
font_family: None,
|
||||
bold: false,
|
||||
italic: false,
|
||||
};
|
||||
ff_engine::apply_edits(
|
||||
&pdf,
|
||||
&sample(),
|
||||
0,
|
||||
&[mk(500.0, "TIÊU ĐỀ CHÍNH CỠ LỚN", 22.0), mk(474.0, "phụ đề bên dưới cỡ nhỏ", 16.0)],
|
||||
&fx,
|
||||
None,
|
||||
)
|
||||
.expect("fixture 2 cỡ chữ");
|
||||
let idxs: Vec<u16> = ff_engine::list_objects(&pdf, &fx, 0, None)
|
||||
.expect("list fx")
|
||||
.into_iter()
|
||||
.filter(|o| o.text.as_deref().map(|t| t.contains("CỠ LỚN") || t.contains("cỡ nhỏ")).unwrap_or(false))
|
||||
.map(|o| o.index)
|
||||
.collect();
|
||||
assert_eq!(idxs.len(), 2);
|
||||
|
||||
ff_engine::apply_edits(
|
||||
&pdf,
|
||||
&fx,
|
||||
0,
|
||||
&[EditOp::ReflowText { indices: idxs, text: "TIÊU ĐỀ MỚI\nphụ đề mới".into() }],
|
||||
&out,
|
||||
None,
|
||||
)
|
||||
.expect("reflow 2 cỡ");
|
||||
|
||||
let after = ff_engine::list_objects(&pdf, &out, 0, None).expect("list out");
|
||||
let size_of = |needle: &str| {
|
||||
after
|
||||
.iter()
|
||||
.find(|o| o.text.as_deref().map(|t| t.contains(needle)).unwrap_or(false))
|
||||
.unwrap_or_else(|| panic!("thiếu dòng {needle:?}"))
|
||||
.font_size
|
||||
.expect("font_size")
|
||||
};
|
||||
let s1 = size_of("TIÊU ĐỀ MỚI");
|
||||
let s2 = size_of("phụ đề mới");
|
||||
assert!((s1 - 22.0).abs() < 0.6, "dòng 1 phải giữ ~22pt, được {s1}");
|
||||
assert!((s2 - 16.0).abs() < 0.6, "dòng 2 phải giữ ~16pt, được {s2}");
|
||||
}
|
||||
|
||||
/// Dòng cứng chỉ DÀI THÊM CHÚT (≤35% bề rộng khối) thì NỞ ra chứ không bị bẻ
|
||||
/// lại thành 2 dòng (hộp text tự nở như Foxit) — sửa "HỢP"→"HỢPP" phải giữ
|
||||
/// nguyên số dòng. Dấu cách cũng phải sống sót qua đường ghi.
|
||||
#[test]
|
||||
fn reflow_hard_line_grows_without_rewrap() {
|
||||
let pdf = pdfium();
|
||||
let fx = tmp("ff_reflow_grow_fx.pdf");
|
||||
let out = tmp("ff_reflow_grow_out.pdf");
|
||||
ff_engine::apply_edits(
|
||||
&pdf,
|
||||
&sample(),
|
||||
0,
|
||||
&[EditOp::AddText {
|
||||
x: 90.0,
|
||||
y: 520.0,
|
||||
text: "Dòng tiêu đề dài vừa khít khung gốc".into(),
|
||||
font_size: 18.0,
|
||||
color: [0, 0, 0, 255],
|
||||
font_family: None,
|
||||
bold: false,
|
||||
italic: false,
|
||||
}],
|
||||
&fx,
|
||||
None,
|
||||
)
|
||||
.expect("fixture 1 dòng");
|
||||
let idx = find_text_index(&pdf, &fx, "vừa khít");
|
||||
|
||||
let new_text = "Dòng tiêu đề dài vừa khít khung gốc nở";
|
||||
ff_engine::apply_edits(
|
||||
&pdf,
|
||||
&fx,
|
||||
0,
|
||||
&[EditOp::ReflowText { indices: vec![idx], text: new_text.into() }],
|
||||
&out,
|
||||
None,
|
||||
)
|
||||
.expect("reflow nở dòng");
|
||||
|
||||
let new_runs: Vec<_> = ff_engine::list_objects(&pdf, &out, 0, None)
|
||||
.expect("list out")
|
||||
.into_iter()
|
||||
.filter(|o| o.text.as_deref().map(|t| t.contains("tiêu đề") || t.contains("nở thêm")).unwrap_or(false))
|
||||
.collect();
|
||||
assert_eq!(new_runs.len(), 1, "dòng chỉ dài thêm ~20% phải GIỮ 1 dòng: {new_runs:?}");
|
||||
assert_eq!(new_runs[0].text.as_deref(), Some(new_text), "nội dung (gồm dấu cách) phải tròn trịa");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_image_adds_image_object() {
|
||||
let pdf = pdfium();
|
||||
|
||||
Reference in New Issue
Block a user