Dot 1 "edit chuan moi PDF": CID/Identity-H font, format TUNG CHU, edit OCR, bu metrics, splash, Open-with
Build Windows portable / build (push) Canceled after 0s

1) fontfix v2 (PDF tu Word/InDesign): tong hop bang name/post khi subset
   thieu (OTS bat buoc); font CID/Identity-H KHONG co cmap -> dung cmap
   (3,1) fmt4 tu /ToUnicode x /CIDToGIDMap (Identity: GID=CID; stream 2B/CID
   parse tu descendant font); chan GID >= numGlyphs (maxp). tounicode.rs tra
   FontMapping {code_to_uni, is_cid, cid_to_gid}.

2) Format TUNG CHU trong o sua (chuan Word/Foxit): boi den -> B/I (nut
   toolbar khong cuop focus + Ctrl+B/I). UI tach span theo vung chon, dat
   data-bold/italic-override; commit gui override tung doan (RichSegDto
   bold/italic). Engine: bang style rich key theo (run nguon, override) -
   doi bien the thi lay font BIEN THE THAT cung ho (fontmatch -> builtin ->
   substitute), khong dung lai font goc. Test:
   rich_seg_bold_italic_override_uses_variant_font.

3) Edit file OCR (scan + text an): khoi toan run render-mode INVISIBLE ->
   ve rect TRANG che vung chu cu tren anh + ve text moi HIEN mau den (ca 4
   duong ve: rich chinh/fallback tu, 1-style chinh/fallback). Test:
   ocr_invisible_block_becomes_visible_with_cover.

4) Bu metrics kieu Adobe Sans MM trong o sua: span mang data-pdfw (be rong
   that tren trang); fitEditLinesToPdf them pass letter-spacing de moi nhom
   style chiem DUNG quang goc khi font thay the lech metrics (>2%); kep
   +/-18% co chu.

5) Khoi dong het "do": cua so tao AN (visible:false), UI show() ngay khi
   frontend san sang + man splash che phan nap dau; luoi an toan Rust hien
   cua so sau 4s neu JS chet som.

6) Open with FoFreeXit.exe: co che argv (initial_file) van hoat dong - loi
   nam o registry Applications tro exe CU (ban portable bi di chuyen/ghi
   de) lam Windows hien lai hop chon app. Fix tu chua: moi lan khoi dong
   dang ky lai HKCU (Applications command + ProgID FoFreeXit.pdf +
   OpenWithProgids .pdf) tro exe hien tai, chay nen qua reg.exe.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 16:44:06 +07:00
co-authored by Claude Fable 5
parent a3b00da10d
commit 74f161a627
9 changed files with 839 additions and 64 deletions
+86 -2
View File
@@ -703,6 +703,62 @@ fn edit_list_objects(path: String, page: u16, password: Option<String>) -> Resul
.collect())
}
/// TỰ ĐĂNG KÝ "Open with" mỗi lần khởi động (HKCU — không cần admin): bản
/// portable hay bị di chuyển/ghi đè → entry Applications trong registry trỏ
/// đường dẫn exe CŨ, Windows không khởi chạy được và hiện lại hộp "chọn ứng
/// dụng khác". Ghi lại command trỏ exe HIỆN TẠI + ProgID cho .pdf để
/// FoFreeXit luôn có mặt (và chạy được) trong menu Open with.
#[cfg(windows)]
fn register_open_with() {
use std::os::windows::process::CommandExt;
const CREATE_NO_WINDOW: u32 = 0x0800_0000;
let Ok(exe) = std::env::current_exe() else { return };
let exe_s = exe.to_string_lossy().to_string();
let open_cmd = format!("\"{exe_s}\" \"%1\"");
let icon = format!("\"{exe_s}\",0");
let reg = |args: &[&str]| {
let _ = std::process::Command::new("reg")
.args(args)
.creation_flags(CREATE_NO_WINDOW)
.status();
};
// Entry của picker "Choose another app" (Windows tạo khi user browse exe).
reg(&[
"add",
r"HKCU\Software\Classes\Applications\FoFreeXit.exe\shell\open\command",
"/ve", "/t", "REG_SZ", "/d", &open_cmd, "/f",
]);
reg(&[
"add",
r"HKCU\Software\Classes\Applications\FoFreeXit.exe",
"/v", "FriendlyAppName", "/t", "REG_SZ", "/d", "FoFreeXit", "/f",
]);
// ProgID riêng + gắn vào danh sách Open With của .pdf.
reg(&[
"add",
r"HKCU\Software\Classes\FoFreeXit.pdf\shell\open\command",
"/ve", "/t", "REG_SZ", "/d", &open_cmd, "/f",
]);
reg(&[
"add",
r"HKCU\Software\Classes\FoFreeXit.pdf",
"/ve", "/t", "REG_SZ", "/d", "Tài liệu PDF (FoFreeXit)", "/f",
]);
reg(&[
"add",
r"HKCU\Software\Classes\FoFreeXit.pdf\DefaultIcon",
"/ve", "/t", "REG_SZ", "/d", &icon, "/f",
]);
reg(&[
"add",
r"HKCU\Software\Classes\.pdf\OpenWithProgids",
"/v", "FoFreeXit.pdf", "/t", "REG_SZ", "/d", "", "/f",
]);
}
#[cfg(not(windows))]
fn register_open_with() {}
/// Bytes (base64) của font ĐÓNG GÓI theo app (Noto) — lưới đỡ WYSIWYG cho ô
/// sửa: glyph không có trong subset nhúng (ký tự mới gõ) hay font không
/// nhúng đều rơi về font này, KHÔNG rơi về font hệ điều hành.
@@ -782,12 +838,17 @@ struct EditOpDto {
rich_lines: Option<Vec<Vec<RichSegDto>>>,
}
/// 1 đoạn cùng style trong reflow nhiều-style.
/// 1 đoạn cùng style trong reflow nhiều-style. `bold`/`italic`: override
/// người dùng đặt riêng cho đoạn (bôi đen 1 chữ bấm B/I) — None = theo run.
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct RichSegDto {
text: String,
style: u16,
#[serde(default)]
bold: Option<bool>,
#[serde(default)]
italic: Option<bool>,
}
fn one() -> f32 {
@@ -826,7 +887,12 @@ fn edit_op_from_dto(d: EditOpDto) -> Result<ff_engine::EditOp, String> {
.into_iter()
.map(|line| {
line.into_iter()
.map(|s| ff_engine::RichSeg { text: s.text, style: s.style })
.map(|s| ff_engine::RichSeg {
text: s.text,
style: s.style,
bold: s.bold,
italic: s.italic,
})
.collect()
})
.collect()
@@ -1451,6 +1517,24 @@ fn main() {
}
tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
.setup(|app| {
// Cửa sổ tạo ẨN (tauri.conf.json visible:false) — UI gọi show()
// khi frontend SẴN SÀNG để user không thấy khung trắng đơ lúc
// WebView khởi tạo. Lưới an toàn: JS lỗi sớm thì vẫn hiện sau 4s.
use tauri::Manager;
let handle = app.handle().clone();
std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_secs(4));
if let Some(w) = handle.get_webview_window("main") {
if !w.is_visible().unwrap_or(true) {
let _ = w.show();
}
}
});
// Đăng ký Open-with ở nền — không chặn khởi động.
std::thread::spawn(register_open_with);
Ok(())
})
.invoke_handler(tauri::generate_handler![
open_document,
render_page,
+2 -1
View File
@@ -13,7 +13,8 @@
"label": "main",
"title": "FoFreeXit — PDF Viewer (Phase 1)",
"width": 1100,
"height": 850
"height": 850,
"visible": false
}
],
"security": {