Files
FoFreeXit/docs/14-foxit-gap-analysis.md
adminandClaude Fable 5 b09bd7d750 Phase 7: OCR & Convert — Tesseract lớp text ẩn (Việt+Anh), PDF→PNG/TXT/DOCX, Office↔PDF
Engine:
- ocr.rs MỚI: find_tesseract (sidecar như qpdf, env FOFREEXIT_TESSERACT_PATH);
  ocr_page_words (render 300 DPI → tesseract tsv → lọc confidence ≥30 → quy
  đổi pixel→điểm PDF); ocr_add_text_layer — thêm text object render-mode
  INVISIBLE đúng khung từng từ lên CHÍNH trang gốc (searchable PDF chuẩn,
  ảnh không đổi), font Unicode nhúng 1 lần, mặc định vie+eng
- convert.rs MỚI: export_images (PNG mỗi trang theo DPI), export_text,
  export_docx tự viết (gom char boxes thành dòng thị giác → đoạn Word với cỡ
  chữ xấp xỉ + ngắt trang; DOCX = zip STORE tự ghi kèm CRC-32, không thêm
  dependency); find_soffice + office_to_pdf + pdf_to_docx_via_soffice
  (LibreOffice headless, profile riêng -env:UserInstallation)

Tauri + UI (thanh 🔁 Chuyển đổi):
- Commands: ocr_run, convert_images, convert_txt, convert_docx (tự chọn
  LibreOffice nếu có → fallback bộ cơ bản, báo engine đã dùng),
  office_convert, convert_tools_status (UI disable nút + báo thiếu công cụ),
  pick_office_file, pick_save_as
- Nút: OCR (select Việt+Anh/Việt/Anh), Xuất PNG 150 DPI, Xuất TXT, Xuất Word,
  Office→PDF (mở luôn kết quả)

Test (6 mới → engine 73/73 xanh ngoài 2 fixture qpdf Linux):
- OCR tiếng Anh trên PDF-scan giả lập THẬT (xoá text chỉ còn ảnh render):
  extract + search thấy từ, toạ độ lớp ẩn khớp vùng chữ
- OCR tiếng Việt đúng dấu ("Việt Nam đất nước")
- export PNG 3 trang / TXT / DOCX (unzip hệ thống giải được, có ngắt trang)
- LibreOffice round-trip 2 chiều (tự bỏ qua nếu máy không có soffice)

Docs: 19-phase7-summary (+ giới hạn v-sau), 20-phase7-user-tests (C1–C10 kèm
hướng dẫn cài Tesseract/LibreOffice trên Windows), README, roadmap (Phase 7
HOÀN TẤT lõi), 14 (kế tiếp: Phase 8 phát hành).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HgnbPwSoRGNH7LHsDUmRpk
2026-07-16 01:33:57 +00:00

12 KiB
Raw Permalink Blame History

14 — Phân tích khoảng cách so với Foxit: định hướng, công nghệ, tiến độ

Trả lời câu hỏi chiến lược: "Với tham vọng thay thế Foxit, đạt chất lượng tương đương hoặc hơn về trải nghiệm và tính năng, thì định hướng, công nghệ và tiến độ hiện tại có đáp ứng được không? Có vấn đề gì khiến các tính năng không đáp ứng được như Foxit không?"

Cập nhật lần đầu: sau Phase 4 Iteration 2 (giữ font khi sửa text).

1. Kết luận ngắn

Định hướng và công nghệ: ĐÁP ỨNG ĐƯỢC, không có rào cản chết người. Stack Rust + Tauri + PDFium + QPDF đủ trần năng lực để đạt (và ở vài điểm vượt) Foxit cho toàn bộ lộ trình Phase 1–8. Lý do tin được điều đó:

  • PDFium là engine của Chrome — render/parse text/font ngang hàng engine thương mại; Foxit bán chính engine của họ cho Google làm gốc PDFium. Ta đứng trên cùng một nền engine với đối thủ.
  • Những chỗ PDFium không expose đủ (ví dụ annotation appearance, /W width của font Type0, coverage cmap khi sửa text) đều đã xử lý được bằng cách tự thao tác tầng PDF object qua lopdf/ttf-parser (annot.rs, fontmatch.rs) — đúng thứ tự leo thang của FINAL TARGET & RULE, và đã chứng minh trong code chứ không phải giả định.
  • Các bài toán "khó nhất" của một PDF editor (sửa text giữ font, tổ chức trang an toàn, file hỏng/mã hoá) đã có lời giải chạy được + test.

Tiến độ: đúng hướng nhưng mới đi ~40% quãng đường tính năng. Phase 1–4 (viewer, annotate, organize, edit lõi) là "core thay Foxit cho cá nhân" và đã xong ở mức lõi. Phần "đáng tiền doanh nghiệp" (bảo mật/chữ ký số, form, OCR, convert) chưa bắt đầu — đó là các phase 5–7 theo roadmap, không phải rủi ro công nghệ mà là khối lượng việc.

2. Những vấn đề TỪNG khiến tính năng không đạt chuẩn Foxit — và cách đã giải

Vấn đề Hậu quả với người dùng Giải pháp đã áp dụng (đã có test)
PDFium set_text re-encode theo font subset → mất glyph ngoài subset Sửa text là đổi font toàn bộ run (Times → Arial), lệch hẳn tài liệu; chỉ đổi cỡ/màu cũng đổi font; mất đậm/nghiêng Iteration 2: quyết định 3 tầng — (1) sửa TẠI CHỖ giữ nguyên font khi chắc chắn an toàn (charset-subset / base-14 + ASCII / cmap của font bytes phủ đủ); (2) thiếu glyph thật → thay font cùng họ metric-compatible (bảng family + fc-match + alias base-14); (3) mới đến font mặc định. Đổi cỡ = scale matrix, không đụng font
Cỡ chữ hiển thị vs Tf size lẫn lộn khi matrix có scale Đặt cỡ 20 trên text scale ×2 → chữ 40pt (phóng đại kép) Quy ước cỡ = "hiển thị"; in-place scale matrix neo baseline; tạo lại thì quy đổi ngược Tf trước khi áp matrix. Test hồi quy riêng
PDF cắt 1 dòng nhìn thấy thành nhiều text run Double-click chỉ sửa được 1 mảnh chữ — trải nghiệm vụn UI gom run cùng baseline liền kề → sửa cả dòng; commit 1 batch (SetText + Delete) = 1 nấc undo
Ô sửa inline dùng font mặc định của WebView Đang gõ không giống kết quả (không WYSIWYG) Ô sửa nhận đúng family/cỡ/màu/kiểu của run (CSS xấp xỉ theo font_family engine trả về)
Kéo-thả không có phản hồi trực tiếp Cảm giác "lag", không giống Foxit Khung đi theo con trỏ ngay khi kéo; thả mới commit
Annotation FreeText hiển thị sai width tiếng Việt ở viewer khác File ghi ra "xấu" ở Acrobat Tự dựng /W (widths) cho font Type0 nhúng bằng ttf-parser (Phase 2)
PDFium segfault khi Drop object đã remove Crash khi xoá object std::mem::forget object đã tách (đã ghi memory + comment)
File hỏng/mã hoá làm hỏng luồng lưu Mất file người dùng QPDF repair + mã hoá lại an toàn (Phase 3), test riêng

Bài học chung: chưa gặp vấn đề nào không giải được trên stack này. Mọi "thư viện không hỗ trợ" đến nay đều xử lý được bằng đọc source thư viện + thao tác tầng PDF object trực tiếp.

3. Khoảng cách CÒN LẠI so với Foxit + giải pháp cụ thể

Xếp theo mức độ ảnh hưởng trải nghiệm:

3.1 Reflow đoạn văn nhiều dòng ("sửa như Word") — ✅ ĐÃ GIẢI (Iteration 3)

  • Đã làm đúng kế hoạch: UI gom block theo baseline cách đều + giao ngang; engine EditOp::ReflowText tự suy hình học từ matrix/bounds các run, đo width bằng hmtx (ttf-parser), bẻ dòng greedy (\n = ngắt cứng), tạo lại các dòng đúng baseline spacing; font giữ theo thang 4 mức (bytes nhúng → font chuẩn base-14 qua FPDFText_LoadStandardFont → cùng họ → fallback). 3 test integration + 3 unit — xem docs/12 mục Iteration 3.
  • Còn lại của mục này (v2): đoạn justify reflow về căn trái; chưa kerning; khối text xoay chưa reflow theo hướng xoay; đoạn lẫn nhiều font thống nhất theo run neo. Đây là các case Foxit xử lý tốt hơn — nâng khi gặp tài liệu thực tế, không chặn trải nghiệm chính.

3.2 Hiệu năng phiên sửa trên file rất lớn

  • Hiện tại: mô hình "materialize tức thì" — mỗi thao tác lưu toàn bộ tài liệu ra file tạm mới rồi render lại. Đúng tuyệt đối (WYSIWYG thật, undo chắc chắn), nhưng file vài trăm MB sẽ trễ nhìn thấy được ở mỗi thao tác; Foxit chỉnh trong RAM và lưu 1 lần.
  • Giải pháp theo nấc (không phá kiến trúc):
    1. (rẻ) Giữ document PDFium mở suốt phiên sửa trong Tauri state, áp op in-memory, chỉ render lại trang — bỏ chu kỳ load-từ-đĩa mỗi op; save ra temp chỉ để làm mốc undo (có thể async).
    2. (vừa) Undo bằng danh sách op thay vì snapshot file: replay từ gốc khi undo (op rẻ vì in-memory).
    3. (nếu cần) Incremental save của QPDF cho file khổng lồ.
  • Ghi chú: với file văn phòng thông thường (<20MB) mô hình hiện tại đã đủ mượt; nấc 1 chỉ là việc tổ chức lại Tauri command, không đổi engine.

3.3 Lưu file sau sửa: font nhúng full + object rác

  • Hiện tại: khi phải thay font (tầng 2/3), font mới nhúng nguyên cả file TTF (Arial ~1MB); object bị thay vẫn nằm lại file dưới dạng rác (đã tách khỏi trang nên không hiển thị, nhưng tốn dung lượng). Foxit subset font khi lưu và dọn object rác.
  • Giải pháp: bước "Lưu tối ưu" chạy khi save: (1) subset font nhúng theo đúng glyph dùng thật — tự viết được trên ttf-parser (đã dựng /W thủ công ở annot.rs, subset TTF là bài quen thuộc: lọc glyf/loca/cmap/hmtx); (2) chạy qpdf --object-streams=generate (đã có sẵn qpdf.rs) để dọn rác + nén object stream. Đưa vào Phase 5 cùng nhóm "lưu file vững".

3.4 Độ phủ font hệ thống khi thay thế

  • Hiện tại: bảng family phổ biến (Windows ~16 họ, macOS, Linux Liberation + fc-match). Font ngoài bảng → fallback mặc định.
  • Giải pháp: Windows dùng DirectWrite/GDI enumeration (crate font-kit hoặc đọc registry Fonts) để match theo TÊN THẬT của mọi font đã cài thay vì bảng tĩnh; giữ bảng tĩnh làm fallback offline. Việc nhỏ, độc lập, làm khi chạm phải file thực tế đầu tiên không match.

3.5 Các tính năng Foxit chưa bắt đầu (không phải rào cản kỹ thuật)

Nhóm Đường đi đã chốt trong roadmap Rủi ro công nghệ
Bảo mật, redaction, chữ ký số PAdES (Phase 5) ✅ ĐÃ LÀM AES-256+quyền, redaction thật (tỉa ký tự), chữ ký số PKCS#7/CMS + xác thực + phát hiện giả mạo (sign.rs), lưu tối ưu — 62/62 test Đã giải; còn lại timestamp/LTV/PFX/multi-sig (docs/15 mục 6.6)
Form AcroForm (Phase 6) ✅ ĐÃ LÀM Liệt kê/điền/tạo field (lopdf) + flatten (PDFium) + FDF/CSV — 67/67 test (form.rs) Đã giải; còn XFDF, radio-group/list/pushbutton khi tạo (docs/17 mục 6)
OCR (Phase 7) ✅ ĐÃ LÀM Tesseract sidecar → lớp text ẩn khớp toạ độ, Việt+Anh có test đúng dấu (ocr.rs) Đã giải; còn tiền xử lý deskew/denoise, OCR theo vùng
Convert Office↔PDF (Phase 7) ✅ ĐÃ LÀM LibreOffice headless 2 chiều + PDF→DOCX/TXT/PNG tự viết fallback (convert.rs) Đã giải mức "đủ dùng"; PDF→Excel + layout hoàn hảo là follow-up
So sánh PDF, in ấn, installer (Phase 8) render diff đã có nền; Tauri bundler Thấp

4. Đánh giá định hướng công nghệ (giữ hay đổi?)

GIỮ. Đánh giá lại từng lựa chọn sau 4 phase:

  • PDFium qua pdfium-render: đúng đắn. Đã 2 lần tưởng "không làm được" (annotation appearance, giữ font khi sửa) và cả 2 lần đều giải được bằng API sẵn có + tự bù tầng PDF object. Trần năng lực còn xa. Lưu ý vận hành: pdfium-render 0.8.37 có API deprecated đổi nghĩa (set_matrix = apply, không phải replace) — đã ghi chú trong code; khi nâng version cần đọc CHANGELOG cẩn thận.
  • Tauri/WebView UI: đủ mượt cho viewer (lazy render 1000+ trang đã chứng minh ở Phase 1). Điểm cần canh: thao tác chuột mô phỏng trong WebView2 khó test tự động → bù bằng checklist test tay (docs/13) — chấp nhận được.
  • QPDF sidecar: gọn, đúng việc (repair/mã hoá/nén), không cần thay.
  • Rust workspace tách engine/UI: cho phép test engine không cần UI — chính là lý do 50 test tự động chạy được trong CI/Linux dù app nhắm Windows.

Điều KHÔNG nên làm: tự viết engine PDF từ đầu, hay chuyển C++/Qt để "giống Foxit hơn" — chi phí khổng lồ, không giải quyết gap nào ở mục 3.

5. Tiến độ so với đích + thứ tự việc đề xuất

Đã xong (lõi, có test): Viewer ✅ · Annotate ✅ · Organize + lưu vững ✅ · Edit text/object giữ font ✅ (iteration 2) · Reflow đoạn "như Word" ✅ (iteration 3) · Bảo mật iteration 1 ✅ (mã hoá AES-256 + quyền, gỡ mật khẩu, redaction thật kiểm chứng được, xoá metadata — docs/15).

Phase 5 đã HOÀN TẤT (iter 1+2): mã hoá+quyền, gỡ mật khẩu, redaction thật

  • tỉa ký tự, xoá metadata, chữ ký số PKCS#7/PAdES + xác thực, lưu tối ưu.

Phase 6 + 7 đã HOÀN TẤT lõi: form đầy đủ; OCR Việt+Anh; convert PNG/TXT/DOCX + Office↔PDF.

Thứ tự đề xuất tiếp theo, bám giá trị người dùng:

  1. Phase 8 — Hoàn thiện & Phát hành: installer Windows (MSI/winget qua Tauri bundler), in ấn, so sánh 2 PDF, preferences, đa ngôn ngữ UI (Việt/ Anh), tối ưu hiệu năng + bộ test hồi quy đầy đủ.
  2. Song song, rải dần: mở rộng font matching (3.4), nấc 1 hiệu năng phiên sửa (3.2), reflow v2 (justify/xoay — 3.1), nâng chữ ký (timestamp/LTV/ PFX/multi-sig), nâng form (XFDF, radio-group), OCR tiền xử lý ảnh, PDF→Excel.

6. Checklist FINAL TARGET & RULE cho chính tài liệu này

  • Đối chiếu từng tính năng với hành vi Foxit làm chuẩn nghiệm thu.
  • Không đề xuất cắt giảm tính năng nào; mọi gap đều kèm giải pháp cụ thể.
  • Giải pháp theo đúng thứ tự: dùng API thư viện → bù tầng PDF object → tự viết.
  • Mỗi gap có tiêu chí nghiệm thu so với Foxit (mục 3).